mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 18:27:23 +01:00
Describe address for debugging, change PresentDetour viewport handling (#1943)
* Describe memory address when printed in log/debug utilities * PresentDetour: Compare against game's internal copy of IDXGISwapChain * Handle ReShade on_present function signature properly
This commit is contained in:
parent
eed7abed12
commit
1109e64552
26 changed files with 445 additions and 591 deletions
|
|
@ -4,6 +4,7 @@ using System.Collections.Generic;
|
||||||
using Dalamud.IoC;
|
using Dalamud.IoC;
|
||||||
using Dalamud.IoC.Internal;
|
using Dalamud.IoC.Internal;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
|
using Dalamud.Utility;
|
||||||
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
@ -28,7 +29,7 @@ internal sealed unsafe partial class AetheryteList : IServiceType, IAetheryteLis
|
||||||
[ServiceManager.ServiceConstructor]
|
[ServiceManager.ServiceConstructor]
|
||||||
private AetheryteList()
|
private AetheryteList()
|
||||||
{
|
{
|
||||||
Log.Verbose($"Teleport address 0x{((nint)this.telepoInstance).ToInt64():X}");
|
Log.Verbose($"Teleport address {Util.DescribeAddress(this.telepoInstance)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ using System.Runtime.InteropServices;
|
||||||
using Dalamud.IoC;
|
using Dalamud.IoC;
|
||||||
using Dalamud.IoC.Internal;
|
using Dalamud.IoC.Internal;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
|
using Dalamud.Utility;
|
||||||
|
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
|
|
@ -34,7 +35,7 @@ internal sealed partial class BuddyList : IServiceType, IBuddyList
|
||||||
{
|
{
|
||||||
this.address = this.clientState.AddressResolver;
|
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)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ internal sealed class ClientState : IInternalDisposableService, IClientState
|
||||||
|
|
||||||
this.ClientLanguage = (ClientLanguage)dalamud.StartInfo.Language;
|
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<SetupTerritoryTypeDelegate>.FromAddress(this.address.SetupTerritoryType, this.SetupTerritoryTypeDetour);
|
this.setupTerritoryTypeHook = Hook<SetupTerritoryTypeDelegate>.FromAddress(this.address.SetupTerritoryType, this.SetupTerritoryTypeDetour);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ using System.Collections.Generic;
|
||||||
using Dalamud.IoC;
|
using Dalamud.IoC;
|
||||||
using Dalamud.IoC.Internal;
|
using Dalamud.IoC.Internal;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
|
using Dalamud.Utility;
|
||||||
|
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
|
|
@ -26,7 +27,7 @@ internal sealed partial class FateTable : IServiceType, IFateTable
|
||||||
{
|
{
|
||||||
this.address = clientState.AddressResolver;
|
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)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ using Dalamud.Hooking;
|
||||||
using Dalamud.IoC;
|
using Dalamud.IoC;
|
||||||
using Dalamud.IoC.Internal;
|
using Dalamud.IoC.Internal;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
|
using Dalamud.Utility;
|
||||||
|
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
@ -35,7 +36,7 @@ internal unsafe class GamepadState : IInternalDisposableService, IGamepadState
|
||||||
private GamepadState(ClientState clientState)
|
private GamepadState(ClientState clientState)
|
||||||
{
|
{
|
||||||
var resolver = clientState.AddressResolver;
|
var resolver = clientState.AddressResolver;
|
||||||
Log.Verbose($"GamepadPoll address 0x{resolver.GamepadPoll.ToInt64():X}");
|
Log.Verbose($"GamepadPoll address {Util.DescribeAddress(resolver.GamepadPoll)}");
|
||||||
this.gamepadPoll = Hook<ControllerPoll>.FromAddress(resolver.GamepadPoll, this.GamepadPollDetour);
|
this.gamepadPoll = Hook<ControllerPoll>.FromAddress(resolver.GamepadPoll, this.GamepadPollDetour);
|
||||||
this.gamepadPoll?.Enable();
|
this.gamepadPoll?.Enable();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using Dalamud.Game.ClientState.JobGauge.Types;
|
||||||
using Dalamud.IoC;
|
using Dalamud.IoC;
|
||||||
using Dalamud.IoC.Internal;
|
using Dalamud.IoC.Internal;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
|
using Dalamud.Utility;
|
||||||
|
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
|
|
@ -27,7 +28,7 @@ internal class JobGauges : IServiceType, IJobGauges
|
||||||
{
|
{
|
||||||
this.Address = clientState.AddressResolver.JobGaugeData;
|
this.Address = clientState.AddressResolver.JobGaugeData;
|
||||||
|
|
||||||
Log.Verbose($"JobGaugeData address 0x{this.Address.ToInt64():X}");
|
Log.Verbose($"JobGaugeData address {Util.DescribeAddress(this.Address)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using System.Runtime.InteropServices;
|
||||||
using Dalamud.IoC;
|
using Dalamud.IoC;
|
||||||
using Dalamud.IoC.Internal;
|
using Dalamud.IoC.Internal;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
|
using Dalamud.Utility;
|
||||||
|
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
|
|
@ -45,7 +46,7 @@ internal class KeyState : IServiceType, IKeyState
|
||||||
this.bufferBase = moduleBaseAddress + Marshal.ReadInt32(addressResolver.KeyboardState);
|
this.bufferBase = moduleBaseAddress + Marshal.ReadInt32(addressResolver.KeyboardState);
|
||||||
this.indexBase = moduleBaseAddress + Marshal.ReadInt32(addressResolver.KeyboardStateIndexArray);
|
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)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ internal sealed partial class ObjectTable : IServiceType, IObjectTable
|
||||||
for (var i = 0; i < this.frameworkThreadEnumerators.Length; i++)
|
for (var i = 0; i < this.frameworkThreadEnumerators.Length; i++)
|
||||||
this.frameworkThreadEnumerators[i] = new(this, 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)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ using System.Runtime.InteropServices;
|
||||||
using Dalamud.IoC;
|
using Dalamud.IoC;
|
||||||
using Dalamud.IoC.Internal;
|
using Dalamud.IoC.Internal;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
|
using Dalamud.Utility;
|
||||||
|
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
|
|
@ -34,7 +35,7 @@ internal sealed unsafe partial class PartyList : IServiceType, IPartyList
|
||||||
{
|
{
|
||||||
this.address = this.clientState.AddressResolver;
|
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)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
|
|
||||||
|
|
@ -55,11 +55,11 @@ internal sealed unsafe class GameGui : IInternalDisposableService, IGameGui
|
||||||
this.address.Setup(sigScanner);
|
this.address.Setup(sigScanner);
|
||||||
|
|
||||||
Log.Verbose("===== G A M E G U I =====");
|
Log.Verbose("===== G A M E G U I =====");
|
||||||
Log.Verbose($"GameGuiManager address 0x{this.address.BaseAddress.ToInt64():X}");
|
Log.Verbose($"GameGuiManager address {Util.DescribeAddress(this.address.BaseAddress)}");
|
||||||
Log.Verbose($"SetGlobalBgm address 0x{this.address.SetGlobalBgm.ToInt64():X}");
|
Log.Verbose($"SetGlobalBgm address {Util.DescribeAddress(this.address.SetGlobalBgm)}");
|
||||||
Log.Verbose($"HandleItemHover address 0x{this.address.HandleItemHover.ToInt64():X}");
|
Log.Verbose($"HandleItemHover address {Util.DescribeAddress(this.address.HandleItemHover)}");
|
||||||
Log.Verbose($"HandleItemOut address 0x{this.address.HandleItemOut.ToInt64():X}");
|
Log.Verbose($"HandleItemOut address {Util.DescribeAddress(this.address.HandleItemOut)}");
|
||||||
Log.Verbose($"HandleImm address 0x{this.address.HandleImm.ToInt64():X}");
|
Log.Verbose($"HandleImm address {Util.DescribeAddress(this.address.HandleImm)}");
|
||||||
|
|
||||||
this.setGlobalBgmHook = Hook<SetGlobalBgmDelegate>.FromAddress(this.address.SetGlobalBgm, this.HandleSetGlobalBgmDetour);
|
this.setGlobalBgmHook = Hook<SetGlobalBgmDelegate>.FromAddress(this.address.SetGlobalBgm, this.HandleSetGlobalBgmDetour);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using Dalamud.Utility;
|
||||||
|
|
||||||
#if !DEBUG
|
#if !DEBUG
|
||||||
using Dalamud.Configuration.Internal;
|
using Dalamud.Configuration.Internal;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -30,7 +32,7 @@ internal sealed class AntiDebug : IInternalDisposableService
|
||||||
this.debugCheckAddress = IntPtr.Zero;
|
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)
|
if (!this.IsEnabled)
|
||||||
{
|
{
|
||||||
|
|
@ -65,7 +67,7 @@ internal sealed class AntiDebug : IInternalDisposableService
|
||||||
this.original = new byte[this.nop.Length];
|
this.original = new byte[this.nop.Length];
|
||||||
if (this.debugCheckAddress != IntPtr.Zero && !this.IsEnabled)
|
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.ReadBytes(this.debugCheckAddress, this.nop.Length, out this.original);
|
||||||
SafeMemory.WriteBytes(this.debugCheckAddress, this.nop);
|
SafeMemory.WriteBytes(this.debugCheckAddress, this.nop);
|
||||||
}
|
}
|
||||||
|
|
@ -87,7 +89,7 @@ internal sealed class AntiDebug : IInternalDisposableService
|
||||||
|
|
||||||
if (this.debugCheckAddress != IntPtr.Zero && this.original != null)
|
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);
|
SafeMemory.WriteBytes(this.debugCheckAddress, this.original);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -1,226 +0,0 @@
|
||||||
namespace Dalamud.Game.Internal.DXGI.Definitions;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Contains a full list of ID3D11Device functions to be used as an indexer into the DirectX Virtual Function Table entries.
|
|
||||||
/// </summary>
|
|
||||||
internal enum ID3D11DeviceVtbl
|
|
||||||
{
|
|
||||||
// IUnknown
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// IUnknown::QueryInterface method (unknwn.h).
|
|
||||||
/// </summary>
|
|
||||||
QueryInterface = 0,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// IUnknown::AddRef method (unknwn.h).
|
|
||||||
/// </summary>
|
|
||||||
AddRef = 1,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// IUnknown::Release method (unknwn.h).
|
|
||||||
/// </summary>
|
|
||||||
Release = 2,
|
|
||||||
|
|
||||||
// ID3D11Device
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::CreateBuffer method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
CreateBuffer = 3,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::CreateTexture1D method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
CreateTexture1D = 4,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::CreateTexture2D method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
CreateTexture2D = 5,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::CreateTexture3D method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
CreateTexture3D = 6,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::CreateShaderResourceView method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
CreateShaderResourceView = 7,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::CreateUnorderedAccessView method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
CreateUnorderedAccessView = 8,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::CreateRenderTargetView method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
CreateRenderTargetView = 9,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::CreateDepthStencilView method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
CreateDepthStencilView = 10,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::CreateInputLayout method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
CreateInputLayout = 11,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::CreateVertexShader method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
CreateVertexShader = 12,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::CreateGeometryShader method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
CreateGeometryShader = 13,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::CreateGeometryShaderWithStreamOutput method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
CreateGeometryShaderWithStreamOutput = 14,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::CreatePixelShader method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
CreatePixelShader = 15,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::CreateHullShader method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
CreateHullShader = 16,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::CreateDomainShader method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
CreateDomainShader = 17,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::CreateComputeShader method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
CreateComputeShader = 18,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::CreateClassLinkage method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
CreateClassLinkage = 19,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::CreateBlendState method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
CreateBlendState = 20,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::CreateDepthStencilState method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
CreateDepthStencilState = 21,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::CreateRasterizerState method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
CreateRasterizerState = 22,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::CreateSamplerState method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
CreateSamplerState = 23,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::CreateQuery method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
CreateQuery = 24,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::CreatePredicate method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
CreatePredicate = 25,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::CreateCounter method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
CreateCounter = 26,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::CreateDeferredContext method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
CreateDeferredContext = 27,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::OpenSharedResource method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
OpenSharedResource = 28,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::CheckFormatSupport method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
CheckFormatSupport = 29,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::CheckMultisampleQualityLevels method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
CheckMultisampleQualityLevels = 30,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::CheckCounterInfo method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
CheckCounterInfo = 31,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::CheckCounter method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
CheckCounter = 32,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::CheckFeatureSupport method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
CheckFeatureSupport = 33,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::GetPrivateData method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
GetPrivateData = 34,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::SetPrivateData method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
SetPrivateData = 35,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::SetPrivateDataInterface method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
SetPrivateDataInterface = 36,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::GetFeatureLevel method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
GetFeatureLevel = 37,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::GetCreationFlags method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
GetCreationFlags = 38,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::GetDeviceRemovedReason method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
GetDeviceRemovedReason = 39,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::GetImmediateContext method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
GetImmediateContext = 40,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::SetExceptionMode method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
SetExceptionMode = 41,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// ID3D11Device::GetExceptionMode method (d3d11.h).
|
|
||||||
/// </summary>
|
|
||||||
GetExceptionMode = 42,
|
|
||||||
}
|
|
||||||
|
|
@ -1,106 +0,0 @@
|
||||||
namespace Dalamud.Game.Internal.DXGI.Definitions;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Contains a full list of IDXGISwapChain functions to be used as an indexer into the SwapChain Virtual Function Table
|
|
||||||
/// entries.
|
|
||||||
/// </summary>
|
|
||||||
internal enum IDXGISwapChainVtbl
|
|
||||||
{
|
|
||||||
// IUnknown
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// IUnknown::QueryInterface method (unknwn.h).
|
|
||||||
/// </summary>
|
|
||||||
QueryInterface = 0,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// IUnknown::AddRef method (unknwn.h).
|
|
||||||
/// </summary>
|
|
||||||
AddRef = 1,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// IUnknown::Release method (unknwn.h).
|
|
||||||
/// </summary>
|
|
||||||
Release = 2,
|
|
||||||
|
|
||||||
// IDXGIObject
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// IDXGIObject::SetPrivateData method (dxgi.h).
|
|
||||||
/// </summary>
|
|
||||||
SetPrivateData = 3,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// IDXGIObject::SetPrivateDataInterface method (dxgi.h).
|
|
||||||
/// </summary>
|
|
||||||
SetPrivateDataInterface = 4,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// IDXGIObject::GetPrivateData method (dxgi.h).
|
|
||||||
/// </summary>
|
|
||||||
GetPrivateData = 5,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// IDXGIObject::GetParent method (dxgi.h).
|
|
||||||
/// </summary>
|
|
||||||
GetParent = 6,
|
|
||||||
|
|
||||||
// IDXGIDeviceSubObject
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// IDXGIDeviceSubObject::GetDevice method (dxgi.h).
|
|
||||||
/// </summary>
|
|
||||||
GetDevice = 7,
|
|
||||||
|
|
||||||
// IDXGISwapChain
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// IDXGISwapChain::Present method (dxgi.h).
|
|
||||||
/// </summary>
|
|
||||||
Present = 8,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// IUnknIDXGISwapChainown::GetBuffer method (dxgi.h).
|
|
||||||
/// </summary>
|
|
||||||
GetBuffer = 9,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// IDXGISwapChain::SetFullscreenState method (dxgi.h).
|
|
||||||
/// </summary>
|
|
||||||
SetFullscreenState = 10,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// IDXGISwapChain::GetFullscreenState method (dxgi.h).
|
|
||||||
/// </summary>
|
|
||||||
GetFullscreenState = 11,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// IDXGISwapChain::GetDesc method (dxgi.h).
|
|
||||||
/// </summary>
|
|
||||||
GetDesc = 12,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// IDXGISwapChain::ResizeBuffers method (dxgi.h).
|
|
||||||
/// </summary>
|
|
||||||
ResizeBuffers = 13,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// IDXGISwapChain::ResizeTarget method (dxgi.h).
|
|
||||||
/// </summary>
|
|
||||||
ResizeTarget = 14,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// IDXGISwapChain::GetContainingOutput method (dxgi.h).
|
|
||||||
/// </summary>
|
|
||||||
GetContainingOutput = 15,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// IDXGISwapChain::GetFrameStatistics method (dxgi.h).
|
|
||||||
/// </summary>
|
|
||||||
GetFrameStatistics = 16,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// IDXGISwapChain::GetLastPresentCount method (dxgi.h).
|
|
||||||
/// </summary>
|
|
||||||
GetLastPresentCount = 17,
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
namespace Dalamud.Game.Internal.DXGI;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An interface binding for the address resolvers that attempt to find native D3D11 methods.
|
|
||||||
/// </summary>
|
|
||||||
public interface ISwapChainAddressResolver
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the address of the native D3D11.Present method.
|
|
||||||
/// </summary>
|
|
||||||
IntPtr Present { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the address of the native D3D11.ResizeBuffers method.
|
|
||||||
/// </summary>
|
|
||||||
IntPtr ResizeBuffers { get; set; }
|
|
||||||
}
|
|
||||||
|
|
@ -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;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This class attempts to determine the D3D11 SwapChain vtable addresses via instantiating a new form and inspecting it.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// If the normal signature based method of resolution fails, this is the backup.
|
|
||||||
/// </remarks>
|
|
||||||
internal class SwapChainVtableResolver : BaseAddressResolver, ISwapChainAddressResolver
|
|
||||||
{
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public IntPtr Present { get; set; }
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public IntPtr ResizeBuffers { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether or not ReShade is loaded/used.
|
|
||||||
/// </summary>
|
|
||||||
public bool IsReshade { get; private set; }
|
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
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<IntPtr> GetVTblAddresses(IntPtr pointer, int numberOfMethods)
|
|
||||||
{
|
|
||||||
return GetVTblAddresses(pointer, 0, numberOfMethods);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<IntPtr> GetVTblAddresses(IntPtr pointer, int startIndex, int numberOfMethods)
|
|
||||||
{
|
|
||||||
var vtblAddresses = new List<IntPtr>();
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -38,8 +38,8 @@ internal sealed class GameNetwork : IInternalDisposableService, IGameNetwork
|
||||||
this.address.Setup(sigScanner);
|
this.address.Setup(sigScanner);
|
||||||
|
|
||||||
Log.Verbose("===== G A M E N E T W O R K =====");
|
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($"ProcessZonePacketDown address {Util.DescribeAddress(this.address.ProcessZonePacketDown)}");
|
||||||
Log.Verbose($"ProcessZonePacketUp address 0x{this.address.ProcessZonePacketUp.ToInt64():X}");
|
Log.Verbose($"ProcessZonePacketUp address {Util.DescribeAddress(this.address.ProcessZonePacketUp)}");
|
||||||
|
|
||||||
this.processZonePacketDownHook = Hook<ProcessZonePacketDownDelegate>.FromAddress(this.address.ProcessZonePacketDown, this.ProcessZonePacketDownDetour);
|
this.processZonePacketDownHook = Hook<ProcessZonePacketDownDelegate>.FromAddress(this.address.ProcessZonePacketDown, this.ProcessZonePacketDownDetour);
|
||||||
this.processZonePacketUpHook = Hook<ProcessZonePacketUpDelegate>.FromAddress(this.address.ProcessZonePacketUp, this.ProcessZonePacketUpDetour);
|
this.processZonePacketUpHook = Hook<ProcessZonePacketUpDelegate>.FromAddress(this.address.ProcessZonePacketUp, this.ProcessZonePacketUpDetour);
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using System.Runtime.InteropServices;
|
||||||
|
|
||||||
using Dalamud.Logging.Internal;
|
using Dalamud.Logging.Internal;
|
||||||
using Dalamud.Memory;
|
using Dalamud.Memory;
|
||||||
|
using Dalamud.Utility;
|
||||||
|
|
||||||
using Iced.Intel;
|
using Iced.Intel;
|
||||||
|
|
||||||
|
|
@ -69,7 +70,7 @@ internal class HookManager : IInternalDisposableService
|
||||||
/// <returns>A new Unhooker instance.</returns>
|
/// <returns>A new Unhooker instance.</returns>
|
||||||
public static Unhooker RegisterUnhooker(IntPtr address, int minBytes, int maxBytes)
|
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));
|
return Unhookers.GetOrAdd(address, _ => new Unhooker(address, minBytes, maxBytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using Dalamud.Memory;
|
using Dalamud.Memory;
|
||||||
|
using Dalamud.Utility;
|
||||||
|
|
||||||
namespace Dalamud.Hooking.Internal;
|
namespace Dalamud.Hooking.Internal;
|
||||||
|
|
||||||
|
|
@ -63,7 +64,7 @@ public class Unhooker
|
||||||
var len = this.trimmed ? this.originalBytes.Length : int.Max(this.GetNaiveHookLength(), this.minBytes);
|
var len = this.trimmed ? this.originalBytes.Length : int.Max(this.GetNaiveHookLength(), this.minBytes);
|
||||||
if (len > 0)
|
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.ChangePermission(this.address, len, MemoryProtection.ExecuteReadWrite, out var oldPermissions);
|
||||||
MemoryHelper.WriteRaw(this.address, this.originalBytes[..len]);
|
MemoryHelper.WriteRaw(this.address, this.originalBytes[..len]);
|
||||||
MemoryHelper.ChangePermission(this.address, len, oldPermissions);
|
MemoryHelper.ChangePermission(this.address, len, oldPermissions);
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ using Dalamud.Configuration.Internal;
|
||||||
using Dalamud.Game;
|
using Dalamud.Game;
|
||||||
using Dalamud.Game.ClientState.GamePad;
|
using Dalamud.Game.ClientState.GamePad;
|
||||||
using Dalamud.Game.ClientState.Keys;
|
using Dalamud.Game.ClientState.Keys;
|
||||||
using Dalamud.Game.Internal.DXGI;
|
|
||||||
using Dalamud.Hooking;
|
using Dalamud.Hooking;
|
||||||
using Dalamud.Hooking.WndProcHook;
|
using Dalamud.Hooking.WndProcHook;
|
||||||
using Dalamud.Interface.ImGuiNotification.Internal;
|
using Dalamud.Interface.ImGuiNotification.Internal;
|
||||||
|
|
@ -79,11 +78,11 @@ internal class InterfaceManager : IInternalDisposableService
|
||||||
private readonly ConcurrentQueue<Action> runBeforeImGuiRender = new();
|
private readonly ConcurrentQueue<Action> runBeforeImGuiRender = new();
|
||||||
private readonly ConcurrentQueue<Action> runAfterImGuiRender = new();
|
private readonly ConcurrentQueue<Action> runAfterImGuiRender = new();
|
||||||
|
|
||||||
private readonly SwapChainVtableResolver address = new();
|
|
||||||
private RawDX11Scene? scene;
|
private RawDX11Scene? scene;
|
||||||
|
|
||||||
private Hook<SetCursorDelegate>? setCursorHook;
|
private Hook<SetCursorDelegate>? setCursorHook;
|
||||||
private Hook<PresentDelegate>? presentHook;
|
private Hook<DxgiPresentDelegate>? dxgiPresentHook;
|
||||||
|
private Hook<ReshadeOnPresentDelegate>? reshadeOnPresentHook;
|
||||||
private Hook<ResizeBuffersDelegate>? resizeBuffersHook;
|
private Hook<ResizeBuffersDelegate>? resizeBuffersHook;
|
||||||
|
|
||||||
private IFontAtlas? dalamudAtlas;
|
private IFontAtlas? dalamudAtlas;
|
||||||
|
|
@ -100,7 +99,10 @@ internal class InterfaceManager : IInternalDisposableService
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
|
[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)]
|
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
|
||||||
private delegate IntPtr ResizeBuffersDelegate(IntPtr swapChain, uint bufferCount, uint width, uint height, uint newFormat, uint swapChainFlags);
|
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;
|
this.wndProcHookManager.PreWndProc -= this.WndProcHookManagerOnPreWndProc;
|
||||||
Interlocked.Exchange(ref this.setCursorHook, null)?.Dispose();
|
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();
|
Interlocked.Exchange(ref this.resizeBuffersHook, null)?.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -629,17 +632,16 @@ internal class InterfaceManager : IInternalDisposableService
|
||||||
args.SuppressWithValue(r.Value);
|
args.SuppressWithValue(r.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
private void ReshadeOnPresentDetour(nint swapChain, uint flags, nint presentParams)
|
||||||
* 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)
|
|
||||||
{
|
{
|
||||||
Debug.Assert(this.presentHook is not null, "How did PresentDetour get called when presentHook is null?");
|
if (!SwapChainHelper.IsGameDeviceSwapChain(swapChain))
|
||||||
Debug.Assert(this.dalamudAtlas is not null, "dalamudAtlas should have been set already");
|
{
|
||||||
|
this.reshadeOnPresentHook!.Original(swapChain, flags, presentParams);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.scene != null && swapChain != this.scene.SwapChain.NativePointer)
|
Debug.Assert(this.reshadeOnPresentHook is not null, "this.reshadeOnPresentHook is not null");
|
||||||
return this.presentHook!.Original(swapChain, syncInterval, presentFlags);
|
Debug.Assert(this.dalamudAtlas is not null, "this.dalamudAtlas is not null");
|
||||||
|
|
||||||
if (this.scene == null)
|
if (this.scene == null)
|
||||||
this.InitScene(swapChain);
|
this.InitScene(swapChain);
|
||||||
|
|
@ -655,7 +657,8 @@ internal class InterfaceManager : IInternalDisposableService
|
||||||
Util.Fatal("Failed to initialize Dalamud base fonts.\nPlease report this error.", "Dalamud");
|
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++;
|
this.CumulativePresentCalls++;
|
||||||
|
|
@ -664,22 +667,49 @@ internal class InterfaceManager : IInternalDisposableService
|
||||||
while (this.runBeforeImGuiRender.TryDequeue(out var action))
|
while (this.runBeforeImGuiRender.TryDequeue(out var action))
|
||||||
action.InvokeSafely();
|
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!);
|
return this.dxgiPresentHook!.Original(swapChain, syncInterval, presentFlags);
|
||||||
this.PostImGuiRender();
|
|
||||||
this.IsMainThreadInPresent = false;
|
|
||||||
|
|
||||||
return pRes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.CumulativePresentCalls++;
|
||||||
|
this.IsMainThreadInPresent = true;
|
||||||
|
|
||||||
|
while (this.runBeforeImGuiRender.TryDequeue(out var action))
|
||||||
|
action.InvokeSafely();
|
||||||
|
|
||||||
RenderImGui(this.scene!);
|
RenderImGui(this.scene!);
|
||||||
this.PostImGuiRender();
|
this.PostImGuiRender();
|
||||||
this.IsMainThreadInPresent = false;
|
this.IsMainThreadInPresent = false;
|
||||||
|
|
||||||
return this.presentHook!.Original(swapChain, syncInterval, presentFlags);
|
return this.dxgiPresentHook!.Original(swapChain, syncInterval, presentFlags);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PostImGuiRender()
|
private void PostImGuiRender()
|
||||||
|
|
@ -711,7 +741,7 @@ internal class InterfaceManager : IInternalDisposableService
|
||||||
|
|
||||||
[ServiceManager.CallWhenServicesReady(
|
[ServiceManager.CallWhenServicesReady(
|
||||||
"InterfaceManager accepts event registration and stuff even when the game window is not ready.")]
|
"InterfaceManager accepts event registration and stuff even when the game window is not ready.")]
|
||||||
private void ContinueConstruction(
|
private unsafe void ContinueConstruction(
|
||||||
TargetSigScanner sigScanner,
|
TargetSigScanner sigScanner,
|
||||||
FontAtlasFactory fontAtlasFactory)
|
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 will wait for scene on its own. We just wait for this.dalamudAtlas.BuildTask in this.InitScene.
|
||||||
_ = this.dalamudAtlas.BuildFontsAsync();
|
_ = this.dalamudAtlas.BuildFontsAsync();
|
||||||
|
|
||||||
this.address.Setup(sigScanner);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (Service<DalamudConfiguration>.Get().WindowIsImmersive)
|
if (Service<DalamudConfiguration>.Get().WindowIsImmersive)
|
||||||
|
|
@ -799,32 +827,52 @@ internal class InterfaceManager : IInternalDisposableService
|
||||||
Log.Error(ex, "Could not enable immersive mode");
|
Log.Error(ex, "Could not enable immersive mode");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setCursorHook = Hook<SetCursorDelegate>.FromImport(null, "user32.dll", "SetCursor", 0, this.SetCursorDetour);
|
this.setCursorHook = Hook<SetCursorDelegate>.FromImport(
|
||||||
this.presentHook = Hook<PresentDelegate>.FromAddress(this.address.Present, this.PresentDetour);
|
null,
|
||||||
this.resizeBuffersHook = Hook<ResizeBuffersDelegate>.FromAddress(this.address.ResizeBuffers, this.ResizeBuffersDetour);
|
"user32.dll",
|
||||||
|
"SetCursor",
|
||||||
|
0,
|
||||||
|
this.SetCursorDetour);
|
||||||
|
|
||||||
|
SwapChainHelper.BusyWaitForGameDeviceSwapChain();
|
||||||
|
SwapChainHelper.DetectReShade();
|
||||||
|
|
||||||
Log.Verbose("===== S W A P C H A I N =====");
|
Log.Verbose("===== S W A P C H A I N =====");
|
||||||
Log.Verbose($"Present address 0x{this.presentHook!.Address.ToInt64():X}");
|
this.resizeBuffersHook = Hook<ResizeBuffersDelegate>.FromAddress(
|
||||||
Log.Verbose($"ResizeBuffers address 0x{this.resizeBuffersHook!.Address.ToInt64():X}");
|
(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<DxgiPresentDelegate>.FromAddress(addr, this.PresentDetour);
|
||||||
|
Log.Verbose($"ReShade::DXGISwapChain::on_present address {Util.DescribeAddress(addr)}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var addr = (nint)SwapChainHelper.ReshadeOnPresent;
|
||||||
|
this.reshadeOnPresentHook = Hook<ReshadeOnPresentDelegate>.FromAddress(addr, this.ReshadeOnPresentDetour);
|
||||||
|
Log.Verbose($"IDXGISwapChain::Present address {Util.DescribeAddress(addr)}");
|
||||||
|
}
|
||||||
|
|
||||||
this.setCursorHook.Enable();
|
this.setCursorHook.Enable();
|
||||||
this.presentHook.Enable();
|
this.dxgiPresentHook?.Enable();
|
||||||
|
this.reshadeOnPresentHook?.Enable();
|
||||||
this.resizeBuffersHook.Enable();
|
this.resizeBuffersHook.Enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
private IntPtr ResizeBuffersDetour(IntPtr swapChain, uint bufferCount, uint width, uint height, uint newFormat, uint swapChainFlags)
|
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
|
#if DEBUG
|
||||||
Log.Verbose($"Calling resizebuffers swap@{swapChain.ToInt64():X}{bufferCount} {width} {height} {newFormat} {swapChainFlags}");
|
Log.Verbose($"Calling resizebuffers swap@{swapChain.ToInt64():X}{bufferCount} {width} {height} {newFormat} {swapChainFlags}");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
this.ResizeBuffers?.InvokeSafely();
|
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();
|
this.scene?.OnPreResize();
|
||||||
|
|
||||||
var ret = this.resizeBuffersHook!.Original(swapChain, bufferCount, width, height, newFormat, swapChainFlags);
|
var ret = this.resizeBuffersHook!.Original(swapChain, bufferCount, width, height, newFormat, swapChainFlags);
|
||||||
|
|
|
||||||
193
Dalamud/Interface/Internal/SwapChainHelper.cs
Normal file
193
Dalamud/Interface/Internal/SwapChainHelper.cs
Normal file
|
|
@ -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;
|
||||||
|
|
||||||
|
/// <summary>Helper for dealing with swap chains.</summary>
|
||||||
|
internal static unsafe class SwapChainHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the function pointer for ReShade's DXGISwapChain::on_present.
|
||||||
|
/// <a href="https://github.com/crosire/reshade/blob/59eeecd0c902129a168cd772a63c46c5254ff2c5/source/dxgi/dxgi_swapchain.hpp#L88">Source.</a>
|
||||||
|
/// </summary>
|
||||||
|
public static delegate* unmanaged<nint, uint, nint, void> ReshadeOnPresent { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>Gets the game's active instance of IDXGISwapChain that is initialized.</summary>
|
||||||
|
/// <value>Address of the game's instance of IDXGISwapChain, or <c>null</c> if not available (yet.)</value>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Gets the vtable of <see cref="GameDeviceSwapChain"/>.</summary>
|
||||||
|
public static IDXGISwapChain.Vtbl<IDXGISwapChain>* GameDeviceSwapChainVtbl
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var s = GameDeviceSwapChain;
|
||||||
|
return (IDXGISwapChain.Vtbl<IDXGISwapChain>*)(s is null ? null : s->lpVtbl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="IsGameDeviceSwapChain{T}"/>
|
||||||
|
public static bool IsGameDeviceSwapChain(nint punk) => IsGameDeviceSwapChain((IUnknown*)punk);
|
||||||
|
|
||||||
|
/// <summary>Determines if the given instance of IUnknown is the game device's swap chain.</summary>
|
||||||
|
/// <param name="punk">Object to check.</param>
|
||||||
|
/// <typeparam name="T">Type of the object to check.</typeparam>
|
||||||
|
/// <returns><c>true</c> if the object is the game's swap chain.</returns>
|
||||||
|
public static bool IsGameDeviceSwapChain<T>(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<IUnknown>);
|
||||||
|
if (gdsc->QueryInterface(iid, (void**)u1.GetAddressOf()).FAILED)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
using var u2 = default(ComPtr<IUnknown>);
|
||||||
|
if (punk->QueryInterface(iid, (void**)u2.GetAddressOf()).FAILED)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return u1.Get() == u2.Get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Wait for the game to have finished initializing the IDXGISwapChain.</summary>
|
||||||
|
public static void BusyWaitForGameDeviceSwapChain()
|
||||||
|
{
|
||||||
|
while (GameDeviceSwapChain is null)
|
||||||
|
Thread.Yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Detects ReShade and populate <see cref="ReshadeOnPresent"/>.</summary>
|
||||||
|
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<nint, uint, nint, void>)reShadeDxgiPresent;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(e, "Failed to get ReShade version info");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -50,7 +50,7 @@ internal unsafe class AddonWidget : IDataWindowWidget
|
||||||
|
|
||||||
var addon = (FFXIVClientStructs.FFXIV.Component.GUI.AtkUnitBase*)address;
|
var addon = (FFXIVClientStructs.FFXIV.Component.GUI.AtkUnitBase*)address;
|
||||||
var name = addon->NameString;
|
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"))
|
if (ImGui.Button("Find Agent"))
|
||||||
{
|
{
|
||||||
|
|
@ -59,7 +59,7 @@ internal unsafe class AddonWidget : IDataWindowWidget
|
||||||
|
|
||||||
if (this.findAgentInterfacePtr != nint.Zero)
|
if (this.findAgentInterfacePtr != nint.Zero)
|
||||||
{
|
{
|
||||||
ImGui.TextUnformatted($"Agent: 0x{this.findAgentInterfacePtr.ToInt64():X}");
|
ImGui.TextUnformatted($"Agent: {Util.DescribeAddress(this.findAgentInterfacePtr)}");
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|
||||||
if (ImGui.Button("C"))
|
if (ImGui.Button("C"))
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
using Dalamud.Game;
|
using Dalamud.Game;
|
||||||
|
using Dalamud.Utility;
|
||||||
|
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
|
|
||||||
|
|
@ -57,7 +58,7 @@ internal class AddressesWidget : IDataWindowWidget
|
||||||
foreach (var valueTuple in debugScannedValue.Value)
|
foreach (var valueTuple in debugScannedValue.Value)
|
||||||
{
|
{
|
||||||
ImGui.TextUnformatted(
|
ImGui.TextUnformatted(
|
||||||
$" {valueTuple.ClassName} - 0x{valueTuple.Address.ToInt64():X}");
|
$" {valueTuple.ClassName} - {Util.DescribeAddress(valueTuple.Address)}");
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
|
|
||||||
if (ImGui.Button($"C##{valueTuple.Address.ToInt64():X}"))
|
if (ImGui.Button($"C##{valueTuple.Address.ToInt64():X}"))
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using Dalamud.Game.ClientState.Conditions;
|
using Dalamud.Game.ClientState.Conditions;
|
||||||
|
using Dalamud.Utility;
|
||||||
|
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
|
|
||||||
|
|
@ -30,7 +31,7 @@ internal class ConditionWidget : IDataWindowWidget
|
||||||
var condition = Service<Condition>.Get();
|
var condition = Service<Condition>.Get();
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
ImGui.Text($"ptr: 0x{condition.Address.ToInt64():X}");
|
ImGui.Text($"ptr: {Util.DescribeAddress(condition.Address)}");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ImGui.Text("Current Conditions:");
|
ImGui.Text("Current Conditions:");
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using Dalamud.Game.ClientState.GamePad;
|
using Dalamud.Game.ClientState.GamePad;
|
||||||
|
using Dalamud.Utility;
|
||||||
|
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
|
|
||||||
|
|
@ -29,14 +30,14 @@ internal class GamepadWidget : IDataWindowWidget
|
||||||
{
|
{
|
||||||
var gamepadState = Service<GamepadState>.Get();
|
var gamepadState = Service<GamepadState>.Get();
|
||||||
|
|
||||||
ImGui.Text($"GamepadInput 0x{gamepadState.GamepadInputAddress.ToInt64():X}");
|
ImGui.Text($"GamepadInput {Util.DescribeAddress(gamepadState.GamepadInputAddress)}");
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
if (ImGui.IsItemHovered())
|
if (ImGui.IsItemHovered())
|
||||||
ImGui.SetMouseCursor(ImGuiMouseCursor.Hand);
|
ImGui.SetMouseCursor(ImGuiMouseCursor.Hand);
|
||||||
|
|
||||||
if (ImGui.IsItemClicked())
|
if (ImGui.IsItemClicked())
|
||||||
ImGui.SetClipboardText($"0x{gamepadState.GamepadInputAddress.ToInt64():X}");
|
ImGui.SetClipboardText($"{Util.DescribeAddress(gamepadState.GamepadInputAddress)}");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
this.DrawHelper(
|
this.DrawHelper(
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using System.Text;
|
||||||
|
|
||||||
using Dalamud.Game.Text.SeStringHandling;
|
using Dalamud.Game.Text.SeStringHandling;
|
||||||
using Dalamud.Memory.Exceptions;
|
using Dalamud.Memory.Exceptions;
|
||||||
|
using Dalamud.Utility;
|
||||||
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.System.Memory;
|
using FFXIVClientStructs.FFXIV.Client.System.Memory;
|
||||||
using FFXIVClientStructs.FFXIV.Client.System.String;
|
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);
|
var result = VirtualProtect(memoryAddress, (nuint)length, newPermissions, out var oldPermissions);
|
||||||
|
|
||||||
if (!result)
|
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();
|
var last = Marshal.GetLastWin32Error();
|
||||||
if (last > 0)
|
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;
|
return oldPermissions;
|
||||||
}
|
}
|
||||||
|
|
@ -862,11 +863,11 @@ public static unsafe class MemoryHelper
|
||||||
var result = NativeFunctions.ReadProcessMemory((nint)0xFFFFFFFF, memoryAddress, value, length, out _);
|
var result = NativeFunctions.ReadProcessMemory((nint)0xFFFFFFFF, memoryAddress, value, length, out _);
|
||||||
|
|
||||||
if (!result)
|
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();
|
var last = Marshal.GetLastWin32Error();
|
||||||
if (last > 0)
|
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 _);
|
var result = NativeFunctions.WriteProcessMemory((nint)0xFFFFFFFF, memoryAddress, data, length, out _);
|
||||||
|
|
||||||
if (!result)
|
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();
|
var last = Marshal.GetLastWin32Error();
|
||||||
if (last > 0)
|
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})");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||||
using Dalamud.Game.ClientState.Objects.Types;
|
using Dalamud.Game.ClientState.Objects.Types;
|
||||||
using Dalamud.Interface.Colors;
|
using Dalamud.Interface.Colors;
|
||||||
using Dalamud.Interface.Utility;
|
using Dalamud.Interface.Utility;
|
||||||
|
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using Lumina.Excel.GeneratedSheets;
|
using Lumina.Excel.GeneratedSheets;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
@ -28,7 +27,8 @@ using Windows.Win32.Storage.FileSystem;
|
||||||
using Windows.Win32.System.Memory;
|
using Windows.Win32.System.Memory;
|
||||||
using Windows.Win32.System.Ole;
|
using Windows.Win32.System.Ole;
|
||||||
|
|
||||||
using HWND = Windows.Win32.Foundation.HWND;
|
using static TerraFX.Interop.Windows.Windows;
|
||||||
|
|
||||||
using Win32_PInvoke = Windows.Win32.PInvoke;
|
using Win32_PInvoke = Windows.Win32.PInvoke;
|
||||||
|
|
||||||
namespace Dalamud.Utility;
|
namespace Dalamud.Utility;
|
||||||
|
|
@ -38,6 +38,28 @@ namespace Dalamud.Utility;
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class Util
|
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 readonly Type GenericSpanType = typeof(Span<>);
|
||||||
private static string? gitHashInternal;
|
private static string? gitHashInternal;
|
||||||
private static int? gitCommitCountInternal;
|
private static int? gitCommitCountInternal;
|
||||||
|
|
@ -160,6 +182,100 @@ public static class Util
|
||||||
return gitHashClientStructsInternal;
|
return gitHashClientStructsInternal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="DescribeAddress(nint)"/>
|
||||||
|
public static unsafe string DescribeAddress(void* p) => DescribeAddress((nint)p);
|
||||||
|
|
||||||
|
/// <summary>Describes a memory address relative to module, or allocation base.</summary>
|
||||||
|
/// <param name="p">Address.</param>
|
||||||
|
/// <returns>Address description.</returns>
|
||||||
|
public static unsafe string DescribeAddress(nint p)
|
||||||
|
{
|
||||||
|
Span<char> 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<IMAGE_SECTION_HEADER> 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>((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();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Read memory from an offset and hexdump them via Serilog.
|
/// Read memory from an offset and hexdump them via Serilog.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -858,7 +974,7 @@ public static class Util
|
||||||
|
|
||||||
Win32_PInvoke.GlobalUnlock(hGlobal);
|
Win32_PInvoke.GlobalUnlock(hGlobal);
|
||||||
|
|
||||||
if (Win32_PInvoke.OpenClipboard(HWND.Null))
|
if (Win32_PInvoke.OpenClipboard(default))
|
||||||
{
|
{
|
||||||
Win32_PInvoke.SetClipboardData(
|
Win32_PInvoke.SetClipboardData(
|
||||||
(uint)CLIPBOARD_FORMAT.CF_HDROP,
|
(uint)CLIPBOARD_FORMAT.CF_HDROP,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue