diff --git a/Penumbra/Game/GameUtils.cs b/Penumbra/Game/GameUtils.cs index 95eb89fc..6536605f 100644 --- a/Penumbra/Game/GameUtils.cs +++ b/Penumbra/Game/GameUtils.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.InteropServices; using Dalamud.Plugin; +using Penumbra.Structs; using Reloaded.Hooks.Definitions.X64; namespace Penumbra.Game @@ -13,13 +14,24 @@ namespace Penumbra.Game [Function( CallingConventions.Microsoft )] public unsafe delegate void* UnloadPlayerResourcesPrototype( IntPtr pResourceManager ); + [Function( CallingConventions.Microsoft )] + public unsafe delegate void* LoadCharacterResourcesPrototype( CharacterResourceManager *pCharacterResourceManager ); + + [Function( CallingConventions.Microsoft )] + public unsafe delegate void* UnloadCharacterResourcePrototype( IntPtr resource ); + public LoadPlayerResourcesPrototype LoadPlayerResources { get; } public UnloadPlayerResourcesPrototype UnloadPlayerResources { get; } + public LoadCharacterResourcesPrototype LoadCharacterResources { get; } + public UnloadCharacterResourcePrototype UnloadCharacterResource { get; } // Object addresses private readonly IntPtr _playerResourceManagerAddress; public IntPtr PlayerResourceManagerPtr => Marshal.ReadIntPtr( _playerResourceManagerAddress ); + private readonly IntPtr _characterResourceManagerAddress; + public unsafe CharacterResourceManager* CharacterResourceManagerPtr => + ( CharacterResourceManager* )Marshal.ReadIntPtr( _characterResourceManagerAddress ).ToPointer(); public GameUtils( DalamudPluginInterface pluginInterface ) { @@ -30,17 +42,54 @@ namespace Penumbra.Game "E8 ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? BA ?? ?? ?? ?? 41 B8 ?? ?? ?? ?? 48 8B 48 30 48 8B 01 FF 50 10 48 85 C0 74 0A " ); var unloadPlayerResourcesAddress = scanner.ScanText( "41 55 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 4C 8B E9 48 83 C1 08" ); + var loadCharacterResourcesAddress = scanner.ScanText( "E8 ?? ?? ?? 00 48 8D 8E ?? ?? 00 00 E8 ?? ?? ?? 00 33 D2" ); + var unloadCharacterResourceAddress = scanner.ScanText( "E8 ?? ?? ?? FF 4C 89 37 48 83 C7 08 48 83 ED 01 75 ?? 48 8B CB" ); - _playerResourceManagerAddress = scanner.GetStaticAddressFromSig( "0F 44 FE 48 8B 0D ?? ?? ?? ?? 48 85 C9 74 05" ); + _playerResourceManagerAddress = scanner.GetStaticAddressFromSig( "0F 44 FE 48 8B 0D ?? ?? ?? ?? 48 85 C9 74 05" ); + _characterResourceManagerAddress = scanner.GetStaticAddressFromSig( "48 8B 0D ?? ?? ?? ?? E8 ?? ?? ?? 00 48 8D 8E ?? ?? 00 00 E8 ?? ?? ?? 00 33 D2" ); LoadPlayerResources = Marshal.GetDelegateForFunctionPointer< LoadPlayerResourcesPrototype >( loadPlayerResourcesAddress ); UnloadPlayerResources = Marshal.GetDelegateForFunctionPointer< UnloadPlayerResourcesPrototype >( unloadPlayerResourcesAddress ); + LoadCharacterResources = Marshal.GetDelegateForFunctionPointer< LoadCharacterResourcesPrototype >( loadCharacterResourcesAddress ); + UnloadCharacterResource = Marshal.GetDelegateForFunctionPointer( unloadCharacterResourceAddress ); } public unsafe void ReloadPlayerResources() { + ReloadCharacterResources(); + UnloadPlayerResources( PlayerResourceManagerPtr ); LoadPlayerResources( PlayerResourceManagerPtr ); } + + public unsafe string ResourceToPath( byte *resource ) => + Marshal.PtrToStringAnsi( new IntPtr( *(char **)( resource + 9 * 8 ) ) ); + + public unsafe void ReloadCharacterResources() + { + var oldResources = new IntPtr[85]; + var resources = new IntPtr(&CharacterResourceManagerPtr->Resources); + var pResources = (void **)resources.ToPointer(); + + Marshal.Copy( resources, oldResources, 0, 85 ); + + LoadCharacterResources( CharacterResourceManagerPtr ); + + for( var i = 0; i < 85; i++ ) + { + if( oldResources[ i ].ToPointer() == pResources[ i ] ) + { + PluginLog.Debug($"Unchanged resource: {ResourceToPath( ( byte* )oldResources[i].ToPointer() )}"); + continue; + } + + PluginLog.Debug( "Freeing " + + $"{ResourceToPath( ( byte* )oldResources[i].ToPointer() )}, replaced with " + + $"{ResourceToPath( ( byte* )pResources[i] )}" ); + + UnloadCharacterResource( oldResources[ i ] ); + } + + } } } \ No newline at end of file diff --git a/Penumbra/Structs/CharacterResourceManager.cs b/Penumbra/Structs/CharacterResourceManager.cs new file mode 100644 index 00000000..d21f04dc --- /dev/null +++ b/Penumbra/Structs/CharacterResourceManager.cs @@ -0,0 +1,13 @@ +using System; +using System.Runtime.InteropServices; + +namespace Penumbra.Structs +{ + [StructLayout( LayoutKind.Sequential )] + public unsafe struct CharacterResourceManager + { + public void* VTable; + + public IntPtr Resources; // Size: 85, I hate C# + } +} \ No newline at end of file