From 600fd2ecd36faa72292a7a8e2e8ce8982f6c708e Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Sun, 16 Jun 2024 01:02:42 +0200 Subject: [PATCH] Get rid off EQDP files --- Penumbra/Collections/Cache/EqdpCache.cs | 115 +++++------------- Penumbra/Collections/Cache/MetaCache.cs | 38 +----- .../Collections/ModCollection.Cache.Access.cs | 36 ------ .../Interop/Hooks/Meta/CalculateHeight.cs | 5 +- .../Interop/Hooks/Meta/ChangeCustomize.cs | 23 ++-- .../Interop/Hooks/Meta/EqdpAccessoryHook.cs | 33 +++++ Penumbra/Interop/Hooks/Meta/EqdpEquipHook.cs | 32 +++++ Penumbra/Interop/Hooks/Meta/EqpHook.cs | 2 +- Penumbra/Interop/Hooks/Meta/EstHook.cs | 2 +- Penumbra/Interop/Hooks/Meta/GetEqpIndirect.cs | 5 +- .../Interop/Hooks/Meta/GetEqpIndirect2.cs | 23 ++-- Penumbra/Interop/Hooks/Meta/GmpHook.cs | 6 +- .../Interop/Hooks/Meta/ModelLoadComplete.cs | 9 +- Penumbra/Interop/Hooks/Meta/RspBustHook.cs | 2 +- Penumbra/Interop/Hooks/Meta/RspHeightHook.cs | 3 +- .../Interop/Hooks/Meta/RspSetupCharacter.cs | 5 +- Penumbra/Interop/Hooks/Meta/RspTailHook.cs | 2 +- Penumbra/Interop/Hooks/Meta/SetupVisor.cs | 6 +- Penumbra/Interop/Hooks/Meta/UpdateModel.cs | 9 +- .../Hooks/Resources/ResolvePathHooksBase.cs | 45 ++++--- Penumbra/Interop/PathResolving/MetaState.cs | 33 ++--- Penumbra/Interop/Services/MetaList.cs | 2 +- Penumbra/Penumbra.cs | 2 - 23 files changed, 192 insertions(+), 246 deletions(-) create mode 100644 Penumbra/Interop/Hooks/Meta/EqdpAccessoryHook.cs create mode 100644 Penumbra/Interop/Hooks/Meta/EqdpEquipHook.cs diff --git a/Penumbra/Collections/Cache/EqdpCache.cs b/Penumbra/Collections/Cache/EqdpCache.cs index c63403ae..5bfe2dbf 100644 --- a/Penumbra/Collections/Cache/EqdpCache.cs +++ b/Penumbra/Collections/Cache/EqdpCache.cs @@ -1,8 +1,5 @@ -using OtterGui; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; -using Penumbra.Interop.Services; -using Penumbra.Interop.Structs; using Penumbra.Meta; using Penumbra.Meta.Files; using Penumbra.Meta.Manipulations; @@ -11,108 +8,60 @@ namespace Penumbra.Collections.Cache; public sealed class EqdpCache(MetaFileManager manager, ModCollection collection) : MetaCacheBase(manager, collection) { - private readonly ExpandedEqdpFile?[] _eqdpFiles = new ExpandedEqdpFile[CharacterUtilityData.EqdpIndices.Length]; // TODO: female Hrothgar + private readonly Dictionary<(PrimaryId Id, GenderRace GenderRace, bool Accessory), EqdpEntry> _fullEntries = []; public override void SetFiles() - { - for (var i = 0; i < CharacterUtilityData.EqdpIndices.Length; ++i) - Manager.SetFile(_eqdpFiles[i], CharacterUtilityData.EqdpIndices[i]); - } + { } - public void SetFile(MetaIndex index) - { - var i = CharacterUtilityData.EqdpIndices.IndexOf(index); - if (i != -1) - Manager.SetFile(_eqdpFiles[i], index); - } - - public void ResetFiles() - { - foreach (var t in CharacterUtilityData.EqdpIndices) - Manager.SetFile(null, t); - } + public bool TryGetFullEntry(PrimaryId id, GenderRace genderRace, bool accessory, out EqdpEntry entry) + => _fullEntries.TryGetValue((id, genderRace, accessory), out entry); protected override void IncorporateChangesInternal() - { - foreach (var (identifier, (_, entry)) in this) - Apply(GetFile(identifier)!, identifier, entry); - - Penumbra.Log.Verbose($"{Collection.AnonymizedName}: Loaded {Count} delayed EQDP manipulations."); - } - - public ExpandedEqdpFile? EqdpFile(GenderRace race, bool accessory) - => _eqdpFiles[Array.IndexOf(CharacterUtilityData.EqdpIndices, CharacterUtilityData.EqdpIdx(race, accessory))]; // TODO: female Hrothgar - - public MetaList.MetaReverter? TemporarilySetFile(GenderRace genderRace, bool accessory) - { - var idx = CharacterUtilityData.EqdpIdx(genderRace, accessory); - if (idx < 0) - { - Penumbra.Log.Warning($"Invalid Gender, Race or Accessory for EQDP file {genderRace}, {accessory}."); - return null; - } - - var i = CharacterUtilityData.EqdpIndices.IndexOf(idx); - if (i < 0) - { - Penumbra.Log.Warning($"Invalid Gender, Race or Accessory for EQDP file {genderRace}, {accessory}."); - return null; - } - - return Manager.TemporarilySetFile(_eqdpFiles[i], idx); - } + { } public void Reset() { - foreach (var file in _eqdpFiles.OfType()) - { - var relevant = CharacterUtility.RelevantIndices[file.Index.Value]; - file.Reset(Keys.Where(m => m.FileIndex() == relevant).Select(m => m.SetId)); - } - Clear(); + _fullEntries.Clear(); } protected override void ApplyModInternal(EqdpIdentifier identifier, EqdpEntry entry) { - if (GetFile(identifier) is { } file) - Apply(file, identifier, entry); + var tuple = (identifier.SetId, identifier.GenderRace, identifier.Slot.IsAccessory()); + var mask = Eqdp.Mask(identifier.Slot); + if (!_fullEntries.TryGetValue(tuple, out var currentEntry)) + currentEntry = ExpandedEqdpFile.GetDefault(Manager, identifier); + + _fullEntries[tuple] = (currentEntry & ~mask) | (entry & mask); } protected override void RevertModInternal(EqdpIdentifier identifier) { - if (GetFile(identifier) is { } file) - Apply(file, identifier, ExpandedEqdpFile.GetDefault(Manager, identifier)); - } + var tuple = (identifier.SetId, identifier.GenderRace, identifier.Slot.IsAccessory()); + var mask = Eqdp.Mask(identifier.Slot); - public static bool Apply(ExpandedEqdpFile file, EqdpIdentifier identifier, EqdpEntry entry) - { - var origEntry = file[identifier.SetId]; - var mask = Eqdp.Mask(identifier.Slot); - if ((origEntry & mask) == entry) - return false; - - file[identifier.SetId] = (origEntry & ~mask) | entry; - return true; + if (_fullEntries.TryGetValue(tuple, out var currentEntry)) + { + var def = ExpandedEqdpFile.GetDefault(Manager, identifier); + var newEntry = (currentEntry & ~mask) | (def & mask); + if (currentEntry != newEntry) + { + _fullEntries[tuple] = newEntry; + } + else + { + var slots = tuple.Item3 ? EquipSlotExtensions.AccessorySlots : EquipSlotExtensions.EquipmentSlots; + if (slots.All(s => !ContainsKey(identifier with { Slot = s }))) + _fullEntries.Remove(tuple); + else + _fullEntries[tuple] = newEntry; + } + } } protected override void Dispose(bool _) { - for (var i = 0; i < _eqdpFiles.Length; ++i) - { - _eqdpFiles[i]?.Dispose(); - _eqdpFiles[i] = null; - } - Clear(); - } - - private ExpandedEqdpFile? GetFile(EqdpIdentifier identifier) - { - if (!Manager.CharacterUtility.Ready) - return null; - - var index = Array.IndexOf(CharacterUtilityData.EqdpIndices, identifier.FileIndex()); - return _eqdpFiles[index] ??= new ExpandedEqdpFile(Manager, identifier.GenderRace, identifier.Slot.IsAccessory()); + _fullEntries.Clear(); } } diff --git a/Penumbra/Collections/Cache/MetaCache.cs b/Penumbra/Collections/Cache/MetaCache.cs index e6083351..614a5a2c 100644 --- a/Penumbra/Collections/Cache/MetaCache.cs +++ b/Penumbra/Collections/Cache/MetaCache.cs @@ -1,7 +1,5 @@ using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; -using Penumbra.Interop.Services; -using Penumbra.Interop.Structs; using Penumbra.Meta; using Penumbra.Meta.Manipulations; using Penumbra.Mods.Editor; @@ -115,48 +113,18 @@ public class MetaCache(MetaFileManager manager, ModCollection collection) ~MetaCache() => Dispose(); - /// Set a single file. - public void SetFile(MetaIndex metaIndex) - { - switch (metaIndex) - { - case MetaIndex.Eqp: - break; - case MetaIndex.Gmp: - break; - case MetaIndex.HumanCmp: - Rsp.SetFiles(); - break; - case MetaIndex.FaceEst: - case MetaIndex.HairEst: - case MetaIndex.HeadEst: - case MetaIndex.BodyEst: - break; - default: - Eqdp.SetFile(metaIndex); - break; - } - } - /// Set the currently relevant IMC files for the collection cache. public void SetImcFiles(bool fromFullCompute) => Imc.SetFiles(fromFullCompute); - public MetaList.MetaReverter? TemporarilySetEqdpFile(GenderRace genderRace, bool accessory) - => Eqdp.TemporarilySetFile(genderRace, accessory); - /// Try to obtain a manipulated IMC file. public bool GetImcFile(Utf8GamePath path, [NotNullWhen(true)] out Meta.Files.ImcFile? file) => Imc.GetFile(path, out file); internal EqdpEntry GetEqdpEntry(GenderRace race, bool accessory, PrimaryId primaryId) - { - var eqdpFile = Eqdp.EqdpFile(race, accessory); - if (eqdpFile != null) - return primaryId.Id < eqdpFile.Count ? eqdpFile[primaryId] : default; - - return Meta.Files.ExpandedEqdpFile.GetDefault(manager, race, accessory, primaryId); - } + => Eqdp.TryGetFullEntry(primaryId, race, accessory, out var entry) + ? entry + : Meta.Files.ExpandedEqdpFile.GetDefault(manager, race, accessory, primaryId); internal EstEntry GetEstEntry(EstType type, GenderRace genderRace, PrimaryId primaryId) => Est.GetEstEntry(new EstIdentifier(primaryId, type, genderRace)); diff --git a/Penumbra/Collections/ModCollection.Cache.Access.cs b/Penumbra/Collections/ModCollection.Cache.Access.cs index d93a0f53..81751128 100644 --- a/Penumbra/Collections/ModCollection.Cache.Access.cs +++ b/Penumbra/Collections/ModCollection.Cache.Access.cs @@ -1,14 +1,9 @@ using OtterGui.Classes; -using Penumbra.GameData.Enums; using Penumbra.Mods; -using Penumbra.Interop.Structs; using Penumbra.Meta.Files; -using Penumbra.Meta.Manipulations; using Penumbra.String.Classes; using Penumbra.Collections.Cache; -using Penumbra.Interop.Services; using Penumbra.Mods.Editor; -using Penumbra.GameData.Structs; namespace Penumbra.Collections; @@ -68,35 +63,4 @@ public partial class ModCollection internal SingleArray Conflicts(Mod mod) => _cache?.Conflicts(mod) ?? new SingleArray(); - - public void SetFiles(CharacterUtility utility) - { - if (_cache == null) - { - utility.ResetAll(); - } - else - { - _cache.Meta.SetFiles(); - Penumbra.Log.Debug($"Set CharacterUtility resources for collection {Identifier}."); - } - } - - public void SetMetaFile(CharacterUtility utility, MetaIndex idx) - { - if (_cache == null) - utility.ResetResource(idx); - else - _cache.Meta.SetFile(idx); - } - - // Used for short periods of changed files. - public MetaList.MetaReverter? TemporarilySetEqdpFile(CharacterUtility utility, GenderRace genderRace, bool accessory) - { - if (_cache != null) - return _cache?.Meta.TemporarilySetEqdpFile(genderRace, accessory); - - var idx = CharacterUtilityData.EqdpIdx(genderRace, accessory); - return idx >= 0 ? utility.TemporarilyResetResource(idx) : null; - } } diff --git a/Penumbra/Interop/Hooks/Meta/CalculateHeight.cs b/Penumbra/Interop/Hooks/Meta/CalculateHeight.cs index 5a207491..7936b831 100644 --- a/Penumbra/Interop/Hooks/Meta/CalculateHeight.cs +++ b/Penumbra/Interop/Hooks/Meta/CalculateHeight.cs @@ -23,10 +23,11 @@ public sealed unsafe class CalculateHeight : FastHook [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private ulong Detour(Character* character) { - _metaState.RspCollection = _collectionResolver.IdentifyCollection((GameObject*)character, true); + var collection = _collectionResolver.IdentifyCollection((GameObject*)character, true); + _metaState.RspCollection.Push(collection); var ret = Task.Result.Original.Invoke(character); Penumbra.Log.Excessive($"[Calculate Height] Invoked on {(nint)character:X} -> {ret}."); - _metaState.RspCollection = ResolveData.Invalid; + _metaState.RspCollection.Pop(); return ret; } } diff --git a/Penumbra/Interop/Hooks/Meta/ChangeCustomize.cs b/Penumbra/Interop/Hooks/Meta/ChangeCustomize.cs index 4e0a5744..f589cf4e 100644 --- a/Penumbra/Interop/Hooks/Meta/ChangeCustomize.cs +++ b/Penumbra/Interop/Hooks/Meta/ChangeCustomize.cs @@ -1,12 +1,12 @@ -using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; +using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using OtterGui.Services; using Penumbra.Collections; -using Penumbra.GameData; -using Penumbra.GameData.Structs; -using Penumbra.Interop.PathResolving; - -namespace Penumbra.Interop.Hooks.Meta; - +using Penumbra.GameData; +using Penumbra.GameData.Structs; +using Penumbra.Interop.PathResolving; + +namespace Penumbra.Interop.Hooks.Meta; + public sealed unsafe class ChangeCustomize : FastHook { private readonly CollectionResolver _collectionResolver; @@ -24,14 +24,15 @@ public sealed unsafe class ChangeCustomize : FastHook [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private bool Detour(Human* human, CustomizeArray* data, byte skipEquipment) { - _metaState.CustomizeChangeCollection = _collectionResolver.IdentifyCollection((DrawObject*)human, true); - _metaState.RspCollection = _metaState.CustomizeChangeCollection; + var collection = _collectionResolver.IdentifyCollection((DrawObject*)human, true); + _metaState.CustomizeChangeCollection = collection; + _metaState.RspCollection.Push(collection); using var decal1 = _metaState.ResolveDecal(_metaState.CustomizeChangeCollection, true); using var decal2 = _metaState.ResolveDecal(_metaState.CustomizeChangeCollection, false); var ret = Task.Result.Original.Invoke(human, data, skipEquipment); Penumbra.Log.Excessive($"[Change Customize] Invoked on {(nint)human:X} with {(nint)data:X}, {skipEquipment} -> {ret}."); _metaState.CustomizeChangeCollection = ResolveData.Invalid; - _metaState.RspCollection = ResolveData.Invalid; + _metaState.RspCollection.Pop(); return ret; } -} +} diff --git a/Penumbra/Interop/Hooks/Meta/EqdpAccessoryHook.cs b/Penumbra/Interop/Hooks/Meta/EqdpAccessoryHook.cs new file mode 100644 index 00000000..475e1eb7 --- /dev/null +++ b/Penumbra/Interop/Hooks/Meta/EqdpAccessoryHook.cs @@ -0,0 +1,33 @@ +using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; +using OtterGui.Services; +using Penumbra.GameData.Enums; +using Penumbra.GameData.Structs; +using Penumbra.Interop.PathResolving; +using Penumbra.Meta.Manipulations; + +namespace Penumbra.Interop.Hooks.Meta; + +public unsafe class EqdpAccessoryHook : FastHook +{ + public delegate void Delegate(CharacterUtility* utility, EqdpEntry* entry, uint id, uint raceCode); + + private readonly MetaState _metaState; + + public EqdpAccessoryHook(HookManager hooks, MetaState metaState) + { + _metaState = metaState; + Task = hooks.CreateHook("GetEqdpAccessoryEntry", "E8 ?? ?? ?? ?? 41 BF ?? ?? ?? ?? 83 FB", Detour, true); + } + + private void Detour(CharacterUtility* utility, EqdpEntry* entry, uint setId, uint raceCode) + { + if (_metaState.EqdpCollection.TryPeek(out var collection) + && collection is { Valid: true, ModCollection.MetaCache: { } cache } + && cache.Eqdp.TryGetFullEntry(new PrimaryId((ushort)setId), (GenderRace)raceCode, true, out var newEntry)) + *entry = newEntry; + else + Task.Result.Original(utility, entry, setId, raceCode); + Penumbra.Log.Information( + $"[GetEqdpAccessoryEntry] Invoked on 0x{(ulong)utility:X} with {setId}, {(GenderRace)raceCode}, returned {(ushort)*entry:B10}."); + } +} diff --git a/Penumbra/Interop/Hooks/Meta/EqdpEquipHook.cs b/Penumbra/Interop/Hooks/Meta/EqdpEquipHook.cs new file mode 100644 index 00000000..9b911710 --- /dev/null +++ b/Penumbra/Interop/Hooks/Meta/EqdpEquipHook.cs @@ -0,0 +1,32 @@ +using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; +using OtterGui.Services; +using Penumbra.GameData.Enums; +using Penumbra.GameData.Structs; +using Penumbra.Interop.PathResolving; + +namespace Penumbra.Interop.Hooks.Meta; + +public unsafe class EqdpEquipHook : FastHook +{ + public delegate void Delegate(CharacterUtility* utility, EqdpEntry* entry, uint id, uint raceCode); + + private readonly MetaState _metaState; + + public EqdpEquipHook(HookManager hooks, MetaState metaState) + { + _metaState = metaState; + Task = hooks.CreateHook("GetEqdpEquipEntry", "E8 ?? ?? ?? ?? 85 DB 75 ?? F6 45", Detour, true); + } + + private void Detour(CharacterUtility* utility, EqdpEntry* entry, uint setId, uint raceCode) + { + if (_metaState.EqdpCollection.TryPeek(out var collection) + && collection is { Valid: true, ModCollection.MetaCache: { } cache } + && cache.Eqdp.TryGetFullEntry(new PrimaryId((ushort)setId), (GenderRace)raceCode, false, out var newEntry)) + *entry = newEntry; + else + Task.Result.Original(utility, entry, setId, raceCode); + Penumbra.Log.Information( + $"[GetEqdpEquipEntry] Invoked on 0x{(ulong)utility:X} with {setId}, {(GenderRace)raceCode}, returned {(ushort)*entry:B10}."); + } +} diff --git a/Penumbra/Interop/Hooks/Meta/EqpHook.cs b/Penumbra/Interop/Hooks/Meta/EqpHook.cs index 448605c1..7107e26b 100644 --- a/Penumbra/Interop/Hooks/Meta/EqpHook.cs +++ b/Penumbra/Interop/Hooks/Meta/EqpHook.cs @@ -19,7 +19,7 @@ public unsafe class EqpHook : FastHook private void Detour(CharacterUtility* utility, EqpEntry* flags, CharacterArmor* armor) { - if (_metaState.EqpCollection is { Valid: true, ModCollection.MetaCache: { } cache }) + if (_metaState.EqpCollection.TryPeek(out var collection) && collection is { Valid: true, ModCollection.MetaCache: { } cache }) { *flags = cache.Eqp.GetValues(armor); *flags = cache.GlobalEqp.Apply(*flags, armor); diff --git a/Penumbra/Interop/Hooks/Meta/EstHook.cs b/Penumbra/Interop/Hooks/Meta/EstHook.cs index 34935edb..23931182 100644 --- a/Penumbra/Interop/Hooks/Meta/EstHook.cs +++ b/Penumbra/Interop/Hooks/Meta/EstHook.cs @@ -21,7 +21,7 @@ public class EstHook : FastHook private EstEntry Detour(uint genderRace, int estType, uint id) { EstEntry ret; - if (_metaState.EstCollection is { Valid: true, ModCollection.MetaCache: { } cache } + if (_metaState.EstCollection.TryPeek(out var collection) && collection is { Valid: true, ModCollection.MetaCache: { } cache } && cache.Est.TryGetValue(Convert(genderRace, estType, id), out var entry)) ret = entry.Entry; else diff --git a/Penumbra/Interop/Hooks/Meta/GetEqpIndirect.cs b/Penumbra/Interop/Hooks/Meta/GetEqpIndirect.cs index beae6acc..a10b511a 100644 --- a/Penumbra/Interop/Hooks/Meta/GetEqpIndirect.cs +++ b/Penumbra/Interop/Hooks/Meta/GetEqpIndirect.cs @@ -29,8 +29,9 @@ public sealed unsafe class GetEqpIndirect : FastHook return; Penumbra.Log.Excessive($"[Get EQP Indirect] Invoked on {(nint)drawObject:X}."); - _metaState.EqpCollection = _collectionResolver.IdentifyCollection(drawObject, true); + var collection = _collectionResolver.IdentifyCollection(drawObject, true); + _metaState.EqpCollection.Push(collection); Task.Result.Original(drawObject); - _metaState.EqpCollection = ResolveData.Invalid; + _metaState.EqpCollection.Pop(); } } diff --git a/Penumbra/Interop/Hooks/Meta/GetEqpIndirect2.cs b/Penumbra/Interop/Hooks/Meta/GetEqpIndirect2.cs index 89aaa9b0..30ec2597 100644 --- a/Penumbra/Interop/Hooks/Meta/GetEqpIndirect2.cs +++ b/Penumbra/Interop/Hooks/Meta/GetEqpIndirect2.cs @@ -1,11 +1,11 @@ -using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; -using OtterGui.Services; -using Penumbra.Collections; -using Penumbra.GameData; -using Penumbra.Interop.PathResolving; - -namespace Penumbra.Interop.Hooks.Meta; - +using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; +using OtterGui.Services; +using Penumbra.Collections; +using Penumbra.GameData; +using Penumbra.Interop.PathResolving; + +namespace Penumbra.Interop.Hooks.Meta; + public sealed unsafe class GetEqpIndirect2 : FastHook { private readonly CollectionResolver _collectionResolver; @@ -29,8 +29,9 @@ public sealed unsafe class GetEqpIndirect2 : FastHook return; Penumbra.Log.Excessive($"[Get EQP Indirect 2] Invoked on {(nint)drawObject:X}."); - _metaState.EqpCollection = _collectionResolver.IdentifyCollection(drawObject, true); + var collection = _collectionResolver.IdentifyCollection(drawObject, true); + _metaState.EqpCollection.Push(collection); Task.Result.Original(drawObject); - _metaState.EqpCollection = ResolveData.Invalid; + _metaState.EqpCollection.Pop(); } -} +} diff --git a/Penumbra/Interop/Hooks/Meta/GmpHook.cs b/Penumbra/Interop/Hooks/Meta/GmpHook.cs index 60966fb7..256d8702 100644 --- a/Penumbra/Interop/Hooks/Meta/GmpHook.cs +++ b/Penumbra/Interop/Hooks/Meta/GmpHook.cs @@ -27,15 +27,15 @@ public unsafe class GmpHook : FastHook private nint Detour(nint gmpResource, uint dividedHeadId) { nint ret; - if (_metaState.GmpCollection is { Valid: true, ModCollection.MetaCache: { } cache } - && cache.Gmp.TryGetValue(new GmpIdentifier(_metaState.UndividedGmpId), out var entry)) + if (_metaState.GmpCollection.TryPeek(out var collection) && collection.Collection is { Valid: true, ModCollection.MetaCache: { } cache } + && cache.Gmp.TryGetValue(new GmpIdentifier(collection.Id), out var entry)) { if (entry.Entry.Enabled) { *StablePointer.Pointer = entry.Entry.Value; // This function already gets the original ID divided by the block size, so we can compute the modulo with a single multiplication and addition. // We then go backwards from our pointer because this gets added by the calling functions. - ret = (nint)(StablePointer.Pointer - (_metaState.UndividedGmpId.Id - dividedHeadId * ExpandedEqpGmpBase.BlockSize)); + ret = (nint)(StablePointer.Pointer - (collection.Id.Id - dividedHeadId * ExpandedEqpGmpBase.BlockSize)); } else { diff --git a/Penumbra/Interop/Hooks/Meta/ModelLoadComplete.cs b/Penumbra/Interop/Hooks/Meta/ModelLoadComplete.cs index 10c12594..2c17362d 100644 --- a/Penumbra/Interop/Hooks/Meta/ModelLoadComplete.cs +++ b/Penumbra/Interop/Hooks/Meta/ModelLoadComplete.cs @@ -23,10 +23,11 @@ public sealed unsafe class ModelLoadComplete : FastHook } var ret = storage; - if (bodyType < 2 && _metaState.RspCollection is { Valid: true, ModCollection.MetaCache: { } cache }) + if (bodyType < 2 && _metaState.RspCollection.TryPeek(out var collection) && collection is { Valid: true, ModCollection.MetaCache: { } cache }) { var bustScale = bustSize / 100f; var clan = (SubRace)(((int)race - 1) * 2 + 1 + isSecondSubRace); diff --git a/Penumbra/Interop/Hooks/Meta/RspHeightHook.cs b/Penumbra/Interop/Hooks/Meta/RspHeightHook.cs index 883f5fc6..cf88c34a 100644 --- a/Penumbra/Interop/Hooks/Meta/RspHeightHook.cs +++ b/Penumbra/Interop/Hooks/Meta/RspHeightHook.cs @@ -1,3 +1,4 @@ +using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using OtterGui.Services; using Penumbra.GameData.Enums; using Penumbra.Interop.PathResolving; @@ -24,7 +25,7 @@ public class RspHeightHook : FastHook private unsafe float Detour(nint cmpResource, Race race, byte gender, byte isSecondSubRace, byte bodyType, byte height) { float scale; - if (bodyType < 2 && _metaState.RspCollection is { Valid: true, ModCollection.MetaCache: { } cache }) + if (bodyType < 2 && _metaState.RspCollection.TryPeek(out var collection) && collection is { Valid: true, ModCollection.MetaCache: { } cache }) { var clan = (SubRace)(((int)race - 1) * 2 + 1 + isSecondSubRace); var (minIdent, maxIdent) = gender == 0 diff --git a/Penumbra/Interop/Hooks/Meta/RspSetupCharacter.cs b/Penumbra/Interop/Hooks/Meta/RspSetupCharacter.cs index 831c99bb..58856f52 100644 --- a/Penumbra/Interop/Hooks/Meta/RspSetupCharacter.cs +++ b/Penumbra/Interop/Hooks/Meta/RspSetupCharacter.cs @@ -31,8 +31,9 @@ public sealed unsafe class RspSetupCharacter : FastHook private unsafe float Detour(nint cmpResource, Race race, byte gender, byte isSecondSubRace, byte bodyType, byte tailLength) { float scale; - if (bodyType < 2 && _metaState.RspCollection is { Valid: true, ModCollection.MetaCache: { } cache }) + if (bodyType < 2 && _metaState.RspCollection.TryPeek(out var collection) && collection is { Valid: true, ModCollection.MetaCache: { } cache }) { var clan = (SubRace)(((int)race - 1) * 2 + 1 + isSecondSubRace); var (minIdent, maxIdent) = gender == 0 diff --git a/Penumbra/Interop/Hooks/Meta/SetupVisor.cs b/Penumbra/Interop/Hooks/Meta/SetupVisor.cs index 8479968f..82b24dc4 100644 --- a/Penumbra/Interop/Hooks/Meta/SetupVisor.cs +++ b/Penumbra/Interop/Hooks/Meta/SetupVisor.cs @@ -27,11 +27,11 @@ public sealed unsafe class SetupVisor : FastHook [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] private byte Detour(DrawObject* drawObject, ushort modelId, byte visorState) { - _metaState.GmpCollection = _collectionResolver.IdentifyCollection(drawObject, true); - _metaState.UndividedGmpId = modelId; + var collection = _collectionResolver.IdentifyCollection(drawObject, true); + _metaState.GmpCollection.Push((collection, modelId)); var ret = Task.Result.Original.Invoke(drawObject, modelId, visorState); Penumbra.Log.Excessive($"[Setup Visor] Invoked on {(nint)drawObject:X} with {modelId}, {visorState} -> {ret}."); - _metaState.GmpCollection = ResolveData.Invalid; + _metaState.GmpCollection.Pop(); return ret; } } diff --git a/Penumbra/Interop/Hooks/Meta/UpdateModel.cs b/Penumbra/Interop/Hooks/Meta/UpdateModel.cs index b0298ac7..76854bca 100644 --- a/Penumbra/Interop/Hooks/Meta/UpdateModel.cs +++ b/Penumbra/Interop/Hooks/Meta/UpdateModel.cs @@ -29,10 +29,11 @@ public sealed unsafe class UpdateModel : FastHook return; Penumbra.Log.Excessive($"[Update Model] Invoked on {(nint)drawObject:X}."); - var collection = _collectionResolver.IdentifyCollection(drawObject, true); - using var eqdp = _metaState.ResolveEqdpData(collection.ModCollection, MetaState.GetDrawObjectGenderRace((nint)drawObject), true, true); - _metaState.EqpCollection = collection; + var collection = _collectionResolver.IdentifyCollection(drawObject, true); + _metaState.EqpCollection.Push(collection); + _metaState.EqdpCollection.Push(collection); Task.Result.Original(drawObject); - _metaState.EqpCollection = ResolveData.Invalid; + _metaState.EqpCollection.Pop(); + _metaState.EqdpCollection.Pop(); } } diff --git a/Penumbra/Interop/Hooks/Resources/ResolvePathHooksBase.cs b/Penumbra/Interop/Hooks/Resources/ResolvePathHooksBase.cs index 17cfa3f6..5941773f 100644 --- a/Penumbra/Interop/Hooks/Resources/ResolvePathHooksBase.cs +++ b/Penumbra/Interop/Hooks/Resources/ResolvePathHooksBase.cs @@ -1,11 +1,9 @@ using System.Text.Unicode; using Dalamud.Hooking; using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; -using OtterGui.Classes; using OtterGui.Services; using Penumbra.Collections; using Penumbra.Interop.PathResolving; -using Penumbra.Meta.Manipulations; namespace Penumbra.Interop.Hooks.Resources; @@ -149,42 +147,51 @@ public sealed unsafe class ResolvePathHooksBase : IDisposable private nint ResolveMdlHuman(nint drawObject, nint pathBuffer, nint pathBufferSize, uint slotIndex) { - var data = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true); - using var eqdp = slotIndex > 9 || _parent.InInternalResolve - ? DisposableContainer.Empty - : _parent.MetaState.ResolveEqdpData(data.ModCollection, MetaState.GetHumanGenderRace(drawObject), slotIndex < 5, slotIndex > 4); - return ResolvePath(data, _resolveMdlPathHook.Original(drawObject, pathBuffer, pathBufferSize, slotIndex)); + var collection = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true); + if (slotIndex < 10) + _parent.MetaState.EqdpCollection.Push(collection); + + var ret = ResolvePath(collection, _resolveMdlPathHook.Original(drawObject, pathBuffer, pathBufferSize, slotIndex)); + if (slotIndex < 10) + _parent.MetaState.EqdpCollection.Pop(); + + return ret; } private nint ResolvePapHuman(nint drawObject, nint pathBuffer, nint pathBufferSize, uint unkAnimationIndex, nint animationName) { - _parent.MetaState.EstCollection = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true); - var ret = ResolvePath(_parent.MetaState.EstCollection, _resolvePapPathHook.Original(drawObject, pathBuffer, pathBufferSize, unkAnimationIndex, animationName)); - _parent.MetaState.EstCollection = ResolveData.Invalid; + var collection = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true); + _parent.MetaState.EstCollection.Push(collection); + var ret = ResolvePath(collection, + _resolvePapPathHook.Original(drawObject, pathBuffer, pathBufferSize, unkAnimationIndex, animationName)); + _parent.MetaState.EstCollection.Pop(); return ret; } private nint ResolvePhybHuman(nint drawObject, nint pathBuffer, nint pathBufferSize, uint partialSkeletonIndex) { - _parent.MetaState.EstCollection = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true); - var ret = ResolvePath(_parent.MetaState.EstCollection, _resolvePhybPathHook.Original(drawObject, pathBuffer, pathBufferSize, partialSkeletonIndex)); - _parent.MetaState.EstCollection = ResolveData.Invalid; + var collection = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true); + _parent.MetaState.EstCollection.Push(collection); + var ret = ResolvePath(collection, _resolvePhybPathHook.Original(drawObject, pathBuffer, pathBufferSize, partialSkeletonIndex)); + _parent.MetaState.EstCollection.Pop(); return ret; } private nint ResolveSklbHuman(nint drawObject, nint pathBuffer, nint pathBufferSize, uint partialSkeletonIndex) { - _parent.MetaState.EstCollection = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true); - var ret = ResolvePath(_parent.MetaState.EstCollection, _resolveSklbPathHook.Original(drawObject, pathBuffer, pathBufferSize, partialSkeletonIndex)); - _parent.MetaState.EstCollection = ResolveData.Invalid; + var collection = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true); + _parent.MetaState.EstCollection.Push(collection); + var ret = ResolvePath(collection, _resolveSklbPathHook.Original(drawObject, pathBuffer, pathBufferSize, partialSkeletonIndex)); + _parent.MetaState.EstCollection.Pop(); return ret; } private nint ResolveSkpHuman(nint drawObject, nint pathBuffer, nint pathBufferSize, uint partialSkeletonIndex) { - _parent.MetaState.EstCollection = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true); - var ret = ResolvePath(_parent.MetaState.EstCollection, _resolveSkpPathHook.Original(drawObject, pathBuffer, pathBufferSize, partialSkeletonIndex)); - _parent.MetaState.EstCollection = ResolveData.Invalid; + var collection = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true); + _parent.MetaState.EstCollection.Push(collection); + var ret = ResolvePath(collection, _resolveSkpPathHook.Original(drawObject, pathBuffer, pathBufferSize, partialSkeletonIndex)); + _parent.MetaState.EstCollection.Pop(); return ret; } diff --git a/Penumbra/Interop/PathResolving/MetaState.cs b/Penumbra/Interop/PathResolving/MetaState.cs index 3da94ce3..4bd23cf8 100644 --- a/Penumbra/Interop/PathResolving/MetaState.cs +++ b/Penumbra/Interop/PathResolving/MetaState.cs @@ -45,12 +45,14 @@ public sealed unsafe class MetaState : IDisposable private readonly CharacterUtility _characterUtility; private readonly CreateCharacterBase _createCharacterBase; - public ResolveData CustomizeChangeCollection = ResolveData.Invalid; - public ResolveData EqpCollection = ResolveData.Invalid; - public ResolveData GmpCollection = ResolveData.Invalid; - public ResolveData EstCollection = ResolveData.Invalid; - public ResolveData RspCollection = ResolveData.Invalid; - public PrimaryId UndividedGmpId = 0; + public ResolveData CustomizeChangeCollection = ResolveData.Invalid; + public readonly Stack EqpCollection = []; + public readonly Stack EqdpCollection = []; + public readonly Stack EstCollection = []; + public readonly Stack RspCollection = []; + + public readonly Stack<(ResolveData Collection, PrimaryId Id)> GmpCollection = []; + private ResolveData _lastCreatedCollection = ResolveData.Invalid; private DisposableContainer _characterBaseCreateMetaChanges = DisposableContainer.Empty; @@ -82,21 +84,6 @@ public sealed unsafe class MetaState : IDisposable return false; } - public DisposableContainer ResolveEqdpData(ModCollection collection, GenderRace race, bool equipment, bool accessory) - => (equipment, accessory) switch - { - (true, true) => new DisposableContainer(race.Dependencies().SelectMany(r => new[] - { - collection.TemporarilySetEqdpFile(_characterUtility, r, false), - collection.TemporarilySetEqdpFile(_characterUtility, r, true), - })), - (true, false) => new DisposableContainer(race.Dependencies() - .Select(r => collection.TemporarilySetEqdpFile(_characterUtility, r, false))), - (false, true) => new DisposableContainer(race.Dependencies() - .Select(r => collection.TemporarilySetEqdpFile(_characterUtility, r, true))), - _ => DisposableContainer.Empty, - }; - public DecalReverter ResolveDecal(ResolveData resolve, bool which) => new(_config, _characterUtility, _resources, resolve, which); @@ -130,7 +117,7 @@ public sealed unsafe class MetaState : IDisposable var decal = new DecalReverter(_config, _characterUtility, _resources, _lastCreatedCollection, UsesDecal(*(uint*)modelCharaId, (nint)customize)); - RspCollection = _lastCreatedCollection; + RspCollection.Push(_lastCreatedCollection); _characterBaseCreateMetaChanges.Dispose(); // Should always be empty. _characterBaseCreateMetaChanges = new DisposableContainer(decal); } @@ -142,7 +129,7 @@ public sealed unsafe class MetaState : IDisposable if (_lastCreatedCollection.Valid && _lastCreatedCollection.AssociatedGameObject != nint.Zero && drawObject != null) _communicator.CreatedCharacterBase.Invoke(_lastCreatedCollection.AssociatedGameObject, _lastCreatedCollection.ModCollection, (nint)drawObject); - RspCollection = ResolveData.Invalid; + RspCollection.Pop(); _lastCreatedCollection = ResolveData.Invalid; } diff --git a/Penumbra/Interop/Services/MetaList.cs b/Penumbra/Interop/Services/MetaList.cs index dc472b8e..24d3f088 100644 --- a/Penumbra/Interop/Services/MetaList.cs +++ b/Penumbra/Interop/Services/MetaList.cs @@ -87,7 +87,7 @@ public unsafe class MetaList : IDisposable => SetResourceInternal(_defaultResourceData, _defaultResourceSize); private void SetResourceToDefaultCollection() - => _utility.Active.Default.SetMetaFile(_utility, GlobalMetaIndex); + {} public void Dispose() { diff --git a/Penumbra/Penumbra.cs b/Penumbra/Penumbra.cs index 3bbfdf65..905b998d 100644 --- a/Penumbra/Penumbra.cs +++ b/Penumbra/Penumbra.cs @@ -144,7 +144,6 @@ public class Penumbra : IDalamudPlugin { if (_characterUtility.Ready) { - _collectionManager.Active.Default.SetFiles(_characterUtility); _residentResources.Reload(); _redrawService.RedrawAll(RedrawType.Redraw); } @@ -153,7 +152,6 @@ public class Penumbra : IDalamudPlugin { if (_characterUtility.Ready) { - _characterUtility.ResetAll(); _residentResources.Reload(); _redrawService.RedrawAll(RedrawType.Redraw); }