mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 18:27:24 +01:00
145 lines
No EOL
4.8 KiB
C#
145 lines
No EOL
4.8 KiB
C#
using System;
|
|
using System.Linq;
|
|
using Dalamud.Utility.Signatures;
|
|
|
|
namespace Penumbra.Interop;
|
|
|
|
public unsafe class CharacterUtility : IDisposable
|
|
{
|
|
public record struct InternalIndex( int Value );
|
|
|
|
// A static pointer to the CharacterUtility address.
|
|
[Signature( "48 8B 05 ?? ?? ?? ?? 83 B9", ScanType = ScanType.StaticAddress )]
|
|
private readonly Structs.CharacterUtility** _characterUtilityAddress = null;
|
|
|
|
// Only required for migration anymore.
|
|
public delegate void LoadResources( Structs.CharacterUtility* address );
|
|
|
|
[Signature( "E8 ?? ?? ?? ?? 48 8D 8F ?? ?? ?? ?? E8 ?? ?? ?? ?? 33 D2 45 33 C0" )]
|
|
public readonly LoadResources LoadCharacterResourcesFunc = null!;
|
|
|
|
public void LoadCharacterResources()
|
|
=> LoadCharacterResourcesFunc.Invoke( Address );
|
|
|
|
public Structs.CharacterUtility* Address
|
|
=> *_characterUtilityAddress;
|
|
|
|
public bool Ready { get; private set; }
|
|
public event Action LoadingFinished;
|
|
|
|
// 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 >();
|
|
|
|
public static readonly InternalIndex[] ReverseIndices
|
|
= Enumerable.Range( 0, Structs.CharacterUtility.TotalNumResources )
|
|
.Select( i => new InternalIndex( Array.IndexOf( RelevantIndices, (Structs.CharacterUtility.Index) i ) ) )
|
|
.ToArray();
|
|
|
|
|
|
private readonly (IntPtr Address, int Size)[] _defaultResources = new (IntPtr, int)[RelevantIndices.Length];
|
|
|
|
public (IntPtr Address, int Size) DefaultResource( Structs.CharacterUtility.Index idx )
|
|
=> _defaultResources[ ReverseIndices[ ( int )idx ].Value ];
|
|
|
|
public (IntPtr Address, int Size) DefaultResource( InternalIndex idx )
|
|
=> _defaultResources[ idx.Value ];
|
|
|
|
public CharacterUtility()
|
|
{
|
|
SignatureHelper.Initialise( this );
|
|
LoadingFinished += () => Penumbra.Log.Debug( "Loading of CharacterUtility finished." );
|
|
LoadDefaultResources( null! );
|
|
if( !Ready )
|
|
{
|
|
Dalamud.Framework.Update += LoadDefaultResources;
|
|
}
|
|
}
|
|
|
|
// We store the default data of the resources so we can always restore them.
|
|
private void LoadDefaultResources( object _ )
|
|
{
|
|
var missingCount = 0;
|
|
if( Address == null )
|
|
{
|
|
return;
|
|
}
|
|
|
|
for( var i = 0; i < RelevantIndices.Length; ++i )
|
|
{
|
|
if( _defaultResources[ i ].Size == 0 )
|
|
{
|
|
var resource = Address->Resource( RelevantIndices[i] );
|
|
var data = resource->GetData();
|
|
if( data.Data != IntPtr.Zero && data.Length != 0 )
|
|
{
|
|
_defaultResources[ i ] = data;
|
|
}
|
|
else
|
|
{
|
|
++missingCount;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( missingCount == 0 )
|
|
{
|
|
Ready = true;
|
|
LoadingFinished.Invoke();
|
|
Dalamud.Framework.Update -= LoadDefaultResources;
|
|
}
|
|
}
|
|
|
|
// Set the data of one of the stored resources to a given pointer and length.
|
|
public bool SetResource( Structs.CharacterUtility.Index resourceIdx, IntPtr data, int length )
|
|
{
|
|
if( !Ready )
|
|
{
|
|
Penumbra.Log.Error( $"Can not set resource {resourceIdx}: CharacterUtility not ready yet." );
|
|
return false;
|
|
}
|
|
|
|
var resource = Address->Resource( resourceIdx );
|
|
var ret = resource->SetData( data, length );
|
|
Penumbra.Log.Verbose( $"Set resource {resourceIdx} to 0x{( ulong )data:X} ({length} bytes).");
|
|
return ret;
|
|
}
|
|
|
|
// Reset the data of one of the stored resources to its default values.
|
|
public void ResetResource( Structs.CharacterUtility.Index resourceIdx )
|
|
{
|
|
if( !Ready )
|
|
{
|
|
Penumbra.Log.Error( $"Can not reset {resourceIdx}: CharacterUtility not ready yet." );
|
|
return;
|
|
}
|
|
|
|
var (data, length) = DefaultResource( resourceIdx);
|
|
var resource = Address->Resource( resourceIdx );
|
|
Penumbra.Log.Verbose( $"Reset resource {resourceIdx} to default at 0x{(ulong)data:X} ({length} bytes).");
|
|
resource->SetData( data, length );
|
|
}
|
|
|
|
// Return all relevant resources to the default resource.
|
|
public void ResetAll()
|
|
{
|
|
if( !Ready )
|
|
{
|
|
Penumbra.Log.Error( "Can not reset all resources: CharacterUtility not ready yet." );
|
|
return;
|
|
}
|
|
|
|
foreach( var idx in RelevantIndices )
|
|
{
|
|
ResetResource( idx );
|
|
}
|
|
|
|
Penumbra.Log.Debug( "Reset all CharacterUtility resources to default." );
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
ResetAll();
|
|
}
|
|
} |