using Luna; using Penumbra.GameData; using Penumbra.GameData.Enums; using Penumbra.Interop.PathResolving; using Penumbra.Meta; using Penumbra.Meta.Files; using Penumbra.Meta.Manipulations; namespace Penumbra.Interop.Hooks.Meta; public class RspTailHook : FastHook, IDisposable { public delegate float Delegate(nint cmpResource, Race clan, byte gender, byte isSecondSubRace, byte bodyType, byte height); private readonly MetaState _metaState; private readonly MetaFileManager _metaFileManager; public RspTailHook(HookManager hooks, MetaState metaState, MetaFileManager metaFileManager) { _metaState = metaState; _metaFileManager = metaFileManager; Task = hooks.CreateHook("GetRspTail", Sigs.GetRspTail, Detour, metaState.Config.EnableMods && !HookOverrides.Instance.Meta.RspTailHook); if (!HookOverrides.Instance.Meta.RspTailHook) _metaState.Config.ModsEnabled += Set; } private unsafe float Detour(nint cmpResource, Race race, byte gender, byte isSecondSubRace, byte bodyType, byte tailLength) { float scale; 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 ? (new RspIdentifier(clan, RspAttribute.MaleMinTail), new RspIdentifier(clan, RspAttribute.MaleMaxTail)) : (new RspIdentifier(clan, RspAttribute.FemaleMinTail), new RspIdentifier(clan, RspAttribute.FemaleMaxTail)); float minEntry, maxEntry; if (cache.Rsp.TryGetValue(minIdent, out var min)) { minEntry = min.Entry.Value; maxEntry = cache.Rsp.TryGetValue(maxIdent, out var max) ? max.Entry.Value : CmpFile.GetDefault(_metaFileManager, minIdent.SubRace, maxIdent.Attribute).Value; } else { var ptr = CmpFile.GetDefaults(_metaFileManager, minIdent.SubRace, minIdent.Attribute); if (cache.Rsp.TryGetValue(maxIdent, out var max)) { minEntry = ptr->Value; maxEntry = max.Entry.Value; } else { minEntry = ptr[0].Value; maxEntry = ptr[1].Value; } } scale = (maxEntry - minEntry) * tailLength / 100f + minEntry; } else { scale = Task.Result.Original(cmpResource, race, gender, isSecondSubRace, bodyType, tailLength); } Penumbra.Log.Excessive( $"[GetRspTail] Invoked on 0x{cmpResource:X} with {race}, {(Gender)(gender + 1)}, {isSecondSubRace == 1}, {bodyType}, {tailLength}, returned {scale}."); return scale; } public void Dispose() => _metaState.Config.ModsEnabled -= Set; }