diff --git a/Penumbra/Interop/CharacterUtility.cs b/Penumbra/Interop/CharacterUtility.cs index 0f3a46d5..63962195 100644 --- a/Penumbra/Interop/CharacterUtility.cs +++ b/Penumbra/Interop/CharacterUtility.cs @@ -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; + /// A static pointer to the CharacterUtility address. + [Signature(Sigs.CharacterUtility, ScanType = ScanType.StaticAddress)] + private readonly CharacterUtilityData** _characterUtilityAddress = null; - // Only required for migration anymore. - public delegate void LoadResources( Structs.CharacterUtility* address ); + /// Only required for migration anymore. + 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 >(); + /// + /// The relevant indices depend on which meta manipulations we allow for. + /// The defines are set in the project configuration. + /// + public static readonly MetaIndex[] + RelevantIndices = Enum.GetValues(); 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 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 _ ) + /// We store the default data of the resources so we can always restore them. + 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. + /// Return all relevant resources to the default resource. 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(); - } -} \ No newline at end of file + => ResetAll(); +} diff --git a/Penumbra/Interop/Structs/CharacterUtility.cs b/Penumbra/Interop/Structs/CharacterUtility.cs deleted file mode 100644 index e491de7b..00000000 --- a/Penumbra/Interop/Structs/CharacterUtility.cs +++ /dev/null @@ -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. -} \ No newline at end of file diff --git a/Penumbra/Interop/Structs/CharacterUtilityData.cs b/Penumbra/Interop/Structs/CharacterUtilityData.cs new file mode 100644 index 00000000..b273091b --- /dev/null +++ b/Penumbra/Interop/Structs/CharacterUtilityData.cs @@ -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; + + /// Obtain the index for the eqdp file corresponding to the given race code and accessory. + 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. +} \ No newline at end of file diff --git a/Penumbra/Interop/Structs/MetaIndex.cs b/Penumbra/Interop/Structs/MetaIndex.cs new file mode 100644 index 00000000..65302264 --- /dev/null +++ b/Penumbra/Interop/Structs/MetaIndex.cs @@ -0,0 +1,74 @@ +namespace Penumbra.Interop.Structs; + +/// Indices for the different meta files contained in CharacterUtility. +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, +}