mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-31 21:03:43 +01:00
Implement ReShade addon interface
This commit is contained in:
parent
42c728ec6c
commit
1f315be94e
7 changed files with 2140 additions and 244 deletions
75
Dalamud/Interface/Internal/InterfaceManager.AsHook.cs
Normal file
75
Dalamud/Interface/Internal/InterfaceManager.AsHook.cs
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
using Dalamud.Utility;
|
||||||
|
|
||||||
|
namespace Dalamud.Interface.Internal;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This class manages interaction with the ImGui interface.
|
||||||
|
/// </summary>
|
||||||
|
internal partial class InterfaceManager
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.dxgiPresentHook!.Original(swapChain, syncInterval, presentFlags);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.CumulativePresentCalls++;
|
||||||
|
this.IsMainThreadInPresent = true;
|
||||||
|
|
||||||
|
while (this.runBeforeImGuiRender.TryDequeue(out var action))
|
||||||
|
action.InvokeSafely();
|
||||||
|
|
||||||
|
RenderImGui(this.scene!);
|
||||||
|
this.PostImGuiRender();
|
||||||
|
this.IsMainThreadInPresent = false;
|
||||||
|
|
||||||
|
return this.dxgiPresentHook!.Original(swapChain, syncInterval, presentFlags);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IntPtr AsHookResizeBuffersDetour(
|
||||||
|
IntPtr swapChain, uint bufferCount, uint width, uint height, uint newFormat, uint swapChainFlags)
|
||||||
|
{
|
||||||
|
if (!SwapChainHelper.IsGameDeviceSwapChain(swapChain))
|
||||||
|
return this.resizeBuffersHook!.Original(swapChain, bufferCount, width, height, newFormat, swapChainFlags);
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
Log.Verbose(
|
||||||
|
$"Calling resizebuffers swap@{swapChain.ToInt64():X}{bufferCount} {width} {height} {newFormat} {swapChainFlags}");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
this.ResizeBuffers?.InvokeSafely();
|
||||||
|
|
||||||
|
this.scene?.OnPreResize();
|
||||||
|
|
||||||
|
var ret = this.resizeBuffersHook!.Original(swapChain, bufferCount, width, height, newFormat, swapChainFlags);
|
||||||
|
if (ret.ToInt64() == 0x887A0001)
|
||||||
|
{
|
||||||
|
Log.Error("invalid call to resizeBuffers");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.scene?.OnPostResize((int)width, (int)height);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
using Dalamud.Utility;
|
||||||
|
|
||||||
|
using TerraFX.Interop.DirectX;
|
||||||
|
|
||||||
|
namespace Dalamud.Interface.Internal;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This class manages interaction with the ImGui interface.
|
||||||
|
/// </summary>
|
||||||
|
internal partial class InterfaceManager
|
||||||
|
{
|
||||||
|
private unsafe void ReShadeAddonInterfaceOnDestroySwapChain(ref ReShadeAddonInterface.ApiObject swapchain)
|
||||||
|
{
|
||||||
|
var swapChain = swapchain.GetNative<IDXGISwapChain>();
|
||||||
|
if (this.scene?.SwapChain.NativePointer != (nint)swapChain)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.scene?.OnPreResize();
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe void ReShadeAddonInterfaceOnInitSwapChain(ref ReShadeAddonInterface.ApiObject swapchain)
|
||||||
|
{
|
||||||
|
var swapChain = swapchain.GetNative<IDXGISwapChain>();
|
||||||
|
if (this.scene?.SwapChain.NativePointer != (nint)swapChain)
|
||||||
|
return;
|
||||||
|
|
||||||
|
DXGI_SWAP_CHAIN_DESC desc;
|
||||||
|
if (swapChain->GetDesc(&desc).FAILED)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.scene?.OnPostResize((int)desc.BufferDesc.Width, (int)desc.BufferDesc.Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReShadeAddonInterfaceOnReShadeOverlay(ref ReShadeAddonInterface.ApiObject runtime)
|
||||||
|
{
|
||||||
|
var swapChain = runtime.GetNative();
|
||||||
|
|
||||||
|
if (this.scene == null)
|
||||||
|
this.InitScene(swapChain);
|
||||||
|
|
||||||
|
if (this.scene?.SwapChain.NativePointer != swapChain)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Debug.Assert(this.dalamudAtlas is not null, "this.dalamudAtlas is not null");
|
||||||
|
|
||||||
|
if (!this.dalamudAtlas!.HasBuiltAtlas)
|
||||||
|
{
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.CumulativePresentCalls++;
|
||||||
|
this.IsMainThreadInPresent = true;
|
||||||
|
|
||||||
|
while (this.runBeforeImGuiRender.TryDequeue(out var action))
|
||||||
|
action.InvokeSafely();
|
||||||
|
|
||||||
|
RenderImGui(this.scene!);
|
||||||
|
this.PostImGuiRender();
|
||||||
|
this.IsMainThreadInPresent = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private nint AsReShadeAddonResizeBuffersDetour(
|
||||||
|
nint swapChain,
|
||||||
|
uint bufferCount,
|
||||||
|
uint width,
|
||||||
|
uint height,
|
||||||
|
uint newFormat,
|
||||||
|
uint swapChainFlags)
|
||||||
|
{
|
||||||
|
// Hooked vtbl instead of registering ReShade event. This check is correct.
|
||||||
|
if (!SwapChainHelper.IsGameDeviceSwapChain(swapChain))
|
||||||
|
return this.resizeBuffersHook!.Original(swapChain, bufferCount, width, height, newFormat, swapChainFlags);
|
||||||
|
|
||||||
|
this.ResizeBuffers?.InvokeSafely();
|
||||||
|
return this.resizeBuffersHook!.Original(swapChain, bufferCount, width, height, newFormat, swapChainFlags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,6 +13,7 @@ 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.Hooking;
|
using Dalamud.Hooking;
|
||||||
|
using Dalamud.Hooking.Internal;
|
||||||
using Dalamud.Hooking.WndProcHook;
|
using Dalamud.Hooking.WndProcHook;
|
||||||
using Dalamud.Interface.ImGuiNotification.Internal;
|
using Dalamud.Interface.ImGuiNotification.Internal;
|
||||||
using Dalamud.Interface.Internal.ManagedAsserts;
|
using Dalamud.Interface.Internal.ManagedAsserts;
|
||||||
|
|
@ -29,10 +30,11 @@ using ImGuiNET;
|
||||||
|
|
||||||
using ImGuiScene;
|
using ImGuiScene;
|
||||||
|
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
using PInvoke;
|
using PInvoke;
|
||||||
|
|
||||||
using SharpDX;
|
using TerraFX.Interop.Windows;
|
||||||
using SharpDX.DXGI;
|
|
||||||
|
|
||||||
// general dev notes, here because it's easiest
|
// general dev notes, here because it's easiest
|
||||||
|
|
||||||
|
|
@ -52,7 +54,7 @@ namespace Dalamud.Interface.Internal;
|
||||||
/// This class manages interaction with the ImGui interface.
|
/// This class manages interaction with the ImGui interface.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ServiceManager.EarlyLoadedService]
|
[ServiceManager.EarlyLoadedService]
|
||||||
internal class InterfaceManager : IInternalDisposableService
|
internal partial class InterfaceManager : IInternalDisposableService
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The default font size, in points.
|
/// The default font size, in points.
|
||||||
|
|
@ -75,6 +77,11 @@ internal class InterfaceManager : IInternalDisposableService
|
||||||
[ServiceManager.ServiceDependency]
|
[ServiceManager.ServiceDependency]
|
||||||
private readonly Framework framework = Service<Framework>.Get();
|
private readonly Framework framework = Service<Framework>.Get();
|
||||||
|
|
||||||
|
// ReShadeAddonInterface requires hooks to be alive to unregister itself.
|
||||||
|
[ServiceManager.ServiceDependency]
|
||||||
|
[UsedImplicitly]
|
||||||
|
private readonly HookManager hookManager = Service<HookManager>.Get();
|
||||||
|
|
||||||
private readonly ConcurrentQueue<Action> runBeforeImGuiRender = new();
|
private readonly ConcurrentQueue<Action> runBeforeImGuiRender = new();
|
||||||
private readonly ConcurrentQueue<Action> runAfterImGuiRender = new();
|
private readonly ConcurrentQueue<Action> runAfterImGuiRender = new();
|
||||||
|
|
||||||
|
|
@ -82,8 +89,8 @@ internal class InterfaceManager : IInternalDisposableService
|
||||||
|
|
||||||
private Hook<SetCursorDelegate>? setCursorHook;
|
private Hook<SetCursorDelegate>? setCursorHook;
|
||||||
private Hook<DxgiPresentDelegate>? dxgiPresentHook;
|
private Hook<DxgiPresentDelegate>? dxgiPresentHook;
|
||||||
private Hook<ReshadeOnPresentDelegate>? reshadeOnPresentHook;
|
|
||||||
private Hook<ResizeBuffersDelegate>? resizeBuffersHook;
|
private Hook<ResizeBuffersDelegate>? resizeBuffersHook;
|
||||||
|
private ReShadeAddonInterface? reShadeAddonInterface;
|
||||||
|
|
||||||
private IFontAtlas? dalamudAtlas;
|
private IFontAtlas? dalamudAtlas;
|
||||||
private ILockedImFont? defaultFontResourceLock;
|
private ILockedImFont? defaultFontResourceLock;
|
||||||
|
|
@ -101,9 +108,6 @@ internal class InterfaceManager : IInternalDisposableService
|
||||||
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
|
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
|
||||||
private delegate IntPtr DxgiPresentDelegate(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);
|
||||||
|
|
||||||
|
|
@ -299,8 +303,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.dxgiPresentHook, 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();
|
||||||
|
Interlocked.Exchange(ref this.reShadeAddonInterface, null)?.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -431,11 +435,11 @@ internal class InterfaceManager : IInternalDisposableService
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var dxgiDev = this.Device.QueryInterfaceOrNull<SharpDX.DXGI.Device>();
|
var dxgiDev = this.Device.QueryInterfaceOrNull<SharpDX.DXGI.Device>();
|
||||||
var dxgiAdapter = dxgiDev?.Adapter.QueryInterfaceOrNull<Adapter4>();
|
var dxgiAdapter = dxgiDev?.Adapter.QueryInterfaceOrNull<SharpDX.DXGI.Adapter4>();
|
||||||
if (dxgiAdapter == null)
|
if (dxgiAdapter == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var memInfo = dxgiAdapter.QueryVideoMemoryInfo(0, MemorySegmentGroup.Local);
|
var memInfo = dxgiAdapter.QueryVideoMemoryInfo(0, SharpDX.DXGI.MemorySegmentGroup.Local);
|
||||||
return (memInfo.CurrentUsage, memInfo.CurrentReservation);
|
return (memInfo.CurrentUsage, memInfo.CurrentReservation);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
|
|
@ -464,11 +468,11 @@ internal class InterfaceManager : IInternalDisposableService
|
||||||
if (this.GameWindowHandle == 0)
|
if (this.GameWindowHandle == 0)
|
||||||
throw new InvalidOperationException("Game window is not yet ready.");
|
throw new InvalidOperationException("Game window is not yet ready.");
|
||||||
var value = enabled ? 1 : 0;
|
var value = enabled ? 1 : 0;
|
||||||
((Result)NativeFunctions.DwmSetWindowAttribute(
|
((HRESULT)NativeFunctions.DwmSetWindowAttribute(
|
||||||
this.GameWindowHandle,
|
this.GameWindowHandle,
|
||||||
NativeFunctions.DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE,
|
NativeFunctions.DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE,
|
||||||
ref value,
|
ref value,
|
||||||
sizeof(int))).CheckError();
|
sizeof(int))).ThrowOnError();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static InterfaceManager WhenFontsReady()
|
private static InterfaceManager WhenFontsReady()
|
||||||
|
|
@ -632,86 +636,6 @@ internal class InterfaceManager : IInternalDisposableService
|
||||||
args.SuppressWithValue(r.Value);
|
args.SuppressWithValue(r.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ReshadeOnPresentDetour(nint swapChain, uint flags, nint presentParams)
|
|
||||||
{
|
|
||||||
if (!SwapChainHelper.IsGameDeviceSwapChain(swapChain))
|
|
||||||
{
|
|
||||||
this.reshadeOnPresentHook!.Original(swapChain, flags, presentParams);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Debug.Assert(this.reshadeOnPresentHook is not null, "this.reshadeOnPresentHook is not null");
|
|
||||||
Debug.Assert(this.dalamudAtlas is not null, "this.dalamudAtlas is not null");
|
|
||||||
|
|
||||||
if (this.scene == null)
|
|
||||||
this.InitScene(swapChain);
|
|
||||||
|
|
||||||
Debug.Assert(this.scene is not null, "InitScene did not set the scene field, but did not throw an exception.");
|
|
||||||
|
|
||||||
if (!this.dalamudAtlas!.HasBuiltAtlas)
|
|
||||||
{
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.reshadeOnPresentHook!.Original(swapChain, flags, presentParams);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.CumulativePresentCalls++;
|
|
||||||
this.IsMainThreadInPresent = true;
|
|
||||||
|
|
||||||
while (this.runBeforeImGuiRender.TryDequeue(out var action))
|
|
||||||
action.InvokeSafely();
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.dxgiPresentHook!.Original(swapChain, syncInterval, presentFlags);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.CumulativePresentCalls++;
|
|
||||||
this.IsMainThreadInPresent = true;
|
|
||||||
|
|
||||||
while (this.runBeforeImGuiRender.TryDequeue(out var action))
|
|
||||||
action.InvokeSafely();
|
|
||||||
|
|
||||||
RenderImGui(this.scene!);
|
|
||||||
this.PostImGuiRender();
|
|
||||||
this.IsMainThreadInPresent = false;
|
|
||||||
|
|
||||||
return this.dxgiPresentHook!.Original(swapChain, syncInterval, presentFlags);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PostImGuiRender()
|
private void PostImGuiRender()
|
||||||
{
|
{
|
||||||
while (this.runAfterImGuiRender.TryDequeue(out var action))
|
while (this.runAfterImGuiRender.TryDequeue(out var action))
|
||||||
|
|
@ -799,10 +723,7 @@ internal class InterfaceManager : IInternalDisposableService
|
||||||
() =>
|
() =>
|
||||||
{
|
{
|
||||||
// Update the ImGui default font.
|
// Update the ImGui default font.
|
||||||
unsafe
|
ImGui.GetIO().NativePtr->FontDefault = fontLocked.ImFont;
|
||||||
{
|
|
||||||
ImGui.GetIO().NativePtr->FontDefault = fontLocked.ImFont;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the reference to the resources of the default font.
|
// Update the reference to the resources of the default font.
|
||||||
this.defaultFontResourceLock?.Dispose();
|
this.defaultFontResourceLock?.Dispose();
|
||||||
|
|
@ -818,7 +739,6 @@ internal class InterfaceManager : IInternalDisposableService
|
||||||
_ = this.dalamudAtlas.BuildFontsAsync();
|
_ = this.dalamudAtlas.BuildFontsAsync();
|
||||||
|
|
||||||
SwapChainHelper.BusyWaitForGameDeviceSwapChain();
|
SwapChainHelper.BusyWaitForGameDeviceSwapChain();
|
||||||
SwapChainHelper.DetectReShade();
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -839,52 +759,36 @@ internal class InterfaceManager : IInternalDisposableService
|
||||||
this.SetCursorDetour);
|
this.SetCursorDetour);
|
||||||
|
|
||||||
Log.Verbose("===== S W A P C H A I N =====");
|
Log.Verbose("===== S W A P C H A I N =====");
|
||||||
this.resizeBuffersHook = Hook<ResizeBuffersDelegate>.FromAddress(
|
if (ReShadeAddonInterface.TryRegisterAddon(out this.reShadeAddonInterface))
|
||||||
(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.resizeBuffersHook = Hook<ResizeBuffersDelegate>.FromAddress(
|
||||||
this.dxgiPresentHook = Hook<DxgiPresentDelegate>.FromAddress(addr, this.PresentDetour);
|
(nint)SwapChainHelper.GameDeviceSwapChainVtbl->ResizeBuffers,
|
||||||
Log.Verbose($"ReShade::DXGISwapChain::on_present address {Util.DescribeAddress(addr)}");
|
this.AsReShadeAddonResizeBuffersDetour);
|
||||||
|
Log.Verbose($"ResizeBuffers address {Util.DescribeAddress(this.resizeBuffersHook!.Address)}");
|
||||||
|
|
||||||
|
Log.Verbose(
|
||||||
|
"Registered as a ReShade({name}: 0x{addr:X}) addon.",
|
||||||
|
ReShadeAddonInterface.ReShadeModule!.FileName,
|
||||||
|
ReShadeAddonInterface.ReShadeModule!.BaseAddress);
|
||||||
|
this.reShadeAddonInterface.InitSwapChain += this.ReShadeAddonInterfaceOnInitSwapChain;
|
||||||
|
this.reShadeAddonInterface.DestroySwapChain += this.ReShadeAddonInterfaceOnDestroySwapChain;
|
||||||
|
this.reShadeAddonInterface.ReShadeOverlay += this.ReShadeAddonInterfaceOnReShadeOverlay;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var addr = (nint)SwapChainHelper.ReshadeOnPresent;
|
this.resizeBuffersHook = Hook<ResizeBuffersDelegate>.FromAddress(
|
||||||
this.reshadeOnPresentHook = Hook<ReshadeOnPresentDelegate>.FromAddress(addr, this.ReshadeOnPresentDetour);
|
(nint)SwapChainHelper.GameDeviceSwapChainVtbl->ResizeBuffers,
|
||||||
|
this.AsHookResizeBuffersDetour);
|
||||||
|
Log.Verbose($"ResizeBuffers address {Util.DescribeAddress(this.resizeBuffersHook!.Address)}");
|
||||||
|
|
||||||
|
var addr = (nint)SwapChainHelper.GameDeviceSwapChainVtbl->Present;
|
||||||
|
this.dxgiPresentHook = Hook<DxgiPresentDelegate>.FromAddress(addr, this.PresentDetour);
|
||||||
Log.Verbose($"IDXGISwapChain::Present address {Util.DescribeAddress(addr)}");
|
Log.Verbose($"IDXGISwapChain::Present address {Util.DescribeAddress(addr)}");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setCursorHook.Enable();
|
this.setCursorHook.Enable();
|
||||||
this.dxgiPresentHook?.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)
|
|
||||||
{
|
|
||||||
if (!SwapChainHelper.IsGameDeviceSwapChain(swapChain))
|
|
||||||
return this.resizeBuffersHook!.Original(swapChain, bufferCount, width, height, newFormat, swapChainFlags);
|
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
Log.Verbose($"Calling resizebuffers swap@{swapChain.ToInt64():X}{bufferCount} {width} {height} {newFormat} {swapChainFlags}");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
this.ResizeBuffers?.InvokeSafely();
|
|
||||||
|
|
||||||
this.scene?.OnPreResize();
|
|
||||||
|
|
||||||
var ret = this.resizeBuffersHook!.Original(swapChain, bufferCount, width, height, newFormat, swapChainFlags);
|
|
||||||
if (ret.ToInt64() == 0x887A0001)
|
|
||||||
{
|
|
||||||
Log.Error("invalid call to resizeBuffers");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.scene?.OnPostResize((int)width, (int)height);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IntPtr SetCursorDetour(IntPtr hCursor)
|
private IntPtr SetCursorDetour(IntPtr hCursor)
|
||||||
|
|
|
||||||
1706
Dalamud/Interface/Internal/ReShadeAddonInterface.AddonEvent.cs
Normal file
1706
Dalamud/Interface/Internal/ReShadeAddonInterface.AddonEvent.cs
Normal file
File diff suppressed because it is too large
Load diff
59
Dalamud/Interface/Internal/ReShadeAddonInterface.Exports.cs
Normal file
59
Dalamud/Interface/Internal/ReShadeAddonInterface.Exports.cs
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using TerraFX.Interop.Windows;
|
||||||
|
|
||||||
|
using static TerraFX.Interop.Windows.Windows;
|
||||||
|
|
||||||
|
namespace Dalamud.Interface.Internal;
|
||||||
|
|
||||||
|
/// <summary>ReShade interface.</summary>
|
||||||
|
[SuppressMessage(
|
||||||
|
"StyleCop.CSharp.LayoutRules",
|
||||||
|
"SA1519:Braces should not be omitted from multi-line child statement",
|
||||||
|
Justification = "Multiple fixed blocks")]
|
||||||
|
internal sealed unsafe partial class ReShadeAddonInterface
|
||||||
|
{
|
||||||
|
private static readonly ExportsStruct Exports;
|
||||||
|
|
||||||
|
static ReShadeAddonInterface()
|
||||||
|
{
|
||||||
|
foreach (var m in Process.GetCurrentProcess().Modules.Cast<ProcessModule>())
|
||||||
|
{
|
||||||
|
ExportsStruct e;
|
||||||
|
if (!GetProcAddressInto(m, nameof(e.ReShadeRegisterAddon), &e.ReShadeRegisterAddon) ||
|
||||||
|
!GetProcAddressInto(m, nameof(e.ReShadeUnregisterAddon), &e.ReShadeUnregisterAddon) ||
|
||||||
|
!GetProcAddressInto(m, nameof(e.ReShadeRegisterEvent), &e.ReShadeRegisterEvent) ||
|
||||||
|
!GetProcAddressInto(m, nameof(e.ReShadeUnregisterEvent), &e.ReShadeUnregisterEvent))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ReShadeModule = m;
|
||||||
|
Exports = e;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool GetProcAddressInto(ProcessModule m, ReadOnlySpan<char> name, void* res)
|
||||||
|
{
|
||||||
|
Span<byte> name8 = stackalloc byte[Encoding.UTF8.GetByteCount(name) + 1];
|
||||||
|
name8[Encoding.UTF8.GetBytes(name, name8)] = 0;
|
||||||
|
*(nint*)res = GetProcAddress((HMODULE)m.BaseAddress, (sbyte*)Unsafe.AsPointer(ref name8[0]));
|
||||||
|
return *(nint*)res != 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Gets the active ReShade module.</summary>
|
||||||
|
public static ProcessModule? ReShadeModule { get; private set; }
|
||||||
|
|
||||||
|
private struct ExportsStruct
|
||||||
|
{
|
||||||
|
public delegate* unmanaged<HMODULE, uint, bool> ReShadeRegisterAddon;
|
||||||
|
public delegate* unmanaged<HMODULE, void> ReShadeUnregisterAddon;
|
||||||
|
public delegate* unmanaged<AddonEvent, void*, void> ReShadeRegisterEvent;
|
||||||
|
public delegate* unmanaged<AddonEvent, void*, void> ReShadeUnregisterEvent;
|
||||||
|
}
|
||||||
|
}
|
||||||
176
Dalamud/Interface/Internal/ReShadeAddonInterface.cs
Normal file
176
Dalamud/Interface/Internal/ReShadeAddonInterface.cs
Normal file
|
|
@ -0,0 +1,176 @@
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
using Dalamud.Hooking;
|
||||||
|
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
|
using TerraFX.Interop.Windows;
|
||||||
|
|
||||||
|
using static TerraFX.Interop.Windows.Windows;
|
||||||
|
|
||||||
|
namespace Dalamud.Interface.Internal;
|
||||||
|
|
||||||
|
/// <summary>ReShade interface.</summary>
|
||||||
|
internal sealed unsafe partial class ReShadeAddonInterface : IDisposable
|
||||||
|
{
|
||||||
|
private const int ReShadeApiVersion = 12;
|
||||||
|
|
||||||
|
private readonly HMODULE hDalamudModule;
|
||||||
|
|
||||||
|
private readonly Hook<GetModuleHandleExWDelegate> addonModuleResolverHook;
|
||||||
|
|
||||||
|
private readonly DelegateStorage<ReShadeOverlayDelegate> reShadeOverlayDelegate;
|
||||||
|
private readonly DelegateStorage<ReShadeInitSwapChain> initSwapChainDelegate;
|
||||||
|
private readonly DelegateStorage<ReShadeDestroySwapChain> destroySwapChainDelegate;
|
||||||
|
|
||||||
|
private ReShadeAddonInterface()
|
||||||
|
{
|
||||||
|
this.hDalamudModule = (HMODULE)Marshal.GetHINSTANCE(typeof(ReShadeAddonInterface).Assembly.ManifestModule);
|
||||||
|
if (!Exports.ReShadeRegisterAddon(this.hDalamudModule, ReShadeApiVersion))
|
||||||
|
throw new InvalidOperationException("ReShadeRegisterAddon failure.");
|
||||||
|
|
||||||
|
this.addonModuleResolverHook = Hook<GetModuleHandleExWDelegate>.FromImport(
|
||||||
|
ReShadeModule!,
|
||||||
|
"kernel32.dll",
|
||||||
|
nameof(GetModuleHandleExW),
|
||||||
|
0,
|
||||||
|
this.GetModuleHandleExWDetour);
|
||||||
|
|
||||||
|
this.addonModuleResolverHook.Enable();
|
||||||
|
Exports.ReShadeRegisterEvent(
|
||||||
|
AddonEvent.ReShadeOverlay,
|
||||||
|
this.reShadeOverlayDelegate = new((ref ApiObject rt) => this.ReShadeOverlay?.Invoke(ref rt)));
|
||||||
|
Exports.ReShadeRegisterEvent(
|
||||||
|
AddonEvent.InitSwapChain,
|
||||||
|
this.initSwapChainDelegate = new((ref ApiObject rt) => this.InitSwapChain?.Invoke(ref rt)));
|
||||||
|
Exports.ReShadeRegisterEvent(
|
||||||
|
AddonEvent.DestroySwapChain,
|
||||||
|
this.destroySwapChainDelegate = new((ref ApiObject rt) => this.DestroySwapChain?.Invoke(ref rt)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Finalizes an instance of the <see cref="ReShadeAddonInterface"/> class.</summary>
|
||||||
|
~ReShadeAddonInterface() => this.ReleaseUnmanagedResources();
|
||||||
|
|
||||||
|
/// <summary>Delegate for <see cref="AddonEvent.ReShadeOverlay"/>.</summary>
|
||||||
|
/// <param name="effectRuntime">Reference to the ReShade runtime.</param>
|
||||||
|
public delegate void ReShadeOverlayDelegate(ref ApiObject effectRuntime);
|
||||||
|
|
||||||
|
/// <summary>Delegate for <see cref="AddonEvent.InitSwapChain"/>.</summary>
|
||||||
|
/// <param name="swapChain">Reference to the ReShade SwapChain wrapper.</param>
|
||||||
|
public delegate void ReShadeInitSwapChain(ref ApiObject swapChain);
|
||||||
|
|
||||||
|
/// <summary>Delegate for <see cref="AddonEvent.DestroySwapChain"/>.</summary>
|
||||||
|
/// <param name="swapChain">Reference to the ReShade SwapChain wrapper.</param>
|
||||||
|
public delegate void ReShadeDestroySwapChain(ref ApiObject swapChain);
|
||||||
|
|
||||||
|
private delegate BOOL GetModuleHandleExWDelegate(uint dwFlags, ushort* lpModuleName, HMODULE* phModule);
|
||||||
|
|
||||||
|
/// <summary>Called on <see cref="AddonEvent.ReShadeOverlay"/>.</summary>
|
||||||
|
public event ReShadeOverlayDelegate? ReShadeOverlay;
|
||||||
|
|
||||||
|
/// <summary>Called on <see cref="AddonEvent.InitSwapChain"/>.</summary>
|
||||||
|
public event ReShadeInitSwapChain? InitSwapChain;
|
||||||
|
|
||||||
|
/// <summary>Called on <see cref="AddonEvent.DestroySwapChain"/>.</summary>
|
||||||
|
public event ReShadeDestroySwapChain? DestroySwapChain;
|
||||||
|
|
||||||
|
/// <summary>Registers Dalamud as a ReShade addon.</summary>
|
||||||
|
/// <param name="r">Initialized interface.</param>
|
||||||
|
/// <returns><c>true</c> on success.</returns>
|
||||||
|
public static bool TryRegisterAddon([NotNullWhen(true)] out ReShadeAddonInterface? r)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
r = Exports.ReShadeRegisterAddon is null ? null : new();
|
||||||
|
return r is not null;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
r = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
this.ReleaseUnmanagedResources();
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReleaseUnmanagedResources()
|
||||||
|
{
|
||||||
|
Exports.ReShadeUnregisterEvent(AddonEvent.InitSwapChain, this.initSwapChainDelegate);
|
||||||
|
Exports.ReShadeUnregisterEvent(AddonEvent.DestroySwapChain, this.destroySwapChainDelegate);
|
||||||
|
Exports.ReShadeUnregisterEvent(AddonEvent.ReShadeOverlay, this.reShadeOverlayDelegate);
|
||||||
|
Exports.ReShadeUnregisterAddon(this.hDalamudModule);
|
||||||
|
this.addonModuleResolverHook.Disable();
|
||||||
|
this.addonModuleResolverHook.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private BOOL GetModuleHandleExWDetour(uint dwFlags, ushort* lpModuleName, HMODULE* phModule)
|
||||||
|
{
|
||||||
|
if ((dwFlags & GET.GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS) == 0)
|
||||||
|
return this.addonModuleResolverHook.Original(dwFlags, lpModuleName, phModule);
|
||||||
|
if ((dwFlags & GET.GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT) == 0)
|
||||||
|
return this.addonModuleResolverHook.Original(dwFlags, lpModuleName, phModule);
|
||||||
|
if (lpModuleName == this.initSwapChainDelegate ||
|
||||||
|
lpModuleName == this.destroySwapChainDelegate ||
|
||||||
|
lpModuleName == this.reShadeOverlayDelegate)
|
||||||
|
{
|
||||||
|
*phModule = this.hDalamudModule;
|
||||||
|
return BOOL.TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.addonModuleResolverHook.Original(dwFlags, lpModuleName, phModule);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>ReShade effect runtime object.</summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct ApiObject
|
||||||
|
{
|
||||||
|
/// <summary>The vtable.</summary>
|
||||||
|
public VTable* Vtbl;
|
||||||
|
|
||||||
|
/// <summary>Gets this object as a typed pointer.</summary>
|
||||||
|
/// <returns>Address of this instance.</returns>
|
||||||
|
/// <remarks>This call is invalid if this object is not already fixed.</remarks>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public ApiObject* AsPointer() => (ApiObject*)Unsafe.AsPointer(ref this);
|
||||||
|
|
||||||
|
/// <summary>Gets the native object.</summary>
|
||||||
|
/// <returns>The native object.</returns>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public nint GetNative() => this.Vtbl->GetNative(this.AsPointer());
|
||||||
|
|
||||||
|
/// <inheritdoc cref="GetNative"/>
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public T* GetNative<T>() where T : unmanaged => (T*)this.GetNative();
|
||||||
|
|
||||||
|
/// <summary>VTable of <see cref="ApiObject"/>.</summary>
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct VTable
|
||||||
|
{
|
||||||
|
/// <inheritdoc cref="ApiObject.GetNative"/>
|
||||||
|
public delegate* unmanaged<ApiObject*, nint> GetNative;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly struct DelegateStorage<T> where T : Delegate
|
||||||
|
{
|
||||||
|
[UsedImplicitly]
|
||||||
|
public readonly T Delegate;
|
||||||
|
|
||||||
|
public readonly void* Address;
|
||||||
|
|
||||||
|
public DelegateStorage(T @delegate)
|
||||||
|
{
|
||||||
|
this.Delegate = @delegate;
|
||||||
|
this.Address = (void*)Marshal.GetFunctionPointerForDelegate(@delegate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator void*(DelegateStorage<T> sto) => sto.Address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,13 +1,7 @@
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
using Dalamud.Game;
|
|
||||||
using Dalamud.Utility;
|
|
||||||
|
|
||||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
|
||||||
|
|
||||||
using Serilog;
|
|
||||||
|
|
||||||
using TerraFX.Interop.DirectX;
|
using TerraFX.Interop.DirectX;
|
||||||
using TerraFX.Interop.Windows;
|
using TerraFX.Interop.Windows;
|
||||||
|
|
||||||
|
|
@ -16,12 +10,6 @@ namespace Dalamud.Interface.Internal;
|
||||||
/// <summary>Helper for dealing with swap chains.</summary>
|
/// <summary>Helper for dealing with swap chains.</summary>
|
||||||
internal static unsafe class SwapChainHelper
|
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>
|
/// <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>
|
/// <value>Address of the game's instance of IDXGISwapChain, or <c>null</c> if not available (yet.)</value>
|
||||||
public static IDXGISwapChain* GameDeviceSwapChain
|
public static IDXGISwapChain* GameDeviceSwapChain
|
||||||
|
|
@ -92,102 +80,4 @@ internal static unsafe class SwapChainHelper
|
||||||
while (GameDeviceSwapChain is null)
|
while (GameDeviceSwapChain is null)
|
||||||
Thread.Yield();
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue