diff --git a/Penumbra.GameData b/Penumbra.GameData index c53e578e..bee73fbe 160000 --- a/Penumbra.GameData +++ b/Penumbra.GameData @@ -1 +1 @@ -Subproject commit c53e578e750d26f83a4e81aca1681c5b01d25a5a +Subproject commit bee73fbe1e263d81067029ad97c8e4a263c88147 diff --git a/Penumbra/Collections/Cache/EqdpCache.cs b/Penumbra/Collections/Cache/EqdpCache.cs index 3937fa72..6b4982a7 100644 --- a/Penumbra/Collections/Cache/EqdpCache.cs +++ b/Penumbra/Collections/Cache/EqdpCache.cs @@ -45,7 +45,7 @@ public readonly struct EqdpCache : IDisposable foreach (var file in _eqdpFiles.OfType()) { var relevant = CharacterUtility.RelevantIndices[file.Index.Value]; - file.Reset(_eqdpManipulations.Where(m => m.FileIndex() == relevant).Select(m => (SetId)m.SetId)); + file.Reset(_eqdpManipulations.Where(m => m.FileIndex() == relevant).Select(m => (PrimaryId)m.SetId)); } _eqdpManipulations.Clear(); diff --git a/Penumbra/Collections/Cache/EstCache.cs b/Penumbra/Collections/Cache/EstCache.cs index 9e2cdef9..2552cd4a 100644 --- a/Penumbra/Collections/Cache/EstCache.cs +++ b/Penumbra/Collections/Cache/EstCache.cs @@ -74,12 +74,12 @@ public struct EstCache : IDisposable }; } - internal ushort GetEstEntry(MetaFileManager manager, EstManipulation.EstType type, GenderRace genderRace, SetId setId) + internal ushort GetEstEntry(MetaFileManager manager, EstManipulation.EstType type, GenderRace genderRace, PrimaryId primaryId) { var file = GetEstFile(type); return file != null - ? file[genderRace, setId.Id] - : EstFile.GetDefault(manager, type, genderRace, setId); + ? file[genderRace, primaryId.Id] + : EstFile.GetDefault(manager, type, genderRace, primaryId); } public void Reset() diff --git a/Penumbra/Collections/Cache/MetaCache.cs b/Penumbra/Collections/Cache/MetaCache.cs index d5acf249..650bd536 100644 --- a/Penumbra/Collections/Cache/MetaCache.cs +++ b/Penumbra/Collections/Cache/MetaCache.cs @@ -187,17 +187,17 @@ public class MetaCache : IDisposable, IEnumerable _imcCache.GetImcFile(path, out file); - internal EqdpEntry GetEqdpEntry(GenderRace race, bool accessory, SetId setId) + internal EqdpEntry GetEqdpEntry(GenderRace race, bool accessory, PrimaryId primaryId) { var eqdpFile = _eqdpCache.EqdpFile(race, accessory); if (eqdpFile != null) - return setId.Id < eqdpFile.Count ? eqdpFile[setId] : default; + return primaryId.Id < eqdpFile.Count ? eqdpFile[primaryId] : default; else - return Meta.Files.ExpandedEqdpFile.GetDefault(_manager, race, accessory, setId); + return Meta.Files.ExpandedEqdpFile.GetDefault(_manager, race, accessory, primaryId); } - internal ushort GetEstEntry(EstManipulation.EstType type, GenderRace genderRace, SetId setId) - => _estCache.GetEstEntry(_manager, type, genderRace, setId); + internal ushort GetEstEntry(EstManipulation.EstType type, GenderRace genderRace, PrimaryId primaryId) + => _estCache.GetEstEntry(_manager, type, genderRace, primaryId); /// Use this when CharacterUtility becomes ready. private void ApplyStoredManipulations() diff --git a/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs b/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs index 1c9dfaa1..f2059253 100644 --- a/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs +++ b/Penumbra/Interop/ResourceTree/ResolveContext.PathResolution.cs @@ -37,7 +37,7 @@ internal partial record ResolveContext private unsafe GenderRace ResolveModelRaceCode() => ResolveEqdpRaceCode(Slot, Equipment.Set); - private unsafe GenderRace ResolveEqdpRaceCode(EquipSlot slot, SetId setId) + private unsafe GenderRace ResolveEqdpRaceCode(EquipSlot slot, PrimaryId primaryId) { var slotIndex = slot.ToIndex(); if (slotIndex >= 10 || ModelType != ModelType.Human) @@ -55,7 +55,7 @@ internal partial record ResolveContext if (metaCache == null) return GenderRace.MidlanderMale; - var entry = metaCache.GetEqdpEntry(characterRaceCode, accessory, setId); + var entry = metaCache.GetEqdpEntry(characterRaceCode, accessory, primaryId); if (entry.ToBits(slot).Item2) return characterRaceCode; @@ -63,7 +63,7 @@ internal partial record ResolveContext if (fallbackRaceCode == GenderRace.MidlanderMale) return GenderRace.MidlanderMale; - entry = metaCache.GetEqdpEntry(fallbackRaceCode, accessory, setId); + entry = metaCache.GetEqdpEntry(fallbackRaceCode, accessory, primaryId); if (entry.ToBits(slot).Item2) return fallbackRaceCode; @@ -229,7 +229,7 @@ internal partial record ResolveContext return Utf8GamePath.FromString(path, out var gamePath) ? gamePath : Utf8GamePath.Empty; } - private unsafe (GenderRace RaceCode, string Slot, SetId Set) ResolveHumanSkeletonData(uint partialSkeletonIndex) + private unsafe (GenderRace RaceCode, string Slot, PrimaryId Set) ResolveHumanSkeletonData(uint partialSkeletonIndex) { var human = (Human*)CharacterBase.Value; var characterRaceCode = (GenderRace)human->RaceSexId; @@ -262,17 +262,17 @@ internal partial record ResolveContext } } - private unsafe (GenderRace RaceCode, string Slot, SetId Set) ResolveHumanEquipmentSkeletonData(EquipSlot slot, EstManipulation.EstType type) + private unsafe (GenderRace RaceCode, string Slot, PrimaryId Set) ResolveHumanEquipmentSkeletonData(EquipSlot slot, EstManipulation.EstType type) { var human = (Human*)CharacterBase.Value; var equipment = ((CharacterArmor*)&human->Head)[slot.ToIndex()]; return ResolveHumanExtraSkeletonData(ResolveEqdpRaceCode(slot, equipment.Set), type, equipment.Set); } - private unsafe (GenderRace RaceCode, string Slot, SetId Set) ResolveHumanExtraSkeletonData(GenderRace raceCode, EstManipulation.EstType type, SetId set) + private unsafe (GenderRace RaceCode, string Slot, PrimaryId Set) ResolveHumanExtraSkeletonData(GenderRace raceCode, EstManipulation.EstType type, PrimaryId primary) { var metaCache = Global.Collection.MetaCache; - var skeletonSet = metaCache == null ? default : metaCache.GetEstEntry(type, raceCode, set); + var skeletonSet = metaCache == null ? default : metaCache.GetEstEntry(type, raceCode, primary); return (raceCode, EstManipulation.ToName(type), skeletonSet); } diff --git a/Penumbra/Interop/ResourceTree/ResolveContext.cs b/Penumbra/Interop/ResourceTree/ResolveContext.cs index d03ee508..431f1ac0 100644 --- a/Penumbra/Interop/ResourceTree/ResolveContext.cs +++ b/Penumbra/Interop/ResourceTree/ResolveContext.cs @@ -25,8 +25,8 @@ internal record GlobalResolveContext(ObjectIdentification Identifier, ModCollect public readonly Dictionary<(Utf8GamePath, nint), ResourceNode> Nodes = new(128); public unsafe ResolveContext CreateContext(CharacterBase* characterBase, uint slotIndex = 0xFFFFFFFFu, - EquipSlot slot = EquipSlot.Unknown, CharacterArmor equipment = default, WeaponType weaponType = default) - => new(this, characterBase, slotIndex, slot, equipment, weaponType); + EquipSlot slot = EquipSlot.Unknown, CharacterArmor equipment = default, SecondaryId secondaryId = default) + => new(this, characterBase, slotIndex, slot, equipment, secondaryId); } internal partial record ResolveContext( @@ -35,7 +35,7 @@ internal partial record ResolveContext( uint SlotIndex, EquipSlot Slot, CharacterArmor Equipment, - WeaponType WeaponType) + SecondaryId SecondaryId) { private static readonly ByteString ShpkPrefix = ByteString.FromSpanUnsafe("shader/sm5/shpk"u8, true, true, true); diff --git a/Penumbra/Meta/Files/EqdpFile.cs b/Penumbra/Meta/Files/EqdpFile.cs index 8a99225f..c76c4efd 100644 --- a/Penumbra/Meta/Files/EqdpFile.cs +++ b/Penumbra/Meta/Files/EqdpFile.cs @@ -38,7 +38,7 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile public int Count => (Length - DataOffset) / EqdpEntrySize; - public EqdpEntry this[SetId id] + public EqdpEntry this[PrimaryId id] { get { @@ -79,7 +79,7 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile MemoryUtility.MemSet(myDataPtr, 0, Length - (int)((byte*)myDataPtr - Data)); } - public void Reset(IEnumerable entries) + public void Reset(IEnumerable entries) { foreach (var entry in entries) this[entry] = GetDefault(entry); @@ -101,18 +101,18 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile Reset(); } - public EqdpEntry GetDefault(SetId setId) - => GetDefault(Manager, Index, setId); + public EqdpEntry GetDefault(PrimaryId primaryId) + => GetDefault(Manager, Index, primaryId); - public static EqdpEntry GetDefault(MetaFileManager manager, CharacterUtility.InternalIndex idx, SetId setId) - => GetDefault((byte*)manager.CharacterUtility.DefaultResource(idx).Address, setId); + public static EqdpEntry GetDefault(MetaFileManager manager, CharacterUtility.InternalIndex idx, PrimaryId primaryId) + => GetDefault((byte*)manager.CharacterUtility.DefaultResource(idx).Address, primaryId); - public static EqdpEntry GetDefault(byte* data, SetId setId) + public static EqdpEntry GetDefault(byte* data, PrimaryId primaryId) { var blockSize = *(ushort*)(data + IdentifierSize); var totalBlockCount = *(ushort*)(data + IdentifierSize + 2); - var blockIdx = setId.Id / blockSize; + var blockIdx = primaryId.Id / blockSize; if (blockIdx >= totalBlockCount) return 0; @@ -121,9 +121,9 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile return 0; var blockData = (ushort*)(data + IdentifierSize + PreambleSize + totalBlockCount * 2 + block * 2); - return (EqdpEntry)(*(blockData + setId.Id % blockSize)); + return (EqdpEntry)(*(blockData + primaryId.Id % blockSize)); } - public static EqdpEntry GetDefault(MetaFileManager manager, GenderRace raceCode, bool accessory, SetId setId) - => GetDefault(manager, CharacterUtility.ReverseIndices[(int)CharacterUtilityData.EqdpIdx(raceCode, accessory)], setId); + public static EqdpEntry GetDefault(MetaFileManager manager, GenderRace raceCode, bool accessory, PrimaryId primaryId) + => GetDefault(manager, CharacterUtility.ReverseIndices[(int)CharacterUtilityData.EqdpIdx(raceCode, accessory)], primaryId); } diff --git a/Penumbra/Meta/Files/EqpGmpFile.cs b/Penumbra/Meta/Files/EqpGmpFile.cs index 6e9fd010..97f57703 100644 --- a/Penumbra/Meta/Files/EqpGmpFile.cs +++ b/Penumbra/Meta/Files/EqpGmpFile.cs @@ -24,7 +24,7 @@ public unsafe class ExpandedEqpGmpBase : MetaBaseFile public ulong ControlBlock => *(ulong*)Data; - protected ulong GetInternal(SetId idx) + protected ulong GetInternal(PrimaryId idx) { return idx.Id switch { @@ -34,7 +34,7 @@ public unsafe class ExpandedEqpGmpBase : MetaBaseFile }; } - protected void SetInternal(SetId idx, ulong value) + protected void SetInternal(PrimaryId idx, ulong value) { idx = idx.Id switch { @@ -81,13 +81,13 @@ public unsafe class ExpandedEqpGmpBase : MetaBaseFile Reset(); } - protected static ulong GetDefaultInternal(MetaFileManager manager, CharacterUtility.InternalIndex fileIndex, SetId setId, ulong def) + protected static ulong GetDefaultInternal(MetaFileManager manager, CharacterUtility.InternalIndex fileIndex, PrimaryId primaryId, ulong def) { var data = (byte*)manager.CharacterUtility.DefaultResource(fileIndex).Address; - if (setId == 0) - setId = 1; + if (primaryId == 0) + primaryId = 1; - var blockIdx = setId.Id / BlockSize; + var blockIdx = primaryId.Id / BlockSize; if (blockIdx >= NumBlocks) return def; @@ -97,7 +97,7 @@ public unsafe class ExpandedEqpGmpBase : MetaBaseFile return def; var count = BitOperations.PopCount(control & (blockBit - 1)); - var idx = setId.Id % BlockSize; + var idx = primaryId.Id % BlockSize; var ptr = (ulong*)data + BlockSize * count + idx; return *ptr; } @@ -112,15 +112,15 @@ public sealed class ExpandedEqpFile : ExpandedEqpGmpBase, IEnumerable : base(manager, false) { } - public EqpEntry this[SetId idx] + public EqpEntry this[PrimaryId idx] { get => (EqpEntry)GetInternal(idx); set => SetInternal(idx, (ulong)value); } - public static EqpEntry GetDefault(MetaFileManager manager, SetId setIdx) - => (EqpEntry)GetDefaultInternal(manager, InternalIndex, setIdx, (ulong)Eqp.DefaultEntry); + public static EqpEntry GetDefault(MetaFileManager manager, PrimaryId primaryIdx) + => (EqpEntry)GetDefaultInternal(manager, InternalIndex, primaryIdx, (ulong)Eqp.DefaultEntry); protected override unsafe void SetEmptyBlock(int idx) { @@ -130,7 +130,7 @@ public sealed class ExpandedEqpFile : ExpandedEqpGmpBase, IEnumerable *ptr = (ulong)Eqp.DefaultEntry; } - public void Reset(IEnumerable entries) + public void Reset(IEnumerable entries) { foreach (var entry in entries) this[entry] = GetDefault(Manager, entry); @@ -155,16 +155,16 @@ public sealed class ExpandedGmpFile : ExpandedEqpGmpBase, IEnumerable : base(manager, true) { } - public GmpEntry this[SetId idx] + public GmpEntry this[PrimaryId idx] { get => (GmpEntry)GetInternal(idx); set => SetInternal(idx, (ulong)value); } - public static GmpEntry GetDefault(MetaFileManager manager, SetId setIdx) - => (GmpEntry)GetDefaultInternal(manager, InternalIndex, setIdx, (ulong)GmpEntry.Default); + public static GmpEntry GetDefault(MetaFileManager manager, PrimaryId primaryIdx) + => (GmpEntry)GetDefaultInternal(manager, InternalIndex, primaryIdx, (ulong)GmpEntry.Default); - public void Reset(IEnumerable entries) + public void Reset(IEnumerable entries) { foreach (var entry in entries) this[entry] = GetDefault(Manager, entry); diff --git a/Penumbra/Meta/Files/EstFile.cs b/Penumbra/Meta/Files/EstFile.cs index 2c7409b4..af441b22 100644 --- a/Penumbra/Meta/Files/EstFile.cs +++ b/Penumbra/Meta/Files/EstFile.cs @@ -167,21 +167,21 @@ public sealed unsafe class EstFile : MetaBaseFile public ushort GetDefault(GenderRace genderRace, ushort setId) => GetDefault(Manager, Index, genderRace, setId); - public static ushort GetDefault(MetaFileManager manager, CharacterUtility.InternalIndex index, GenderRace genderRace, SetId setId) + public static ushort GetDefault(MetaFileManager manager, CharacterUtility.InternalIndex index, GenderRace genderRace, PrimaryId primaryId) { var data = (byte*)manager.CharacterUtility.DefaultResource(index).Address; var count = *(int*)data; var span = new ReadOnlySpan(data + 4, count); - var (idx, found) = FindEntry(span, genderRace, setId.Id); + var (idx, found) = FindEntry(span, genderRace, primaryId.Id); if (!found) return 0; return *(ushort*)(data + 4 + count * EntryDescSize + idx * EntrySize); } - public static ushort GetDefault(MetaFileManager manager, MetaIndex metaIndex, GenderRace genderRace, SetId setId) - => GetDefault(manager, CharacterUtility.ReverseIndices[(int)metaIndex], genderRace, setId); + public static ushort GetDefault(MetaFileManager manager, MetaIndex metaIndex, GenderRace genderRace, PrimaryId primaryId) + => GetDefault(manager, CharacterUtility.ReverseIndices[(int)metaIndex], genderRace, primaryId); - public static ushort GetDefault(MetaFileManager manager, EstManipulation.EstType estType, GenderRace genderRace, SetId setId) - => GetDefault(manager, (MetaIndex)estType, genderRace, setId); + public static ushort GetDefault(MetaFileManager manager, EstManipulation.EstType estType, GenderRace genderRace, PrimaryId primaryId) + => GetDefault(manager, (MetaIndex)estType, genderRace, primaryId); } diff --git a/Penumbra/Meta/Manipulations/EqdpManipulation.cs b/Penumbra/Meta/Manipulations/EqdpManipulation.cs index df7ed2e4..0426dfce 100644 --- a/Penumbra/Meta/Manipulations/EqdpManipulation.cs +++ b/Penumbra/Meta/Manipulations/EqdpManipulation.cs @@ -18,13 +18,13 @@ public readonly struct EqdpManipulation : IMetaManipulation [JsonConverter(typeof(StringEnumConverter))] public ModelRace Race { get; private init; } - public SetId SetId { get; private init; } + public PrimaryId SetId { get; private init; } [JsonConverter(typeof(StringEnumConverter))] public EquipSlot Slot { get; private init; } [JsonConstructor] - public EqdpManipulation(EqdpEntry entry, EquipSlot slot, Gender gender, ModelRace race, SetId setId) + public EqdpManipulation(EqdpEntry entry, EquipSlot slot, Gender gender, ModelRace race, PrimaryId setId) { Gender = gender; Race = race; diff --git a/Penumbra/Meta/Manipulations/EqpManipulation.cs b/Penumbra/Meta/Manipulations/EqpManipulation.cs index 4373e8e9..d59938b6 100644 --- a/Penumbra/Meta/Manipulations/EqpManipulation.cs +++ b/Penumbra/Meta/Manipulations/EqpManipulation.cs @@ -15,13 +15,13 @@ public readonly struct EqpManipulation : IMetaManipulation [JsonConverter(typeof(ForceNumericFlagEnumConverter))] public EqpEntry Entry { get; private init; } - public SetId SetId { get; private init; } + public PrimaryId SetId { get; private init; } [JsonConverter(typeof(StringEnumConverter))] public EquipSlot Slot { get; private init; } [JsonConstructor] - public EqpManipulation(EqpEntry entry, EquipSlot slot, SetId setId) + public EqpManipulation(EqpEntry entry, EquipSlot slot, PrimaryId setId) { Slot = slot; SetId = setId; diff --git a/Penumbra/Meta/Manipulations/EstManipulation.cs b/Penumbra/Meta/Manipulations/EstManipulation.cs index 455c39ff..d3c92ad3 100644 --- a/Penumbra/Meta/Manipulations/EstManipulation.cs +++ b/Penumbra/Meta/Manipulations/EstManipulation.cs @@ -36,13 +36,14 @@ public readonly struct EstManipulation : IMetaManipulation [JsonConverter(typeof(StringEnumConverter))] public ModelRace Race { get; private init; } - public SetId SetId { get; private init; } + public PrimaryId SetId { get; private init; } [JsonConverter(typeof(StringEnumConverter))] public EstType Slot { get; private init; } + [JsonConstructor] - public EstManipulation(Gender gender, ModelRace race, EstType slot, SetId setId, ushort entry) + public EstManipulation(Gender gender, ModelRace race, EstType slot, PrimaryId setId, ushort entry) { Entry = entry; Gender = gender; diff --git a/Penumbra/Meta/Manipulations/GmpManipulation.cs b/Penumbra/Meta/Manipulations/GmpManipulation.cs index 928b6f55..ee58295d 100644 --- a/Penumbra/Meta/Manipulations/GmpManipulation.cs +++ b/Penumbra/Meta/Manipulations/GmpManipulation.cs @@ -8,11 +8,11 @@ namespace Penumbra.Meta.Manipulations; [StructLayout(LayoutKind.Sequential, Pack = 1)] public readonly struct GmpManipulation : IMetaManipulation { - public GmpEntry Entry { get; private init; } - public SetId SetId { get; private init; } + public GmpEntry Entry { get; private init; } + public PrimaryId SetId { get; private init; } [JsonConstructor] - public GmpManipulation(GmpEntry entry, SetId setId) + public GmpManipulation(GmpEntry entry, PrimaryId setId) { Entry = entry; SetId = setId; diff --git a/Penumbra/Meta/Manipulations/ImcManipulation.cs b/Penumbra/Meta/Manipulations/ImcManipulation.cs index 391daacc..a1c4b5bf 100644 --- a/Penumbra/Meta/Manipulations/ImcManipulation.cs +++ b/Penumbra/Meta/Manipulations/ImcManipulation.cs @@ -12,10 +12,10 @@ namespace Penumbra.Meta.Manipulations; [StructLayout(LayoutKind.Sequential, Pack = 1)] public readonly struct ImcManipulation : IMetaManipulation { - public ImcEntry Entry { get; private init; } - public SetId PrimaryId { get; private init; } - public SetId SecondaryId { get; private init; } - public Variant Variant { get; private init; } + public ImcEntry Entry { get; private init; } + public PrimaryId PrimaryId { get; private init; } + public PrimaryId SecondaryId { get; private init; } + public Variant Variant { get; private init; } [JsonConverter(typeof(StringEnumConverter))] public ObjectType ObjectType { get; private init; } @@ -26,7 +26,7 @@ public readonly struct ImcManipulation : IMetaManipulation [JsonConverter(typeof(StringEnumConverter))] public BodySlot BodySlot { get; private init; } - public ImcManipulation(EquipSlot equipSlot, ushort variant, SetId primaryId, ImcEntry entry) + public ImcManipulation(EquipSlot equipSlot, ushort variant, PrimaryId primaryId, ImcEntry entry) { Entry = entry; PrimaryId = primaryId; @@ -42,7 +42,7 @@ public readonly struct ImcManipulation : IMetaManipulation // so we change the unused value to something nonsensical in that case, just so they do not compare equal, // and clamp the variant to 255. [JsonConstructor] - internal ImcManipulation(ObjectType objectType, BodySlot bodySlot, SetId primaryId, SetId secondaryId, ushort variant, + internal ImcManipulation(ObjectType objectType, BodySlot bodySlot, PrimaryId primaryId, PrimaryId secondaryId, ushort variant, EquipSlot equipSlot, ImcEntry entry) { Entry = entry; diff --git a/Penumbra/Meta/Manipulations/MetaManipulation.cs b/Penumbra/Meta/Manipulations/MetaManipulation.cs index 94b45cdf..e057d1a4 100644 --- a/Penumbra/Meta/Manipulations/MetaManipulation.cs +++ b/Penumbra/Meta/Manipulations/MetaManipulation.cs @@ -269,5 +269,23 @@ public readonly struct MetaManipulation : IEquatable, ICompara Type.Gmp => $"{Gmp.Entry.Value}", Type.Rsp => $"{Rsp.Entry}", _ => string.Empty, - }; + }; + + public static bool operator ==(MetaManipulation left, MetaManipulation right) + => left.Equals(right); + + public static bool operator !=(MetaManipulation left, MetaManipulation right) + => !(left == right); + + public static bool operator <(MetaManipulation left, MetaManipulation right) + => left.CompareTo(right) < 0; + + public static bool operator <=(MetaManipulation left, MetaManipulation right) + => left.CompareTo(right) <= 0; + + public static bool operator >(MetaManipulation left, MetaManipulation right) + => left.CompareTo(right) > 0; + + public static bool operator >=(MetaManipulation left, MetaManipulation right) + => left.CompareTo(right) >= 0; } diff --git a/Penumbra/Mods/ItemSwap/CustomizationSwap.cs b/Penumbra/Mods/ItemSwap/CustomizationSwap.cs index 36237e47..fc32df0c 100644 --- a/Penumbra/Mods/ItemSwap/CustomizationSwap.cs +++ b/Penumbra/Mods/ItemSwap/CustomizationSwap.cs @@ -12,7 +12,7 @@ public static class CustomizationSwap { /// The .mdl file for customizations is unique per racecode, slot and id, thus the .mdl redirection itself is independent of the mode. public static FileSwap CreateMdl(MetaFileManager manager, Func redirections, BodySlot slot, GenderRace race, - SetId idFrom, SetId idTo) + PrimaryId idFrom, PrimaryId idTo) { if (idFrom.Id > byte.MaxValue) throw new Exception($"The Customization ID {idFrom} is too large for {slot}."); @@ -43,7 +43,7 @@ public static class CustomizationSwap } public static FileSwap CreateMtrl(MetaFileManager manager, Func redirections, BodySlot slot, GenderRace race, - SetId idFrom, SetId idTo, byte variant, + PrimaryId idFrom, PrimaryId idTo, byte variant, ref string fileName, ref bool dataWasChanged) { variant = slot is BodySlot.Face or BodySlot.Ear ? byte.MaxValue : variant; @@ -79,7 +79,7 @@ public static class CustomizationSwap } public static FileSwap CreateTex(MetaFileManager manager, Func redirections, BodySlot slot, GenderRace race, - SetId idFrom, ref MtrlFile.Texture texture, + PrimaryId idFrom, ref MtrlFile.Texture texture, ref bool dataWasChanged) { var path = texture.Path; diff --git a/Penumbra/Mods/ItemSwap/EquipmentSwap.cs b/Penumbra/Mods/ItemSwap/EquipmentSwap.cs index d634349e..f7f82a59 100644 --- a/Penumbra/Mods/ItemSwap/EquipmentSwap.cs +++ b/Penumbra/Mods/ItemSwap/EquipmentSwap.cs @@ -185,13 +185,13 @@ public static class EquipmentSwap } public static MetaSwap? CreateEqdp(MetaFileManager manager, Func redirections, - Func manips, EquipSlot slot, GenderRace gr, SetId idFrom, - SetId idTo, byte mtrlTo) + Func manips, EquipSlot slot, GenderRace gr, PrimaryId idFrom, + PrimaryId idTo, byte mtrlTo) => CreateEqdp(manager, redirections, manips, slot, slot, gr, idFrom, idTo, mtrlTo); public static MetaSwap? CreateEqdp(MetaFileManager manager, Func redirections, - Func manips, EquipSlot slotFrom, EquipSlot slotTo, GenderRace gr, SetId idFrom, - SetId idTo, byte mtrlTo) + Func manips, EquipSlot slotFrom, EquipSlot slotTo, GenderRace gr, PrimaryId idFrom, + PrimaryId idTo, byte mtrlTo) { var (gender, race) = gr.Split(); var eqdpFrom = new EqdpManipulation(ExpandedEqdpFile.GetDefault(manager, gr, slotFrom.IsAccessory(), idFrom), slotFrom, gender, @@ -214,11 +214,11 @@ public static class EquipmentSwap } public static FileSwap CreateMdl(MetaFileManager manager, Func redirections, EquipSlot slot, GenderRace gr, - SetId idFrom, SetId idTo, byte mtrlTo) + PrimaryId idFrom, PrimaryId idTo, byte mtrlTo) => CreateMdl(manager, redirections, slot, slot, gr, idFrom, idTo, mtrlTo); public static FileSwap CreateMdl(MetaFileManager manager, Func redirections, EquipSlot slotFrom, EquipSlot slotTo, - GenderRace gr, SetId idFrom, SetId idTo, byte mtrlTo) + GenderRace gr, PrimaryId idFrom, PrimaryId idTo, byte mtrlTo) { var mdlPathFrom = slotFrom.IsAccessory() ? GamePaths.Accessory.Mdl.Path(idFrom, gr, slotFrom) @@ -236,7 +236,7 @@ public static class EquipmentSwap return mdl; } - private static void LookupItem(EquipItem i, out EquipSlot slot, out SetId modelId, out Variant variant) + private static void LookupItem(EquipItem i, out EquipSlot slot, out PrimaryId modelId, out Variant variant) { slot = i.Type.ToSlot(); if (!slot.IsEquipmentPiece()) @@ -247,7 +247,7 @@ public static class EquipmentSwap } private static (ImcFile, Variant[], EquipItem[]) GetVariants(MetaFileManager manager, ObjectIdentification identifier, EquipSlot slotFrom, - SetId idFrom, SetId idTo, Variant variantFrom) + PrimaryId idFrom, PrimaryId idTo, Variant variantFrom) { var entry = new ImcManipulation(slotFrom, variantFrom.Id, idFrom, default); var imc = new ImcFile(manager, entry); @@ -270,8 +270,8 @@ public static class EquipmentSwap return (imc, variants, items); } - public static MetaSwap? CreateGmp(MetaFileManager manager, Func manips, EquipSlot slot, SetId idFrom, - SetId idTo) + public static MetaSwap? CreateGmp(MetaFileManager manager, Func manips, EquipSlot slot, PrimaryId idFrom, + PrimaryId idTo) { if (slot is not EquipSlot.Head) return null; @@ -283,12 +283,12 @@ public static class EquipmentSwap public static MetaSwap CreateImc(MetaFileManager manager, Func redirections, Func manips, EquipSlot slot, - SetId idFrom, SetId idTo, Variant variantFrom, Variant variantTo, ImcFile imcFileFrom, ImcFile imcFileTo) + PrimaryId idFrom, PrimaryId idTo, Variant variantFrom, Variant variantTo, ImcFile imcFileFrom, ImcFile imcFileTo) => CreateImc(manager, redirections, manips, slot, slot, idFrom, idTo, variantFrom, variantTo, imcFileFrom, imcFileTo); public static MetaSwap CreateImc(MetaFileManager manager, Func redirections, Func manips, - EquipSlot slotFrom, EquipSlot slotTo, SetId idFrom, SetId idTo, + EquipSlot slotFrom, EquipSlot slotTo, PrimaryId idFrom, PrimaryId idTo, Variant variantFrom, Variant variantTo, ImcFile imcFileFrom, ImcFile imcFileTo) { var entryFrom = imcFileFrom.GetEntry(ImcFile.PartIndex(slotFrom), variantFrom); @@ -322,7 +322,7 @@ public static class EquipmentSwap // Example: Abyssos Helm / Body - public static FileSwap? CreateAvfx(MetaFileManager manager, Func redirections, SetId idFrom, SetId idTo, byte vfxId) + public static FileSwap? CreateAvfx(MetaFileManager manager, Func redirections, PrimaryId idFrom, PrimaryId idTo, byte vfxId) { if (vfxId == 0) return null; @@ -340,8 +340,8 @@ public static class EquipmentSwap return avfx; } - public static MetaSwap? CreateEqp(MetaFileManager manager, Func manips, EquipSlot slot, SetId idFrom, - SetId idTo) + public static MetaSwap? CreateEqp(MetaFileManager manager, Func manips, EquipSlot slot, PrimaryId idFrom, + PrimaryId idTo) { if (slot.IsAccessory()) return null; @@ -353,13 +353,13 @@ public static class EquipmentSwap return new MetaSwap(manips, eqpFrom, eqpTo); } - public static FileSwap? CreateMtrl(MetaFileManager manager, Func redirections, EquipSlot slot, SetId idFrom, - SetId idTo, byte variantTo, ref string fileName, + public static FileSwap? CreateMtrl(MetaFileManager manager, Func redirections, EquipSlot slot, PrimaryId idFrom, + PrimaryId idTo, byte variantTo, ref string fileName, ref bool dataWasChanged) => CreateMtrl(manager, redirections, slot, slot, idFrom, idTo, variantTo, ref fileName, ref dataWasChanged); public static FileSwap? CreateMtrl(MetaFileManager manager, Func redirections, EquipSlot slotFrom, EquipSlot slotTo, - SetId idFrom, SetId idTo, byte variantTo, ref string fileName, + PrimaryId idFrom, PrimaryId idTo, byte variantTo, ref string fileName, ref bool dataWasChanged) { var prefix = slotTo.IsAccessory() ? 'a' : 'e'; @@ -397,13 +397,13 @@ public static class EquipmentSwap return mtrl; } - public static FileSwap CreateTex(MetaFileManager manager, Func redirections, char prefix, SetId idFrom, SetId idTo, + public static FileSwap CreateTex(MetaFileManager manager, Func redirections, char prefix, PrimaryId idFrom, PrimaryId idTo, ref MtrlFile.Texture texture, ref bool dataWasChanged) => CreateTex(manager, redirections, prefix, EquipSlot.Unknown, EquipSlot.Unknown, idFrom, idTo, ref texture, ref dataWasChanged); public static FileSwap CreateTex(MetaFileManager manager, Func redirections, char prefix, EquipSlot slotFrom, - EquipSlot slotTo, SetId idFrom, - SetId idTo, ref MtrlFile.Texture texture, ref bool dataWasChanged) + EquipSlot slotTo, PrimaryId idFrom, + PrimaryId idTo, ref MtrlFile.Texture texture, ref bool dataWasChanged) { var path = texture.Path; var addedDashes = false; diff --git a/Penumbra/Mods/ItemSwap/ItemSwap.cs b/Penumbra/Mods/ItemSwap/ItemSwap.cs index 90bee553..b269d89c 100644 --- a/Penumbra/Mods/ItemSwap/ItemSwap.cs +++ b/Penumbra/Mods/ItemSwap/ItemSwap.cs @@ -150,7 +150,7 @@ public static class ItemSwap /// metaChanges is not manipulated, but IReadOnlySet does not support TryGetValue. public static MetaSwap? CreateEst(MetaFileManager manager, Func redirections, Func manips, EstManipulation.EstType type, - GenderRace genderRace, SetId idFrom, SetId idTo, bool ownMdl) + GenderRace genderRace, PrimaryId idFrom, PrimaryId idTo, bool ownMdl) { if (type == 0) return null; @@ -195,7 +195,7 @@ public static class ItemSwap } } - public static string ReplaceAnyId(string path, char idType, SetId id, bool condition = true) + public static string ReplaceAnyId(string path, char idType, PrimaryId id, bool condition = true) => condition ? Regex.Replace(path, $"{idType}\\d{{4}}", $"{idType}{id.Id:D4}") : path; @@ -203,10 +203,10 @@ public static class ItemSwap public static string ReplaceAnyRace(string path, GenderRace to, bool condition = true) => ReplaceAnyId(path, 'c', (ushort)to, condition); - public static string ReplaceAnyBody(string path, BodySlot slot, SetId to, bool condition = true) + public static string ReplaceAnyBody(string path, BodySlot slot, PrimaryId to, bool condition = true) => ReplaceAnyId(path, slot.ToAbbreviation(), to, condition); - public static string ReplaceId(string path, char type, SetId idFrom, SetId idTo, bool condition = true) + public static string ReplaceId(string path, char type, PrimaryId idFrom, PrimaryId idTo, bool condition = true) => condition ? path.Replace($"{type}{idFrom.Id:D4}", $"{type}{idTo.Id:D4}") : path; @@ -219,7 +219,7 @@ public static class ItemSwap public static string ReplaceRace(string path, GenderRace from, GenderRace to, bool condition = true) => ReplaceId(path, 'c', (ushort)from, (ushort)to, condition); - public static string ReplaceBody(string path, BodySlot slot, SetId idFrom, SetId idTo, bool condition = true) + public static string ReplaceBody(string path, BodySlot slot, PrimaryId idFrom, PrimaryId idTo, bool condition = true) => ReplaceId(path, slot.ToAbbreviation(), idFrom, idTo, condition); public static string AddSuffix(string path, string ext, string suffix, bool condition = true) diff --git a/Penumbra/Mods/ItemSwap/ItemSwapContainer.cs b/Penumbra/Mods/ItemSwap/ItemSwapContainer.cs index 9ca02c4e..ff722945 100644 --- a/Penumbra/Mods/ItemSwap/ItemSwapContainer.cs +++ b/Penumbra/Mods/ItemSwap/ItemSwapContainer.cs @@ -145,7 +145,7 @@ public class ItemSwapContainer return ret; } - public bool LoadCustomization(MetaFileManager manager, BodySlot slot, GenderRace race, SetId from, SetId to, + public bool LoadCustomization(MetaFileManager manager, BodySlot slot, GenderRace race, PrimaryId from, PrimaryId to, ModCollection? collection = null) { var pathResolver = PathResolver(collection); diff --git a/Penumbra/Mods/Manager/ModCacheManager.cs b/Penumbra/Mods/Manager/ModCacheManager.cs index 0af78431..a95567ce 100644 --- a/Penumbra/Mods/Manager/ModCacheManager.cs +++ b/Penumbra/Mods/Manager/ModCacheManager.cs @@ -139,7 +139,7 @@ public class ModCacheManager : IDisposable { case ModPathChangeType.Added: case ModPathChangeType.Reloaded: - Refresh(mod); + RefreshWithChangedItems(mod); break; } } @@ -151,10 +151,28 @@ public class ModCacheManager : IDisposable } private void OnModDiscoveryFinished() - => Parallel.ForEach(_modManager, Refresh); + { + if (!_identifier.Awaiter.IsCompletedSuccessfully || _updatingItems) + { + Parallel.ForEach(_modManager, RefreshWithoutChangedItems); + } + else + { + _updatingItems = true; + Parallel.ForEach(_modManager, RefreshWithChangedItems); + _updatingItems = false; + } + } private void OnIdentifierCreation() - => Parallel.ForEach(_modManager, UpdateChangedItems); + { + if (_updatingItems) + return; + + _updatingItems = true; + Parallel.ForEach(_modManager, UpdateChangedItems); + _updatingItems = false; + } private static void UpdateFileCount(Mod mod) => mod.TotalFileCount = mod.AllSubMods.Sum(s => s.Files.Count); @@ -173,15 +191,8 @@ public class ModCacheManager : IDisposable private void UpdateChangedItems(Mod mod) { - if (_updatingItems) - return; - - _updatingItems = true; var changedItems = (SortedList)mod.ChangedItems; changedItems.Clear(); - if (!_identifier.Awaiter.IsCompletedSuccessfully) - return; - foreach (var gamePath in mod.AllSubMods.SelectMany(m => m.Files.Keys.Concat(m.FileSwaps.Keys))) _identifier.Identify(changedItems, gamePath.ToString()); @@ -189,7 +200,6 @@ public class ModCacheManager : IDisposable ComputeChangedItems(_identifier, changedItems, manip); mod.LowerChangedItemsString = string.Join("\0", mod.ChangedItems.Keys.Select(k => k.ToLowerInvariant())); - _updatingItems = false; } private static void UpdateCounts(Mod mod) @@ -210,10 +220,16 @@ public class ModCacheManager : IDisposable } } - private void Refresh(Mod mod) + private void RefreshWithChangedItems(Mod mod) { UpdateTags(mod); UpdateCounts(mod); UpdateChangedItems(mod); } + + private void RefreshWithoutChangedItems(Mod mod) + { + UpdateTags(mod); + UpdateCounts(mod); + } } diff --git a/Penumbra/Mods/Subclasses/SubMod.cs b/Penumbra/Mods/Subclasses/SubMod.cs index 542b14d2..a081f7bc 100644 --- a/Penumbra/Mods/Subclasses/SubMod.cs +++ b/Penumbra/Mods/Subclasses/SubMod.cs @@ -30,9 +30,9 @@ public sealed class SubMod : ISubMod public bool IsDefault => GroupIdx < 0; - public Dictionary FileData = new(); - public Dictionary FileSwapData = new(); - public HashSet ManipulationData = new(); + public Dictionary FileData = []; + public Dictionary FileSwapData = []; + public HashSet ManipulationData = []; public SubMod(IMod parentMod) => ParentMod = parentMod; diff --git a/Penumbra/Penumbra.csproj b/Penumbra/Penumbra.csproj index ec433113..54f7a16f 100644 --- a/Penumbra/Penumbra.csproj +++ b/Penumbra/Penumbra.csproj @@ -91,8 +91,9 @@ - - + + + diff --git a/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs b/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs index c2910b51..d31a08ae 100644 --- a/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs +++ b/Penumbra/UI/AdvancedWindow/ItemSwapTab.cs @@ -210,26 +210,26 @@ public class ItemSwapTab : IDisposable, ITab break; case SwapType.Hair when _targetId > 0 && _sourceId > 0: _swapData.LoadCustomization(_metaFileManager, BodySlot.Hair, Names.CombinedRace(_currentGender, _currentRace), - (SetId)_sourceId, - (SetId)_targetId, + (PrimaryId)_sourceId, + (PrimaryId)_targetId, _useCurrentCollection ? _collectionManager.Active.Current : null); break; case SwapType.Face when _targetId > 0 && _sourceId > 0: _swapData.LoadCustomization(_metaFileManager, BodySlot.Face, Names.CombinedRace(_currentGender, _currentRace), - (SetId)_sourceId, - (SetId)_targetId, + (PrimaryId)_sourceId, + (PrimaryId)_targetId, _useCurrentCollection ? _collectionManager.Active.Current : null); break; case SwapType.Ears when _targetId > 0 && _sourceId > 0: _swapData.LoadCustomization(_metaFileManager, BodySlot.Ear, Names.CombinedRace(_currentGender, ModelRace.Viera), - (SetId)_sourceId, - (SetId)_targetId, + (PrimaryId)_sourceId, + (PrimaryId)_targetId, _useCurrentCollection ? _collectionManager.Active.Current : null); break; case SwapType.Tail when _targetId > 0 && _sourceId > 0: _swapData.LoadCustomization(_metaFileManager, BodySlot.Tail, Names.CombinedRace(_currentGender, _currentRace), - (SetId)_sourceId, - (SetId)_targetId, + (PrimaryId)_sourceId, + (PrimaryId)_targetId, _useCurrentCollection ? _collectionManager.Active.Current : null); break; case SwapType.Weapon: break; diff --git a/Penumbra/UI/ModsTab/ModFileSystemSelector.cs b/Penumbra/UI/ModsTab/ModFileSystemSelector.cs index c307f6f4..cf65901e 100644 --- a/Penumbra/UI/ModsTab/ModFileSystemSelector.cs +++ b/Penumbra/UI/ModsTab/ModFileSystemSelector.cs @@ -169,6 +169,7 @@ public sealed class ModFileSystemSelector : FileSystemSelector