diff --git a/Penumbra.GameData b/Penumbra.GameData
index a1e637f8..9f1816f1 160000
--- a/Penumbra.GameData
+++ b/Penumbra.GameData
@@ -1 +1 @@
-Subproject commit a1e637f835c1a42732825e8e0690aeef0024b101
+Subproject commit 9f1816f1b75003d01c5576769831c10f3d8948a7
diff --git a/Penumbra/Interop/Hooks/Animation/ApricotListenerSoundPlay.cs b/Penumbra/Interop/Hooks/Animation/ApricotListenerSoundPlay.cs
index 2e05c1b6..361fcd4e 100644
--- a/Penumbra/Interop/Hooks/Animation/ApricotListenerSoundPlay.cs
+++ b/Penumbra/Interop/Hooks/Animation/ApricotListenerSoundPlay.cs
@@ -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;
/// Called for some sound effects caused by animations or VFX.
-public sealed unsafe class ApricotListenerSoundPlay : FastHook
+/// Actual function got inlined.
+public sealed unsafe class ApricotListenerSoundPlayCaller : FastHook
{
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("Apricot Listener Sound Play", Sigs.ApricotListenerSoundPlay, Detour, HookSettings.VfxIdentificationHooks);
+ Task = hooks.CreateHook("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**)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**)apricotIInstanceListenner)[1](apricotIInstanceListenner);
if (gameObject != null)
{
newData = _collectionResolver.IdentifyCollection(gameObject, true);
@@ -47,14 +61,14 @@ public sealed unsafe class ApricotListenerSoundPlay : FastHook