Metamanipulations seemingly working.

This commit is contained in:
Ottermandias 2022-03-14 15:23:00 +01:00
parent 707570615c
commit 6f527a1dbc
26 changed files with 1637 additions and 1237 deletions

View file

@ -47,7 +47,8 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile
throw new IndexOutOfRangeException();
}
return *( EqdpEntry* )( Data + DataOffset + EqdpEntrySize * idx );
var x = new ReadOnlySpan< ushort >( ( ushort* )Data, Length / 2 );
return ( EqdpEntry )( *( ushort* )( Data + DataOffset + EqdpEntrySize * idx ) );
}
set
{
@ -56,7 +57,7 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile
throw new IndexOutOfRangeException();
}
*( EqdpEntry* )( Data + DataOffset + EqdpEntrySize * idx ) = value;
*( ushort* )( Data + DataOffset + EqdpEntrySize * idx ) = ( ushort )value;
}
}
@ -65,9 +66,12 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile
var def = ( byte* )DefaultData.Data;
Functions.MemCpyUnchecked( Data, def, IdentifierSize + PreambleSize );
var controlPtr = ( ushort* )( def + IdentifierSize + PreambleSize );
var dataBasePtr = ( byte* )( controlPtr + BlockCount );
var myDataPtr = ( ushort* )( Data + IdentifierSize + PreambleSize + 2 * BlockCount );
var controlPtr = ( ushort* )( def + IdentifierSize + PreambleSize );
var dataBasePtr = controlPtr + BlockCount;
var myDataPtrStart = ( ushort* )( Data + IdentifierSize + PreambleSize + 2 * BlockCount );
var myDataPtr = myDataPtrStart;
var myControlPtr = ( ushort* )( Data + IdentifierSize + PreambleSize );
var x = new ReadOnlySpan< ushort >( ( ushort* )Data, Length / 2 );
for( var i = 0; i < BlockCount; ++i )
{
if( controlPtr[ i ] == CollapsedBlock )
@ -76,11 +80,16 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile
}
else
{
var y = new ReadOnlySpan< ushort >( dataBasePtr + controlPtr[ i ], BlockSize );
var z = new ReadOnlySpan< ushort >( myDataPtr, BlockSize );
Functions.MemCpyUnchecked( myDataPtr, dataBasePtr + controlPtr[ i ], BlockSize * EqdpEntrySize );
}
myDataPtr += BlockSize;
myControlPtr[ i ] = ( ushort )( i * BlockSize );
myDataPtr += BlockSize;
}
Functions.MemSet( myDataPtr, 0, Length - ( int )( ( byte* )myDataPtr - Data ) );
}
public void Reset( IEnumerable< int > entries )
@ -102,7 +111,7 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile
DataOffset = IdentifierSize + PreambleSize + totalBlockCount * BlockHeaderSize;
var fullLength = DataOffset + totalBlockCount * totalBlockSize;
fullLength += ( FileAlignment - ( Length & ( FileAlignment - 1 ) ) ) & ( FileAlignment - 1 );
fullLength += ( FileAlignment - ( fullLength & ( FileAlignment - 1 ) ) ) & ( FileAlignment - 1 );
AllocateData( fullLength );
Reset();
}
@ -128,8 +137,9 @@ public sealed unsafe class ExpandedEqdpFile : MetaBaseFile
return 0;
}
var blockData = ( EqdpEntry* )( data + IdentifierSize + PreambleSize + totalBlockCount * 2 + block );
return *( blockData + blockIdx % blockSize );
var blockData = ( ushort* )( data + IdentifierSize + PreambleSize + totalBlockCount * 2 + block * 2 );
var x = new ReadOnlySpan< ushort >( blockData, blockSize );
return (EqdpEntry) (*( blockData + setIdx % blockSize ));
}
public static EqdpEntry GetDefault( GenderRace raceCode, bool accessory, int setIdx )

View file

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using System.Runtime.CompilerServices;
using Penumbra.GameData.Structs;
using Penumbra.GameData.Util;
using Penumbra.Interop.Structs;
@ -24,17 +25,17 @@ public unsafe class ExpandedEqpGmpBase : MetaBaseFile
public ulong ControlBlock
=> *( ulong* )Data;
protected T Get< T >( int idx ) where T : unmanaged
protected ulong GetInternal( int idx )
{
return idx switch
{
>= Count => throw new IndexOutOfRangeException(),
<= 1 => *( ( T* )Data + 1 ),
_ => *( ( T* )Data + idx ),
<= 1 => *( ( ulong* )Data + 1 ),
_ => *( ( ulong* )Data + idx ),
};
}
protected void Set< T >( int idx, T value ) where T : unmanaged
protected void SetInternal( int idx, ulong value )
{
idx = idx switch
{
@ -43,7 +44,7 @@ public unsafe class ExpandedEqpGmpBase : MetaBaseFile
_ => idx,
};
*( ( T* )Data + idx ) = value;
*( ( ulong* )Data + idx ) = value;
}
protected virtual void SetEmptyBlock( int idx )
@ -53,21 +54,24 @@ public unsafe class ExpandedEqpGmpBase : MetaBaseFile
public sealed override void Reset()
{
var ptr = ( byte* )DefaultData.Data;
var controlBlock = *( ulong* )ptr;
*( ulong* )Data = ulong.MaxValue;
for( var i = 0; i < 64; ++i )
var ptr = ( byte* )DefaultData.Data;
var controlBlock = *( ulong* )ptr;
var expandedBlocks = 0;
for( var i = 0; i < NumBlocks; ++i )
{
var collapsed = ( ( controlBlock >> i ) & 1 ) == 0;
if( !collapsed )
{
Functions.MemCpyUnchecked( Data + i * BlockSize * EntrySize, ptr + i * BlockSize * EntrySize, BlockSize * EntrySize );
Functions.MemCpyUnchecked( Data + i * BlockSize * EntrySize, ptr + expandedBlocks * BlockSize * EntrySize, BlockSize * EntrySize );
expandedBlocks++;
}
else
{
SetEmptyBlock( i );
}
}
*( ulong* )Data = ulong.MaxValue;
}
public ExpandedEqpGmpBase( bool gmp )
@ -77,7 +81,7 @@ public unsafe class ExpandedEqpGmpBase : MetaBaseFile
Reset();
}
protected static T GetDefault< T >( int fileIdx, int setIdx, T def ) where T : unmanaged
protected static ulong GetDefaultInternal( int fileIdx, int setIdx, ulong def )
{
var data = ( byte* )Penumbra.CharacterUtility.DefaultResources[ fileIdx ].Address;
if( setIdx == 0 )
@ -100,7 +104,7 @@ public unsafe class ExpandedEqpGmpBase : MetaBaseFile
var count = BitOperations.PopCount( control & ( blockBit - 1 ) );
var idx = setIdx % BlockSize;
var ptr = ( T* )data + BlockSize * count + idx;
var ptr = ( ulong* )data + BlockSize * count + idx;
return *ptr;
}
}
@ -113,12 +117,12 @@ public sealed class ExpandedEqpFile : ExpandedEqpGmpBase
public EqpEntry this[ int idx ]
{
get => Get< EqpEntry >( idx );
set => Set( idx, value );
get => ( EqpEntry )GetInternal( idx );
set => SetInternal( idx, ( ulong )value );
}
public static EqpEntry GetDefault( int setIdx )
=> GetDefault( CharacterUtility.EqpIdx, setIdx, Eqp.DefaultEntry );
=> ( EqpEntry )GetDefaultInternal( CharacterUtility.EqpIdx, setIdx, ( ulong )Eqp.DefaultEntry );
protected override unsafe void SetEmptyBlock( int idx )
{
@ -147,12 +151,12 @@ public sealed class ExpandedGmpFile : ExpandedEqpGmpBase
public GmpEntry this[ int idx ]
{
get => Get< GmpEntry >( idx );
set => Set( idx, value );
get => ( GmpEntry )GetInternal( idx );
set => SetInternal( idx, ( ulong )value );
}
public static GmpEntry GetDefault( int setIdx )
=> GetDefault( CharacterUtility.GmpIdx, setIdx, GmpEntry.Default );
=> ( GmpEntry )GetDefaultInternal( CharacterUtility.GmpIdx, setIdx, ( ulong )GmpEntry.Default );
public void Reset( IEnumerable< int > entries )
{

View file

@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Numerics;
using Dalamud.Logging;
using Dalamud.Memory;
@ -7,6 +8,7 @@ using Penumbra.GameData.ByteString;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Util;
using Penumbra.Interop.Structs;
using Penumbra.Meta.Manipulations;
namespace Penumbra.Meta.Files;
@ -64,41 +66,57 @@ public unsafe class ImcFile : MetaBaseFile
=> NumParts * sizeof( ImcEntry ) * ( Count + 1 ) + PreambleSize;
public int Count
=> *( ushort* )Data;
public ushort PartMask
=> *( ushort* )( Data + 2 );
=> CountInternal( Data );
public readonly int NumParts;
public readonly Utf8GamePath Path;
public ImcEntry* DefaultPartPtr( int partIdx )
private static int CountInternal( byte* data )
=> *( ushort* )data;
private static ushort PartMask( byte* data )
=> *( ushort* )( data + 2 );
private static ImcEntry* DefaultPartPtr( byte* data, int partIdx )
{
var flag = 1 << partIdx;
if( ( PartMask & flag ) == 0 )
if( ( PartMask( data ) & flag ) == 0 )
{
return null;
}
return ( ImcEntry* )( Data + PreambleSize ) + partIdx;
return ( ImcEntry* )( data + PreambleSize ) + partIdx;
}
public ImcEntry* VariantPtr( 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;
if( ( PartMask & flag ) == 0 || variantIdx >= Count )
if( ( PartMask( data ) & flag ) == 0 || variantIdx >= CountInternal( data ) )
{
return null;
}
var numParts = NumParts;
var ptr = ( ImcEntry* )( Data + PreambleSize );
var numParts = BitOperations.PopCount( PartMask( data ) );
var ptr = ( ImcEntry* )( data + PreambleSize );
ptr += numParts;
ptr += variantIdx * numParts;
ptr += partIdx;
return ptr;
}
public ImcEntry GetEntry( int partIdx, int variantIdx )
{
var ptr = VariantPtr( Data, partIdx, variantIdx );
return ptr == null ? new ImcEntry() : *ptr;
}
public static int PartIndex( EquipSlot slot )
=> slot switch
{
@ -122,7 +140,6 @@ public unsafe class ImcFile : MetaBaseFile
return true;
}
var numParts = NumParts;
if( ActualLength > Length )
{
PluginLog.Warning( "Adding too many variants to IMC, size exceeded." );
@ -130,10 +147,10 @@ public unsafe class ImcFile : MetaBaseFile
}
var defaultPtr = ( ImcEntry* )( Data + PreambleSize );
var endPtr = defaultPtr + ( numVariants + 1 ) * numParts;
for( var ptr = defaultPtr + numParts; ptr < endPtr; ptr += numParts )
var endPtr = defaultPtr + ( numVariants + 1 ) * NumParts;
for( var ptr = defaultPtr + NumParts; ptr < endPtr; ptr += NumParts )
{
Functions.MemCpyUnchecked( ptr, defaultPtr, numParts * sizeof( ImcEntry ) );
Functions.MemCpyUnchecked( ptr, defaultPtr, NumParts * sizeof( ImcEntry ) );
}
PluginLog.Verbose( "Expanded imc from {Count} to {NewCount} variants.", Count, numVariants );
@ -143,15 +160,14 @@ public unsafe class ImcFile : MetaBaseFile
public bool SetEntry( int partIdx, int variantIdx, ImcEntry entry )
{
var numParts = NumParts;
if( partIdx >= numParts )
if( partIdx >= NumParts )
{
return false;
}
EnsureVariantCount( variantIdx + 1 );
EnsureVariantCount( variantIdx );
var variantPtr = VariantPtr( partIdx, variantIdx );
var variantPtr = VariantPtr( Data, partIdx, variantIdx );
if( variantPtr == null )
{
PluginLog.Error( "Error during expansion of imc file." );
@ -168,9 +184,20 @@ public unsafe class ImcFile : MetaBaseFile
}
public override void Reset()
{
var file = Dalamud.GameData.GetFile( Path.ToString() );
fixed( byte* ptr = file!.Data )
{
Functions.MemCpyUnchecked( Data, ptr, file.Data.Length );
Functions.MemSet( Data + file.Data.Length, 0, Length - file.Data.Length );
}
}
public ImcFile( Utf8GamePath path )
: base( 0 )
{
Path = path;
var file = Dalamud.GameData.GetFile( path.ToString() );
if( file == null )
{
@ -182,6 +209,22 @@ public unsafe class ImcFile : MetaBaseFile
NumParts = BitOperations.PopCount( *( ushort* )( ptr + 2 ) );
AllocateData( file.Data.Length + sizeof( ImcEntry ) * 100 * NumParts );
Functions.MemCpyUnchecked( Data, ptr, file.Data.Length );
Functions.MemSet( Data + file.Data.Length, 0, sizeof( ImcEntry ) * 100 * NumParts );
}
}
public static ImcEntry GetDefault( Utf8GamePath path, EquipSlot slot, int variantIdx )
{
var file = Dalamud.GameData.GetFile( path.ToString() );
if( file == null )
{
throw new Exception();
}
fixed( byte* ptr = file.Data )
{
var entry = VariantPtr( ptr, PartIndex( slot ), variantIdx );
return entry == null ? new ImcEntry() : *entry;
}
}