Fix IMC adding new variants.

This commit is contained in:
Ottermandias 2022-03-20 23:45:16 +01:00
parent 98b4b29ff5
commit 7540694050
3 changed files with 59 additions and 32 deletions

View file

@ -70,6 +70,9 @@ public unsafe class ImcFile : MetaBaseFile
public readonly int NumParts; public readonly int NumParts;
public bool ChangesSinceLoad = true; public bool ChangesSinceLoad = true;
public ReadOnlySpan< ImcEntry > Span
=> new(( ImcEntry* )( Data + PreambleSize ), ( Length - PreambleSize ) / sizeof( ImcEntry ));
private static int CountInternal( byte* data ) private static int CountInternal( byte* data )
=> *( ushort* )data; => *( ushort* )data;
@ -89,24 +92,15 @@ public unsafe class ImcFile : MetaBaseFile
private static ImcEntry* VariantPtr( byte* data, int partIdx, int variantIdx ) private static ImcEntry* VariantPtr( byte* data, int partIdx, int variantIdx )
{ {
if( variantIdx == 0 )
{
return DefaultPartPtr( data, partIdx );
}
--variantIdx;
var flag = 1 << partIdx; var flag = 1 << partIdx;
if( ( PartMask( data ) & flag ) == 0 || variantIdx > CountInternal( data ) )
if( ( PartMask( data ) & flag ) == 0 || variantIdx >= CountInternal( data ) )
{ {
return null; return null;
} }
var numParts = BitOperations.PopCount( PartMask( data ) ); var numParts = BitOperations.PopCount( PartMask( data ) );
var ptr = ( ImcEntry* )( data + PreambleSize ); var ptr = ( ImcEntry* )( data + PreambleSize );
ptr += numParts; ptr += variantIdx * numParts + partIdx;
ptr += variantIdx * numParts;
ptr += partIdx;
return ptr; return ptr;
} }
@ -139,21 +133,22 @@ public unsafe class ImcFile : MetaBaseFile
return true; return true;
} }
var oldCount = Count;
*( ushort* )Data = ( ushort )numVariants;
if( ActualLength > Length ) if( ActualLength > Length )
{ {
PluginLog.Warning( "Adding too many variants to IMC, size exceeded." ); var newLength = ( ( ( ActualLength - 1 ) >> 7 ) + 1 ) << 7;
return false; PluginLog.Verbose( "Resized IMC {Path} from {Length} to {NewLength}.", Path, Length, newLength );
ResizeResources( newLength );
} }
var defaultPtr = ( ImcEntry* )( Data + PreambleSize ); var defaultPtr = ( ImcEntry* )( Data + PreambleSize );
var endPtr = defaultPtr + ( numVariants + 1 ) * NumParts; for( var i = oldCount + 1; i < numVariants + 1; ++i )
for( var ptr = defaultPtr + NumParts; ptr < endPtr; ptr += NumParts )
{ {
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 ); PluginLog.Verbose( "Expanded IMC {Path} from {Count} to {NewCount} variants.", Path, oldCount, numVariants );
*( ushort* )Data = ( ushort )numVariants;
return true; return true;
} }
@ -240,7 +235,7 @@ public unsafe class ImcFile : MetaBaseFile
} }
var requiredLength = ActualLength; var requiredLength = ActualLength;
resource->SetData( (IntPtr) Data, Length ); resource->SetData( ( IntPtr )Data, Length );
if( length >= requiredLength ) if( length >= requiredLength )
{ {
Functions.MemCpyUnchecked( ( void* )data, Data, requiredLength ); Functions.MemCpyUnchecked( ( void* )data, Data, requiredLength );

View file

@ -1,5 +1,6 @@
using System; using System;
using Dalamud.Memory; using Dalamud.Memory;
using Penumbra.GameData.Util;
namespace Penumbra.Meta.Files; namespace Penumbra.Meta.Files;
@ -44,6 +45,31 @@ public unsafe class MetaBaseFile : IDisposable
Data = null; 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. // Manually free memory.
public void Dispose() public void Dispose()
{ {

View file

@ -20,7 +20,8 @@ public partial class MetaManager
public readonly Dictionary< ImcManipulation, Mod.Mod > Manipulations = new(); public readonly Dictionary< ImcManipulation, Mod.Mod > Manipulations = new();
private readonly ModCollection _collection; private readonly ModCollection _collection;
private readonly ResourceLoader.ResourceLoadCustomizationDelegate? _previousDelegate; private static int _imcManagerCount;
private static ResourceLoader.ResourceLoadCustomizationDelegate? _previousDelegate;
public MetaManagerImc( ModCollection collection ) public MetaManagerImc( ModCollection collection )
@ -97,18 +98,22 @@ public partial class MetaManager
Files.Clear(); Files.Clear();
Manipulations.Clear(); Manipulations.Clear();
RestoreDelegate();
} }
[Conditional( "USE_IMC" )] [Conditional( "USE_IMC" )]
private unsafe void SetupDelegate() private static unsafe void SetupDelegate()
{
if( _imcManagerCount++ == 0 )
{ {
Penumbra.ResourceLoader.ResourceLoadCustomization = ImcLoadHandler; Penumbra.ResourceLoader.ResourceLoadCustomization = ImcLoadHandler;
Penumbra.ResourceLoader.ResourceLoaded += ImcResourceHandler; Penumbra.ResourceLoader.ResourceLoaded += ImcResourceHandler;
} }
}
[Conditional( "USE_IMC" )] [Conditional( "USE_IMC" )]
private unsafe void RestoreDelegate() private static unsafe void RestoreDelegate()
{
if( --_imcManagerCount == 0 )
{ {
if( Penumbra.ResourceLoader.ResourceLoadCustomization == ImcLoadHandler ) if( Penumbra.ResourceLoader.ResourceLoadCustomization == ImcLoadHandler )
{ {
@ -117,6 +122,7 @@ public partial class MetaManager
Penumbra.ResourceLoader.ResourceLoaded -= ImcResourceHandler; Penumbra.ResourceLoader.ResourceLoaded -= ImcResourceHandler;
} }
}
private FullPath CreateImcPath( Utf8GamePath path ) private FullPath CreateImcPath( Utf8GamePath path )
=> new($"|{_collection.Name}|{path}"); => new($"|{_collection.Name}|{path}");