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:
srkizer 2024-07-19 04:28:25 +09:00 committed by GitHub
parent eed7abed12
commit 1109e64552
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 445 additions and 591 deletions

View file

@ -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

View file

@ -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,
}

View file

@ -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,
}

View file

@ -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; }
}

View file

@ -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;
}
}