Start restructuring CharacterUtility

This commit is contained in:
Ottermandias 2023-03-23 20:35:33 +01:00
parent 56286e0123
commit 7bad131542
4 changed files with 250 additions and 252 deletions

View file

@ -4,151 +4,146 @@ using System.Linq;
using Dalamud.Game;
using Dalamud.Utility.Signatures;
using Penumbra.GameData;
using Penumbra.Interop.Structs;
namespace Penumbra.Interop;
public unsafe partial class CharacterUtility : IDisposable
{
public record struct InternalIndex( int Value );
public record struct InternalIndex(int Value);
// A static pointer to the CharacterUtility address.
[Signature( Sigs.CharacterUtility, ScanType = ScanType.StaticAddress )]
private readonly Structs.CharacterUtility** _characterUtilityAddress = null;
/// <summary> A static pointer to the CharacterUtility address. </summary>
[Signature(Sigs.CharacterUtility, ScanType = ScanType.StaticAddress)]
private readonly CharacterUtilityData** _characterUtilityAddress = null;
// Only required for migration anymore.
public delegate void LoadResources( Structs.CharacterUtility* address );
/// <summary> Only required for migration anymore. </summary>
public delegate void LoadResources(CharacterUtilityData* address);
[Signature( Sigs.LoadCharacterResources )]
[Signature(Sigs.LoadCharacterResources)]
public readonly LoadResources LoadCharacterResourcesFunc = null!;
public void LoadCharacterResources()
=> LoadCharacterResourcesFunc.Invoke( Address );
=> LoadCharacterResourcesFunc.Invoke(Address);
public Structs.CharacterUtility* Address
public CharacterUtilityData* Address
=> *_characterUtilityAddress;
public bool Ready { get; private set; }
public bool Ready { get; private set; }
public event Action LoadingFinished;
private IntPtr _defaultTransparentResource;
private IntPtr _defaultDecalResource;
public IntPtr DefaultTransparentResource { get; private set; }
public IntPtr DefaultDecalResource { get; private set; }
// The relevant indices depend on which meta manipulations we allow for.
// The defines are set in the project configuration.
public static readonly Structs.CharacterUtility.Index[]
RelevantIndices = Enum.GetValues< Structs.CharacterUtility.Index >();
/// <summary>
/// The relevant indices depend on which meta manipulations we allow for.
/// The defines are set in the project configuration.
/// </summary>
public static readonly MetaIndex[]
RelevantIndices = Enum.GetValues<MetaIndex>();
public static readonly InternalIndex[] ReverseIndices
= Enumerable.Range( 0, Structs.CharacterUtility.TotalNumResources )
.Select( i => new InternalIndex( Array.IndexOf( RelevantIndices, ( Structs.CharacterUtility.Index )i ) ) )
.ToArray();
= Enumerable.Range(0, CharacterUtilityData.TotalNumResources)
.Select(i => new InternalIndex(Array.IndexOf(RelevantIndices, (MetaIndex)i)))
.ToArray();
private readonly List[] _lists = Enumerable.Range( 0, RelevantIndices.Length )
.Select( idx => new List( new InternalIndex( idx ) ) )
.ToArray();
private readonly List[] _lists = Enumerable.Range(0, RelevantIndices.Length)
.Select(idx => new List(new InternalIndex(idx)))
.ToArray();
public IReadOnlyList< List > Lists
public IReadOnlyList<List> Lists
=> _lists;
public (IntPtr Address, int Size) DefaultResource( InternalIndex idx )
=> _lists[ idx.Value ].DefaultResource;
public (IntPtr Address, int Size) DefaultResource(InternalIndex idx)
=> _lists[idx.Value].DefaultResource;
private readonly Framework _framework;
public CharacterUtility(Framework framework)
{
SignatureHelper.Initialise( this );
SignatureHelper.Initialise(this);
_framework = framework;
LoadingFinished += () => Penumbra.Log.Debug( "Loading of CharacterUtility finished." );
LoadDefaultResources( null! );
if( !Ready )
{
LoadingFinished += () => Penumbra.Log.Debug("Loading of CharacterUtility finished.");
LoadDefaultResources(null!);
if (!Ready)
_framework.Update += LoadDefaultResources;
}
}
// We store the default data of the resources so we can always restore them.
private void LoadDefaultResources( object _ )
/// <summary> We store the default data of the resources so we can always restore them. </summary>
private void LoadDefaultResources(object _)
{
if( Address == null )
{
if (Address == null)
return;
}
var anyMissing = false;
for( var i = 0; i < RelevantIndices.Length; ++i )
for (var i = 0; i < RelevantIndices.Length; ++i)
{
var list = _lists[ i ];
if( !list.Ready )
{
var resource = Address->Resource( RelevantIndices[ i ] );
var (data, length) = resource->GetData();
list.SetDefaultResource( data, length );
anyMissing |= !_lists[ i ].Ready;
}
var list = _lists[i];
if (list.Ready)
continue;
var resource = Address->Resource(RelevantIndices[i]);
var (data, length) = resource->GetData();
list.SetDefaultResource(data, length);
anyMissing |= !_lists[i].Ready;
}
if( _defaultTransparentResource == IntPtr.Zero )
if (DefaultTransparentResource == IntPtr.Zero)
{
_defaultTransparentResource = ( IntPtr )Address->TransparentTexResource;
anyMissing |= _defaultTransparentResource == IntPtr.Zero;
DefaultTransparentResource = (IntPtr)Address->TransparentTexResource;
anyMissing |= DefaultTransparentResource == IntPtr.Zero;
}
if( _defaultDecalResource == IntPtr.Zero )
if (DefaultDecalResource == IntPtr.Zero)
{
_defaultDecalResource = ( IntPtr )Address->DecalTexResource;
anyMissing |= _defaultDecalResource == IntPtr.Zero;
DefaultDecalResource = (IntPtr)Address->DecalTexResource;
anyMissing |= DefaultDecalResource == IntPtr.Zero;
}
if( !anyMissing )
{
Ready = true;
_framework.Update -= LoadDefaultResources;
LoadingFinished.Invoke();
}
if (anyMissing)
return;
Ready = true;
_framework.Update -= LoadDefaultResources;
LoadingFinished.Invoke();
}
public void SetResource( Structs.CharacterUtility.Index resourceIdx, IntPtr data, int length )
public void SetResource(MetaIndex resourceIdx, IntPtr data, int length)
{
var idx = ReverseIndices[ ( int )resourceIdx ];
var list = _lists[ idx.Value ];
list.SetResource( data, length );
var idx = ReverseIndices[(int)resourceIdx];
var list = _lists[idx.Value];
list.SetResource(data, length);
}
public void ResetResource( Structs.CharacterUtility.Index resourceIdx )
public void ResetResource(MetaIndex resourceIdx)
{
var idx = ReverseIndices[ ( int )resourceIdx ];
var list = _lists[ idx.Value ];
var idx = ReverseIndices[(int)resourceIdx];
var list = _lists[idx.Value];
list.ResetResource();
}
public List.MetaReverter TemporarilySetResource( Structs.CharacterUtility.Index resourceIdx, IntPtr data, int length )
public List.MetaReverter TemporarilySetResource(MetaIndex resourceIdx, IntPtr data, int length)
{
var idx = ReverseIndices[ ( int )resourceIdx ];
var list = _lists[ idx.Value ];
return list.TemporarilySetResource( data, length );
var idx = ReverseIndices[(int)resourceIdx];
var list = _lists[idx.Value];
return list.TemporarilySetResource(data, length);
}
public List.MetaReverter TemporarilyResetResource( Structs.CharacterUtility.Index resourceIdx )
public List.MetaReverter TemporarilyResetResource(MetaIndex resourceIdx)
{
var idx = ReverseIndices[ ( int )resourceIdx ];
var list = _lists[ idx.Value ];
var idx = ReverseIndices[(int)resourceIdx];
var list = _lists[idx.Value];
return list.TemporarilyResetResource();
}
// Return all relevant resources to the default resource.
/// <summary> Return all relevant resources to the default resource. </summary>
public void ResetAll()
{
foreach( var list in _lists )
{
foreach (var list in _lists)
list.Dispose();
}
Address->TransparentTexResource = ( Structs.TextureResourceHandle* )_defaultTransparentResource;
Address->DecalTexResource = ( Structs.TextureResourceHandle* )_defaultDecalResource;
Address->TransparentTexResource = (TextureResourceHandle*)DefaultTransparentResource;
Address->DecalTexResource = (TextureResourceHandle*)DefaultDecalResource;
}
public void Dispose()
{
ResetAll();
}
=> ResetAll();
}

View file

@ -1,170 +0,0 @@
using System;
using System.Linq;
using System.Runtime.InteropServices;
using Penumbra.GameData.Enums;
namespace Penumbra.Interop.Structs;
[StructLayout( LayoutKind.Explicit )]
public unsafe struct CharacterUtility
{
public enum Index : int
{
Eqp = 0,
Gmp = 2,
Eqdp0101 = 3,
Eqdp0201,
Eqdp0301,
Eqdp0401,
Eqdp0501,
Eqdp0601,
Eqdp0701,
Eqdp0801,
Eqdp0901,
Eqdp1001,
Eqdp1101,
Eqdp1201,
Eqdp1301,
Eqdp1401,
Eqdp1501,
//Eqdp1601, // TODO: female Hrothgar
Eqdp1701 = Eqdp1501 + 2,
Eqdp1801,
Eqdp0104,
Eqdp0204,
Eqdp0504,
Eqdp0604,
Eqdp0704,
Eqdp0804,
Eqdp1304,
Eqdp1404,
Eqdp9104,
Eqdp9204,
Eqdp0101Acc,
Eqdp0201Acc,
Eqdp0301Acc,
Eqdp0401Acc,
Eqdp0501Acc,
Eqdp0601Acc,
Eqdp0701Acc,
Eqdp0801Acc,
Eqdp0901Acc,
Eqdp1001Acc,
Eqdp1101Acc,
Eqdp1201Acc,
Eqdp1301Acc,
Eqdp1401Acc,
Eqdp1501Acc,
//Eqdp1601Acc, // TODO: female Hrothgar
Eqdp1701Acc = Eqdp1501Acc + 2,
Eqdp1801Acc,
Eqdp0104Acc,
Eqdp0204Acc,
Eqdp0504Acc,
Eqdp0604Acc,
Eqdp0704Acc,
Eqdp0804Acc,
Eqdp1304Acc,
Eqdp1404Acc,
Eqdp9104Acc,
Eqdp9204Acc,
HumanCmp = 64,
FaceEst,
HairEst,
HeadEst,
BodyEst,
}
public const int IndexTransparentTex = 72;
public const int IndexDecalTex = 73;
public static readonly Index[] EqdpIndices = Enum.GetNames< Index >()
.Zip( Enum.GetValues< Index >() )
.Where( n => n.First.StartsWith( "Eqdp" ) )
.Select( n => n.Second ).ToArray();
public const int TotalNumResources = 87;
public static Index EqdpIdx( GenderRace raceCode, bool accessory )
=> +( int )raceCode switch
{
0101 => accessory ? Index.Eqdp0101Acc : Index.Eqdp0101,
0201 => accessory ? Index.Eqdp0201Acc : Index.Eqdp0201,
0301 => accessory ? Index.Eqdp0301Acc : Index.Eqdp0301,
0401 => accessory ? Index.Eqdp0401Acc : Index.Eqdp0401,
0501 => accessory ? Index.Eqdp0501Acc : Index.Eqdp0501,
0601 => accessory ? Index.Eqdp0601Acc : Index.Eqdp0601,
0701 => accessory ? Index.Eqdp0701Acc : Index.Eqdp0701,
0801 => accessory ? Index.Eqdp0801Acc : Index.Eqdp0801,
0901 => accessory ? Index.Eqdp0901Acc : Index.Eqdp0901,
1001 => accessory ? Index.Eqdp1001Acc : Index.Eqdp1001,
1101 => accessory ? Index.Eqdp1101Acc : Index.Eqdp1101,
1201 => accessory ? Index.Eqdp1201Acc : Index.Eqdp1201,
1301 => accessory ? Index.Eqdp1301Acc : Index.Eqdp1301,
1401 => accessory ? Index.Eqdp1401Acc : Index.Eqdp1401,
1501 => accessory ? Index.Eqdp1501Acc : Index.Eqdp1501,
//1601 => accessory ? RelevantIndex.Eqdp1601Acc : RelevantIndex.Eqdp1601, Female Hrothgar
1701 => accessory ? Index.Eqdp1701Acc : Index.Eqdp1701,
1801 => accessory ? Index.Eqdp1801Acc : Index.Eqdp1801,
0104 => accessory ? Index.Eqdp0104Acc : Index.Eqdp0104,
0204 => accessory ? Index.Eqdp0204Acc : Index.Eqdp0204,
0504 => accessory ? Index.Eqdp0504Acc : Index.Eqdp0504,
0604 => accessory ? Index.Eqdp0604Acc : Index.Eqdp0604,
0704 => accessory ? Index.Eqdp0704Acc : Index.Eqdp0704,
0804 => accessory ? Index.Eqdp0804Acc : Index.Eqdp0804,
1304 => accessory ? Index.Eqdp1304Acc : Index.Eqdp1304,
1404 => accessory ? Index.Eqdp1404Acc : Index.Eqdp1404,
9104 => accessory ? Index.Eqdp9104Acc : Index.Eqdp9104,
9204 => accessory ? Index.Eqdp9204Acc : Index.Eqdp9204,
_ => ( Index )( -1 ),
};
[FieldOffset( 0 )]
public void* VTable;
[FieldOffset( 8 )]
public fixed ulong Resources[TotalNumResources];
[FieldOffset( 8 + ( int )Index.Eqp * 8 )]
public ResourceHandle* EqpResource;
[FieldOffset( 8 + ( int )Index.Gmp * 8 )]
public ResourceHandle* GmpResource;
public ResourceHandle* Resource( int idx )
=> ( ResourceHandle* )Resources[ idx ];
public ResourceHandle* Resource( Index idx )
=> Resource( ( int )idx );
public ResourceHandle* EqdpResource( GenderRace raceCode, bool accessory )
=> Resource( ( int )EqdpIdx( raceCode, accessory ) );
[FieldOffset( 8 + ( int )Index.HumanCmp * 8 )]
public ResourceHandle* HumanCmpResource;
[FieldOffset( 8 + ( int )Index.FaceEst * 8 )]
public ResourceHandle* FaceEstResource;
[FieldOffset( 8 + ( int )Index.HairEst * 8 )]
public ResourceHandle* HairEstResource;
[FieldOffset( 8 + ( int )Index.BodyEst * 8 )]
public ResourceHandle* BodyEstResource;
[FieldOffset( 8 + ( int )Index.HeadEst * 8 )]
public ResourceHandle* HeadEstResource;
[FieldOffset( 8 + IndexTransparentTex * 8 )]
public TextureResourceHandle* TransparentTexResource;
[FieldOffset( 8 + IndexDecalTex * 8 )]
public TextureResourceHandle* DecalTexResource;
// not included resources have no known use case.
}

View file

@ -0,0 +1,99 @@
using System;
using System.Linq;
using System.Runtime.InteropServices;
using Penumbra.GameData.Enums;
namespace Penumbra.Interop.Structs;
[StructLayout( LayoutKind.Explicit )]
public unsafe struct CharacterUtilityData
{
public const int IndexTransparentTex = 72;
public const int IndexDecalTex = 73;
public static readonly MetaIndex[] EqdpIndices = Enum.GetNames< MetaIndex >()
.Zip( Enum.GetValues< MetaIndex >() )
.Where( n => n.First.StartsWith( "Eqdp" ) )
.Select( n => n.Second ).ToArray();
public const int TotalNumResources = 87;
/// <summary> Obtain the index for the eqdp file corresponding to the given race code and accessory. </summary>
public static MetaIndex EqdpIdx( GenderRace raceCode, bool accessory )
=> +( int )raceCode switch
{
0101 => accessory ? MetaIndex.Eqdp0101Acc : MetaIndex.Eqdp0101,
0201 => accessory ? MetaIndex.Eqdp0201Acc : MetaIndex.Eqdp0201,
0301 => accessory ? MetaIndex.Eqdp0301Acc : MetaIndex.Eqdp0301,
0401 => accessory ? MetaIndex.Eqdp0401Acc : MetaIndex.Eqdp0401,
0501 => accessory ? MetaIndex.Eqdp0501Acc : MetaIndex.Eqdp0501,
0601 => accessory ? MetaIndex.Eqdp0601Acc : MetaIndex.Eqdp0601,
0701 => accessory ? MetaIndex.Eqdp0701Acc : MetaIndex.Eqdp0701,
0801 => accessory ? MetaIndex.Eqdp0801Acc : MetaIndex.Eqdp0801,
0901 => accessory ? MetaIndex.Eqdp0901Acc : MetaIndex.Eqdp0901,
1001 => accessory ? MetaIndex.Eqdp1001Acc : MetaIndex.Eqdp1001,
1101 => accessory ? MetaIndex.Eqdp1101Acc : MetaIndex.Eqdp1101,
1201 => accessory ? MetaIndex.Eqdp1201Acc : MetaIndex.Eqdp1201,
1301 => accessory ? MetaIndex.Eqdp1301Acc : MetaIndex.Eqdp1301,
1401 => accessory ? MetaIndex.Eqdp1401Acc : MetaIndex.Eqdp1401,
1501 => accessory ? MetaIndex.Eqdp1501Acc : MetaIndex.Eqdp1501,
//1601 => accessory ? MetaIndex.Eqdp1601Acc : MetaIndex.Eqdp1601, Female Hrothgar
1701 => accessory ? MetaIndex.Eqdp1701Acc : MetaIndex.Eqdp1701,
1801 => accessory ? MetaIndex.Eqdp1801Acc : MetaIndex.Eqdp1801,
0104 => accessory ? MetaIndex.Eqdp0104Acc : MetaIndex.Eqdp0104,
0204 => accessory ? MetaIndex.Eqdp0204Acc : MetaIndex.Eqdp0204,
0504 => accessory ? MetaIndex.Eqdp0504Acc : MetaIndex.Eqdp0504,
0604 => accessory ? MetaIndex.Eqdp0604Acc : MetaIndex.Eqdp0604,
0704 => accessory ? MetaIndex.Eqdp0704Acc : MetaIndex.Eqdp0704,
0804 => accessory ? MetaIndex.Eqdp0804Acc : MetaIndex.Eqdp0804,
1304 => accessory ? MetaIndex.Eqdp1304Acc : MetaIndex.Eqdp1304,
1404 => accessory ? MetaIndex.Eqdp1404Acc : MetaIndex.Eqdp1404,
9104 => accessory ? MetaIndex.Eqdp9104Acc : MetaIndex.Eqdp9104,
9204 => accessory ? MetaIndex.Eqdp9204Acc : MetaIndex.Eqdp9204,
_ => ( MetaIndex )( -1 ),
};
[FieldOffset( 0 )]
public void* VTable;
[FieldOffset( 8 )]
public fixed ulong Resources[TotalNumResources];
[FieldOffset( 8 + ( int )MetaIndex.Eqp * 8 )]
public ResourceHandle* EqpResource;
[FieldOffset( 8 + ( int )MetaIndex.Gmp * 8 )]
public ResourceHandle* GmpResource;
public ResourceHandle* Resource( int idx )
=> ( ResourceHandle* )Resources[ idx ];
public ResourceHandle* Resource( MetaIndex idx )
=> Resource( ( int )idx );
public ResourceHandle* EqdpResource( GenderRace raceCode, bool accessory )
=> Resource( ( int )EqdpIdx( raceCode, accessory ) );
[FieldOffset( 8 + ( int )MetaIndex.HumanCmp * 8 )]
public ResourceHandle* HumanCmpResource;
[FieldOffset( 8 + ( int )MetaIndex.FaceEst * 8 )]
public ResourceHandle* FaceEstResource;
[FieldOffset( 8 + ( int )MetaIndex.HairEst * 8 )]
public ResourceHandle* HairEstResource;
[FieldOffset( 8 + ( int )MetaIndex.BodyEst * 8 )]
public ResourceHandle* BodyEstResource;
[FieldOffset( 8 + ( int )MetaIndex.HeadEst * 8 )]
public ResourceHandle* HeadEstResource;
[FieldOffset( 8 + IndexTransparentTex * 8 )]
public TextureResourceHandle* TransparentTexResource;
[FieldOffset( 8 + IndexDecalTex * 8 )]
public TextureResourceHandle* DecalTexResource;
// not included resources have no known use case.
}

View file

@ -0,0 +1,74 @@
namespace Penumbra.Interop.Structs;
/// <summary> Indices for the different meta files contained in CharacterUtility. </summary>
public enum MetaIndex : int
{
Eqp = 0,
Gmp = 2,
Eqdp0101 = 3,
Eqdp0201,
Eqdp0301,
Eqdp0401,
Eqdp0501,
Eqdp0601,
Eqdp0701,
Eqdp0801,
Eqdp0901,
Eqdp1001,
Eqdp1101,
Eqdp1201,
Eqdp1301,
Eqdp1401,
Eqdp1501,
//Eqdp1601, // TODO: female Hrothgar
Eqdp1701 = Eqdp1501 + 2,
Eqdp1801,
Eqdp0104,
Eqdp0204,
Eqdp0504,
Eqdp0604,
Eqdp0704,
Eqdp0804,
Eqdp1304,
Eqdp1404,
Eqdp9104,
Eqdp9204,
Eqdp0101Acc,
Eqdp0201Acc,
Eqdp0301Acc,
Eqdp0401Acc,
Eqdp0501Acc,
Eqdp0601Acc,
Eqdp0701Acc,
Eqdp0801Acc,
Eqdp0901Acc,
Eqdp1001Acc,
Eqdp1101Acc,
Eqdp1201Acc,
Eqdp1301Acc,
Eqdp1401Acc,
Eqdp1501Acc,
//Eqdp1601Acc, // TODO: female Hrothgar
Eqdp1701Acc = Eqdp1501Acc + 2,
Eqdp1801Acc,
Eqdp0104Acc,
Eqdp0204Acc,
Eqdp0504Acc,
Eqdp0604Acc,
Eqdp0704Acc,
Eqdp0804Acc,
Eqdp1304Acc,
Eqdp1404Acc,
Eqdp9104Acc,
Eqdp9204Acc,
HumanCmp = 64,
FaceEst,
HairEst,
HeadEst,
BodyEst,
}