mirror of
https://github.com/xivdev/Penumbra.git
synced 2026-01-03 14:23:43 +01:00
Reinstate the inlined ApricotSoundPlay hook one layer hup.
This commit is contained in:
parent
a4548bbf04
commit
258f7e9732
2 changed files with 29 additions and 15 deletions
|
|
@ -1,4 +1,3 @@
|
|||
using Dalamud.Hooking;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||
using OtterGui.Services;
|
||||
|
|
@ -11,33 +10,48 @@ using Penumbra.Services;
|
|||
namespace Penumbra.Interop.Hooks.Animation;
|
||||
|
||||
/// <summary> Called for some sound effects caused by animations or VFX. </summary>
|
||||
public sealed unsafe class ApricotListenerSoundPlay : FastHook<ApricotListenerSoundPlay.Delegate>
|
||||
/// <remarks> Actual function got inlined. </remarks>
|
||||
public sealed unsafe class ApricotListenerSoundPlayCaller : FastHook<ApricotListenerSoundPlayCaller.Delegate>
|
||||
{
|
||||
private readonly GameState _state;
|
||||
private readonly CollectionResolver _collectionResolver;
|
||||
private readonly CrashHandlerService _crashHandler;
|
||||
|
||||
// TODO because of inlining.
|
||||
public ApricotListenerSoundPlay(HookManager hooks, GameState state, CollectionResolver collectionResolver, CrashHandlerService crashHandler)
|
||||
public ApricotListenerSoundPlayCaller(HookManager hooks, GameState state, CollectionResolver collectionResolver,
|
||||
CrashHandlerService crashHandler)
|
||||
{
|
||||
_state = state;
|
||||
_collectionResolver = collectionResolver;
|
||||
_crashHandler = crashHandler;
|
||||
Task = hooks.CreateHook<Delegate>("Apricot Listener Sound Play", Sigs.ApricotListenerSoundPlay, Detour, HookSettings.VfxIdentificationHooks);
|
||||
Task = hooks.CreateHook<Delegate>("Apricot Listener Sound Play Caller", Sigs.ApricotListenerSoundPlayCaller, Detour,
|
||||
true); //HookSettings.VfxIdentificationHooks);
|
||||
}
|
||||
|
||||
public delegate nint Delegate(nint a1, nint a2, nint a3, nint a4, nint a5, nint a6);
|
||||
public delegate nint Delegate(nint a1, nint a2, float a3);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
private nint Detour(nint a1, nint a2, nint a3, nint a4, nint a5, nint a6)
|
||||
private nint Detour(nint a1, nint unused, float timeOffset)
|
||||
{
|
||||
Penumbra.Log.Excessive($"[Apricot Listener Sound Play] Invoked on 0x{a1:X} with {a2}, {a3}, {a4}, {a5}, {a6}.");
|
||||
if (a6 == nint.Zero)
|
||||
return Task.Result.Original(a1, a2, a3, a4, a5, a6);
|
||||
// Short-circuiting and sanity checks done by game.
|
||||
var playTime = a1 == nint.Zero ? -1 : *(float*)(a1 + 0x250);
|
||||
if (playTime < 0)
|
||||
return Task.Result.Original(a1, unused, timeOffset);
|
||||
|
||||
// a6 is some instance of Apricot.IInstanceListenner, in some cases we can obtain the associated caster via vfunc 1.
|
||||
var gameObject = (*(delegate* unmanaged<nint, GameObject*>**)a6)[1](a6);
|
||||
var someIntermediate = *(nint*)(a1 + 0x1F8);
|
||||
var flags = someIntermediate == nint.Zero ? (ushort)0 : *(ushort*)(someIntermediate + 0x49C);
|
||||
if (((flags >> 13) & 1) == 0)
|
||||
return Task.Result.Original(a1, unused, timeOffset);
|
||||
|
||||
Penumbra.Log.Information(
|
||||
$"[Apricot Listener Sound Play Caller] Invoked on 0x{a1:X} with {unused}, {timeOffset}.");
|
||||
// Fetch the IInstanceListenner (sixth argument to inlined call of SoundPlay)
|
||||
var apricotIInstanceListenner = *(nint*)(someIntermediate + 0x270);
|
||||
if (apricotIInstanceListenner == nint.Zero)
|
||||
return Task.Result.Original(a1, unused, timeOffset);
|
||||
|
||||
// In some cases we can obtain the associated caster via vfunc 1.
|
||||
var newData = ResolveData.Invalid;
|
||||
var gameObject = (*(delegate* unmanaged<nint, GameObject*>**)apricotIInstanceListenner)[1](apricotIInstanceListenner);
|
||||
if (gameObject != null)
|
||||
{
|
||||
newData = _collectionResolver.IdentifyCollection(gameObject, true);
|
||||
|
|
@ -47,14 +61,14 @@ public sealed unsafe class ApricotListenerSoundPlay : FastHook<ApricotListenerSo
|
|||
// for VfxListenner we can obtain the associated draw object as its first member,
|
||||
// if the object has different type, drawObject will contain other values or garbage,
|
||||
// but only be used in a dictionary pointer lookup, so this does not hurt.
|
||||
var drawObject = ((DrawObject**)a6)[1];
|
||||
var drawObject = ((DrawObject**)apricotIInstanceListenner)[1];
|
||||
if (drawObject != null)
|
||||
newData = _collectionResolver.IdentifyCollection(drawObject, true);
|
||||
}
|
||||
|
||||
_crashHandler.LogAnimation(newData.AssociatedGameObject, newData.ModCollection, AnimationInvocationType.ApricotSoundPlay);
|
||||
var last = _state.SetAnimationData(newData);
|
||||
var ret = Task.Result.Original(a1, a2, a3, a4, a5, a6);
|
||||
var ret = Task.Result.Original(a1, unused, timeOffset);
|
||||
_state.RestoreAnimationData(last);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue