diff --git a/Penumbra.GameData b/Penumbra.GameData index ec35e664..539d1387 160000 --- a/Penumbra.GameData +++ b/Penumbra.GameData @@ -1 +1 @@ -Subproject commit ec35e66499eb388b4e7917e4fae4615218d33335 +Subproject commit 539d138700543e7c2c6c918f9f68e33228111e4d diff --git a/Penumbra/Interop/Hooks/Meta/EqpHook.cs b/Penumbra/Interop/Hooks/Meta/EqpHook.cs new file mode 100644 index 00000000..457b9428 --- /dev/null +++ b/Penumbra/Interop/Hooks/Meta/EqpHook.cs @@ -0,0 +1,34 @@ +using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; +using OtterGui.Services; +using Penumbra.GameData.Structs; +using Penumbra.Interop.PathResolving; + +namespace Penumbra.Interop.Hooks.Meta; + +public unsafe class EqpHook : FastHook +{ + public delegate void Delegate(CharacterUtility* utility, EqpEntry* flags, CharacterArmor* armor); + + private readonly MetaState _metaState; + + public EqpHook(HookManager hooks, MetaState metaState) + { + _metaState = metaState; + Task = hooks.CreateHook("GetEqpFlags", "E8 ?? ?? ?? ?? 0F B6 44 24 ?? C0 E8", Detour, true); + } + + private void Detour(CharacterUtility* utility, EqpEntry* flags, CharacterArmor* armor) + { + if (_metaState.EqpCollection.Valid) + { + using var eqp = _metaState.ResolveEqpData(_metaState.EqpCollection.ModCollection); + Task.Result.Original(utility, flags, armor); + } + else + { + Task.Result.Original(utility, flags, armor); + } + + Penumbra.Log.Excessive($"[GetEqpFlags] Invoked on 0x{(nint)utility:X} with 0x{(ulong)armor:X}, returned 0x{(ulong)*flags:X16}."); + } +} diff --git a/Penumbra/Interop/Hooks/Meta/GetEqpIndirect.cs b/Penumbra/Interop/Hooks/Meta/GetEqpIndirect.cs index 8ffc050f..beae6acc 100644 --- a/Penumbra/Interop/Hooks/Meta/GetEqpIndirect.cs +++ b/Penumbra/Interop/Hooks/Meta/GetEqpIndirect.cs @@ -1,5 +1,6 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using OtterGui.Services; +using Penumbra.Collections; using Penumbra.GameData; using Penumbra.Interop.PathResolving; @@ -28,8 +29,8 @@ public sealed unsafe class GetEqpIndirect : FastHook return; Penumbra.Log.Excessive($"[Get EQP Indirect] Invoked on {(nint)drawObject:X}."); - var collection = _collectionResolver.IdentifyCollection(drawObject, true); - using var eqp = _metaState.ResolveEqpData(collection.ModCollection); + _metaState.EqpCollection = _collectionResolver.IdentifyCollection(drawObject, true); Task.Result.Original(drawObject); + _metaState.EqpCollection = ResolveData.Invalid; } } diff --git a/Penumbra/Interop/Hooks/Meta/GetEqpIndirect2.cs b/Penumbra/Interop/Hooks/Meta/GetEqpIndirect2.cs new file mode 100644 index 00000000..89aaa9b0 --- /dev/null +++ b/Penumbra/Interop/Hooks/Meta/GetEqpIndirect2.cs @@ -0,0 +1,36 @@ +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; + private readonly MetaState _metaState; + + public GetEqpIndirect2(HookManager hooks, CollectionResolver collectionResolver, MetaState metaState) + { + _collectionResolver = collectionResolver; + _metaState = metaState; + Task = hooks.CreateHook("Get EQP Indirect 2", Sigs.GetEqpIndirect2, Detour, true); + } + + public delegate void Delegate(DrawObject* drawObject); + + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] + private void Detour(DrawObject* drawObject) + { + // Shortcut because this is also called all the time. + // Same thing is checked at the beginning of the original function. + if (((*(uint*)((nint)drawObject + Offsets.GetEqpIndirect2Skip) >> 0x12) & 1) == 0) + return; + + Penumbra.Log.Excessive($"[Get EQP Indirect 2] Invoked on {(nint)drawObject:X}."); + _metaState.EqpCollection = _collectionResolver.IdentifyCollection(drawObject, true); + Task.Result.Original(drawObject); + _metaState.EqpCollection = ResolveData.Invalid; + } +} diff --git a/Penumbra/Interop/Hooks/Meta/ModelLoadComplete.cs b/Penumbra/Interop/Hooks/Meta/ModelLoadComplete.cs index 9f191fdd..10c12594 100644 --- a/Penumbra/Interop/Hooks/Meta/ModelLoadComplete.cs +++ b/Penumbra/Interop/Hooks/Meta/ModelLoadComplete.cs @@ -1,5 +1,6 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Scene; using OtterGui.Services; +using Penumbra.Collections; using Penumbra.Interop.PathResolving; namespace Penumbra.Interop.Hooks.Meta; @@ -23,8 +24,9 @@ public sealed unsafe class ModelLoadComplete : FastHook Penumbra.Log.Excessive($"[Update Model] Invoked on {(nint)drawObject:X}."); var collection = _collectionResolver.IdentifyCollection(drawObject, true); - using var eqp = _metaState.ResolveEqpData(collection.ModCollection); using var eqdp = _metaState.ResolveEqdpData(collection.ModCollection, MetaState.GetDrawObjectGenderRace((nint)drawObject), true, true); - Task.Result.Original.Invoke(drawObject); + _metaState.EqpCollection = collection; + Task.Result.Original(drawObject); + _metaState.EqpCollection = ResolveData.Invalid; } } diff --git a/Penumbra/Interop/PathResolving/MetaState.cs b/Penumbra/Interop/PathResolving/MetaState.cs index 5f07ffc5..6fa5c263 100644 --- a/Penumbra/Interop/PathResolving/MetaState.cs +++ b/Penumbra/Interop/PathResolving/MetaState.cs @@ -46,6 +46,7 @@ public sealed unsafe class MetaState : IDisposable private readonly CreateCharacterBase _createCharacterBase; public ResolveData CustomizeChangeCollection = ResolveData.Invalid; + public ResolveData EqpCollection = ResolveData.Invalid; private ResolveData _lastCreatedCollection = ResolveData.Invalid; private DisposableContainer _characterBaseCreateMetaChanges = DisposableContainer.Empty;