mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-14 20:54:16 +01:00
Support for parsing TexTools .Meta Files and the corresponding game data.
This commit is contained in:
parent
88ba14e595
commit
739627b7c2
15 changed files with 1915 additions and 1 deletions
216
Penumbra/MetaData/EqpFile.cs
Normal file
216
Penumbra/MetaData/EqpFile.cs
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Lumina.Data;
|
||||
using Penumbra.Game;
|
||||
|
||||
namespace Penumbra.MetaData
|
||||
{
|
||||
// EQP Structure:
|
||||
// 64 x [Block collapsed or not bit]
|
||||
// 159 x [EquipmentParameter:ulong]
|
||||
// (CountSetBits(Block Collapsed or not) - 1) x 160 x [EquipmentParameter:ulong]
|
||||
// Item 0 does not exist and is sent to Item 1 instead.
|
||||
public sealed class EqpFile : EqpGmpBase
|
||||
{
|
||||
private readonly EqpEntry[]?[] _entries = new EqpEntry[TotalBlockCount][];
|
||||
|
||||
protected override ulong ControlBlock
|
||||
{
|
||||
get => ( ulong )_entries[ 0 ]![ 0 ];
|
||||
set => _entries[ 0 ]![ 0 ] = ( EqpEntry )value;
|
||||
}
|
||||
|
||||
private EqpFile( EqpFile clone )
|
||||
{
|
||||
ExpandedBlockCount = clone.ExpandedBlockCount;
|
||||
_entries = clone.Clone( clone._entries );
|
||||
}
|
||||
|
||||
public byte[] WriteBytes()
|
||||
=> WriteBytes( _entries, E => ( ulong )E );
|
||||
|
||||
public EqpFile Clone()
|
||||
=> new( this );
|
||||
|
||||
public EqpFile( FileResource file )
|
||||
=> ReadFile( _entries, file, I => ( EqpEntry )I );
|
||||
|
||||
public EqpEntry GetEntry( ushort setId )
|
||||
=> GetEntry( _entries, setId, ( EqpEntry )0 );
|
||||
|
||||
public bool SetEntry( ushort setId, EqpEntry entry )
|
||||
=> SetEntry( _entries, setId, entry, E => E == 0, ( E1, E2 ) => E1 == E2 );
|
||||
|
||||
public ref EqpEntry this[ ushort setId ]
|
||||
=> ref GetTrueEntry( _entries, setId );
|
||||
}
|
||||
|
||||
public class EqpGmpBase
|
||||
{
|
||||
protected const ushort ParameterSize = 8;
|
||||
protected const ushort BlockSize = 160;
|
||||
protected const ushort TotalBlockCount = 64;
|
||||
|
||||
protected int ExpandedBlockCount { get; set; }
|
||||
|
||||
private static int BlockIdx( ushort idx )
|
||||
=> idx / BlockSize;
|
||||
|
||||
private static int SubIdx( ushort idx )
|
||||
=> idx % BlockSize;
|
||||
|
||||
protected virtual ulong ControlBlock { get; set; }
|
||||
|
||||
protected T[]?[] Clone< T >( T[]?[] clone )
|
||||
{
|
||||
var ret = new T[TotalBlockCount][];
|
||||
for( var i = 0; i < TotalBlockCount; ++i )
|
||||
{
|
||||
if( clone[ i ] != null )
|
||||
{
|
||||
ret[ i ] = ( T[] )clone[ i ]!.Clone();
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
protected EqpGmpBase()
|
||||
{ }
|
||||
|
||||
protected bool ExpandBlock< T >( T[]?[] blocks, int idx )
|
||||
{
|
||||
if( idx >= TotalBlockCount || blocks[ idx ] != null )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
blocks[ idx ] = new T[BlockSize];
|
||||
++ExpandedBlockCount;
|
||||
ControlBlock |= 1ul << idx;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected bool CollapseBlock< T >( T[]?[] blocks, int idx )
|
||||
{
|
||||
if( idx >= TotalBlockCount || blocks[ idx ] == null )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
blocks[ idx ] = null;
|
||||
--ExpandedBlockCount;
|
||||
ControlBlock &= ~( 1ul << idx );
|
||||
return true;
|
||||
}
|
||||
|
||||
protected T GetEntry< T >( T[]?[] blocks, ushort idx, T defaultEntry )
|
||||
{
|
||||
// Skip the zeroth item.
|
||||
idx = idx == 0 ? 1 : idx;
|
||||
var block = BlockIdx( idx );
|
||||
var array = block < blocks.Length ? blocks[ block ] : null;
|
||||
if( array == null )
|
||||
{
|
||||
return defaultEntry;
|
||||
}
|
||||
|
||||
return array[ SubIdx( idx ) ];
|
||||
}
|
||||
|
||||
protected ref T GetTrueEntry< T >( T[]?[] blocks, ushort idx )
|
||||
{
|
||||
// Skip the zeroth item.
|
||||
idx = idx == 0 ? 1 : idx;
|
||||
var block = BlockIdx( idx );
|
||||
if( block >= TotalBlockCount )
|
||||
{
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
ExpandBlock( blocks, block );
|
||||
var array = blocks[ block ]!;
|
||||
return ref array[ SubIdx( idx ) ];
|
||||
}
|
||||
|
||||
protected byte[] WriteBytes< T >( T[]?[] blocks, Func< T, ulong > transform )
|
||||
{
|
||||
var dataSize = ExpandedBlockCount * BlockSize * ParameterSize;
|
||||
using var mem = new MemoryStream( dataSize );
|
||||
using var bw = new BinaryWriter( mem );
|
||||
|
||||
foreach( var parameter in blocks.Where( array => array != null )
|
||||
.SelectMany( array => array ) )
|
||||
{
|
||||
bw.Write( transform( parameter ) );
|
||||
}
|
||||
|
||||
return mem.ToArray();
|
||||
}
|
||||
|
||||
protected void ReadFile< T >( T[]?[] blocks, FileResource file, Func< ulong, T > convert )
|
||||
{
|
||||
file.Reader.BaseStream.Seek( 0, SeekOrigin.Begin );
|
||||
var blockBits = file.Reader.ReadUInt64();
|
||||
// reset to 0 and just put the bitmask in the first block
|
||||
// item 0 is not accessible and it simplifies printing.
|
||||
file.Reader.BaseStream.Seek( 0, SeekOrigin.Begin );
|
||||
|
||||
ExpandedBlockCount = 0;
|
||||
for( var i = 0; i < TotalBlockCount; ++i )
|
||||
{
|
||||
var flag = 1ul << i;
|
||||
if( ( blockBits & flag ) != flag )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
++ExpandedBlockCount;
|
||||
|
||||
var tmp = new T[BlockSize];
|
||||
for( var j = 0; j < BlockSize; ++j )
|
||||
{
|
||||
tmp[ j ] = convert( file.Reader.ReadUInt64() );
|
||||
}
|
||||
|
||||
blocks[ i ] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
protected bool SetEntry< T >( T[]?[] blocks, ushort idx, T entry, Func< T, bool > isDefault, Func< T, T, bool > isEqual )
|
||||
{
|
||||
var block = BlockIdx( idx );
|
||||
if( block >= TotalBlockCount )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !isDefault( entry ) )
|
||||
{
|
||||
ExpandBlock( blocks, block );
|
||||
if( !isEqual( entry, blocks[ block ]![ SubIdx( idx ) ] ) )
|
||||
{
|
||||
blocks[ block ]![ SubIdx( idx ) ] = entry;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var array = blocks[ block ];
|
||||
if( array != null )
|
||||
{
|
||||
array[ SubIdx( idx ) ] = entry;
|
||||
if( array.All( e => e!.Equals( 0ul ) ) )
|
||||
{
|
||||
CollapseBlock( blocks, block );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue