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