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.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<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;
Task = hooks.CreateHook<Delegate>("GetEstEntry", Sigs.GetEstEntry, Detour, metaState.Config.EnableMods && HookSettings.MetaEntryHooks);
_metaState = metaState;
_characterUtility = characterUtility;
Task = hooks.CreateHook<Delegate>("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()

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 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 sealed unsafe class ResolvePathHooksBase : IDisposable
private readonly Hook<PerSlotResolveDelegate> _resolveSkpPathHook;
private readonly Hook<TmbResolveDelegate> _resolveTmbPathHook;
private readonly Hook<VfxResolveDelegate> _resolveVfxPathHook;
private readonly Hook<SkeletonVFuncDelegate>? _vFunc81Hook;
private readonly Hook<SkeletonVFuncDelegate>? _vFunc83Hook;
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);
_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);
_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);
_resolveVfxPathHook = Create<VfxResolveDelegate>( $"{name}.{nameof(ResolveVfx)}", hooks, vTable[93], type, ResolveVfx, ResolveVfxHuman);
_resolveEidPathHook = Create<SingleResolveDelegate>( $"{name}.{nameof(ResolveEid)}", hooks, vTable[94], ResolveEid);
// @formatter:on
if (HookSettings.ResourceHooks)
Enable();
@ -77,6 +85,8 @@ public sealed unsafe class ResolvePathHooksBase : IDisposable
_resolveSkpPathHook.Enable();
_resolveTmbPathHook.Enable();
_resolveVfxPathHook.Enable();
_vFunc81Hook?.Enable();
_vFunc83Hook?.Enable();
}
public void Disable()
@ -93,6 +103,8 @@ public sealed unsafe class ResolvePathHooksBase : IDisposable
_resolveSkpPathHook.Disable();
_resolveTmbPathHook.Disable();
_resolveVfxPathHook.Disable();
_vFunc81Hook?.Disable();
_vFunc83Hook?.Disable();
}
public void Dispose()
@ -109,6 +121,8 @@ public sealed unsafe class ResolvePathHooksBase : IDisposable
_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 @@ public sealed unsafe class ResolvePathHooksBase : IDisposable
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<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
{
Type.Human => human,
_ => other,
};
if (del == null)
return null;
return hooks.CreateHook(name, address, del).Result;
}