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 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 );

View file

@ -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()
{

View file

@ -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 )