diff --git a/Dalamud/Game/Internal/DXGI/DXHookD3D11.cs b/Dalamud/Game/Internal/DXGI/DXHookD3D11.cs deleted file mode 100644 index 92739ae6e..000000000 --- a/Dalamud/Game/Internal/DXGI/DXHookD3D11.cs +++ /dev/null @@ -1,89 +0,0 @@ -using SharpDX.Direct3D; -using SharpDX.Direct3D11; -using SharpDX.DXGI; -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; - -namespace Dalamud.Game.Internal.DXGI -{ - public class DXHookD3D11 - { - const int DXGI_SWAPCHAIN_METHOD_COUNT = 18; - const int D3D11_DEVICE_METHOD_COUNT = 43; - - public static SharpDX.DXGI.SwapChainDescription CreateSwapChainDescription(IntPtr renderForm) - { - return new SharpDX.DXGI.SwapChainDescription - { - BufferCount = 1, - Flags = SharpDX.DXGI.SwapChainFlags.None, - IsWindowed = true, - ModeDescription = new SharpDX.DXGI.ModeDescription(100, 100, new Rational(60, 1), SharpDX.DXGI.Format.R8G8B8A8_UNorm), - OutputHandle = renderForm, - SampleDescription = new SharpDX.DXGI.SampleDescription(1, 0), - SwapEffect = SharpDX.DXGI.SwapEffect.Discard, - Usage = SharpDX.DXGI.Usage.RenderTargetOutput - }; - } - - protected IntPtr[] GetVTblAddresses(IntPtr pointer, int numberOfMethods) - { - return GetVTblAddresses(pointer, 0, numberOfMethods); - } - - protected IntPtr[] GetVTblAddresses(IntPtr pointer, int startIndex, int numberOfMethods) - { - List vtblAddresses = new List(); - IntPtr vTable = Marshal.ReadIntPtr(pointer); - for (int 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.ToArray(); - } - - List _d3d11VTblAddresses = null; - List _dxgiSwapChainVTblAddresses = null; - - #region Internal device resources - SharpDX.Direct3D11.Device _device; - SharpDX.DXGI.SwapChain _swapChain; - SharpDX.Windows.RenderForm _renderForm; - #endregion - - #region Main device resources - public SharpDX.Windows.RenderForm RenderForm { get => _renderForm; set => _renderForm = value; } - #endregion - - public IntPtr Hook() - { - - if (_d3d11VTblAddresses == null) - { - _d3d11VTblAddresses = new List(); - _dxgiSwapChainVTblAddresses = new List(); - - #region Get Device and SwapChain method addresses - // Create temporary device + swapchain and determine method addresses - RenderForm = new SharpDX.Windows.RenderForm(); - SharpDX.Direct3D11.Device.CreateWithSwapChain( - DriverType.Hardware, - DeviceCreationFlags.BgraSupport, - CreateSwapChainDescription(RenderForm.Handle), - out _device, - out _swapChain - ); - if (_device != null && _swapChain != null) - { - _d3d11VTblAddresses.AddRange(GetVTblAddresses(_device.NativePointer, D3D11_DEVICE_METHOD_COUNT)); - _dxgiSwapChainVTblAddresses.AddRange(GetVTblAddresses(_swapChain.NativePointer, DXGI_SWAPCHAIN_METHOD_COUNT)); - } - _device.Dispose(); - _swapChain.Dispose(); - #endregion - } - - return _dxgiSwapChainVTblAddresses[8]; - } - } -} diff --git a/Dalamud/Game/Internal/DXGI/ISwapChainAddressResolver.cs b/Dalamud/Game/Internal/DXGI/ISwapChainAddressResolver.cs new file mode 100644 index 000000000..ad2e003f6 --- /dev/null +++ b/Dalamud/Game/Internal/DXGI/ISwapChainAddressResolver.cs @@ -0,0 +1,7 @@ +using System; + +namespace Dalamud.Game.Internal.DXGI { + public interface ISwapChainAddressResolver { + IntPtr Present { get; set; } + } +} diff --git a/Dalamud/Game/Internal/DXGI/SwapChainAddressResolver.cs b/Dalamud/Game/Internal/DXGI/SwapChainSigResolver.cs similarity index 58% rename from Dalamud/Game/Internal/DXGI/SwapChainAddressResolver.cs rename to Dalamud/Game/Internal/DXGI/SwapChainSigResolver.cs index 7f54490e1..37c28197e 100644 --- a/Dalamud/Game/Internal/DXGI/SwapChainAddressResolver.cs +++ b/Dalamud/Game/Internal/DXGI/SwapChainSigResolver.cs @@ -1,13 +1,16 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Text; +using System.Threading.Tasks; using Serilog; namespace Dalamud.Game.Internal.DXGI { - public sealed class SwapChainAddressResolver : BaseAddressResolver + public sealed class SwapChainSigResolver : BaseAddressResolver, ISwapChainAddressResolver { - public IntPtr Present { get; private set; } + public IntPtr Present { get; set; } //public IntPtr ResizeBuffers { get; private set; } protected override void Setup64Bit(SigScanner sig) @@ -17,7 +20,11 @@ namespace Dalamud.Game.Internal.DXGI Log.Debug($"Found DXGI: {module.BaseAddress.ToInt64():X}"); var scanner = new SigScanner(module); - Present = scanner.ScanModule("48 89 5C 24 ?? 48 89 74 24 ?? 55 57 41 56 48 8D 6C 24 ??"); + + // This(code after the function head - offset of it) was picked to avoid running into issues with other hooks being installed into this function. + Present = scanner.ScanModule("41 8B F0 8B FA 89 54 24 ?? 48 8B D9 48 89 4D ?? C6 44 24 ?? 00") - 0x37; + + // seems unnecessary for now, but we may need to handle it //ResizeBuffers = scanner.ScanModule("48 8B C4 55 41 54 41 55 41 56 41 57 48 8D 68 ?? 48 81 EC C0 00 00 00"); } diff --git a/Dalamud/Game/Internal/DXGI/SwapChainVtableResolver.cs b/Dalamud/Game/Internal/DXGI/SwapChainVtableResolver.cs new file mode 100644 index 000000000..a9981b8ed --- /dev/null +++ b/Dalamud/Game/Internal/DXGI/SwapChainVtableResolver.cs @@ -0,0 +1,98 @@ +using SharpDX.Direct3D; +using SharpDX.Direct3D11; +using SharpDX.DXGI; +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using SharpDX.Windows; +using Device = SharpDX.Direct3D11.Device; + +namespace Dalamud.Game.Internal.DXGI +{ + /* + * This method of getting the SwapChain Addresses is currently not used. + * If the normal AddressResolver(SigScanner) fails, we should use it as a fallback.(Linux?) + */ + public class SwapChainVtableResolver : BaseAddressResolver, ISwapChainAddressResolver + { + private const int DxgiSwapchainMethodCount = 18; + private const int D3D11DeviceMethodCount = 43; + + private static SwapChainDescription CreateSwapChainDescription(IntPtr renderForm) { + return new SwapChainDescription { + BufferCount = 1, + Flags = SwapChainFlags.None, + IsWindowed = true, + ModeDescription = new ModeDescription(100, 100, new Rational(60, 1), Format.R8G8B8A8_UNorm), + OutputHandle = renderForm, + SampleDescription = new SampleDescription(1, 0), + SwapEffect = SwapEffect.Discard, + Usage = Usage.RenderTargetOutput + }; + } + + private IntPtr[] GetVTblAddresses(IntPtr pointer, int numberOfMethods) + { + return GetVTblAddresses(pointer, 0, numberOfMethods); + } + + private IntPtr[] GetVTblAddresses(IntPtr pointer, int startIndex, int numberOfMethods) + { + List vtblAddresses = new List(); + IntPtr vTable = Marshal.ReadIntPtr(pointer); + for (int 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.ToArray(); + } + + private List d3d11VTblAddresses = null; + private List dxgiSwapChainVTblAddresses = null; + + #region Internal device resources + + private Device device; + private SwapChain swapChain; + private RenderForm renderForm; + #endregion + + #region Addresses + + public IntPtr Present { get; set; } + + #endregion + + protected override void Setup64Bit(SigScanner sig) { + if (this.d3d11VTblAddresses == null) { + this.d3d11VTblAddresses = new List(); + this.dxgiSwapChainVTblAddresses = new List(); + + #region Get Device and SwapChain method addresses + + // Create temporary device + swapchain and determine method addresses + this.renderForm = new RenderForm(); + Device.CreateWithSwapChain( + DriverType.Hardware, + DeviceCreationFlags.BgraSupport, + CreateSwapChainDescription(this.renderForm.Handle), + out this.device, + out this.swapChain + ); + + if (this.device != null && this.swapChain != null) { + this.d3d11VTblAddresses.AddRange( + GetVTblAddresses(this.device.NativePointer, D3D11DeviceMethodCount)); + this.dxgiSwapChainVTblAddresses.AddRange( + GetVTblAddresses(this.swapChain.NativePointer, DxgiSwapchainMethodCount)); + } + + this.device?.Dispose(); + this.swapChain?.Dispose(); + + #endregion + } + + Present = this.dxgiSwapChainVTblAddresses[8]; + } + } +} diff --git a/Dalamud/Interface/InterfaceManager.cs b/Dalamud/Interface/InterfaceManager.cs index a5d585fcd..7bf76fc68 100644 --- a/Dalamud/Interface/InterfaceManager.cs +++ b/Dalamud/Interface/InterfaceManager.cs @@ -1,6 +1,8 @@ using System; +using System.Diagnostics; using System.Runtime.InteropServices; using Dalamud.Game; +using Dalamud.Game.Internal; using Dalamud.Game.Internal.DXGI; using Dalamud.Hooking; using ImGuiNET; @@ -27,7 +29,7 @@ namespace Dalamud.Interface private readonly Hook presentHook; - private DXHookD3D11 dXHookD3D11 = new DXHookD3D11(); + private ISwapChainAddressResolver Address { get; } private RawDX11Scene scene; @@ -38,13 +40,30 @@ namespace Dalamud.Interface public InterfaceManager(SigScanner scanner) { + try { + var sigResolver = new SwapChainSigResolver(); + sigResolver.Setup(scanner); + + Log.Verbose("Found SwapChain via signatures."); + + Address = sigResolver; + } catch (Exception ex) { + // The SigScanner method fails on wine/proton since DXGI is not a real DLL. We fall back to vtable to detect our Present function address. + Log.Error(ex, "Could not get SwapChain address via sig method, falling back to vtable..."); + + var vtableResolver = new SwapChainVtableResolver(); + vtableResolver.Setup(scanner); + + Log.Verbose("Found SwapChain via vtable."); + + Address = vtableResolver; + } - IntPtr addr = dXHookD3D11.Hook(); Log.Verbose("===== S W A P C H A I N ====="); - Log.Verbose("Present address {Present}", addr); + Log.Verbose("Present address {Present}", Address.Present); this.presentHook = - new Hook(addr, + new Hook(Address.Present, new PresentDelegate(PresentDetour), this); } @@ -53,20 +72,14 @@ namespace Dalamud.Interface { this.presentHook.Enable(); - if (this.scene != null) - { - this.scene.Enable(); - } + this.scene?.Enable(); } - public void Disable() + private void Disable() { this.presentHook.Disable(); - if (this.scene != null) - { - this.scene.Disable(); - } + this.scene?.Disable(); } public void Dispose()