Add and rework hooks around EST entries.

This commit is contained in:
Ottermandias 2024-07-17 18:02:48 +02:00
parent 1922353ba3
commit e7c786b239
3 changed files with 65 additions and 22 deletions

@ -1 +1 @@
Subproject commit c25ea7b19a6db37dd36e12b9a7a71f72a192ab57 Subproject commit 94df458dfb2a704a611fa77d955808284aeb23ac

View file

@ -3,51 +3,58 @@ using Penumbra.GameData;
using Penumbra.GameData.Enums; using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs; using Penumbra.GameData.Structs;
using Penumbra.Interop.PathResolving; using Penumbra.Interop.PathResolving;
using Penumbra.Interop.Structs;
using Penumbra.Meta.Manipulations; using Penumbra.Meta.Manipulations;
using CharacterUtility = Penumbra.Interop.Services.CharacterUtility;
namespace Penumbra.Interop.Hooks.Meta; namespace Penumbra.Interop.Hooks.Meta;
public class EstHook : FastHook<EstHook.Delegate>, IDisposable public unsafe class EstHook : FastHook<EstHook.Delegate>, 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; _metaState = metaState;
Task = hooks.CreateHook<Delegate>("GetEstEntry", Sigs.GetEstEntry, Detour, metaState.Config.EnableMods && HookSettings.MetaEntryHooks); _characterUtility = characterUtility;
Task = hooks.CreateHook<Delegate>("FindEstEntry", Sigs.FindEstEntry, Detour,
metaState.Config.EnableMods && HookSettings.MetaEntryHooks);
_metaState.Config.ModsEnabled += Toggle; _metaState.Config.ModsEnabled += Toggle;
} }
private EstEntry Detour(uint genderRace, int estType, uint id) private EstEntry Detour(ResourceHandle* estResource, uint genderRace, uint id)
{ {
EstEntry ret; EstEntry ret;
if (_metaState.EstCollection.TryPeek(out var collection) if (_metaState.EstCollection.TryPeek(out var collection)
&& collection is { Valid: true, ModCollection.MetaCache: { } cache } && 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; ret = entry.Entry;
else 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; return ret;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [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 i = new PrimaryId((ushort)id);
var gr = (GenderRace)genderRace; var gr = (GenderRace)genderRace;
var type = estType switch
{ if (estResource == _characterUtility.Address->BodyEstResource)
1 => EstType.Face, return new EstIdentifier(i, EstType.Body, gr);
2 => EstType.Hair, if (estResource == _characterUtility.Address->HairEstResource)
3 => EstType.Head, return new EstIdentifier(i, EstType.Hair, gr);
4 => EstType.Body, if (estResource == _characterUtility.Address->FaceEstResource)
_ => (EstType)0, return new EstIdentifier(i, EstType.Face, gr);
}; if (estResource == _characterUtility.Address->HeadEstResource)
return new EstIdentifier(i, type, gr); return new EstIdentifier(i, EstType.Head, gr);
return new EstIdentifier(i, 0, gr);
} }
public void Dispose() public void Dispose()

View file

@ -19,6 +19,7 @@ public sealed unsafe class ResolvePathHooksBase : IDisposable
private delegate nint NamedResolveDelegate(nint drawObject, nint pathBuffer, nint pathBufferSize, uint slotIndex, nint name); 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 PerSlotResolveDelegate(nint drawObject, nint pathBuffer, nint pathBufferSize, uint slotIndex);
private delegate nint SingleResolveDelegate(nint drawObject, nint pathBuffer, nint pathBufferSize); 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); private delegate nint TmbResolveDelegate(nint drawObject, nint pathBuffer, nint pathBufferSize, nint timelineName);
@ -37,6 +38,8 @@ public sealed unsafe class ResolvePathHooksBase : IDisposable
private readonly Hook<PerSlotResolveDelegate> _resolveSkpPathHook; private readonly Hook<PerSlotResolveDelegate> _resolveSkpPathHook;
private readonly Hook<TmbResolveDelegate> _resolveTmbPathHook; private readonly Hook<TmbResolveDelegate> _resolveTmbPathHook;
private readonly Hook<VfxResolveDelegate> _resolveVfxPathHook; private readonly Hook<VfxResolveDelegate> _resolveVfxPathHook;
private readonly Hook<SkeletonVFuncDelegate>? _vFunc81Hook;
private readonly Hook<SkeletonVFuncDelegate>? _vFunc83Hook;
private readonly PathState _parent; private readonly PathState _parent;
@ -49,6 +52,9 @@ public sealed unsafe class ResolvePathHooksBase : IDisposable
_resolveSkpPathHook = Create<PerSlotResolveDelegate>($"{name}.{nameof(ResolveSkp)}", hooks, vTable[78], type, ResolveSkp, ResolveSkpHuman); _resolveSkpPathHook = Create<PerSlotResolveDelegate>($"{name}.{nameof(ResolveSkp)}", hooks, vTable[78], type, ResolveSkp, ResolveSkpHuman);
_resolvePhybPathHook = Create<PerSlotResolveDelegate>($"{name}.{nameof(ResolvePhyb)}", hooks, vTable[79], type, ResolvePhyb, ResolvePhybHuman); _resolvePhybPathHook = Create<PerSlotResolveDelegate>($"{name}.{nameof(ResolvePhyb)}", hooks, vTable[79], type, ResolvePhyb, ResolvePhybHuman);
_vFunc81Hook = Create<SkeletonVFuncDelegate>( $"{name}.{nameof(VFunc81)}", hooks, vTable[81], type, null, VFunc81);
_vFunc83Hook = Create<SkeletonVFuncDelegate>( $"{name}.{nameof(VFunc83)}", hooks, vTable[83], type, null, VFunc83);
_resolvePapPathHook = Create<NamedResolveDelegate>( $"{name}.{nameof(ResolvePap)}", hooks, vTable[84], type, ResolvePap, ResolvePapHuman); _resolvePapPathHook = Create<NamedResolveDelegate>( $"{name}.{nameof(ResolvePap)}", hooks, vTable[84], type, ResolvePap, ResolvePapHuman);
_resolveTmbPathHook = Create<TmbResolveDelegate>( $"{name}.{nameof(ResolveTmb)}", hooks, vTable[85], ResolveTmb); _resolveTmbPathHook = Create<TmbResolveDelegate>( $"{name}.{nameof(ResolveTmb)}", hooks, vTable[85], ResolveTmb);
@ -58,6 +64,8 @@ public sealed unsafe class ResolvePathHooksBase : IDisposable
_resolveDecalPathHook = Create<PerSlotResolveDelegate>($"{name}.{nameof(ResolveDecal)}", hooks, vTable[92], ResolveDecal); _resolveDecalPathHook = Create<PerSlotResolveDelegate>($"{name}.{nameof(ResolveDecal)}", hooks, vTable[92], ResolveDecal);
_resolveVfxPathHook = Create<VfxResolveDelegate>( $"{name}.{nameof(ResolveVfx)}", hooks, vTable[93], type, ResolveVfx, ResolveVfxHuman); _resolveVfxPathHook = Create<VfxResolveDelegate>( $"{name}.{nameof(ResolveVfx)}", hooks, vTable[93], type, ResolveVfx, ResolveVfxHuman);
_resolveEidPathHook = Create<SingleResolveDelegate>( $"{name}.{nameof(ResolveEid)}", hooks, vTable[94], ResolveEid); _resolveEidPathHook = Create<SingleResolveDelegate>( $"{name}.{nameof(ResolveEid)}", hooks, vTable[94], ResolveEid);
// @formatter:on // @formatter:on
if (HookSettings.ResourceHooks) if (HookSettings.ResourceHooks)
Enable(); Enable();
@ -77,6 +85,8 @@ public sealed unsafe class ResolvePathHooksBase : IDisposable
_resolveSkpPathHook.Enable(); _resolveSkpPathHook.Enable();
_resolveTmbPathHook.Enable(); _resolveTmbPathHook.Enable();
_resolveVfxPathHook.Enable(); _resolveVfxPathHook.Enable();
_vFunc81Hook?.Enable();
_vFunc83Hook?.Enable();
} }
public void Disable() public void Disable()
@ -93,6 +103,8 @@ public sealed unsafe class ResolvePathHooksBase : IDisposable
_resolveSkpPathHook.Disable(); _resolveSkpPathHook.Disable();
_resolveTmbPathHook.Disable(); _resolveTmbPathHook.Disable();
_resolveVfxPathHook.Disable(); _resolveVfxPathHook.Disable();
_vFunc81Hook?.Disable();
_vFunc83Hook?.Disable();
} }
public void Dispose() public void Dispose()
@ -109,6 +121,8 @@ public sealed unsafe class ResolvePathHooksBase : IDisposable
_resolveSkpPathHook.Dispose(); _resolveSkpPathHook.Dispose();
_resolveTmbPathHook.Dispose(); _resolveTmbPathHook.Dispose();
_resolveVfxPathHook.Dispose(); _resolveVfxPathHook.Dispose();
_vFunc81Hook?.Dispose();
_vFunc83Hook?.Dispose();
} }
private nint ResolveDecal(nint drawObject, nint pathBuffer, nint pathBufferSize, uint slotIndex) private nint ResolveDecal(nint drawObject, nint pathBuffer, nint pathBufferSize, uint slotIndex)
@ -224,14 +238,36 @@ public sealed unsafe class ResolvePathHooksBase : IDisposable
return ResolvePath(drawObject, pathBuffer); 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)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
private static Hook<T> Create<T>(string name, HookManager hooks, nint address, Type type, T other, T human) where T : Delegate [return: NotNullIfNotNull(nameof(other))]
private static Hook<T>? Create<T>(string name, HookManager hooks, nint address, Type type, T? other, T human) where T : Delegate
{ {
var del = type switch var del = type switch
{ {
Type.Human => human, Type.Human => human,
_ => other, _ => other,
}; };
if (del == null)
return null;
return hooks.CreateHook(name, address, del).Result; return hooks.CreateHook(name, address, del).Result;
} }