diff --git a/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs b/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs
index 3eadefd0f..a3d44d423 100644
--- a/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs
+++ b/Dalamud/Game/ClientState/Aetherytes/AetheryteList.cs
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using Dalamud.IoC;
using Dalamud.IoC.Internal;
using Dalamud.Plugin.Services;
+using Dalamud.Utility;
using FFXIVClientStructs.FFXIV.Client.Game.UI;
using Serilog;
@@ -28,7 +29,7 @@ internal sealed unsafe partial class AetheryteList : IServiceType, IAetheryteLis
[ServiceManager.ServiceConstructor]
private AetheryteList()
{
- Log.Verbose($"Teleport address 0x{((nint)this.telepoInstance).ToInt64():X}");
+ Log.Verbose($"Teleport address {Util.DescribeAddress(this.telepoInstance)}");
}
///
diff --git a/Dalamud/Game/ClientState/Buddy/BuddyList.cs b/Dalamud/Game/ClientState/Buddy/BuddyList.cs
index bafe9ded6..db9b8e562 100644
--- a/Dalamud/Game/ClientState/Buddy/BuddyList.cs
+++ b/Dalamud/Game/ClientState/Buddy/BuddyList.cs
@@ -6,6 +6,7 @@ using System.Runtime.InteropServices;
using Dalamud.IoC;
using Dalamud.IoC.Internal;
using Dalamud.Plugin.Services;
+using Dalamud.Utility;
using Serilog;
@@ -34,7 +35,7 @@ internal sealed partial class BuddyList : IServiceType, IBuddyList
{
this.address = this.clientState.AddressResolver;
- Log.Verbose($"Buddy list address 0x{this.address.BuddyList.ToInt64():X}");
+ Log.Verbose($"Buddy list address {Util.DescribeAddress(this.address.BuddyList)}");
}
///
diff --git a/Dalamud/Game/ClientState/ClientState.cs b/Dalamud/Game/ClientState/ClientState.cs
index d6fd8f37b..b31bcf780 100644
--- a/Dalamud/Game/ClientState/ClientState.cs
+++ b/Dalamud/Game/ClientState/ClientState.cs
@@ -54,7 +54,7 @@ internal sealed class ClientState : IInternalDisposableService, IClientState
this.ClientLanguage = (ClientLanguage)dalamud.StartInfo.Language;
- Log.Verbose($"SetupTerritoryType address 0x{this.address.SetupTerritoryType.ToInt64():X}");
+ Log.Verbose($"SetupTerritoryType address {Util.DescribeAddress(this.address.SetupTerritoryType)}");
this.setupTerritoryTypeHook = Hook.FromAddress(this.address.SetupTerritoryType, this.SetupTerritoryTypeDetour);
diff --git a/Dalamud/Game/ClientState/Fates/FateTable.cs b/Dalamud/Game/ClientState/Fates/FateTable.cs
index e28a8d113..abd5bac41 100644
--- a/Dalamud/Game/ClientState/Fates/FateTable.cs
+++ b/Dalamud/Game/ClientState/Fates/FateTable.cs
@@ -4,6 +4,7 @@ using System.Collections.Generic;
using Dalamud.IoC;
using Dalamud.IoC.Internal;
using Dalamud.Plugin.Services;
+using Dalamud.Utility;
using Serilog;
@@ -26,7 +27,7 @@ internal sealed partial class FateTable : IServiceType, IFateTable
{
this.address = clientState.AddressResolver;
- Log.Verbose($"Fate table address 0x{this.address.FateTablePtr.ToInt64():X}");
+ Log.Verbose($"Fate table address {Util.DescribeAddress(this.address.FateTablePtr)}");
}
///
diff --git a/Dalamud/Game/ClientState/GamePad/GamepadState.cs b/Dalamud/Game/ClientState/GamePad/GamepadState.cs
index 79698d2cc..05d691823 100644
--- a/Dalamud/Game/ClientState/GamePad/GamepadState.cs
+++ b/Dalamud/Game/ClientState/GamePad/GamepadState.cs
@@ -4,6 +4,7 @@ using Dalamud.Hooking;
using Dalamud.IoC;
using Dalamud.IoC.Internal;
using Dalamud.Plugin.Services;
+using Dalamud.Utility;
using ImGuiNET;
using Serilog;
@@ -35,7 +36,7 @@ internal unsafe class GamepadState : IInternalDisposableService, IGamepadState
private GamepadState(ClientState clientState)
{
var resolver = clientState.AddressResolver;
- Log.Verbose($"GamepadPoll address 0x{resolver.GamepadPoll.ToInt64():X}");
+ Log.Verbose($"GamepadPoll address {Util.DescribeAddress(resolver.GamepadPoll)}");
this.gamepadPoll = Hook.FromAddress(resolver.GamepadPoll, this.GamepadPollDetour);
this.gamepadPoll?.Enable();
}
diff --git a/Dalamud/Game/ClientState/JobGauge/JobGauges.cs b/Dalamud/Game/ClientState/JobGauge/JobGauges.cs
index 4afdd8130..e0ae986c5 100644
--- a/Dalamud/Game/ClientState/JobGauge/JobGauges.cs
+++ b/Dalamud/Game/ClientState/JobGauge/JobGauges.cs
@@ -5,6 +5,7 @@ using Dalamud.Game.ClientState.JobGauge.Types;
using Dalamud.IoC;
using Dalamud.IoC.Internal;
using Dalamud.Plugin.Services;
+using Dalamud.Utility;
using Serilog;
@@ -27,7 +28,7 @@ internal class JobGauges : IServiceType, IJobGauges
{
this.Address = clientState.AddressResolver.JobGaugeData;
- Log.Verbose($"JobGaugeData address 0x{this.Address.ToInt64():X}");
+ Log.Verbose($"JobGaugeData address {Util.DescribeAddress(this.Address)}");
}
///
diff --git a/Dalamud/Game/ClientState/Keys/KeyState.cs b/Dalamud/Game/ClientState/Keys/KeyState.cs
index 7e4292598..35f0c2161 100644
--- a/Dalamud/Game/ClientState/Keys/KeyState.cs
+++ b/Dalamud/Game/ClientState/Keys/KeyState.cs
@@ -5,6 +5,7 @@ using System.Runtime.InteropServices;
using Dalamud.IoC;
using Dalamud.IoC.Internal;
using Dalamud.Plugin.Services;
+using Dalamud.Utility;
using Serilog;
@@ -45,7 +46,7 @@ internal class KeyState : IServiceType, IKeyState
this.bufferBase = moduleBaseAddress + Marshal.ReadInt32(addressResolver.KeyboardState);
this.indexBase = moduleBaseAddress + Marshal.ReadInt32(addressResolver.KeyboardStateIndexArray);
- Log.Verbose($"Keyboard state buffer address 0x{this.bufferBase.ToInt64():X}");
+ Log.Verbose($"Keyboard state buffer address {Util.DescribeAddress(this.bufferBase)}");
}
///
diff --git a/Dalamud/Game/ClientState/Objects/ObjectTable.cs b/Dalamud/Game/ClientState/Objects/ObjectTable.cs
index d88383c22..50f4e81ce 100644
--- a/Dalamud/Game/ClientState/Objects/ObjectTable.cs
+++ b/Dalamud/Game/ClientState/Objects/ObjectTable.cs
@@ -53,7 +53,7 @@ internal sealed partial class ObjectTable : IServiceType, IObjectTable
for (var i = 0; i < this.frameworkThreadEnumerators.Length; i++)
this.frameworkThreadEnumerators[i] = new(this, i);
- Log.Verbose($"Object table address 0x{this.clientState.AddressResolver.ObjectTable.ToInt64():X}");
+ Log.Verbose($"Object table address {Util.DescribeAddress(this.clientState.AddressResolver.ObjectTable)}");
}
///
diff --git a/Dalamud/Game/ClientState/Party/PartyList.cs b/Dalamud/Game/ClientState/Party/PartyList.cs
index d6e235d30..4fbd8fdfb 100644
--- a/Dalamud/Game/ClientState/Party/PartyList.cs
+++ b/Dalamud/Game/ClientState/Party/PartyList.cs
@@ -6,6 +6,7 @@ using System.Runtime.InteropServices;
using Dalamud.IoC;
using Dalamud.IoC.Internal;
using Dalamud.Plugin.Services;
+using Dalamud.Utility;
using Serilog;
@@ -34,7 +35,7 @@ internal sealed unsafe partial class PartyList : IServiceType, IPartyList
{
this.address = this.clientState.AddressResolver;
- Log.Verbose($"Group manager address 0x{this.address.GroupManager.ToInt64():X}");
+ Log.Verbose($"Group manager address {Util.DescribeAddress(this.address.GroupManager)}");
}
///
diff --git a/Dalamud/Game/Gui/GameGui.cs b/Dalamud/Game/Gui/GameGui.cs
index e92473539..80463a119 100644
--- a/Dalamud/Game/Gui/GameGui.cs
+++ b/Dalamud/Game/Gui/GameGui.cs
@@ -55,11 +55,11 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui
this.address.Setup(sigScanner);
Log.Verbose("===== G A M E G U I =====");
- Log.Verbose($"GameGuiManager address 0x{this.address.BaseAddress.ToInt64():X}");
- Log.Verbose($"SetGlobalBgm address 0x{this.address.SetGlobalBgm.ToInt64():X}");
- Log.Verbose($"HandleItemHover address 0x{this.address.HandleItemHover.ToInt64():X}");
- Log.Verbose($"HandleItemOut address 0x{this.address.HandleItemOut.ToInt64():X}");
- Log.Verbose($"HandleImm address 0x{this.address.HandleImm.ToInt64():X}");
+ Log.Verbose($"GameGuiManager address {Util.DescribeAddress(this.address.BaseAddress)}");
+ Log.Verbose($"SetGlobalBgm address {Util.DescribeAddress(this.address.SetGlobalBgm)}");
+ Log.Verbose($"HandleItemHover address {Util.DescribeAddress(this.address.HandleItemHover)}");
+ Log.Verbose($"HandleItemOut address {Util.DescribeAddress(this.address.HandleItemOut)}");
+ Log.Verbose($"HandleImm address {Util.DescribeAddress(this.address.HandleImm)}");
this.setGlobalBgmHook = Hook.FromAddress(this.address.SetGlobalBgm, this.HandleSetGlobalBgmDetour);
diff --git a/Dalamud/Game/Internal/AntiDebug.cs b/Dalamud/Game/Internal/AntiDebug.cs
index dfc6bde90..48b8688a1 100644
--- a/Dalamud/Game/Internal/AntiDebug.cs
+++ b/Dalamud/Game/Internal/AntiDebug.cs
@@ -1,5 +1,7 @@
using System.Collections.Generic;
+using Dalamud.Utility;
+
#if !DEBUG
using Dalamud.Configuration.Internal;
#endif
@@ -30,7 +32,7 @@ internal sealed class AntiDebug : IInternalDisposableService
this.debugCheckAddress = IntPtr.Zero;
}
- Log.Verbose($"Debug check address 0x{this.debugCheckAddress.ToInt64():X}");
+ Log.Verbose($"Debug check address {Util.DescribeAddress(this.debugCheckAddress)}");
if (!this.IsEnabled)
{
@@ -65,7 +67,7 @@ internal sealed class AntiDebug : IInternalDisposableService
this.original = new byte[this.nop.Length];
if (this.debugCheckAddress != IntPtr.Zero && !this.IsEnabled)
{
- Log.Information($"Overwriting debug check at 0x{this.debugCheckAddress.ToInt64():X}");
+ Log.Information($"Overwriting debug check at {Util.DescribeAddress(this.debugCheckAddress)}");
SafeMemory.ReadBytes(this.debugCheckAddress, this.nop.Length, out this.original);
SafeMemory.WriteBytes(this.debugCheckAddress, this.nop);
}
@@ -87,7 +89,7 @@ internal sealed class AntiDebug : IInternalDisposableService
if (this.debugCheckAddress != IntPtr.Zero && this.original != null)
{
- Log.Information($"Reverting debug check at 0x{this.debugCheckAddress.ToInt64():X}");
+ Log.Information($"Reverting debug check at {Util.DescribeAddress(this.debugCheckAddress)}");
SafeMemory.WriteBytes(this.debugCheckAddress, this.original);
}
else
diff --git a/Dalamud/Game/Internal/DXGI/Definitions/ID3D11DeviceVtbl.cs b/Dalamud/Game/Internal/DXGI/Definitions/ID3D11DeviceVtbl.cs
deleted file mode 100644
index 4bd369dd3..000000000
--- a/Dalamud/Game/Internal/DXGI/Definitions/ID3D11DeviceVtbl.cs
+++ /dev/null
@@ -1,226 +0,0 @@
-namespace Dalamud.Game.Internal.DXGI.Definitions;
-
-///
-/// Contains a full list of ID3D11Device functions to be used as an indexer into the DirectX Virtual Function Table entries.
-///
-internal enum ID3D11DeviceVtbl
-{
- // IUnknown
-
- ///
- /// IUnknown::QueryInterface method (unknwn.h).
- ///
- QueryInterface = 0,
-
- ///
- /// IUnknown::AddRef method (unknwn.h).
- ///
- AddRef = 1,
-
- ///
- /// IUnknown::Release method (unknwn.h).
- ///
- Release = 2,
-
- // ID3D11Device
-
- ///
- /// ID3D11Device::CreateBuffer method (d3d11.h).
- ///
- CreateBuffer = 3,
-
- ///
- /// ID3D11Device::CreateTexture1D method (d3d11.h).
- ///
- CreateTexture1D = 4,
-
- ///
- /// ID3D11Device::CreateTexture2D method (d3d11.h).
- ///
- CreateTexture2D = 5,
-
- ///
- /// ID3D11Device::CreateTexture3D method (d3d11.h).
- ///
- CreateTexture3D = 6,
-
- ///
- /// ID3D11Device::CreateShaderResourceView method (d3d11.h).
- ///
- CreateShaderResourceView = 7,
-
- ///
- /// ID3D11Device::CreateUnorderedAccessView method (d3d11.h).
- ///
- CreateUnorderedAccessView = 8,
-
- ///
- /// ID3D11Device::CreateRenderTargetView method (d3d11.h).
- ///
- CreateRenderTargetView = 9,
-
- ///
- /// ID3D11Device::CreateDepthStencilView method (d3d11.h).
- ///
- CreateDepthStencilView = 10,
-
- ///
- /// ID3D11Device::CreateInputLayout method (d3d11.h).
- ///
- CreateInputLayout = 11,
-
- ///
- /// ID3D11Device::CreateVertexShader method (d3d11.h).
- ///
- CreateVertexShader = 12,
-
- ///
- /// ID3D11Device::CreateGeometryShader method (d3d11.h).
- ///
- CreateGeometryShader = 13,
-
- ///
- /// ID3D11Device::CreateGeometryShaderWithStreamOutput method (d3d11.h).
- ///
- CreateGeometryShaderWithStreamOutput = 14,
-
- ///
- /// ID3D11Device::CreatePixelShader method (d3d11.h).
- ///
- CreatePixelShader = 15,
-
- ///
- /// ID3D11Device::CreateHullShader method (d3d11.h).
- ///
- CreateHullShader = 16,
-
- ///
- /// ID3D11Device::CreateDomainShader method (d3d11.h).
- ///
- CreateDomainShader = 17,
-
- ///
- /// ID3D11Device::CreateComputeShader method (d3d11.h).
- ///
- CreateComputeShader = 18,
-
- ///
- /// ID3D11Device::CreateClassLinkage method (d3d11.h).
- ///
- CreateClassLinkage = 19,
-
- ///
- /// ID3D11Device::CreateBlendState method (d3d11.h).
- ///
- CreateBlendState = 20,
-
- ///
- /// ID3D11Device::CreateDepthStencilState method (d3d11.h).
- ///
- CreateDepthStencilState = 21,
-
- ///
- /// ID3D11Device::CreateRasterizerState method (d3d11.h).
- ///
- CreateRasterizerState = 22,
-
- ///
- /// ID3D11Device::CreateSamplerState method (d3d11.h).
- ///
- CreateSamplerState = 23,
-
- ///
- /// ID3D11Device::CreateQuery method (d3d11.h).
- ///
- CreateQuery = 24,
-
- ///
- /// ID3D11Device::CreatePredicate method (d3d11.h).
- ///
- CreatePredicate = 25,
-
- ///
- /// ID3D11Device::CreateCounter method (d3d11.h).
- ///
- CreateCounter = 26,
-
- ///
- /// ID3D11Device::CreateDeferredContext method (d3d11.h).
- ///
- CreateDeferredContext = 27,
-
- ///
- /// ID3D11Device::OpenSharedResource method (d3d11.h).
- ///
- OpenSharedResource = 28,
-
- ///
- /// ID3D11Device::CheckFormatSupport method (d3d11.h).
- ///
- CheckFormatSupport = 29,
-
- ///
- /// ID3D11Device::CheckMultisampleQualityLevels method (d3d11.h).
- ///
- CheckMultisampleQualityLevels = 30,
-
- ///
- /// ID3D11Device::CheckCounterInfo method (d3d11.h).
- ///
- CheckCounterInfo = 31,
-
- ///
- /// ID3D11Device::CheckCounter method (d3d11.h).
- ///
- CheckCounter = 32,
-
- ///
- /// ID3D11Device::CheckFeatureSupport method (d3d11.h).
- ///
- CheckFeatureSupport = 33,
-
- ///
- /// ID3D11Device::GetPrivateData method (d3d11.h).
- ///
- GetPrivateData = 34,
-
- ///
- /// ID3D11Device::SetPrivateData method (d3d11.h).
- ///
- SetPrivateData = 35,
-
- ///
- /// ID3D11Device::SetPrivateDataInterface method (d3d11.h).
- ///
- SetPrivateDataInterface = 36,
-
- ///
- /// ID3D11Device::GetFeatureLevel method (d3d11.h).
- ///
- GetFeatureLevel = 37,
-
- ///
- /// ID3D11Device::GetCreationFlags method (d3d11.h).
- ///
- GetCreationFlags = 38,
-
- ///
- /// ID3D11Device::GetDeviceRemovedReason method (d3d11.h).
- ///
- GetDeviceRemovedReason = 39,
-
- ///
- /// ID3D11Device::GetImmediateContext method (d3d11.h).
- ///
- GetImmediateContext = 40,
-
- ///
- /// ID3D11Device::SetExceptionMode method (d3d11.h).
- ///
- SetExceptionMode = 41,
-
- ///
- /// ID3D11Device::GetExceptionMode method (d3d11.h).
- ///
- GetExceptionMode = 42,
-}
diff --git a/Dalamud/Game/Internal/DXGI/Definitions/IDXGISwapChainVtbl.cs b/Dalamud/Game/Internal/DXGI/Definitions/IDXGISwapChainVtbl.cs
deleted file mode 100644
index 4bcb6fb72..000000000
--- a/Dalamud/Game/Internal/DXGI/Definitions/IDXGISwapChainVtbl.cs
+++ /dev/null
@@ -1,106 +0,0 @@
-namespace Dalamud.Game.Internal.DXGI.Definitions;
-
-///
-/// Contains a full list of IDXGISwapChain functions to be used as an indexer into the SwapChain Virtual Function Table
-/// entries.
-///
-internal enum IDXGISwapChainVtbl
-{
- // IUnknown
-
- ///
- /// IUnknown::QueryInterface method (unknwn.h).
- ///
- QueryInterface = 0,
-
- ///
- /// IUnknown::AddRef method (unknwn.h).
- ///
- AddRef = 1,
-
- ///
- /// IUnknown::Release method (unknwn.h).
- ///
- Release = 2,
-
- // IDXGIObject
-
- ///
- /// IDXGIObject::SetPrivateData method (dxgi.h).
- ///
- SetPrivateData = 3,
-
- ///
- /// IDXGIObject::SetPrivateDataInterface method (dxgi.h).
- ///
- SetPrivateDataInterface = 4,
-
- ///
- /// IDXGIObject::GetPrivateData method (dxgi.h).
- ///
- GetPrivateData = 5,
-
- ///
- /// IDXGIObject::GetParent method (dxgi.h).
- ///
- GetParent = 6,
-
- // IDXGIDeviceSubObject
-
- ///
- /// IDXGIDeviceSubObject::GetDevice method (dxgi.h).
- ///
- GetDevice = 7,
-
- // IDXGISwapChain
-
- ///
- /// IDXGISwapChain::Present method (dxgi.h).
- ///
- Present = 8,
-
- ///
- /// IUnknIDXGISwapChainown::GetBuffer method (dxgi.h).
- ///
- GetBuffer = 9,
-
- ///
- /// IDXGISwapChain::SetFullscreenState method (dxgi.h).
- ///
- SetFullscreenState = 10,
-
- ///
- /// IDXGISwapChain::GetFullscreenState method (dxgi.h).
- ///
- GetFullscreenState = 11,
-
- ///
- /// IDXGISwapChain::GetDesc method (dxgi.h).
- ///
- GetDesc = 12,
-
- ///
- /// IDXGISwapChain::ResizeBuffers method (dxgi.h).
- ///
- ResizeBuffers = 13,
-
- ///
- /// IDXGISwapChain::ResizeTarget method (dxgi.h).
- ///
- ResizeTarget = 14,
-
- ///
- /// IDXGISwapChain::GetContainingOutput method (dxgi.h).
- ///
- GetContainingOutput = 15,
-
- ///
- /// IDXGISwapChain::GetFrameStatistics method (dxgi.h).
- ///
- GetFrameStatistics = 16,
-
- ///
- /// IDXGISwapChain::GetLastPresentCount method (dxgi.h).
- ///
- GetLastPresentCount = 17,
-}
diff --git a/Dalamud/Game/Internal/DXGI/ISwapChainAddressResolver.cs b/Dalamud/Game/Internal/DXGI/ISwapChainAddressResolver.cs
deleted file mode 100644
index daccb8d14..000000000
--- a/Dalamud/Game/Internal/DXGI/ISwapChainAddressResolver.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-namespace Dalamud.Game.Internal.DXGI;
-
-///
-/// An interface binding for the address resolvers that attempt to find native D3D11 methods.
-///
-public interface ISwapChainAddressResolver
-{
- ///
- /// Gets or sets the address of the native D3D11.Present method.
- ///
- IntPtr Present { get; set; }
-
- ///
- /// Gets or sets the address of the native D3D11.ResizeBuffers method.
- ///
- IntPtr ResizeBuffers { get; set; }
-}
diff --git a/Dalamud/Game/Internal/DXGI/SwapChainVtableResolver.cs b/Dalamud/Game/Internal/DXGI/SwapChainVtableResolver.cs
deleted file mode 100644
index ba880d0e5..000000000
--- a/Dalamud/Game/Internal/DXGI/SwapChainVtableResolver.cs
+++ /dev/null
@@ -1,169 +0,0 @@
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Runtime.InteropServices;
-
-using Dalamud.Game.Internal.DXGI.Definitions;
-using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
-using Serilog;
-
-namespace Dalamud.Game.Internal.DXGI;
-
-///
-/// This class attempts to determine the D3D11 SwapChain vtable addresses via instantiating a new form and inspecting it.
-///
-///
-/// If the normal signature based method of resolution fails, this is the backup.
-///
-internal class SwapChainVtableResolver : BaseAddressResolver, ISwapChainAddressResolver
-{
- ///
- public IntPtr Present { get; set; }
-
- ///
- public IntPtr ResizeBuffers { get; set; }
-
- ///
- /// Gets a value indicating whether or not ReShade is loaded/used.
- ///
- public bool IsReshade { get; private set; }
-
- ///
- protected override unsafe void Setup64Bit(ISigScanner sig)
- {
- Device* kernelDev;
- SwapChain* swapChain;
- void* dxgiSwapChain;
-
- while (true)
- {
- kernelDev = Device.Instance();
- if (kernelDev == null)
- continue;
-
- swapChain = kernelDev->SwapChain;
- if (swapChain == null)
- continue;
-
- dxgiSwapChain = swapChain->DXGISwapChain;
- if (dxgiSwapChain == null)
- continue;
-
- break;
- }
-
- var scVtbl = GetVTblAddresses(new IntPtr(dxgiSwapChain), Enum.GetValues(typeof(IDXGISwapChainVtbl)).Length);
-
- this.Present = scVtbl[(int)IDXGISwapChainVtbl.Present];
-
- var modules = Process.GetCurrentProcess().Modules;
- foreach (ProcessModule processModule in modules)
- {
- if (processModule.FileName == null || !processModule.FileName.EndsWith("game\\dxgi.dll"))
- continue;
-
- try
- {
- var fileInfo = FileVersionInfo.GetVersionInfo(processModule.FileName);
-
- if (fileInfo.FileDescription == null)
- break;
-
- if (!fileInfo.FileDescription.Contains("GShade") && !fileInfo.FileDescription.Contains("ReShade"))
- break;
-
- // warning: these comments may no longer be accurate.
- // reshade master@4232872 RVA
- // var p = processModule.BaseAddress + 0x82C7E0; // DXGISwapChain::Present
- // var p = processModule.BaseAddress + 0x82FAC0; // DXGISwapChain::runtime_present
- // DXGISwapChain::handle_device_loss =>df DXGISwapChain::Present => DXGISwapChain::runtime_present
- // 5.2+ - F6 C2 01 0F 85
- // 6.0+ - F6 C2 01 0F 85 88
-
- var scanner = new SigScanner(processModule);
- var reShadeDxgiPresent = IntPtr.Zero;
-
- if (fileInfo.FileVersion?.StartsWith("6.") == true)
- {
- // No Addon
- if (scanner.TryScanText("F6 C2 01 0F 85 A8", out reShadeDxgiPresent))
- {
- Log.Information("Hooking present for ReShade 6 No-Addon");
- }
-
- // Addon
- else if (scanner.TryScanText("F6 C2 01 0F 85 88", out reShadeDxgiPresent))
- {
- Log.Information("Hooking present for ReShade 6 Addon");
- }
-
- // Fallback
- else
- {
- Log.Error("Failed to get ReShade 6 DXGISwapChain::on_present offset!");
- }
- }
-
- // Looks like this sig only works for GShade 4
- if (reShadeDxgiPresent == IntPtr.Zero && fileInfo.FileDescription?.Contains("GShade 4.") == true)
- {
- if (scanner.TryScanText("E8 ?? ?? ?? ?? 45 0F B6 5E ??", out reShadeDxgiPresent))
- {
- Log.Information("Hooking present for GShade 4");
- }
- else
- {
- Log.Error("Failed to find GShade 4 DXGISwapChain::on_present offset!");
- }
- }
-
- if (reShadeDxgiPresent == IntPtr.Zero)
- {
- if (scanner.TryScanText("F6 C2 01 0F 85", out reShadeDxgiPresent))
- {
- Log.Information("Hooking present for ReShade with fallback 5.X sig");
- }
- else
- {
- Log.Error("Failed to find ReShade DXGISwapChain::on_present offset with fallback sig!");
- }
- }
-
- Log.Information("ReShade DLL: {FileName} ({Info} - {Version}) with DXGISwapChain::on_present at {Address}",
- processModule.FileName,
- fileInfo.FileDescription ?? "Unknown",
- fileInfo.FileVersion ?? "Unknown",
- reShadeDxgiPresent.ToString("X"));
-
- if (reShadeDxgiPresent != IntPtr.Zero)
- {
- this.Present = reShadeDxgiPresent;
- this.IsReshade = true;
- }
-
- break;
- }
- catch (Exception e)
- {
- Log.Error(e, "Failed to get ReShade version info");
- break;
- }
- }
-
- this.ResizeBuffers = scVtbl[(int)IDXGISwapChainVtbl.ResizeBuffers];
- }
-
- private static List GetVTblAddresses(IntPtr pointer, int numberOfMethods)
- {
- return GetVTblAddresses(pointer, 0, numberOfMethods);
- }
-
- private static List GetVTblAddresses(IntPtr pointer, int startIndex, int numberOfMethods)
- {
- var vtblAddresses = new List();
- var vTable = Marshal.ReadIntPtr(pointer);
- for (var i = startIndex; i < startIndex + numberOfMethods; i++)
- vtblAddresses.Add(Marshal.ReadIntPtr(vTable, i * IntPtr.Size)); // using IntPtr.Size allows us to support both 32 and 64-bit processes
-
- return vtblAddresses;
- }
-}
diff --git a/Dalamud/Game/Network/GameNetwork.cs b/Dalamud/Game/Network/GameNetwork.cs
index ad08f3684..b51232ece 100644
--- a/Dalamud/Game/Network/GameNetwork.cs
+++ b/Dalamud/Game/Network/GameNetwork.cs
@@ -38,8 +38,8 @@ internal sealed class GameNetwork : IInternalDisposableService, IGameNetwork
this.address.Setup(sigScanner);
Log.Verbose("===== G A M E N E T W O R K =====");
- Log.Verbose($"ProcessZonePacketDown address 0x{this.address.ProcessZonePacketDown.ToInt64():X}");
- Log.Verbose($"ProcessZonePacketUp address 0x{this.address.ProcessZonePacketUp.ToInt64():X}");
+ Log.Verbose($"ProcessZonePacketDown address {Util.DescribeAddress(this.address.ProcessZonePacketDown)}");
+ Log.Verbose($"ProcessZonePacketUp address {Util.DescribeAddress(this.address.ProcessZonePacketUp)}");
this.processZonePacketDownHook = Hook.FromAddress(this.address.ProcessZonePacketDown, this.ProcessZonePacketDownDetour);
this.processZonePacketUpHook = Hook.FromAddress(this.address.ProcessZonePacketUp, this.ProcessZonePacketUpDetour);
diff --git a/Dalamud/Hooking/Internal/HookManager.cs b/Dalamud/Hooking/Internal/HookManager.cs
index 44c1e6735..8f5dba583 100644
--- a/Dalamud/Hooking/Internal/HookManager.cs
+++ b/Dalamud/Hooking/Internal/HookManager.cs
@@ -5,6 +5,7 @@ using System.Runtime.InteropServices;
using Dalamud.Logging.Internal;
using Dalamud.Memory;
+using Dalamud.Utility;
using Iced.Intel;
@@ -69,7 +70,7 @@ internal class HookManager : IInternalDisposableService
/// A new Unhooker instance.
public static Unhooker RegisterUnhooker(IntPtr address, int minBytes, int maxBytes)
{
- Log.Verbose($"Registering hook at 0x{address.ToInt64():X} (minBytes=0x{minBytes:X}, maxBytes=0x{maxBytes:X})");
+ Log.Verbose($"Registering hook at {Util.DescribeAddress(address)} (minBytes=0x{minBytes:X}, maxBytes=0x{maxBytes:X})");
return Unhookers.GetOrAdd(address, _ => new Unhooker(address, minBytes, maxBytes));
}
diff --git a/Dalamud/Hooking/Internal/Unhooker.cs b/Dalamud/Hooking/Internal/Unhooker.cs
index 79ee59b66..33e4640f4 100644
--- a/Dalamud/Hooking/Internal/Unhooker.cs
+++ b/Dalamud/Hooking/Internal/Unhooker.cs
@@ -1,4 +1,5 @@
using Dalamud.Memory;
+using Dalamud.Utility;
namespace Dalamud.Hooking.Internal;
@@ -63,7 +64,7 @@ public class Unhooker
var len = this.trimmed ? this.originalBytes.Length : int.Max(this.GetNaiveHookLength(), this.minBytes);
if (len > 0)
{
- HookManager.Log.Verbose($"Reverting hook at 0x{this.address.ToInt64():X} ({len} bytes, trimmed={this.trimmed})");
+ HookManager.Log.Verbose($"Reverting hook at {Util.DescribeAddress(this.address)} ({len} bytes, trimmed={this.trimmed})");
MemoryHelper.ChangePermission(this.address, len, MemoryProtection.ExecuteReadWrite, out var oldPermissions);
MemoryHelper.WriteRaw(this.address, this.originalBytes[..len]);
MemoryHelper.ChangePermission(this.address, len, oldPermissions);
diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs
index 26b5c8ce2..06a93e453 100644
--- a/Dalamud/Interface/Internal/InterfaceManager.cs
+++ b/Dalamud/Interface/Internal/InterfaceManager.cs
@@ -12,7 +12,6 @@ using Dalamud.Configuration.Internal;
using Dalamud.Game;
using Dalamud.Game.ClientState.GamePad;
using Dalamud.Game.ClientState.Keys;
-using Dalamud.Game.Internal.DXGI;
using Dalamud.Hooking;
using Dalamud.Hooking.WndProcHook;
using Dalamud.Interface.ImGuiNotification.Internal;
@@ -79,11 +78,11 @@ internal class InterfaceManager : IInternalDisposableService
private readonly ConcurrentQueue runBeforeImGuiRender = new();
private readonly ConcurrentQueue runAfterImGuiRender = new();
- private readonly SwapChainVtableResolver address = new();
private RawDX11Scene? scene;
private Hook? setCursorHook;
- private Hook? presentHook;
+ private Hook? dxgiPresentHook;
+ private Hook? reshadeOnPresentHook;
private Hook? resizeBuffersHook;
private IFontAtlas? dalamudAtlas;
@@ -100,7 +99,10 @@ internal class InterfaceManager : IInternalDisposableService
}
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
- private delegate IntPtr PresentDelegate(IntPtr swapChain, uint syncInterval, uint presentFlags);
+ private delegate IntPtr DxgiPresentDelegate(IntPtr swapChain, uint syncInterval, uint presentFlags);
+
+ [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
+ private delegate void ReshadeOnPresentDelegate(nint swapChain, uint flags, nint presentParams);
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
private delegate IntPtr ResizeBuffersDelegate(IntPtr swapChain, uint bufferCount, uint width, uint height, uint newFormat, uint swapChainFlags);
@@ -296,7 +298,8 @@ internal class InterfaceManager : IInternalDisposableService
{
this.wndProcHookManager.PreWndProc -= this.WndProcHookManagerOnPreWndProc;
Interlocked.Exchange(ref this.setCursorHook, null)?.Dispose();
- Interlocked.Exchange(ref this.presentHook, null)?.Dispose();
+ Interlocked.Exchange(ref this.dxgiPresentHook, null)?.Dispose();
+ Interlocked.Exchange(ref this.reshadeOnPresentHook, null)?.Dispose();
Interlocked.Exchange(ref this.resizeBuffersHook, null)?.Dispose();
}
}
@@ -629,17 +632,16 @@ internal class InterfaceManager : IInternalDisposableService
args.SuppressWithValue(r.Value);
}
- /*
- * NOTE(goat): When hooking ReShade DXGISwapChain::runtime_present, this is missing the syncInterval arg.
- * Seems to work fine regardless, I guess, so whatever.
- */
- private IntPtr PresentDetour(IntPtr swapChain, uint syncInterval, uint presentFlags)
+ private void ReshadeOnPresentDetour(nint swapChain, uint flags, nint presentParams)
{
- Debug.Assert(this.presentHook is not null, "How did PresentDetour get called when presentHook is null?");
- Debug.Assert(this.dalamudAtlas is not null, "dalamudAtlas should have been set already");
+ if (!SwapChainHelper.IsGameDeviceSwapChain(swapChain))
+ {
+ this.reshadeOnPresentHook!.Original(swapChain, flags, presentParams);
+ return;
+ }
- if (this.scene != null && swapChain != this.scene.SwapChain.NativePointer)
- return this.presentHook!.Original(swapChain, syncInterval, presentFlags);
+ Debug.Assert(this.reshadeOnPresentHook is not null, "this.reshadeOnPresentHook is not null");
+ Debug.Assert(this.dalamudAtlas is not null, "this.dalamudAtlas is not null");
if (this.scene == null)
this.InitScene(swapChain);
@@ -655,7 +657,8 @@ internal class InterfaceManager : IInternalDisposableService
Util.Fatal("Failed to initialize Dalamud base fonts.\nPlease report this error.", "Dalamud");
}
- return this.presentHook!.Original(swapChain, syncInterval, presentFlags);
+ this.reshadeOnPresentHook!.Original(swapChain, flags, presentParams);
+ return;
}
this.CumulativePresentCalls++;
@@ -664,22 +667,49 @@ internal class InterfaceManager : IInternalDisposableService
while (this.runBeforeImGuiRender.TryDequeue(out var action))
action.InvokeSafely();
- if (this.address.IsReshade)
+ this.reshadeOnPresentHook!.Original(swapChain, flags, presentParams);
+
+ RenderImGui(this.scene!);
+ this.PostImGuiRender();
+ this.IsMainThreadInPresent = false;
+ }
+
+ private IntPtr PresentDetour(IntPtr swapChain, uint syncInterval, uint presentFlags)
+ {
+ if (!SwapChainHelper.IsGameDeviceSwapChain(swapChain))
+ return this.dxgiPresentHook!.Original(swapChain, syncInterval, presentFlags);
+
+ Debug.Assert(this.dxgiPresentHook is not null, "How did PresentDetour get called when presentHook is null?");
+ Debug.Assert(this.dalamudAtlas is not null, "dalamudAtlas should have been set already");
+
+ if (this.scene == null)
+ this.InitScene(swapChain);
+
+ Debug.Assert(this.scene is not null, "InitScene did not set the scene field, but did not throw an exception.");
+
+ if (!this.dalamudAtlas!.HasBuiltAtlas)
{
- var pRes = this.presentHook!.Original(swapChain, syncInterval, presentFlags);
+ if (this.dalamudAtlas.BuildTask.Exception != null)
+ {
+ // TODO: Can we do something more user-friendly here? Unload instead?
+ Log.Error(this.dalamudAtlas.BuildTask.Exception, "Failed to initialize Dalamud base fonts");
+ Util.Fatal("Failed to initialize Dalamud base fonts.\nPlease report this error.", "Dalamud");
+ }
- RenderImGui(this.scene!);
- this.PostImGuiRender();
- this.IsMainThreadInPresent = false;
-
- return pRes;
+ return this.dxgiPresentHook!.Original(swapChain, syncInterval, presentFlags);
}
+ this.CumulativePresentCalls++;
+ this.IsMainThreadInPresent = true;
+
+ while (this.runBeforeImGuiRender.TryDequeue(out var action))
+ action.InvokeSafely();
+
RenderImGui(this.scene!);
this.PostImGuiRender();
this.IsMainThreadInPresent = false;
- return this.presentHook!.Original(swapChain, syncInterval, presentFlags);
+ return this.dxgiPresentHook!.Original(swapChain, syncInterval, presentFlags);
}
private void PostImGuiRender()
@@ -711,7 +741,7 @@ internal class InterfaceManager : IInternalDisposableService
[ServiceManager.CallWhenServicesReady(
"InterfaceManager accepts event registration and stuff even when the game window is not ready.")]
- private void ContinueConstruction(
+ private unsafe void ContinueConstruction(
TargetSigScanner sigScanner,
FontAtlasFactory fontAtlasFactory)
{
@@ -787,8 +817,6 @@ internal class InterfaceManager : IInternalDisposableService
// This will wait for scene on its own. We just wait for this.dalamudAtlas.BuildTask in this.InitScene.
_ = this.dalamudAtlas.BuildFontsAsync();
- this.address.Setup(sigScanner);
-
try
{
if (Service.Get().WindowIsImmersive)
@@ -799,32 +827,52 @@ internal class InterfaceManager : IInternalDisposableService
Log.Error(ex, "Could not enable immersive mode");
}
- this.setCursorHook = Hook.FromImport(null, "user32.dll", "SetCursor", 0, this.SetCursorDetour);
- this.presentHook = Hook.FromAddress(this.address.Present, this.PresentDetour);
- this.resizeBuffersHook = Hook.FromAddress(this.address.ResizeBuffers, this.ResizeBuffersDetour);
+ this.setCursorHook = Hook.FromImport(
+ null,
+ "user32.dll",
+ "SetCursor",
+ 0,
+ this.SetCursorDetour);
+
+ SwapChainHelper.BusyWaitForGameDeviceSwapChain();
+ SwapChainHelper.DetectReShade();
Log.Verbose("===== S W A P C H A I N =====");
- Log.Verbose($"Present address 0x{this.presentHook!.Address.ToInt64():X}");
- Log.Verbose($"ResizeBuffers address 0x{this.resizeBuffersHook!.Address.ToInt64():X}");
+ this.resizeBuffersHook = Hook.FromAddress(
+ (nint)SwapChainHelper.GameDeviceSwapChainVtbl->ResizeBuffers,
+ this.ResizeBuffersDetour);
+ Log.Verbose($"ResizeBuffers address {Util.DescribeAddress(this.resizeBuffersHook!.Address)}");
+
+ if (SwapChainHelper.ReshadeOnPresent is null)
+ {
+ var addr = (nint)SwapChainHelper.GameDeviceSwapChainVtbl->Present;
+ this.dxgiPresentHook = Hook.FromAddress(addr, this.PresentDetour);
+ Log.Verbose($"ReShade::DXGISwapChain::on_present address {Util.DescribeAddress(addr)}");
+ }
+ else
+ {
+ var addr = (nint)SwapChainHelper.ReshadeOnPresent;
+ this.reshadeOnPresentHook = Hook.FromAddress(addr, this.ReshadeOnPresentDetour);
+ Log.Verbose($"IDXGISwapChain::Present address {Util.DescribeAddress(addr)}");
+ }
this.setCursorHook.Enable();
- this.presentHook.Enable();
+ this.dxgiPresentHook?.Enable();
+ this.reshadeOnPresentHook?.Enable();
this.resizeBuffersHook.Enable();
}
private IntPtr ResizeBuffersDetour(IntPtr swapChain, uint bufferCount, uint width, uint height, uint newFormat, uint swapChainFlags)
{
+ if (!SwapChainHelper.IsGameDeviceSwapChain(swapChain))
+ return this.resizeBuffersHook!.Original(swapChain, bufferCount, width, height, newFormat, swapChainFlags);
+
#if DEBUG
Log.Verbose($"Calling resizebuffers swap@{swapChain.ToInt64():X}{bufferCount} {width} {height} {newFormat} {swapChainFlags}");
#endif
this.ResizeBuffers?.InvokeSafely();
- // We have to ensure we're working with the main swapchain,
- // as viewports might be resizing as well
- if (this.scene == null || swapChain != this.scene.SwapChain.NativePointer)
- return this.resizeBuffersHook!.Original(swapChain, bufferCount, width, height, newFormat, swapChainFlags);
-
this.scene?.OnPreResize();
var ret = this.resizeBuffersHook!.Original(swapChain, bufferCount, width, height, newFormat, swapChainFlags);
diff --git a/Dalamud/Interface/Internal/SwapChainHelper.cs b/Dalamud/Interface/Internal/SwapChainHelper.cs
new file mode 100644
index 000000000..09c1f52fd
--- /dev/null
+++ b/Dalamud/Interface/Internal/SwapChainHelper.cs
@@ -0,0 +1,193 @@
+using System.Diagnostics;
+using System.Threading;
+
+using Dalamud.Game;
+using Dalamud.Utility;
+
+using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
+
+using Serilog;
+
+using TerraFX.Interop.DirectX;
+using TerraFX.Interop.Windows;
+
+namespace Dalamud.Interface.Internal;
+
+/// Helper for dealing with swap chains.
+internal static unsafe class SwapChainHelper
+{
+ ///
+ /// Gets the function pointer for ReShade's DXGISwapChain::on_present.
+ /// Source.
+ ///
+ public static delegate* unmanaged ReshadeOnPresent { get; private set; }
+
+ /// Gets the game's active instance of IDXGISwapChain that is initialized.
+ /// Address of the game's instance of IDXGISwapChain, or null if not available (yet.)
+ public static IDXGISwapChain* GameDeviceSwapChain
+ {
+ get
+ {
+ var kernelDev = Device.Instance();
+ if (kernelDev == null)
+ return null;
+
+ var swapChain = kernelDev->SwapChain;
+ if (swapChain == null)
+ return null;
+
+ // BackBuffer should be something from IDXGISwapChain->GetBuffer, which means that IDXGISwapChain itself
+ // must have been fully initialized.
+ if (swapChain->BackBuffer == null)
+ return null;
+
+ return (IDXGISwapChain*)swapChain->DXGISwapChain;
+ }
+ }
+
+ /// Gets the vtable of .
+ public static IDXGISwapChain.Vtbl* GameDeviceSwapChainVtbl
+ {
+ get
+ {
+ var s = GameDeviceSwapChain;
+ return (IDXGISwapChain.Vtbl*)(s is null ? null : s->lpVtbl);
+ }
+ }
+
+ ///
+ public static bool IsGameDeviceSwapChain(nint punk) => IsGameDeviceSwapChain((IUnknown*)punk);
+
+ /// Determines if the given instance of IUnknown is the game device's swap chain.
+ /// Object to check.
+ /// Type of the object to check.
+ /// true if the object is the game's swap chain.
+ public static bool IsGameDeviceSwapChain(T* punk) where T : unmanaged, IUnknown.Interface
+ {
+ // https://learn.microsoft.com/en-us/windows/win32/api/unknwn/nf-unknwn-iunknown-queryinterface(refiid_void)
+ // For any given COM object (also known as a COM component), a specific query for the IUnknown interface on any
+ // of the object's interfaces must always return the same pointer value.
+
+ var gdsc = GameDeviceSwapChain;
+ if (gdsc is null || punk is null)
+ return false;
+
+ fixed (Guid* iid = &IID.IID_IUnknown)
+ {
+ using var u1 = default(ComPtr);
+ if (gdsc->QueryInterface(iid, (void**)u1.GetAddressOf()).FAILED)
+ return false;
+
+ using var u2 = default(ComPtr);
+ if (punk->QueryInterface(iid, (void**)u2.GetAddressOf()).FAILED)
+ return false;
+
+ return u1.Get() == u2.Get();
+ }
+ }
+
+ /// Wait for the game to have finished initializing the IDXGISwapChain.
+ public static void BusyWaitForGameDeviceSwapChain()
+ {
+ while (GameDeviceSwapChain is null)
+ Thread.Yield();
+ }
+
+ /// Detects ReShade and populate .
+ public static void DetectReShade()
+ {
+ var modules = Process.GetCurrentProcess().Modules;
+ foreach (ProcessModule processModule in modules)
+ {
+ if (!processModule.FileName.EndsWith("game\\dxgi.dll", StringComparison.InvariantCultureIgnoreCase))
+ continue;
+
+ try
+ {
+ var fileInfo = FileVersionInfo.GetVersionInfo(processModule.FileName);
+
+ if (fileInfo.FileDescription == null)
+ break;
+
+ if (!fileInfo.FileDescription.Contains("GShade") && !fileInfo.FileDescription.Contains("ReShade"))
+ break;
+
+ // warning: these comments may no longer be accurate.
+ // reshade master@4232872 RVA
+ // var p = processModule.BaseAddress + 0x82C7E0; // DXGISwapChain::Present
+ // var p = processModule.BaseAddress + 0x82FAC0; // DXGISwapChain::runtime_present
+ // DXGISwapChain::handle_device_loss =>df DXGISwapChain::Present => DXGISwapChain::runtime_present
+ // 5.2+ - F6 C2 01 0F 85
+ // 6.0+ - F6 C2 01 0F 85 88
+
+ var scanner = new SigScanner(processModule);
+ var reShadeDxgiPresent = nint.Zero;
+
+ if (fileInfo.FileVersion?.StartsWith("6.") == true)
+ {
+ // No Addon
+ if (scanner.TryScanText("F6 C2 01 0F 85 A8", out reShadeDxgiPresent))
+ {
+ Log.Information("Hooking present for ReShade 6 No-Addon");
+ }
+
+ // Addon
+ else if (scanner.TryScanText("F6 C2 01 0F 85 88", out reShadeDxgiPresent))
+ {
+ Log.Information("Hooking present for ReShade 6 Addon");
+ }
+
+ // Fallback
+ else
+ {
+ Log.Error("Failed to get ReShade 6 DXGISwapChain::on_present offset!");
+ }
+ }
+
+ // Looks like this sig only works for GShade 4
+ if (reShadeDxgiPresent == nint.Zero && fileInfo.FileDescription?.Contains("GShade 4.") == true)
+ {
+ if (scanner.TryScanText("E8 ?? ?? ?? ?? 45 0F B6 5E ??", out reShadeDxgiPresent))
+ {
+ Log.Information("Hooking present for GShade 4");
+ }
+ else
+ {
+ Log.Error("Failed to find GShade 4 DXGISwapChain::on_present offset!");
+ }
+ }
+
+ if (reShadeDxgiPresent == nint.Zero)
+ {
+ if (scanner.TryScanText("F6 C2 01 0F 85", out reShadeDxgiPresent))
+ {
+ Log.Information("Hooking present for ReShade with fallback 5.X sig");
+ }
+ else
+ {
+ Log.Error("Failed to find ReShade DXGISwapChain::on_present offset with fallback sig!");
+ }
+ }
+
+ Log.Information(
+ "ReShade DLL: {FileName} ({Info} - {Version}) with DXGISwapChain::on_present at {Address}",
+ processModule.FileName,
+ fileInfo.FileDescription ?? "Unknown",
+ fileInfo.FileVersion ?? "Unknown",
+ Util.DescribeAddress(reShadeDxgiPresent));
+
+ if (reShadeDxgiPresent != nint.Zero)
+ {
+ ReshadeOnPresent = (delegate* unmanaged)reShadeDxgiPresent;
+ }
+
+ break;
+ }
+ catch (Exception e)
+ {
+ Log.Error(e, "Failed to get ReShade version info");
+ break;
+ }
+ }
+ }
+}
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonWidget.cs
index cf4979368..18bcd9334 100644
--- a/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonWidget.cs
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/AddonWidget.cs
@@ -50,7 +50,7 @@ internal unsafe class AddonWidget : IDataWindowWidget
var addon = (FFXIVClientStructs.FFXIV.Component.GUI.AtkUnitBase*)address;
var name = addon->NameString;
- ImGui.TextUnformatted($"{name} - 0x{address.ToInt64():X}\n v:{addon->IsVisible} x:{addon->X} y:{addon->Y} s:{addon->Scale}, w:{addon->RootNode->Width}, h:{addon->RootNode->Height}");
+ ImGui.TextUnformatted($"{name} - {Util.DescribeAddress(address)}\n v:{addon->IsVisible} x:{addon->X} y:{addon->Y} s:{addon->Scale}, w:{addon->RootNode->Width}, h:{addon->RootNode->Height}");
if (ImGui.Button("Find Agent"))
{
@@ -59,7 +59,7 @@ internal unsafe class AddonWidget : IDataWindowWidget
if (this.findAgentInterfacePtr != nint.Zero)
{
- ImGui.TextUnformatted($"Agent: 0x{this.findAgentInterfacePtr.ToInt64():X}");
+ ImGui.TextUnformatted($"Agent: {Util.DescribeAddress(this.findAgentInterfacePtr)}");
ImGui.SameLine();
if (ImGui.Button("C"))
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/AddressesWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/AddressesWidget.cs
index eb1164eb2..c7ed526e1 100644
--- a/Dalamud/Interface/Internal/Windows/Data/Widgets/AddressesWidget.cs
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/AddressesWidget.cs
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using Dalamud.Game;
+using Dalamud.Utility;
using ImGuiNET;
@@ -57,7 +58,7 @@ internal class AddressesWidget : IDataWindowWidget
foreach (var valueTuple in debugScannedValue.Value)
{
ImGui.TextUnformatted(
- $" {valueTuple.ClassName} - 0x{valueTuple.Address.ToInt64():X}");
+ $" {valueTuple.ClassName} - {Util.DescribeAddress(valueTuple.Address)}");
ImGui.SameLine();
if (ImGui.Button($"C##{valueTuple.Address.ToInt64():X}"))
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/ConditionWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/ConditionWidget.cs
index c3305c168..355a73a71 100644
--- a/Dalamud/Interface/Internal/Windows/Data/Widgets/ConditionWidget.cs
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/ConditionWidget.cs
@@ -1,4 +1,5 @@
using Dalamud.Game.ClientState.Conditions;
+using Dalamud.Utility;
using ImGuiNET;
@@ -30,7 +31,7 @@ internal class ConditionWidget : IDataWindowWidget
var condition = Service.Get();
#if DEBUG
- ImGui.Text($"ptr: 0x{condition.Address.ToInt64():X}");
+ ImGui.Text($"ptr: {Util.DescribeAddress(condition.Address)}");
#endif
ImGui.Text("Current Conditions:");
diff --git a/Dalamud/Interface/Internal/Windows/Data/Widgets/GamepadWidget.cs b/Dalamud/Interface/Internal/Windows/Data/Widgets/GamepadWidget.cs
index 3e87540c3..5121d82e6 100644
--- a/Dalamud/Interface/Internal/Windows/Data/Widgets/GamepadWidget.cs
+++ b/Dalamud/Interface/Internal/Windows/Data/Widgets/GamepadWidget.cs
@@ -1,4 +1,5 @@
using Dalamud.Game.ClientState.GamePad;
+using Dalamud.Utility;
using ImGuiNET;
@@ -29,14 +30,14 @@ internal class GamepadWidget : IDataWindowWidget
{
var gamepadState = Service.Get();
- ImGui.Text($"GamepadInput 0x{gamepadState.GamepadInputAddress.ToInt64():X}");
+ ImGui.Text($"GamepadInput {Util.DescribeAddress(gamepadState.GamepadInputAddress)}");
#if DEBUG
if (ImGui.IsItemHovered())
ImGui.SetMouseCursor(ImGuiMouseCursor.Hand);
if (ImGui.IsItemClicked())
- ImGui.SetClipboardText($"0x{gamepadState.GamepadInputAddress.ToInt64():X}");
+ ImGui.SetClipboardText($"{Util.DescribeAddress(gamepadState.GamepadInputAddress)}");
#endif
this.DrawHelper(
diff --git a/Dalamud/Memory/MemoryHelper.cs b/Dalamud/Memory/MemoryHelper.cs
index 09f45e2d3..212feb128 100644
--- a/Dalamud/Memory/MemoryHelper.cs
+++ b/Dalamud/Memory/MemoryHelper.cs
@@ -5,6 +5,7 @@ using System.Text;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Memory.Exceptions;
+using Dalamud.Utility;
using FFXIVClientStructs.FFXIV.Client.System.Memory;
using FFXIVClientStructs.FFXIV.Client.System.String;
@@ -787,11 +788,11 @@ public static unsafe class MemoryHelper
var result = VirtualProtect(memoryAddress, (nuint)length, newPermissions, out var oldPermissions);
if (!result)
- throw new MemoryPermissionException($"Unable to change permissions at 0x{memoryAddress.ToInt64():X} of length {length} and permission {newPermissions} (result={result})");
+ throw new MemoryPermissionException($"Unable to change permissions at {Util.DescribeAddress(memoryAddress)} of length {length} and permission {newPermissions} (result={result})");
var last = Marshal.GetLastWin32Error();
if (last > 0)
- throw new MemoryPermissionException($"Unable to change permissions at 0x{memoryAddress.ToInt64():X} of length {length} and permission {newPermissions} (error={last})");
+ throw new MemoryPermissionException($"Unable to change permissions at {Util.DescribeAddress(memoryAddress)} of length {length} and permission {newPermissions} (error={last})");
return oldPermissions;
}
@@ -862,11 +863,11 @@ public static unsafe class MemoryHelper
var result = NativeFunctions.ReadProcessMemory((nint)0xFFFFFFFF, memoryAddress, value, length, out _);
if (!result)
- throw new MemoryReadException($"Unable to read memory at 0x{memoryAddress.ToInt64():X} of length {length} (result={result})");
+ throw new MemoryReadException($"Unable to read memory at {Util.DescribeAddress(memoryAddress)} of length {length} (result={result})");
var last = Marshal.GetLastWin32Error();
if (last > 0)
- throw new MemoryReadException($"Unable to read memory at 0x{memoryAddress.ToInt64():X} of length {length} (error={last})");
+ throw new MemoryReadException($"Unable to read memory at {Util.DescribeAddress(memoryAddress)} of length {length} (error={last})");
}
}
@@ -884,11 +885,11 @@ public static unsafe class MemoryHelper
var result = NativeFunctions.WriteProcessMemory((nint)0xFFFFFFFF, memoryAddress, data, length, out _);
if (!result)
- throw new MemoryWriteException($"Unable to write memory at 0x{memoryAddress.ToInt64():X} of length {length} (result={result})");
+ throw new MemoryWriteException($"Unable to write memory at {Util.DescribeAddress(memoryAddress)} of length {length} (result={result})");
var last = Marshal.GetLastWin32Error();
if (last > 0)
- throw new MemoryWriteException($"Unable to write memory at 0x{memoryAddress.ToInt64():X} of length {length} (error={last})");
+ throw new MemoryWriteException($"Unable to write memory at {Util.DescribeAddress(memoryAddress)} of length {length} (error={last})");
}
}
diff --git a/Dalamud/Utility/Util.cs b/Dalamud/Utility/Util.cs
index a9be33244..927f7b310 100644
--- a/Dalamud/Utility/Util.cs
+++ b/Dalamud/Utility/Util.cs
@@ -19,7 +19,6 @@ using Dalamud.Game.ClientState.Objects.SubKinds;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Interface.Colors;
using Dalamud.Interface.Utility;
-
using ImGuiNET;
using Lumina.Excel.GeneratedSheets;
using Serilog;
@@ -28,7 +27,8 @@ using Windows.Win32.Storage.FileSystem;
using Windows.Win32.System.Memory;
using Windows.Win32.System.Ole;
-using HWND = Windows.Win32.Foundation.HWND;
+using static TerraFX.Interop.Windows.Windows;
+
using Win32_PInvoke = Windows.Win32.PInvoke;
namespace Dalamud.Utility;
@@ -38,6 +38,28 @@ namespace Dalamud.Utility;
///
public static class Util
{
+ private static readonly string[] PageProtectionFlagNames = [
+ "PAGE_NOACCESS",
+ "PAGE_READONLY",
+ "PAGE_READWRITE",
+ "PAGE_WRITECOPY",
+ "PAGE_EXECUTE",
+ "PAGE_EXECUTE_READ",
+ "PAGE_EXECUTE_READWRITE",
+ "PAGE_EXECUTE_WRITECOPY",
+ "PAGE_GUARD",
+ "PAGE_NOCACHE",
+ "PAGE_WRITECOMBINE",
+ "PAGE_GRAPHICS_NOACCESS",
+ "PAGE_GRAPHICS_READONLY",
+ "PAGE_GRAPHICS_READWRITE",
+ "PAGE_GRAPHICS_EXECUTE",
+ "PAGE_GRAPHICS_EXECUTE_READ",
+ "PAGE_GRAPHICS_EXECUTE_READWRITE",
+ "PAGE_GRAPHICS_COHERENT",
+ "PAGE_GRAPHICS_NOCACHE",
+ ];
+
private static readonly Type GenericSpanType = typeof(Span<>);
private static string? gitHashInternal;
private static int? gitCommitCountInternal;
@@ -160,6 +182,100 @@ public static class Util
return gitHashClientStructsInternal;
}
+ ///
+ public static unsafe string DescribeAddress(void* p) => DescribeAddress((nint)p);
+
+ /// Describes a memory address relative to module, or allocation base.
+ /// Address.
+ /// Address description.
+ public static unsafe string DescribeAddress(nint p)
+ {
+ Span namebuf = stackalloc char[9];
+ var modules = Process.GetCurrentProcess().Modules;
+ for (var i = 0; i < modules.Count; i++)
+ {
+ if (p < modules[i].BaseAddress) continue;
+ var d = p - modules[i].BaseAddress;
+ if (d > modules[i].ModuleMemorySize) continue;
+
+ // Display module name without path, only if there exists exactly one module loaded in the memory.
+ var fileName = modules[i].ModuleName;
+ for (var j = 0; j < modules.Count; j++)
+ {
+ if (i == j)
+ continue;
+ if (!modules[j].ModuleName.Equals(fileName, StringComparison.InvariantCultureIgnoreCase))
+ continue;
+ fileName = modules[i].FileName;
+ break;
+ }
+
+ var dos = (IMAGE_DOS_HEADER*)modules[i].BaseAddress;
+ if (dos->e_magic != 0x5A4D)
+ return $"0x{p:X}({fileName}+0x{d:X}: ???)";
+
+ Span sections;
+ switch (((IMAGE_NT_HEADERS32*)(modules[i].BaseAddress + dos->e_lfanew))->OptionalHeader.Magic)
+ {
+ case IMAGE.IMAGE_NT_OPTIONAL_HDR32_MAGIC:
+ {
+ var nth = (IMAGE_NT_HEADERS32*)(modules[i].BaseAddress + dos->e_lfanew);
+ if (d < dos->e_lfanew + sizeof(IMAGE_NT_HEADERS32)
+ + (nth->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER)))
+ goto default;
+ sections = new(
+ (void*)(modules[i].BaseAddress + dos->e_lfanew + sizeof(IMAGE_NT_HEADERS32)),
+ nth->FileHeader.NumberOfSections);
+ break;
+ }
+
+ case IMAGE.IMAGE_NT_OPTIONAL_HDR64_MAGIC:
+ {
+ var nth = (IMAGE_NT_HEADERS64*)(modules[i].BaseAddress + dos->e_lfanew);
+ if (d < dos->e_lfanew + sizeof(IMAGE_NT_HEADERS64)
+ + (nth->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER)))
+ goto default;
+ sections = new(
+ (void*)(modules[i].BaseAddress + dos->e_lfanew + sizeof(IMAGE_NT_HEADERS64)),
+ nth->FileHeader.NumberOfSections);
+ break;
+ }
+
+ default:
+ return $"0x{p:X}({fileName}+0x{d:X}: header)";
+ }
+
+ for (var j = 0; j < sections.Length; j++)
+ {
+ if (d >= sections[j].VirtualAddress && d < sections[j].VirtualAddress + sections[j].Misc.VirtualSize)
+ {
+ var d2 = d - sections[j].VirtualAddress;
+ var name8 = new Span((byte*)Unsafe.AsPointer(ref sections[j].Name[0]), 8).TrimEnd((byte)0);
+ return $"0x{p:X}({fileName}+0x{d:X}({namebuf[..Encoding.UTF8.GetChars(name8, namebuf)]}+0x{d2:X}))";
+ }
+ }
+
+ return $"0x{p:X}({fileName}+0x{d:X}: ???)";
+ }
+
+ MEMORY_BASIC_INFORMATION mbi;
+ if (VirtualQuery((void*)p, &mbi, (nuint)sizeof(MEMORY_BASIC_INFORMATION)) == 0)
+ return $"0x{p:X}(???)";
+
+ var sb = new StringBuilder();
+ sb.Append($"0x{p:X}(");
+ for (int i = 0, c = 0; i < PageProtectionFlagNames.Length; i++)
+ {
+ if ((mbi.Protect & (1 << i)) == 0)
+ continue;
+ if (c++ == 0)
+ sb.Append(" | ");
+ sb.Append(PageProtectionFlagNames[i]);
+ }
+
+ return sb.Append(')').ToString();
+ }
+
///
/// Read memory from an offset and hexdump them via Serilog.
///
@@ -858,7 +974,7 @@ public static class Util
Win32_PInvoke.GlobalUnlock(hGlobal);
- if (Win32_PInvoke.OpenClipboard(HWND.Null))
+ if (Win32_PInvoke.OpenClipboard(default))
{
Win32_PInvoke.SetClipboardData(
(uint)CLIPBOARD_FORMAT.CF_HDROP,