From ab3cc6dc78d56cd1a790ae16d158697343de5bac Mon Sep 17 00:00:00 2001 From: pmgr <26606291+pmgr@users.noreply.github.com> Date: Sun, 21 Feb 2021 03:39:36 +0000 Subject: [PATCH] Implement reloading eqdp, gmp, awt, etc. --- Penumbra/Game/GameUtils.cs | 51 +++++++++++++++++++- Penumbra/Structs/CharacterResourceManager.cs | 13 +++++ 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 Penumbra/Structs/CharacterResourceManager.cs 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