From 7540694050991ac6f98121e2aa8bdf0a2936f9e4 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Sun, 20 Mar 2022 23:45:16 +0100 Subject: [PATCH] Fix IMC adding new variants. --- Penumbra/Meta/Files/ImcFile.cs | 33 ++++++++++-------------- Penumbra/Meta/Files/MetaBaseFile.cs | 26 +++++++++++++++++++ Penumbra/Meta/Manager/MetaManager.Imc.cs | 32 +++++++++++++---------- 3 files changed, 59 insertions(+), 32 deletions(-) diff --git a/Penumbra/Meta/Files/ImcFile.cs b/Penumbra/Meta/Files/ImcFile.cs index 7f2e27fc..1a992a89 100644 --- a/Penumbra/Meta/Files/ImcFile.cs +++ b/Penumbra/Meta/Files/ImcFile.cs @@ -70,6 +70,9 @@ public unsafe class ImcFile : MetaBaseFile public readonly int NumParts; public bool ChangesSinceLoad = true; + public ReadOnlySpan< ImcEntry > Span + => new(( ImcEntry* )( Data + PreambleSize ), ( Length - PreambleSize ) / sizeof( ImcEntry )); + private static int CountInternal( byte* data ) => *( ushort* )data; @@ -89,24 +92,15 @@ public unsafe class ImcFile : MetaBaseFile private static ImcEntry* VariantPtr( byte* data, int partIdx, int variantIdx ) { - if( variantIdx == 0 ) - { - return DefaultPartPtr( data, partIdx ); - } - - --variantIdx; var flag = 1 << partIdx; - - if( ( PartMask( data ) & flag ) == 0 || variantIdx >= CountInternal( data ) ) + if( ( PartMask( data ) & flag ) == 0 || variantIdx > CountInternal( data ) ) { return null; } var numParts = BitOperations.PopCount( PartMask( data ) ); var ptr = ( ImcEntry* )( data + PreambleSize ); - ptr += numParts; - ptr += variantIdx * numParts; - ptr += partIdx; + ptr += variantIdx * numParts + partIdx; return ptr; } @@ -139,21 +133,22 @@ public unsafe class ImcFile : MetaBaseFile return true; } + var oldCount = Count; + *( ushort* )Data = ( ushort )numVariants; if( ActualLength > Length ) { - PluginLog.Warning( "Adding too many variants to IMC, size exceeded." ); - return false; + var newLength = ( ( ( ActualLength - 1 ) >> 7 ) + 1 ) << 7; + PluginLog.Verbose( "Resized IMC {Path} from {Length} to {NewLength}.", Path, Length, newLength ); + ResizeResources( newLength ); } var defaultPtr = ( ImcEntry* )( Data + PreambleSize ); - var endPtr = defaultPtr + ( numVariants + 1 ) * NumParts; - for( var ptr = defaultPtr + NumParts; ptr < endPtr; ptr += NumParts ) + for( var i = oldCount + 1; i < numVariants + 1; ++i ) { - Functions.MemCpyUnchecked( ptr, defaultPtr, NumParts * sizeof( ImcEntry ) ); + Functions.MemCpyUnchecked( defaultPtr + i, defaultPtr, NumParts * sizeof( ImcEntry ) ); } - PluginLog.Verbose( "Expanded imc from {Count} to {NewCount} variants.", Count, numVariants ); - *( ushort* )Data = ( ushort )numVariants; + PluginLog.Verbose( "Expanded IMC {Path} from {Count} to {NewCount} variants.", Path, oldCount, numVariants ); return true; } @@ -240,7 +235,7 @@ public unsafe class ImcFile : MetaBaseFile } var requiredLength = ActualLength; - resource->SetData( (IntPtr) Data, Length ); + resource->SetData( ( IntPtr )Data, Length ); if( length >= requiredLength ) { Functions.MemCpyUnchecked( ( void* )data, Data, requiredLength ); diff --git a/Penumbra/Meta/Files/MetaBaseFile.cs b/Penumbra/Meta/Files/MetaBaseFile.cs index 1d24cfb4..e6e79740 100644 --- a/Penumbra/Meta/Files/MetaBaseFile.cs +++ b/Penumbra/Meta/Files/MetaBaseFile.cs @@ -1,5 +1,6 @@ using System; using Dalamud.Memory; +using Penumbra.GameData.Util; namespace Penumbra.Meta.Files; @@ -44,6 +45,31 @@ public unsafe class MetaBaseFile : IDisposable Data = null; } + // Resize memory while retaining data. + protected void ResizeResources( int newLength ) + { + if( newLength == Length ) + { + return; + } + + var data = ( byte* )MemoryHelper.GameAllocateDefault( ( ulong )newLength ); + if( newLength > Length ) + { + Functions.MemCpyUnchecked( data, Data, Length ); + Functions.MemSet( data + Length, 0, newLength - Length ); + } + else + { + Functions.MemCpyUnchecked( data, Data, newLength ); + } + + ReleaseUnmanagedResources(); + GC.AddMemoryPressure( newLength ); + Data = data; + Length = newLength; + } + // Manually free memory. public void Dispose() { diff --git a/Penumbra/Meta/Manager/MetaManager.Imc.cs b/Penumbra/Meta/Manager/MetaManager.Imc.cs index a67f79c6..c8c54a0e 100644 --- a/Penumbra/Meta/Manager/MetaManager.Imc.cs +++ b/Penumbra/Meta/Manager/MetaManager.Imc.cs @@ -20,7 +20,8 @@ public partial class MetaManager public readonly Dictionary< ImcManipulation, Mod.Mod > Manipulations = new(); private readonly ModCollection _collection; - private readonly ResourceLoader.ResourceLoadCustomizationDelegate? _previousDelegate; + private static int _imcManagerCount; + private static ResourceLoader.ResourceLoadCustomizationDelegate? _previousDelegate; public MetaManagerImc( ModCollection collection ) @@ -97,25 +98,30 @@ public partial class MetaManager Files.Clear(); Manipulations.Clear(); - RestoreDelegate(); } [Conditional( "USE_IMC" )] - private unsafe void SetupDelegate() + private static unsafe void SetupDelegate() { - Penumbra.ResourceLoader.ResourceLoadCustomization = ImcLoadHandler; - Penumbra.ResourceLoader.ResourceLoaded += ImcResourceHandler; - } - - [Conditional( "USE_IMC" )] - private unsafe void RestoreDelegate() - { - if( Penumbra.ResourceLoader.ResourceLoadCustomization == ImcLoadHandler ) + if( _imcManagerCount++ == 0 ) { - Penumbra.ResourceLoader.ResourceLoadCustomization = _previousDelegate; + Penumbra.ResourceLoader.ResourceLoadCustomization = ImcLoadHandler; + Penumbra.ResourceLoader.ResourceLoaded += ImcResourceHandler; } + } - Penumbra.ResourceLoader.ResourceLoaded -= ImcResourceHandler; + [Conditional( "USE_IMC" )] + private static unsafe void RestoreDelegate() + { + if( --_imcManagerCount == 0 ) + { + if( Penumbra.ResourceLoader.ResourceLoadCustomization == ImcLoadHandler ) + { + Penumbra.ResourceLoader.ResourceLoadCustomization = _previousDelegate; + } + + Penumbra.ResourceLoader.ResourceLoaded -= ImcResourceHandler; + } } private FullPath CreateImcPath( Utf8GamePath path )