mirror of
https://github.com/xivdev/Penumbra.git
synced 2026-02-20 14:57:50 +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 +1 @@
|
||||||
Subproject commit a1e637f835c1a42732825e8e0690aeef0024b101
|
Subproject commit 9f1816f1b75003d01c5576769831c10f3d8948a7
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
using Dalamud.Hooking;
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||||
using OtterGui.Services;
|
using OtterGui.Services;
|
||||||
|
|
@ -11,33 +10,48 @@ using Penumbra.Services;
|
||||||
namespace Penumbra.Interop.Hooks.Animation;
|
namespace Penumbra.Interop.Hooks.Animation;
|
||||||
|
|
||||||
/// <summary> Called for some sound effects caused by animations or VFX. </summary>
|
/// <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 GameState _state;
|
||||||
private readonly CollectionResolver _collectionResolver;
|
private readonly CollectionResolver _collectionResolver;
|
||||||
private readonly CrashHandlerService _crashHandler;
|
private readonly CrashHandlerService _crashHandler;
|
||||||
|
|
||||||
// TODO because of inlining.
|
public ApricotListenerSoundPlayCaller(HookManager hooks, GameState state, CollectionResolver collectionResolver,
|
||||||
public ApricotListenerSoundPlay(HookManager hooks, GameState state, CollectionResolver collectionResolver, CrashHandlerService crashHandler)
|
CrashHandlerService crashHandler)
|
||||||
{
|
{
|
||||||
_state = state;
|
_state = state;
|
||||||
_collectionResolver = collectionResolver;
|
_collectionResolver = collectionResolver;
|
||||||
_crashHandler = crashHandler;
|
_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)]
|
[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}.");
|
// Short-circuiting and sanity checks done by game.
|
||||||
if (a6 == nint.Zero)
|
var playTime = a1 == nint.Zero ? -1 : *(float*)(a1 + 0x250);
|
||||||
return Task.Result.Original(a1, a2, a3, a4, a5, a6);
|
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 someIntermediate = *(nint*)(a1 + 0x1F8);
|
||||||
var gameObject = (*(delegate* unmanaged<nint, GameObject*>**)a6)[1](a6);
|
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 newData = ResolveData.Invalid;
|
||||||
|
var gameObject = (*(delegate* unmanaged<nint, GameObject*>**)apricotIInstanceListenner)[1](apricotIInstanceListenner);
|
||||||
if (gameObject != null)
|
if (gameObject != null)
|
||||||
{
|
{
|
||||||
newData = _collectionResolver.IdentifyCollection(gameObject, true);
|
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,
|
// 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,
|
// 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.
|
// 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)
|
if (drawObject != null)
|
||||||
newData = _collectionResolver.IdentifyCollection(drawObject, true);
|
newData = _collectionResolver.IdentifyCollection(drawObject, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
_crashHandler.LogAnimation(newData.AssociatedGameObject, newData.ModCollection, AnimationInvocationType.ApricotSoundPlay);
|
_crashHandler.LogAnimation(newData.AssociatedGameObject, newData.ModCollection, AnimationInvocationType.ApricotSoundPlay);
|
||||||
var last = _state.SetAnimationData(newData);
|
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);
|
_state.RestoreAnimationData(last);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue