From e534ce37d58b49e0c88a4add7cf10ba0f6242f94 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Thu, 29 Dec 2022 13:09:40 +0100 Subject: [PATCH] Add display for subfile resources and clean up better. --- .../Interop/Loader/ResourceLoader.Debug.cs | 6 +-- .../Loader/ResourceLoader.Replacement.cs | 4 +- Penumbra/Interop/Loader/ResourceLoader.cs | 2 +- .../Interop/Resolver/PathResolver.Subfiles.cs | 45 ++++++++++++++++--- Penumbra/Interop/Resolver/PathResolver.cs | 9 ++++ Penumbra/Interop/Structs/ResourceHandle.cs | 9 ++++ Penumbra/Penumbra.cs | 10 ++--- Penumbra/UI/ConfigWindow.DebugTab.cs | 29 +++++++++++- 8 files changed, 95 insertions(+), 19 deletions(-) diff --git a/Penumbra/Interop/Loader/ResourceLoader.Debug.cs b/Penumbra/Interop/Loader/ResourceLoader.Debug.cs index 693c73ec..682ead7e 100644 --- a/Penumbra/Interop/Loader/ResourceLoader.Debug.cs +++ b/Penumbra/Interop/Loader/ResourceLoader.Debug.cs @@ -249,8 +249,6 @@ public unsafe partial class ResourceLoader Penumbra.Log.Information( $"[ResourceLoader] [{handle->FileType}] Loaded {pathString} to 0x{( ulong )handle:X}. (Refcount {handle->RefCount})" ); } - private static void LogLoadedFile( ByteString path, bool success, bool custom ) - => Penumbra.Log.Information( success - ? $"[ResourceLoader] Loaded {path} from {( custom ? "local files" : "SqPack" )}" - : $"[ResourceLoader] Failed to load {path} from {( custom ? "local files" : "SqPack" )}." ); + private static void LogLoadedFile( Structs.ResourceHandle* resource, ByteString path, bool success, bool custom ) + => Penumbra.Log.Information( $"[ResourceLoader] Loading {path} from {( custom ? "local files" : "SqPack" )} into 0x{( ulong )resource:X} returned {success}." ); } \ No newline at end of file diff --git a/Penumbra/Interop/Loader/ResourceLoader.Replacement.cs b/Penumbra/Interop/Loader/ResourceLoader.Replacement.cs index 8e28d2ce..28c8ead9 100644 --- a/Penumbra/Interop/Loader/ResourceLoader.Replacement.cs +++ b/Penumbra/Interop/Loader/ResourceLoader.Replacement.cs @@ -214,7 +214,7 @@ public unsafe partial class ResourceLoader SeFileDescriptor* fileDescriptor, int priority, bool isSync ) { var ret = Penumbra.ResourceLoader.ReadSqPackHook.Original( resourceManager, fileDescriptor, priority, isSync ); - FileLoaded?.Invoke( path, ret != 0, false ); + FileLoaded?.Invoke( fileDescriptor->ResourceHandle, path, ret != 0, false ); return ret; } @@ -242,7 +242,7 @@ public unsafe partial class ResourceLoader // Use the SE ReadFile function. var ret = ReadFile( resourceManager, fileDescriptor, priority, isSync ); - FileLoaded?.Invoke( gamePath, ret != 0, true ); + FileLoaded?.Invoke( fileDescriptor->ResourceHandle, gamePath, ret != 0, true ); return ret; } diff --git a/Penumbra/Interop/Loader/ResourceLoader.cs b/Penumbra/Interop/Loader/ResourceLoader.cs index de91fb2a..ac00d62d 100644 --- a/Penumbra/Interop/Loader/ResourceLoader.cs +++ b/Penumbra/Interop/Loader/ResourceLoader.cs @@ -128,7 +128,7 @@ public unsafe partial class ResourceLoader : IDisposable // Event fired whenever a resource is newly loaded. // Success indicates the return value of the loading function (which does not imply that the resource was actually successfully loaded) // custom is true if the file was loaded from local files instead of the default SqPacks. - public delegate void FileLoadedDelegate( ByteString path, bool success, bool custom ); + public delegate void FileLoadedDelegate( ResourceHandle* resource, ByteString path, bool success, bool custom ); public event FileLoadedDelegate? FileLoaded; // Customization point to control how path resolving is handled. diff --git a/Penumbra/Interop/Resolver/PathResolver.Subfiles.cs b/Penumbra/Interop/Resolver/PathResolver.Subfiles.cs index a62141d6..f94b3551 100644 --- a/Penumbra/Interop/Resolver/PathResolver.Subfiles.cs +++ b/Penumbra/Interop/Resolver/PathResolver.Subfiles.cs @@ -1,5 +1,7 @@ using System; +using System.Collections; using System.Collections.Concurrent; +using System.Collections.Generic; using Dalamud.Hooking; using Dalamud.Utility.Signatures; using FFXIVClientStructs.FFXIV.Client.System.Resource; @@ -17,7 +19,7 @@ public unsafe partial class PathResolver // 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 SubfileHelper : IDisposable + public class SubfileHelper : IDisposable, IReadOnlyCollection> { private readonly ResourceLoader _loader; @@ -81,7 +83,8 @@ public unsafe partial class PathResolver _loadMtrlTexHook.Enable(); _apricotResourceLoadHook.Enable(); _loader.ResourceLoadCustomization += SubfileLoadHandler; - _loader.ResourceLoaded += SubfileContainerLoaded; + _loader.ResourceLoaded += SubfileContainerRequested; + _loader.FileLoaded += SubfileContainerLoaded; } public void Disable() @@ -90,7 +93,8 @@ public unsafe partial class PathResolver _loadMtrlTexHook.Disable(); _apricotResourceLoadHook.Disable(); _loader.ResourceLoadCustomization -= SubfileLoadHandler; - _loader.ResourceLoaded -= SubfileContainerLoaded; + _loader.ResourceLoaded -= SubfileContainerRequested; + _loader.FileLoaded -= SubfileContainerLoaded; } public void Dispose() @@ -101,13 +105,28 @@ public unsafe partial class PathResolver _apricotResourceLoadHook.Dispose(); } - private void SubfileContainerLoaded( ResourceHandle* handle, Utf8GamePath originalPath, FullPath? manipulatedPath, ResolveData resolveData ) + private void SubfileContainerRequested( ResourceHandle* handle, Utf8GamePath originalPath, FullPath? manipulatedPath, ResolveData resolveData ) { switch( handle->FileType ) { case ResourceType.Mtrl: case ResourceType.Avfx: - _subFileCollection[ ( IntPtr )handle ] = resolveData; + if( handle->FileSize == 0 ) + { + _subFileCollection[ ( IntPtr )handle ] = resolveData; + } + + break; + } + } + + private void SubfileContainerLoaded( ResourceHandle* handle, ByteString path, bool success, bool custom ) + { + switch( handle->FileType ) + { + case ResourceType.Mtrl: + case ResourceType.Avfx: + _subFileCollection.TryRemove( ( IntPtr )handle, out _ ); break; } } @@ -131,7 +150,6 @@ public unsafe partial class PathResolver // Was called with True on my client and with false on other peoples clients, // which caused problems. ret = Penumbra.ResourceLoader.DefaultLoadResource( path, resourceManager, fileDescriptor, priority, true ); - _subFileCollection.TryRemove( ( IntPtr )fileDescriptor->ResourceHandle, out _ ); return true; } @@ -184,5 +202,20 @@ public unsafe partial class PathResolver _avfxData = ResolveData.Invalid; return ret; } + + public IEnumerator< KeyValuePair< IntPtr, ResolveData > > GetEnumerator() + => _subFileCollection.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() + => GetEnumerator(); + + public int Count + => _subFileCollection.Count; + + internal ResolveData MtrlData + => _mtrlData; + + internal ResolveData AvfxData + => _avfxData; } } \ No newline at end of file diff --git a/Penumbra/Interop/Resolver/PathResolver.cs b/Penumbra/Interop/Resolver/PathResolver.cs index 1bdaab54..e09042f1 100644 --- a/Penumbra/Interop/Resolver/PathResolver.cs +++ b/Penumbra/Interop/Resolver/PathResolver.cs @@ -169,4 +169,13 @@ public partial class PathResolver : IDisposable internal IEnumerable< KeyValuePair< int, global::Dalamud.Game.ClientState.Objects.Types.GameObject > > CutsceneActors => Cutscenes.Actors; + + internal IEnumerable< KeyValuePair< IntPtr, ResolveData > > ResourceCollections + => _subFiles; + + internal ResolveData CurrentMtrlData + => _subFiles.MtrlData; + + internal ResolveData CurrentAvfxData + => _subFiles.AvfxData; } \ No newline at end of file diff --git a/Penumbra/Interop/Structs/ResourceHandle.cs b/Penumbra/Interop/Structs/ResourceHandle.cs index c8b3522e..329dbc2b 100644 --- a/Penumbra/Interop/Structs/ResourceHandle.cs +++ b/Penumbra/Interop/Structs/ResourceHandle.cs @@ -67,6 +67,15 @@ public unsafe struct ResourceHandle [FieldOffset( 0x10 )] public uint Id; + [FieldOffset( 0x28 )] + public uint FileSize; + + [FieldOffset( 0x2C )] + public uint FileSize2; + + [FieldOffset( 0x34 )] + public uint FileSize3; + [FieldOffset( 0x48 )] public byte* FileNameData; diff --git a/Penumbra/Penumbra.cs b/Penumbra/Penumbra.cs index 32538a81..b2550a69 100644 --- a/Penumbra/Penumbra.cs +++ b/Penumbra/Penumbra.cs @@ -138,11 +138,6 @@ public class Penumbra : IDalamudPlugin ResourceLoader.EnableFullLogging(); } - if( CharacterUtility.Ready ) - { - ResidentResources.Reload(); - } - Api = new PenumbraApi( this ); IpcProviders = new PenumbraIpcProviders( Dalamud.PluginInterface, Api ); SubscribeItemLinks(); @@ -159,6 +154,11 @@ public class Penumbra : IDalamudPlugin OtterTex.NativeDll.Initialize( Dalamud.PluginInterface.AssemblyLocation.DirectoryName ); Log.Information( $"Loading native OtterTex assembly from {OtterTex.NativeDll.Directory}." ); + + if( CharacterUtility.Ready ) + { + ResidentResources.Reload(); + } } catch { diff --git a/Penumbra/UI/ConfigWindow.DebugTab.cs b/Penumbra/UI/ConfigWindow.DebugTab.cs index 4a234656..8d57358a 100644 --- a/Penumbra/UI/ConfigWindow.DebugTab.cs +++ b/Penumbra/UI/ConfigWindow.DebugTab.cs @@ -239,7 +239,7 @@ public partial class ConfigWindow { if( pathTree ) { - using var table = ImRaii.Table( "###PathCollectionResolverTable", 2, ImGuiTableFlags.SizingFixedFit ); + using var table = ImRaii.Table( "###PathCollectionResolverTable", 3, ImGuiTableFlags.SizingFixedFit ); if( table ) { foreach( var (path, collection) in _window._penumbra.PathResolver.PathCollections ) @@ -248,6 +248,33 @@ public partial class ConfigWindow ImGuiNative.igTextUnformatted( path.Path, path.Path + path.Length ); ImGui.TableNextColumn(); ImGui.TextUnformatted( collection.ModCollection.Name ); + ImGui.TableNextColumn(); + ImGui.TextUnformatted( collection.AssociatedGameObject.ToString("X") ); + } + } + } + } + + using( var resourceTree = ImRaii.TreeNode( "Subfile Collections" ) ) + { + if( resourceTree ) + { + using var table = ImRaii.Table( "###ResourceCollectionResolverTable", 3, ImGuiTableFlags.SizingFixedFit ); + if( table ) + { + ImGuiUtil.DrawTableColumn( "Current Mtrl Data" ); + ImGuiUtil.DrawTableColumn( _window._penumbra.PathResolver.CurrentMtrlData.ModCollection.Name ); + ImGuiUtil.DrawTableColumn( $"0x{_window._penumbra.PathResolver.CurrentMtrlData.AssociatedGameObject:X}" ); + + ImGuiUtil.DrawTableColumn( "Current Avfx Data" ); + ImGuiUtil.DrawTableColumn( _window._penumbra.PathResolver.CurrentAvfxData.ModCollection.Name ); + ImGuiUtil.DrawTableColumn( $"0x{_window._penumbra.PathResolver.CurrentAvfxData.AssociatedGameObject:X}" ); + + foreach( var (resource, resolve) in _window._penumbra.PathResolver.ResourceCollections ) + { + ImGuiUtil.DrawTableColumn( $"0x{resource:X}" ); + ImGuiUtil.DrawTableColumn( resolve.ModCollection.Name ); + ImGuiUtil.DrawTableColumn( $"0x{resolve.AssociatedGameObject:X}" ); } } }