mirror of
https://github.com/goatcorp/Dalamud.git
synced 2026-02-23 16:27:44 +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
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue