diff --git a/Penumbra/Interop/Resolver/PathResolver.AnimationState.cs b/Penumbra/Interop/Resolver/PathResolver.AnimationState.cs index 3153800c..fcc04234 100644 --- a/Penumbra/Interop/Resolver/PathResolver.AnimationState.cs +++ b/Penumbra/Interop/Resolver/PathResolver.AnimationState.cs @@ -1,8 +1,11 @@ using System; +using System.Collections.Generic; using Dalamud.Hooking; using Dalamud.Utility.Signatures; using Penumbra.Collections; using Penumbra.GameData.Enums; +using Penumbra.Interop.Structs; +using Penumbra.String; using Penumbra.String.Classes; using GameObject = FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject; @@ -14,9 +17,8 @@ public unsafe partial class PathResolver { private readonly DrawObjectState _drawObjectState; - private ResolveData _animationLoadData = ResolveData.Invalid; - private ResolveData _lastAvfxData = ResolveData.Invalid; - private ResolveData _characterSoundData = ResolveData.Invalid; + private ResolveData _animationLoadData = ResolveData.Invalid; + private ResolveData _characterSoundData = ResolveData.Invalid; public AnimationState( DrawObjectState drawObjectState ) { @@ -24,15 +26,7 @@ public unsafe partial class PathResolver SignatureHelper.Initialise( this ); } - public void UpdateAvfx( ResourceType type, ResolveData data ) - { - if( type == ResourceType.Avfx ) - { - _lastAvfxData = data; - } - } - - public bool HandleFiles( ResourceType type, Utf8GamePath _, out ResolveData resolveData ) + public bool HandleFiles( ResourceType type, Utf8GamePath path, out ResolveData resolveData ) { switch( type ) { @@ -60,9 +54,6 @@ public unsafe partial class PathResolver break; case ResourceType.Avfx: - _lastAvfxData = _animationLoadData.Valid - ? _animationLoadData - : Penumbra.CollectionManager.Default.ToResolveData(); if( _animationLoadData.Valid ) { resolveData = _animationLoadData; @@ -71,12 +62,6 @@ public unsafe partial class PathResolver break; case ResourceType.Atex: - if( _lastAvfxData.Valid ) - { - resolveData = _lastAvfxData; - return true; - } - if( _animationLoadData.Valid ) { resolveData = _animationLoadData; @@ -99,7 +84,6 @@ public unsafe partial class PathResolver _someActionLoadHook.Enable(); _someOtherAvfxHook.Enable(); _loadCharacterSoundHook.Enable(); - //_apricotResourceLoadHook.Enable(); } public void Disable() @@ -111,7 +95,6 @@ public unsafe partial class PathResolver _someActionLoadHook.Disable(); _someOtherAvfxHook.Disable(); _loadCharacterSoundHook.Disable(); - //_apricotResourceLoadHook.Disable(); } public void Dispose() @@ -123,7 +106,6 @@ public unsafe partial class PathResolver _someActionLoadHook.Dispose(); _someOtherAvfxHook.Dispose(); _loadCharacterSoundHook.Dispose(); - //_apricotResourceLoadHook.Dispose(); } // Characters load some of their voice lines or whatever with this function. @@ -261,17 +243,5 @@ public unsafe partial class PathResolver _someOtherAvfxHook.Original( unk ); _animationLoadData = last; } - - //private delegate byte ApricotResourceLoadDelegate( IntPtr handle, IntPtr unk1, byte unk2 ); - // - //[Signature( "48 89 74 24 ?? 57 48 83 EC ?? 41 0F B6 F0 48 8B F9", DetourName = nameof( ApricotResourceLoadDetour ) )] - //private readonly Hook< ApricotResourceLoadDelegate > _apricotResourceLoadHook = null!; - // - // - //private byte ApricotResourceLoadDetour( IntPtr handle, IntPtr unk1, byte unk2 ) - //{ - // Penumbra.Log.Information( $"{handle:X} {new ByteString( ( ( ResourceHandle* )handle )->FileName() )} {unk1:X} {unk2} {_lastAvfxData.ModCollection.Name}" ); - // return _apricotResourceLoadHook.Original( handle, unk1, unk2 ); - //} } } \ No newline at end of file diff --git a/Penumbra/Interop/Resolver/PathResolver.Material.cs b/Penumbra/Interop/Resolver/PathResolver.Subfiles.cs similarity index 57% rename from Penumbra/Interop/Resolver/PathResolver.Material.cs rename to Penumbra/Interop/Resolver/PathResolver.Subfiles.cs index 90893c20..8eecf362 100644 --- a/Penumbra/Interop/Resolver/PathResolver.Material.cs +++ b/Penumbra/Interop/Resolver/PathResolver.Subfiles.cs @@ -1,5 +1,4 @@ using System; -using System.Linq; using Dalamud.Hooking; using Dalamud.Utility.Signatures; using FFXIVClientStructs.FFXIV.Client.System.Resource; @@ -13,16 +12,17 @@ namespace Penumbra.Interop.Resolver; public unsafe partial class PathResolver { - // Materials do contain their own paths to textures and shader packages. + // Materials and avfx do contain their own paths to textures and shader packages or atex respectively. // Those are loaded synchronously. // Thus, we need to ensure the correct files are loaded when a material is loaded. - public class MaterialState : IDisposable + public class SubfileHelper : IDisposable { private readonly PathState _paths; private ResolveData _mtrlData = ResolveData.Invalid; + private ResolveData _avfxData = ResolveData.Invalid; - public MaterialState( PathState paths ) + public SubfileHelper( PathState paths ) { SignatureHelper.Initialise( this ); _paths = paths; @@ -31,10 +31,20 @@ public unsafe partial class PathResolver // Check specifically for shpk and tex files whether we are currently in a material load. public bool HandleSubFiles( ResourceType type, out ResolveData collection ) { - if( _mtrlData.Valid && type is ResourceType.Tex or ResourceType.Shpk ) + switch( type ) { - collection = _mtrlData; - return true; + case ResourceType.Tex: + case ResourceType.Shpk: + if( _mtrlData.Valid ) + { + collection = _mtrlData; + return true; + } + + break; + case ResourceType.Atex when _avfxData.Valid: + collection = _avfxData; + return true; } collection = ResolveData.Invalid; @@ -45,29 +55,35 @@ public unsafe partial class PathResolver public static void HandleCollection( ResolveData resolveData, string path, bool nonDefault, ResourceType type, FullPath? resolved, out (FullPath?, ResolveData) data ) { - if( nonDefault && type == ResourceType.Mtrl ) + if( nonDefault ) { - var fullPath = new FullPath( $"|{resolveData.ModCollection.Name}_{resolveData.ModCollection.ChangeCounter}|{path}" ); - data = ( fullPath, resolveData ); - } - else - { - data = ( resolved, resolveData ); + switch( type ) + { + case ResourceType.Mtrl: + case ResourceType.Avfx: + var fullPath = new FullPath( $"|{resolveData.ModCollection.Name}_{resolveData.ModCollection.ChangeCounter}|{path}" ); + data = ( fullPath, resolveData ); + return; + } } + + data = ( resolved, resolveData ); } public void Enable() { _loadMtrlShpkHook.Enable(); _loadMtrlTexHook.Enable(); - Penumbra.ResourceLoader.ResourceLoadCustomization += MtrlLoadHandler; + _apricotResourceLoadHook.Enable(); + Penumbra.ResourceLoader.ResourceLoadCustomization += SubfileLoadHandler; } public void Disable() { _loadMtrlShpkHook.Disable(); _loadMtrlTexHook.Disable(); - Penumbra.ResourceLoader.ResourceLoadCustomization -= MtrlLoadHandler; + _apricotResourceLoadHook.Disable(); + Penumbra.ResourceLoader.ResourceLoadCustomization -= SubfileLoadHandler; } public void Dispose() @@ -75,17 +91,21 @@ public unsafe partial class PathResolver Disable(); _loadMtrlShpkHook.Dispose(); _loadMtrlTexHook.Dispose(); + _apricotResourceLoadHook.Dispose(); } // We need to set the correct collection for the actual material path that is loaded // before actually loading the file. - public bool MtrlLoadHandler( ByteString split, ByteString path, ResourceManager* resourceManager, + public bool SubfileLoadHandler( ByteString split, ByteString path, ResourceManager* resourceManager, SeFileDescriptor* fileDescriptor, int priority, bool isSync, out byte ret ) { ret = 0; - if( fileDescriptor->ResourceHandle->FileType != ResourceType.Mtrl ) + switch( fileDescriptor->ResourceHandle->FileType ) { - return false; + case ResourceType.Mtrl: + case ResourceType.Avfx: + break; + default: return false; } var lastUnderscore = split.LastIndexOf( ( byte )'_' ); @@ -94,17 +114,14 @@ public unsafe partial class PathResolver || Penumbra.CollectionManager.ByName( name, out collection ) ) { #if DEBUG - Penumbra.Log.Verbose( $"Using MtrlLoadHandler with collection {name} for path {path}." ); + Penumbra.Log.Verbose( $"Using {nameof(SubfileLoadHandler)} with collection {name} for path {path}." ); #endif - - var objFromObjTable = Dalamud.Objects.FirstOrDefault( f => f.Name.TextValue == name ); - var gameObjAddr = objFromObjTable?.Address ?? IntPtr.Zero; - _paths.SetCollection( gameObjAddr, path, collection ); + _paths.SetCollection( IntPtr.Zero, path, collection ); } else { #if DEBUG - Penumbra.Log.Verbose( $"Using MtrlLoadHandler with no collection for path {path}." ); + Penumbra.Log.Verbose( $"Using {nameof( SubfileLoadHandler )} with no collection for path {path}." ); #endif } @@ -124,7 +141,7 @@ public unsafe partial class PathResolver private byte LoadMtrlTexDetour( IntPtr mtrlResourceHandle ) { - LoadMtrlHelper( mtrlResourceHandle ); + _mtrlData = LoadFileHelper( mtrlResourceHandle ); var ret = _loadMtrlTexHook.Original( mtrlResourceHandle ); _mtrlData = ResolveData.Invalid; return ret; @@ -136,22 +153,37 @@ public unsafe partial class PathResolver private byte LoadMtrlShpkDetour( IntPtr mtrlResourceHandle ) { - LoadMtrlHelper( mtrlResourceHandle ); + _mtrlData = LoadFileHelper( mtrlResourceHandle ); var ret = _loadMtrlShpkHook.Original( mtrlResourceHandle ); _mtrlData = ResolveData.Invalid; return ret; } - private void LoadMtrlHelper( IntPtr mtrlResourceHandle ) + private ResolveData LoadFileHelper( IntPtr resourceHandle ) { - if( mtrlResourceHandle == IntPtr.Zero ) + if( resourceHandle == IntPtr.Zero ) { - return; + return ResolveData.Invalid; } - var mtrl = ( MtrlResource* )mtrlResourceHandle; - var mtrlPath = ByteString.FromSpanUnsafe( mtrl->Handle.FileNameSpan(), true, null, true ); - _mtrlData = _paths.TryGetValue( mtrlPath, out var c ) ? c : ResolveData.Invalid; + var resource = ( ResourceHandle* )resourceHandle; + var filePath = ByteString.FromSpanUnsafe( resource->FileNameSpan(), true, null, true ); + return _paths.TryGetValue( filePath, out var c ) ? c : ResolveData.Invalid; + } + + + private delegate byte ApricotResourceLoadDelegate( IntPtr handle, IntPtr unk1, byte unk2 ); + + [Signature( "48 89 74 24 ?? 57 48 83 EC ?? 41 0F B6 F0 48 8B F9", DetourName = nameof( ApricotResourceLoadDetour ) )] + private readonly Hook _apricotResourceLoadHook = null!; + + + private byte ApricotResourceLoadDetour( IntPtr handle, IntPtr unk1, byte unk2 ) + { + _avfxData = LoadFileHelper( handle ); + var ret = _apricotResourceLoadHook.Original( handle, unk1, unk2 ); + _avfxData = ResolveData.Invalid; + return ret; } } } \ No newline at end of file diff --git a/Penumbra/Interop/Resolver/PathResolver.cs b/Penumbra/Interop/Resolver/PathResolver.cs index 14f345d6..a8acce6d 100644 --- a/Penumbra/Interop/Resolver/PathResolver.cs +++ b/Penumbra/Interop/Resolver/PathResolver.cs @@ -30,7 +30,7 @@ public partial class PathResolver : IDisposable private readonly AnimationState _animations; private readonly PathState _paths; private readonly MetaState _meta; - private readonly MaterialState _materials; + private readonly SubfileHelper _subFiles; static PathResolver() => ValidHumanModels = GetValidHumanModels( Dalamud.GameData ); @@ -42,7 +42,7 @@ public partial class PathResolver : IDisposable _animations = new AnimationState( DrawObjects ); _paths = new PathState( this ); _meta = new MetaState( _paths.HumanVTable ); - _materials = new MaterialState( _paths ); + _subFiles = new SubfileHelper( _paths ); } // The modified resolver that handles game path resolving. @@ -54,7 +54,7 @@ public partial class PathResolver : IDisposable // If not use the default collection. // We can remove paths after they have actually been loaded. // A potential next request will add the path anew. - var nonDefault = _materials.HandleSubFiles( type, out var resolveData ) + var nonDefault = _subFiles.HandleSubFiles( type, out var resolveData ) || _paths.Consume( gamePath.Path, out resolveData ) || _animations.HandleFiles( type, gamePath, out resolveData ) || DrawObjects.HandleDecalFile( type, gamePath, out resolveData ); @@ -70,8 +70,7 @@ public partial class PathResolver : IDisposable // so that the functions loading tex and shpk can find that path and use its collection. // We also need to handle defaulted materials against a non-default collection. var path = resolved == null ? gamePath.Path.ToString() : resolved.Value.FullName; - MaterialState.HandleCollection( resolveData, path, nonDefault, type, resolved, out data ); - _animations.UpdateAvfx( type, data.Item2 ); + SubfileHelper.HandleCollection( resolveData, path, nonDefault, type, resolved, out data ); return true; } @@ -89,7 +88,7 @@ public partial class PathResolver : IDisposable _animations.Enable(); _paths.Enable(); _meta.Enable(); - _materials.Enable(); + _subFiles.Enable(); _loader.ResolvePathCustomization += CharacterResolver; Penumbra.Log.Debug( "Character Path Resolver enabled." ); @@ -109,7 +108,7 @@ public partial class PathResolver : IDisposable IdentifiedCache.Disable(); _paths.Disable(); _meta.Disable(); - _materials.Disable(); + _subFiles.Disable(); _loader.ResolvePathCustomization -= CharacterResolver; Penumbra.Log.Debug( "Character Path Resolver disabled." ); @@ -124,7 +123,7 @@ public partial class PathResolver : IDisposable Cutscenes.Dispose(); IdentifiedCache.Dispose(); _meta.Dispose(); - _materials.Dispose(); + _subFiles.Dispose(); } public static unsafe (IntPtr, ResolveData) IdentifyDrawObject( IntPtr drawObject )