diff --git a/Penumbra.GameData b/Penumbra.GameData index c25ea7b1..94df458d 160000 --- a/Penumbra.GameData +++ b/Penumbra.GameData @@ -1 +1 @@ -Subproject commit c25ea7b19a6db37dd36e12b9a7a71f72a192ab57 +Subproject commit 94df458dfb2a704a611fa77d955808284aeb23ac diff --git a/Penumbra/Interop/Hooks/Meta/EstHook.cs b/Penumbra/Interop/Hooks/Meta/EstHook.cs index 5b272019..ce002664 100644 --- a/Penumbra/Interop/Hooks/Meta/EstHook.cs +++ b/Penumbra/Interop/Hooks/Meta/EstHook.cs @@ -3,51 +3,58 @@ using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; using Penumbra.Interop.PathResolving; +using Penumbra.Interop.Structs; using Penumbra.Meta.Manipulations; +using CharacterUtility = Penumbra.Interop.Services.CharacterUtility; namespace Penumbra.Interop.Hooks.Meta; -public class EstHook : FastHook, IDisposable +public unsafe class EstHook : FastHook, IDisposable { - public delegate EstEntry Delegate(uint id, int estType, uint genderRace); + public delegate EstEntry Delegate(ResourceHandle* estResource, uint id, uint genderRace); - private readonly MetaState _metaState; + private readonly CharacterUtility _characterUtility; + private readonly MetaState _metaState; - public EstHook(HookManager hooks, MetaState metaState) + public EstHook(HookManager hooks, MetaState metaState, CharacterUtility characterUtility) { - _metaState = metaState; - Task = hooks.CreateHook("GetEstEntry", Sigs.GetEstEntry, Detour, metaState.Config.EnableMods && HookSettings.MetaEntryHooks); + _metaState = metaState; + _characterUtility = characterUtility; + Task = hooks.CreateHook("FindEstEntry", Sigs.FindEstEntry, Detour, + metaState.Config.EnableMods && HookSettings.MetaEntryHooks); _metaState.Config.ModsEnabled += Toggle; } - private EstEntry Detour(uint genderRace, int estType, uint id) + private EstEntry Detour(ResourceHandle* estResource, uint genderRace, uint id) { EstEntry ret; if (_metaState.EstCollection.TryPeek(out var collection) && collection is { Valid: true, ModCollection.MetaCache: { } cache } - && cache.Est.TryGetValue(Convert(genderRace, estType, id), out var entry)) + && cache.Est.TryGetValue(Convert(estResource, genderRace, id), out var entry)) ret = entry.Entry; else - ret = Task.Result.Original(genderRace, estType, id); + ret = Task.Result.Original(estResource, genderRace, id); - Penumbra.Log.Excessive($"[GetEstEntry] Invoked with {genderRace}, {estType}, {id}, returned {ret.Value}."); + Penumbra.Log.Information($"[FindEstEntry] Invoked with 0x{(nint)estResource:X}, {genderRace}, {id}, returned {ret.Value}."); return ret; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static EstIdentifier Convert(uint genderRace, int estType, uint id) + private EstIdentifier Convert(ResourceHandle* estResource, uint genderRace, uint id) { var i = new PrimaryId((ushort)id); var gr = (GenderRace)genderRace; - var type = estType switch - { - 1 => EstType.Face, - 2 => EstType.Hair, - 3 => EstType.Head, - 4 => EstType.Body, - _ => (EstType)0, - }; - return new EstIdentifier(i, type, gr); + + if (estResource == _characterUtility.Address->BodyEstResource) + return new EstIdentifier(i, EstType.Body, gr); + if (estResource == _characterUtility.Address->HairEstResource) + return new EstIdentifier(i, EstType.Hair, gr); + if (estResource == _characterUtility.Address->FaceEstResource) + return new EstIdentifier(i, EstType.Face, gr); + if (estResource == _characterUtility.Address->HeadEstResource) + return new EstIdentifier(i, EstType.Head, gr); + + return new EstIdentifier(i, 0, gr); } public void Dispose() diff --git a/Penumbra/Interop/Hooks/Resources/ResolvePathHooksBase.cs b/Penumbra/Interop/Hooks/Resources/ResolvePathHooksBase.cs index 8fa6d861..e1b6e46e 100644 --- a/Penumbra/Interop/Hooks/Resources/ResolvePathHooksBase.cs +++ b/Penumbra/Interop/Hooks/Resources/ResolvePathHooksBase.cs @@ -19,6 +19,7 @@ public enum Type private delegate nint NamedResolveDelegate(nint drawObject, nint pathBuffer, nint pathBufferSize, uint slotIndex, nint name); private delegate nint PerSlotResolveDelegate(nint drawObject, nint pathBuffer, nint pathBufferSize, uint slotIndex); private delegate nint SingleResolveDelegate(nint drawObject, nint pathBuffer, nint pathBufferSize); + private delegate nint SkeletonVFuncDelegate(nint drawObject, int estType, nint unk); private delegate nint TmbResolveDelegate(nint drawObject, nint pathBuffer, nint pathBufferSize, nint timelineName); @@ -37,6 +38,8 @@ public enum Type private readonly Hook _resolveSkpPathHook; private readonly Hook _resolveTmbPathHook; private readonly Hook _resolveVfxPathHook; + private readonly Hook? _vFunc81Hook; + private readonly Hook? _vFunc83Hook; private readonly PathState _parent; @@ -49,6 +52,9 @@ public ResolvePathHooksBase(string name, HookManager hooks, PathState parent, ni _resolveSkpPathHook = Create($"{name}.{nameof(ResolveSkp)}", hooks, vTable[78], type, ResolveSkp, ResolveSkpHuman); _resolvePhybPathHook = Create($"{name}.{nameof(ResolvePhyb)}", hooks, vTable[79], type, ResolvePhyb, ResolvePhybHuman); + _vFunc81Hook = Create( $"{name}.{nameof(VFunc81)}", hooks, vTable[81], type, null, VFunc81); + + _vFunc83Hook = Create( $"{name}.{nameof(VFunc83)}", hooks, vTable[83], type, null, VFunc83); _resolvePapPathHook = Create( $"{name}.{nameof(ResolvePap)}", hooks, vTable[84], type, ResolvePap, ResolvePapHuman); _resolveTmbPathHook = Create( $"{name}.{nameof(ResolveTmb)}", hooks, vTable[85], ResolveTmb); @@ -58,6 +64,8 @@ public ResolvePathHooksBase(string name, HookManager hooks, PathState parent, ni _resolveDecalPathHook = Create($"{name}.{nameof(ResolveDecal)}", hooks, vTable[92], ResolveDecal); _resolveVfxPathHook = Create( $"{name}.{nameof(ResolveVfx)}", hooks, vTable[93], type, ResolveVfx, ResolveVfxHuman); _resolveEidPathHook = Create( $"{name}.{nameof(ResolveEid)}", hooks, vTable[94], ResolveEid); + + // @formatter:on if (HookSettings.ResourceHooks) Enable(); @@ -77,6 +85,8 @@ public void Enable() _resolveSkpPathHook.Enable(); _resolveTmbPathHook.Enable(); _resolveVfxPathHook.Enable(); + _vFunc81Hook?.Enable(); + _vFunc83Hook?.Enable(); } public void Disable() @@ -93,6 +103,8 @@ public void Disable() _resolveSkpPathHook.Disable(); _resolveTmbPathHook.Disable(); _resolveVfxPathHook.Disable(); + _vFunc81Hook?.Disable(); + _vFunc83Hook?.Disable(); } public void Dispose() @@ -109,6 +121,8 @@ public void Dispose() _resolveSkpPathHook.Dispose(); _resolveTmbPathHook.Dispose(); _resolveVfxPathHook.Dispose(); + _vFunc81Hook?.Dispose(); + _vFunc83Hook?.Dispose(); } private nint ResolveDecal(nint drawObject, nint pathBuffer, nint pathBufferSize, uint slotIndex) @@ -224,14 +238,36 @@ private nint ResolveVfxHuman(nint drawObject, nint pathBuffer, nint pathBufferSi return ResolvePath(drawObject, pathBuffer); } + private nint VFunc81(nint drawObject, int estType, nint unk) + { + var collection = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true); + _parent.MetaState.EstCollection.Push(collection); + var ret = _vFunc81Hook!.Original(drawObject, estType, unk); + _parent.MetaState.EstCollection.Pop(); + return ret; + } + + private nint VFunc83(nint drawObject, int estType, nint unk) + { + var collection = _parent.CollectionResolver.IdentifyCollection((DrawObject*)drawObject, true); + _parent.MetaState.EstCollection.Push(collection); + var ret = _vFunc83Hook!.Original(drawObject, estType, unk); + _parent.MetaState.EstCollection.Pop(); + return ret; + } + [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - private static Hook Create(string name, HookManager hooks, nint address, Type type, T other, T human) where T : Delegate + [return: NotNullIfNotNull(nameof(other))] + private static Hook? Create(string name, HookManager hooks, nint address, Type type, T? other, T human) where T : Delegate { var del = type switch { Type.Human => human, _ => other, }; + if (del == null) + return null; + return hooks.CreateHook(name, address, del).Result; }