diff --git a/Penumbra/Interop/Resolver/PathResolver.Animation.cs b/Penumbra/Interop/Resolver/PathResolver.Animation.cs index 798929a7..026c4a5a 100644 --- a/Penumbra/Interop/Resolver/PathResolver.Animation.cs +++ b/Penumbra/Interop/Resolver/PathResolver.Animation.cs @@ -8,55 +8,54 @@ namespace Penumbra.Interop.Resolver; public unsafe partial class PathResolver { + private ModCollection? _animationLoadCollection; + + public delegate byte LoadTimelineResourcesDelegate( IntPtr timeline ); + + // The timeline object loads the requested .tmb and .pap files. The .tmb files load the respective .avfx files. + // We can obtain the associated game object from the timelines 28'th vfunc and use that to apply the correct collection. + [Signature( "E8 ?? ?? ?? ?? 83 7F ?? ?? 75 ?? 0F B6 87", DetourName = nameof( LoadTimelineResourcesDetour ) )] + public Hook< LoadTimelineResourcesDelegate >? LoadTimelineResourcesHook; + + private byte LoadTimelineResourcesDetour( IntPtr timeline ) + { + byte ret; + var old = _animationLoadCollection; + try + { + var getGameObjectIdx = ( ( delegate* unmanaged < IntPtr, int>** )timeline )[ 0 ][ 28 ]; + var idx = getGameObjectIdx( timeline ); + if( idx >= 0 && idx < Dalamud.Objects.Length ) + { + var obj = Dalamud.Objects[ idx ]; + _animationLoadCollection = obj != null ? IdentifyCollection( ( GameObject* )obj.Address ) : null; + } + else + { + _animationLoadCollection = null; + } + } + finally + { + ret = LoadTimelineResourcesHook!.Original( timeline ); + } + + _animationLoadCollection = old; + + return ret; + } + // Probably used when the base idle animation gets loaded. // Make it aware of the correct collection to load the correct pap files. - //[Signature( "E8 ?? ?? ?? ?? BA ?? ?? ?? ?? 48 8B CF 44 8B C2 E8 ?? ?? ?? ?? 48 8B 05", DetourName = "CharacterBaseLoadAnimationDetour" )] - //public Hook< CharacterBaseDestructorDelegate >? CharacterBaseLoadAnimationHook; - // - //private ModCollection? _animationLoadCollection; - // - //private void CharacterBaseLoadAnimationDetour( IntPtr drawObject ) - //{ - // _animationLoadCollection = _lastCreatedCollection - // ?? ( FindParent( drawObject, out var collection ) != null ? collection : Penumbra.CollectionManager.Default ); - // CharacterBaseLoadAnimationHook!.Original( drawObject ); - // _animationLoadCollection = null; - //} + [Signature( "E8 ?? ?? ?? ?? BA ?? ?? ?? ?? 48 8B CF 44 8B C2 E8 ?? ?? ?? ?? 48 8B 05", DetourName = "CharacterBaseLoadAnimationDetour" )] + public Hook< CharacterBaseDestructorDelegate >? CharacterBaseLoadAnimationHook; - // Probably used when action paps are loaded. - // Make it aware of the correct collection to load the correct pap files. - //public delegate void PapLoadFunction( IntPtr drawObject, IntPtr a2, uint a3, IntPtr a4, uint a5, uint a6, uint a7 ); - - //[Signature( "E8 ?? ?? ?? ?? 0F 10 00 0F 11 06", DetourName = "RandomPapDetour" )] - //public Hook< PapLoadFunction >? RandomPapHook; - - //private void RandomPapDetour( IntPtr drawObject, IntPtr a2, uint a3, IntPtr a4, uint a5, uint a6, uint a7 ) - //{ - // _animationLoadCollection = _lastCreatedCollection - // ?? ( FindParent( drawObject, out var collection ) != null ? collection : Penumbra.CollectionManager.Default ); - // RandomPapHook!.Original( drawObject, a2, a3, a4, a5, a6, a7 ); - // _animationLoadCollection = null; - //} - - //private void TestFunction() - //{ - // var p = Dalamud.Objects.FirstOrDefault( o => o.Name.ToString() == "Demi-Phoenix" ); - // if( p != null ) - // { - // var draw = ( ( GameObject* )p.Address )->DrawObject; - // PluginLog.Information( $"{p.Address:X} {( draw != null ? ( ( IntPtr )draw ).ToString( "X" ) : "NULL" )}" ); - // } - //} - // - //public delegate void TmbLoadFunction(IntPtr drawObject, ushort a2, uint a3, IntPtr a4, IntPtr a5 ); - // - //[Signature( "E8 ?? ?? ?? ?? E9 ?? ?? ?? ?? 44 38 75 ?? 74 ?? 44 89 B3 ", DetourName ="RandomTmbDetour" )] - //public Hook< TmbLoadFunction > UnkHook = null; - // - //private void RandomTmbDetour( IntPtr drawObject, ushort a2, uint a3, IntPtr a4, IntPtr a5 ) - //{ - // //PluginLog.Information($"{drawObject:X} {a2:X}, {a3:X} {a4:X} {a5:X}" ); - // //TestFunction(); - // UnkHook!.Original( drawObject, a2, a3, a4, a5); - //} + private void CharacterBaseLoadAnimationDetour( IntPtr drawObject ) + { + var last = _animationLoadCollection; + _animationLoadCollection = _lastCreatedCollection + ?? ( FindParent( drawObject, out var collection ) != null ? collection : Penumbra.CollectionManager.Default ); + CharacterBaseLoadAnimationHook!.Original( drawObject ); + _animationLoadCollection = last; + } } \ No newline at end of file diff --git a/Penumbra/Interop/Resolver/PathResolver.Data.cs b/Penumbra/Interop/Resolver/PathResolver.Data.cs index 53ed569f..11d09ad2 100644 --- a/Penumbra/Interop/Resolver/PathResolver.Data.cs +++ b/Penumbra/Interop/Resolver/PathResolver.Data.cs @@ -83,7 +83,6 @@ public unsafe partial class PathResolver LastGameObject = oldGame; } - private void EnableDataHooks() { CharacterBaseCreateHook?.Enable(); @@ -91,8 +90,8 @@ public unsafe partial class PathResolver CharacterBaseDestructorHook?.Enable(); WeaponReloadHook?.Enable(); Penumbra.CollectionManager.CollectionChanged += CheckCollections; - //CharacterBaseLoadAnimationHook?.Enable(); - //RandomPapHook?.Enable(); + LoadTimelineResourcesHook?.Enable(); + CharacterBaseLoadAnimationHook?.Enable(); } private void DisableDataHooks() @@ -102,8 +101,8 @@ public unsafe partial class PathResolver CharacterBaseCreateHook?.Disable(); EnableDrawHook?.Disable(); CharacterBaseDestructorHook?.Disable(); - //RandomPapHook?.Disable(); - //CharacterBaseLoadAnimationHook?.Disable(); + LoadTimelineResourcesHook?.Disable(); + CharacterBaseLoadAnimationHook?.Disable(); } private void DisposeDataHooks() @@ -112,8 +111,8 @@ public unsafe partial class PathResolver CharacterBaseCreateHook?.Dispose(); EnableDrawHook?.Dispose(); CharacterBaseDestructorHook?.Dispose(); - //RandomPapHook?.Dispose(); - //CharacterBaseLoadAnimationHook?.Dispose(); + LoadTimelineResourcesHook?.Dispose(); + CharacterBaseLoadAnimationHook?.Dispose(); } // This map links DrawObjects directly to Actors (by ObjectTable index) and their collections. diff --git a/Penumbra/Interop/Resolver/PathResolver.cs b/Penumbra/Interop/Resolver/PathResolver.cs index 98800933..77f413f6 100644 --- a/Penumbra/Interop/Resolver/PathResolver.cs +++ b/Penumbra/Interop/Resolver/PathResolver.cs @@ -40,7 +40,8 @@ public partial class PathResolver : IDisposable // A potential next request will add the path anew. var nonDefault = HandleMaterialSubFiles( type, out var collection ) || PathCollections.TryRemove( gamePath.Path, out collection ) - //|| HandlePapFile( type, gamePath, out collection ) + //|| HandlePapFile( type, gamePath, out collection ) + || HandleAnimationFile( type, gamePath, out collection ) || HandleDecalFile( type, gamePath, out collection ); if( !nonDefault ) { @@ -72,18 +73,24 @@ public partial class PathResolver : IDisposable return false; } - //private bool HandlePapFile( ResourceType type, Utf8GamePath _, out ModCollection? collection ) - //{ - // if( type is ResourceType.Pap or ResourceType.Tmb - // && _animationLoadCollection != null ) - // { - // collection = _animationLoadCollection; - // return true; - // } - // - // collection = null; - // return false; - //} + private bool HandleAnimationFile( ResourceType type, Utf8GamePath _, out ModCollection? collection ) + { + if( _animationLoadCollection != null ) + { + switch( type ) + { + case ResourceType.Tmb: + case ResourceType.Pap: + case ResourceType.Avfx: + case ResourceType.Atex: + collection = _animationLoadCollection; + return true; + } + } + + collection = null; + return false; + } public void Enable() {