diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.MdlTab.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.MdlTab.cs index 26fcd1ee..98bd66d6 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.MdlTab.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.MdlTab.cs @@ -14,7 +14,7 @@ public partial class ModEditWindow private readonly ModEditWindow _edit; public MdlFile Mdl { get; private set; } - private List[] _attributes; + private List?[] _attributes; public bool ImportKeepMaterials; public bool ImportKeepAttributes; @@ -290,15 +290,21 @@ public partial class ModEditWindow } /// Create a list of attributes per sub mesh. - private static List[] CreateAttributes(MdlFile mdl) - => mdl.SubMeshes.Select(s => Enumerable.Range(0, 32) - .Where(idx => ((s.AttributeIndexMask >> idx) & 1) == 1) - .Select(idx => mdl.Attributes[idx]) - .ToList() - ).ToArray(); + private static List?[] CreateAttributes(MdlFile mdl) + => mdl.SubMeshes.Select(s => + { + var maxAttribute = 31 - BitOperations.LeadingZeroCount(s.AttributeIndexMask); + // TODO: Research what results in this - it seems to primarily be reproducible on bgparts, is it garbage data, or an alternative usage of the value? + return maxAttribute < mdl.Attributes.Length + ? Enumerable.Range(0, 32) + .Where(idx => ((s.AttributeIndexMask >> idx) & 1) == 1) + .Select(idx => mdl.Attributes[idx]) + .ToList() + : null; + }).ToArray(); /// Obtain the attributes associated with a sub mesh by its index. - public IReadOnlyList GetSubMeshAttributes(int subMeshIndex) + public IReadOnlyList? GetSubMeshAttributes(int subMeshIndex) => _attributes[subMeshIndex]; /// Remove or add attributes from a sub mesh by its index. @@ -308,6 +314,8 @@ public partial class ModEditWindow public void UpdateSubMeshAttribute(int subMeshIndex, string? old, string? @new) { var attributes = _attributes[subMeshIndex]; + if (attributes == null) + return; if (old != null) attributes.Remove(old); @@ -325,6 +333,9 @@ public partial class ModEditWindow foreach (var (attributes, subMeshIndex) in _attributes.WithIndex()) { + if (attributes == null) + continue; + var mask = 0u; foreach (var attribute in attributes) diff --git a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs index cc6493b6..bc763d7d 100644 --- a/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs +++ b/Penumbra/UI/AdvancedWindow/ModEditWindow.Models.cs @@ -326,7 +326,7 @@ public partial class ModEditWindow // Sub meshes for (var subMeshOffset = 0; subMeshOffset < mesh.SubMeshCount; subMeshOffset++) - ret |= DrawSubMeshAttributes(tab, meshIndex, disabled, subMeshOffset); + ret |= DrawSubMeshAttributes(tab, meshIndex, subMeshOffset, disabled); return ret; } @@ -354,7 +354,7 @@ public partial class ModEditWindow return ret; } - private bool DrawSubMeshAttributes(MdlTab tab, int meshIndex, bool disabled, int subMeshOffset) + private bool DrawSubMeshAttributes(MdlTab tab, int meshIndex, int subMeshOffset, bool disabled) { using var _ = ImRaii.PushId(subMeshOffset); @@ -369,6 +369,12 @@ public partial class ModEditWindow var widget = _subMeshAttributeTagWidgets[subMeshIndex]; var attributes = tab.GetSubMeshAttributes(subMeshIndex); + if (attributes == null) + { + attributes = ["invalid attribute data"]; + disabled = true; + } + var tagIndex = widget.Draw(string.Empty, string.Empty, attributes, out var editedAttribute, !disabled); if (tagIndex < 0)