mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-13 12:14:17 +01:00
Switch CharacterUtility to use linked lists of changes.
This commit is contained in:
parent
9753c14b32
commit
af3a07c227
2 changed files with 171 additions and 55 deletions
147
Penumbra/Interop/CharacterUtility.List.cs
Normal file
147
Penumbra/Interop/CharacterUtility.List.cs
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Penumbra.Interop;
|
||||||
|
|
||||||
|
public unsafe partial class CharacterUtility
|
||||||
|
{
|
||||||
|
public class List : IDisposable
|
||||||
|
{
|
||||||
|
private readonly LinkedList< MetaReverter > _entries = new();
|
||||||
|
public readonly InternalIndex Index;
|
||||||
|
public readonly Structs.CharacterUtility.Index GlobalIndex;
|
||||||
|
|
||||||
|
private IntPtr _defaultResourceData = IntPtr.Zero;
|
||||||
|
private int _defaultResourceSize = 0;
|
||||||
|
public bool Ready { get; private set; } = false;
|
||||||
|
|
||||||
|
public List( InternalIndex index )
|
||||||
|
{
|
||||||
|
Index = index;
|
||||||
|
GlobalIndex = RelevantIndices[ index.Value ];
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetDefaultResource( IntPtr data, int size )
|
||||||
|
{
|
||||||
|
if( !Ready )
|
||||||
|
{
|
||||||
|
_defaultResourceData = data;
|
||||||
|
_defaultResourceSize = size;
|
||||||
|
Ready = _defaultResourceData != IntPtr.Zero && size != 0;
|
||||||
|
if( _entries.Count > 0 )
|
||||||
|
{
|
||||||
|
var first = _entries.First!.Value;
|
||||||
|
SetResource( first.Data, first.Length );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public (IntPtr Address, int Size) DefaultResource
|
||||||
|
=> ( _defaultResourceData, _defaultResourceSize );
|
||||||
|
|
||||||
|
public MetaReverter TemporarilySetResource( IntPtr data, int length )
|
||||||
|
{
|
||||||
|
Penumbra.Log.Verbose( $"Temporarily set resource {GlobalIndex} to 0x{( ulong )data:X} ({length} bytes)." );
|
||||||
|
var reverter = new MetaReverter( this, data, length );
|
||||||
|
_entries.AddFirst( reverter );
|
||||||
|
SetResourceInternal( data, length );
|
||||||
|
return reverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MetaReverter TemporarilyResetResource()
|
||||||
|
{
|
||||||
|
Penumbra.Log.Verbose( $"Temporarily reset resource {GlobalIndex} to default at 0x{_defaultResourceData:X} ({_defaultResourceSize} bytes)." );
|
||||||
|
var reverter = new MetaReverter( this );
|
||||||
|
_entries.AddFirst( reverter );
|
||||||
|
ResetResourceInternal();
|
||||||
|
return reverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetResource( IntPtr data, int length )
|
||||||
|
{
|
||||||
|
Penumbra.Log.Verbose( $"Set resource {GlobalIndex} to 0x{( ulong )data:X} ({length} bytes)." );
|
||||||
|
SetResourceInternal( data, length );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ResetResource()
|
||||||
|
{
|
||||||
|
Penumbra.Log.Verbose( $"Reset resource {GlobalIndex} to default at 0x{_defaultResourceData:X} ({_defaultResourceSize} bytes)." );
|
||||||
|
ResetResourceInternal();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Set the currently stored data of this resource to new values.
|
||||||
|
private void SetResourceInternal( IntPtr data, int length )
|
||||||
|
{
|
||||||
|
if( Ready )
|
||||||
|
{
|
||||||
|
var resource = Penumbra.CharacterUtility.Address->Resource( GlobalIndex );
|
||||||
|
resource->SetData( data, length );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the currently stored data of this resource to its default values.
|
||||||
|
private void ResetResourceInternal()
|
||||||
|
=> SetResourceInternal( _defaultResourceData, _defaultResourceSize );
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if( _entries.Count > 0 )
|
||||||
|
{
|
||||||
|
_entries.Clear();
|
||||||
|
ResetResourceInternal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class MetaReverter : IDisposable
|
||||||
|
{
|
||||||
|
public readonly List List;
|
||||||
|
public readonly IntPtr Data;
|
||||||
|
public readonly int Length;
|
||||||
|
public readonly bool Resetter;
|
||||||
|
|
||||||
|
public MetaReverter( List list, IntPtr data, int length )
|
||||||
|
{
|
||||||
|
List = list;
|
||||||
|
Data = data;
|
||||||
|
Length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MetaReverter( List list )
|
||||||
|
{
|
||||||
|
List = list;
|
||||||
|
Data = IntPtr.Zero;
|
||||||
|
Length = 0;
|
||||||
|
Resetter = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
var list = List._entries;
|
||||||
|
var wasCurrent = ReferenceEquals( this, list.First?.Value );
|
||||||
|
list.Remove( this );
|
||||||
|
if( !wasCurrent )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( list.Count == 0 )
|
||||||
|
{
|
||||||
|
List.ResetResourceInternal();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var next = list.First!.Value;
|
||||||
|
if( next.Resetter )
|
||||||
|
{
|
||||||
|
List.ResetResourceInternal();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
List.SetResourceInternal( next.Data, next.Length );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,7 +4,7 @@ using Dalamud.Utility.Signatures;
|
||||||
|
|
||||||
namespace Penumbra.Interop;
|
namespace Penumbra.Interop;
|
||||||
|
|
||||||
public unsafe class CharacterUtility : IDisposable
|
public unsafe partial class CharacterUtility : IDisposable
|
||||||
{
|
{
|
||||||
public record struct InternalIndex( int Value );
|
public record struct InternalIndex( int Value );
|
||||||
|
|
||||||
|
|
@ -34,17 +34,15 @@ public unsafe class CharacterUtility : IDisposable
|
||||||
|
|
||||||
public static readonly InternalIndex[] ReverseIndices
|
public static readonly InternalIndex[] ReverseIndices
|
||||||
= Enumerable.Range( 0, Structs.CharacterUtility.TotalNumResources )
|
= Enumerable.Range( 0, Structs.CharacterUtility.TotalNumResources )
|
||||||
.Select( i => new InternalIndex( Array.IndexOf( RelevantIndices, (Structs.CharacterUtility.Index) i ) ) )
|
.Select( i => new InternalIndex( Array.IndexOf( RelevantIndices, ( Structs.CharacterUtility.Index )i ) ) )
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
|
private readonly List[] _lists = Enumerable.Range( 0, RelevantIndices.Length )
|
||||||
private readonly (IntPtr Address, int Size)[] _defaultResources = new (IntPtr, int)[RelevantIndices.Length];
|
.Select( idx => new List( new InternalIndex( idx ) ) )
|
||||||
|
.ToArray();
|
||||||
public (IntPtr Address, int Size) DefaultResource( Structs.CharacterUtility.Index idx )
|
|
||||||
=> _defaultResources[ ReverseIndices[ ( int )idx ].Value ];
|
|
||||||
|
|
||||||
public (IntPtr Address, int Size) DefaultResource( InternalIndex idx )
|
public (IntPtr Address, int Size) DefaultResource( InternalIndex idx )
|
||||||
=> _defaultResources[ idx.Value ];
|
=> _lists[ idx.Value ].DefaultResource;
|
||||||
|
|
||||||
public CharacterUtility()
|
public CharacterUtility()
|
||||||
{
|
{
|
||||||
|
|
@ -60,30 +58,25 @@ public unsafe class CharacterUtility : IDisposable
|
||||||
// We store the default data of the resources so we can always restore them.
|
// We store the default data of the resources so we can always restore them.
|
||||||
private void LoadDefaultResources( object _ )
|
private void LoadDefaultResources( object _ )
|
||||||
{
|
{
|
||||||
var missingCount = 0;
|
|
||||||
if( Address == null )
|
if( Address == null )
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var anyMissing = false;
|
||||||
for( var i = 0; i < RelevantIndices.Length; ++i )
|
for( var i = 0; i < RelevantIndices.Length; ++i )
|
||||||
{
|
{
|
||||||
if( _defaultResources[ i ].Size == 0 )
|
var list = _lists[ i ];
|
||||||
|
if( !list.Ready )
|
||||||
{
|
{
|
||||||
var resource = Address->Resource( RelevantIndices[i] );
|
var resource = Address->Resource( RelevantIndices[ i ] );
|
||||||
var data = resource->GetData();
|
var (data, length) = resource->GetData();
|
||||||
if( data.Data != IntPtr.Zero && data.Length != 0 )
|
list.SetDefaultResource( data, length );
|
||||||
{
|
anyMissing |= !_lists[ i ].Ready;
|
||||||
_defaultResources[ i ] = data;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
++missingCount;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( missingCount == 0 )
|
if( !anyMissing )
|
||||||
{
|
{
|
||||||
Ready = true;
|
Ready = true;
|
||||||
LoadingFinished.Invoke();
|
LoadingFinished.Invoke();
|
||||||
|
|
@ -91,51 +84,27 @@ public unsafe class CharacterUtility : IDisposable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the data of one of the stored resources to a given pointer and length.
|
public List.MetaReverter SetResource( Structs.CharacterUtility.Index resourceIdx, IntPtr data, int length )
|
||||||
public bool SetResource( Structs.CharacterUtility.Index resourceIdx, IntPtr data, int length )
|
|
||||||
{
|
{
|
||||||
if( !Ready )
|
var idx = ReverseIndices[ ( int )resourceIdx ];
|
||||||
{
|
var list = _lists[ idx.Value ];
|
||||||
Penumbra.Log.Error( $"Can not set resource {resourceIdx}: CharacterUtility not ready yet." );
|
return list.SetResource( data, length );
|
||||||
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 List.MetaReverter ResetResource( Structs.CharacterUtility.Index resourceIdx )
|
||||||
public void ResetResource( Structs.CharacterUtility.Index resourceIdx )
|
|
||||||
{
|
{
|
||||||
if( !Ready )
|
var idx = ReverseIndices[ ( int )resourceIdx ];
|
||||||
{
|
var list = _lists[ idx.Value ];
|
||||||
Penumbra.Log.Error( $"Can not reset {resourceIdx}: CharacterUtility not ready yet." );
|
return list.ResetResource();
|
||||||
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.
|
// Return all relevant resources to the default resource.
|
||||||
public void ResetAll()
|
public void ResetAll()
|
||||||
{
|
{
|
||||||
if( !Ready )
|
foreach( var list in _lists )
|
||||||
{
|
{
|
||||||
Penumbra.Log.Error( "Can not reset all resources: CharacterUtility not ready yet." );
|
list.Dispose();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach( var idx in RelevantIndices )
|
|
||||||
{
|
|
||||||
ResetResource( idx );
|
|
||||||
}
|
|
||||||
|
|
||||||
Penumbra.Log.Debug( "Reset all CharacterUtility resources to default." );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue