From e261b4c0c5f75e1ed05bfbab3a5e8c3bd90932c5 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Thu, 14 Jul 2022 20:27:47 +0200 Subject: [PATCH] Maybe improve IMC handling. --- Penumbra/Interop/MetaFileManager.cs | 108 ++--------------------- Penumbra/Meta/Files/ImcFile.cs | 15 ++-- Penumbra/Meta/Manager/MetaManager.Imc.cs | 29 ++---- Penumbra/Penumbra.cs | 1 - 4 files changed, 21 insertions(+), 132 deletions(-) diff --git a/Penumbra/Interop/MetaFileManager.cs b/Penumbra/Interop/MetaFileManager.cs index 71b31c72..880a0ee8 100644 --- a/Penumbra/Interop/MetaFileManager.cs +++ b/Penumbra/Interop/MetaFileManager.cs @@ -1,38 +1,23 @@ using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using Dalamud.Hooking; -using Dalamud.Logging; using Dalamud.Utility.Signatures; using FFXIVClientStructs.FFXIV.Client.System.Memory; -using Penumbra.GameData.ByteString; -using Penumbra.Interop.Structs; -using Penumbra.Meta.Files; namespace Penumbra.Interop; -public unsafe class MetaFileManager : IDisposable +public unsafe class MetaFileManager { public MetaFileManager() { SignatureHelper.Initialise( this ); - InitImc(); } - public void Dispose() - { - DisposeImc(); - } - - // Allocate in the games space for file storage. // We only need this if using any meta file. [Signature( "E8 ?? ?? ?? ?? 41 B9 ?? ?? ?? ?? 4C 8B C0" )] - public IntPtr GetFileSpaceAddress; + private readonly IntPtr _getFileSpaceAddress = IntPtr.Zero; public IMemorySpace* GetFileSpace() - => ( ( delegate* unmanaged< IMemorySpace* > )GetFileSpaceAddress )(); + => ( ( delegate* unmanaged< IMemorySpace* > )_getFileSpaceAddress )(); public void* AllocateFileMemory( ulong length, ulong alignment = 0 ) => GetFileSpace()->Malloc( length, alignment ); @@ -40,87 +25,12 @@ public unsafe class MetaFileManager : IDisposable public void* AllocateFileMemory( int length, int alignment = 0 ) => AllocateFileMemory( ( ulong )length, ( ulong )alignment ); + public void* AllocateDefaultMemory( ulong length, ulong alignment = 0 ) + => GetFileSpace()->Malloc( length, alignment ); - // We only need this for IMC files, since we need to hook their cleanup function. - [Signature( "48 8D 05 ?? ?? ?? ?? 48 8B 8B ?? ?? ?? ?? 48 89 03", ScanType = ScanType.StaticAddress )] - public IntPtr* DefaultResourceHandleVTable; + public void* AllocateDefaultMemory( int length, int alignment = 0 ) + => IMemorySpace.GetDefaultSpace()->Malloc( ( ulong )length, ( ulong )alignment ); - public delegate void ClearResource( ResourceHandle* resource ); - public Hook< ClearResource > ClearDefaultResourceHook = null!; - - private readonly Dictionary< IntPtr, (ImcFile, IntPtr, int) > _originalImcData = new(); - private readonly Dictionary< ImcFile, IntPtr > _currentUse = new(); - - // We store the original data of loaded IMCs so that we can restore it before they get destroyed, - // similar to the other meta files, just with arbitrary destruction. - private void ClearDefaultResourceDetour( ResourceHandle* resource ) - { - if( _originalImcData.TryGetValue( ( IntPtr )resource, out var data ) ) - { - ClearImcData( resource, data.Item1, data.Item2, data.Item3); - } - - ClearDefaultResourceHook.Original( resource ); - } - - // Reset all files from a given IMC cache if they exist. - public void ResetByFile( ImcFile file ) - { - if( !_currentUse.TryGetValue( file, out var resource ) ) - { - return; - } - - if( _originalImcData.TryGetValue( resource, out var data ) ) - { - ClearImcData((ResourceHandle*) resource, file, data.Item2, data.Item3 ); - } - else - { - _currentUse.Remove( file ); - } - } - - // Clear a single IMC resource and reset it to its original data. - private void ClearImcData( ResourceHandle* resource, ImcFile file, IntPtr data, int length) - { - var name = new FullPath( Utf8String.FromSpanUnsafe( resource->FileNameSpan(), true ).ToString() ); - PluginLog.Debug( "Restoring data of {$Name:l} (0x{Resource}) to 0x{Data:X} and Length {Length} before deletion.", name, - ( ulong )resource, ( ulong )data, length ); - resource->SetData( data, length ); - _originalImcData.Remove( ( IntPtr )resource ); - _currentUse.Remove( file ); - } - - // Called when a new IMC is manipulated to store its data. - public void AddImcFile( ResourceHandle* resource, ImcFile file, IntPtr data, int length) - { - PluginLog.Debug( "Storing data 0x{Data:X} of Length {Length} for {$Name:l} (0x{Resource:X}).", ( ulong )data, length, - Utf8String.FromSpanUnsafe( resource->FileNameSpan(), true, null, null ), ( ulong )resource ); - _originalImcData[ ( IntPtr )resource ] = ( file, data, length ); - _currentUse[ file ] = ( IntPtr )resource; - } - - // Initialize the hook at VFunc 25, which is called when default resources (and IMC resources do not overwrite it) destroy their data. - private void InitImc() - { - ClearDefaultResourceHook = Hook< ClearResource >.FromAddress( DefaultResourceHandleVTable[ 25 ], ClearDefaultResourceDetour ); - ClearDefaultResourceHook.Enable(); - } - - private void DisposeImc() - { - ClearDefaultResourceHook.Disable(); - ClearDefaultResourceHook.Dispose(); - // Restore all IMCs to their default values on dispose. - // This should only be relevant when testing/disabling/reenabling penumbra. - foreach( var (resourcePtr, (file, data, length)) in _originalImcData ) - { - var resource = ( ResourceHandle* )resourcePtr; - resource->SetData( data, length ); - } - - _originalImcData.Clear(); - _currentUse.Clear(); - } + public void Free( IntPtr ptr, int length ) + => IMemorySpace.Free( ( void* )ptr, ( ulong )length ); } \ No newline at end of file diff --git a/Penumbra/Meta/Files/ImcFile.cs b/Penumbra/Meta/Files/ImcFile.cs index 17ced337..e8fd8416 100644 --- a/Penumbra/Meta/Files/ImcFile.cs +++ b/Penumbra/Meta/Files/ImcFile.cs @@ -128,7 +128,6 @@ public unsafe class ImcFile : MetaBaseFile var newLength = ( ( ( ActualLength - 1 ) >> 7 ) + 1 ) << 7; PluginLog.Verbose( "Resized IMC {Path} from {Length} to {NewLength}.", Path, Length, newLength ); ResizeResources( newLength ); - ChangesSinceLoad = true; } var defaultPtr = ( ImcEntry* )( Data + PreambleSize ); @@ -218,18 +217,18 @@ public unsafe class ImcFile : MetaBaseFile } } - public void Replace( ResourceHandle* resource, bool firstTime ) + public void Replace( ResourceHandle* resource ) { var (data, length) = resource->GetData(); - if( data == IntPtr.Zero ) + var newData = Penumbra.MetaFileManager.AllocateDefaultMemory( Length, 8 ); + if( newData == null ) { + PluginLog.Error("Could not replace loaded IMC data at 0x{Data:X}, allocation failed." ); return; } + Functions.MemCpyUnchecked( newData, Data, Length ); - resource->SetData( ( IntPtr )Data, Length ); - if( firstTime ) - { - Penumbra.MetaFileManager.AddImcFile( resource, this, data, length ); - } + Penumbra.MetaFileManager.Free( data, length ); + resource->SetData( ( IntPtr )newData, Length ); } } \ No newline at end of file diff --git a/Penumbra/Meta/Manager/MetaManager.Imc.cs b/Penumbra/Meta/Manager/MetaManager.Imc.cs index 3dd7752c..77c90023 100644 --- a/Penumbra/Meta/Manager/MetaManager.Imc.cs +++ b/Penumbra/Meta/Manager/MetaManager.Imc.cs @@ -119,9 +119,9 @@ public partial class MetaManager { foreach( var file in _imcFiles.Values ) { - Penumbra.MetaFileManager.ResetByFile( file ); file.Dispose(); } + _imcFiles.Clear(); _imcManipulations.Clear(); RestoreImcDelegate(); @@ -132,7 +132,6 @@ public partial class MetaManager if( _imcManagerCount++ == 0 ) { Penumbra.ResourceLoader.ResourceLoadCustomization += ImcLoadHandler; - Penumbra.ResourceLoader.ResourceLoaded += ImcResourceHandler; } } @@ -141,7 +140,6 @@ public partial class MetaManager if( --_imcManagerCount == 0 ) { Penumbra.ResourceLoader.ResourceLoadCustomization -= ImcLoadHandler; - Penumbra.ResourceLoader.ResourceLoaded -= ImcResourceHandler; } } @@ -163,33 +161,16 @@ public partial class MetaManager var lastUnderscore = split.LastIndexOf( ( byte )'_' ); var name = lastUnderscore == -1 ? split.ToString() : split.Substring( 0, lastUnderscore ).ToString(); - if( Penumbra.CollectionManager.ByName( name, out var collection ) + if( ( Penumbra.TempMods.Collections.TryGetValue( name, out var collection ) + || Penumbra.CollectionManager.ByName( name, out collection ) ) && collection.HasCache && collection.MetaCache!._imcFiles.TryGetValue( Utf8GamePath.FromSpan( path.Span, out var p ) ? p : Utf8GamePath.Empty, out var file ) ) { PluginLog.Debug( "Loaded {GamePath:l} from file and replaced with IMC from collection {Collection:l}.", path, - collection.Name ); - file.Replace( fileDescriptor->ResourceHandle, true ); - file.ChangesSinceLoad = false; + collection.AnonymizedName ); + file.Replace( fileDescriptor->ResourceHandle ); } return true; } - - private static unsafe void ImcResourceHandler( ResourceHandle* resource, Utf8GamePath gamePath, FullPath? _2, object? resolveData ) - { - // Only check imcs. - if( resource->FileType != ResourceType.Imc - || resolveData is not ModCollection { HasCache: true } collection - || !collection.MetaCache!._imcFiles.TryGetValue( gamePath, out var file ) - || !file.ChangesSinceLoad ) - { - return; - } - - PluginLog.Debug( "File {GamePath:l} was already loaded but IMC in collection {Collection:l} was changed, so reloaded.", gamePath, - collection.Name ); - file.Replace( resource, false ); - file.ChangesSinceLoad = false; - } } \ No newline at end of file diff --git a/Penumbra/Penumbra.cs b/Penumbra/Penumbra.cs index a20637d4..3a4753dd 100644 --- a/Penumbra/Penumbra.cs +++ b/Penumbra/Penumbra.cs @@ -258,7 +258,6 @@ public class Penumbra : IDalamudPlugin PathResolver.Dispose(); ResourceLogger.Dispose(); - MetaFileManager.Dispose(); ResourceLoader.Dispose(); CharacterUtility.Dispose();