diff --git a/Dalamud/Dalamud.csproj b/Dalamud/Dalamud.csproj
index 28892907e..359a77692 100644
--- a/Dalamud/Dalamud.csproj
+++ b/Dalamud/Dalamud.csproj
@@ -74,6 +74,7 @@
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/Dalamud/Game/Network/WinSockHandlers.cs b/Dalamud/Game/Network/Internal/WinSockHandlers.cs
similarity index 74%
rename from Dalamud/Game/Network/WinSockHandlers.cs
rename to Dalamud/Game/Network/Internal/WinSockHandlers.cs
index 8a20bb98f..68be91fb1 100644
--- a/Dalamud/Game/Network/WinSockHandlers.cs
+++ b/Dalamud/Game/Network/Internal/WinSockHandlers.cs
@@ -3,8 +3,9 @@ using System.Net.Sockets;
using System.Runtime.InteropServices;
using Dalamud.Hooking;
+using Dalamud.Hooking.Internal;
-namespace Dalamud.Game
+namespace Dalamud.Game.Network.Internal
{
///
/// This class enables TCP optimizations in the game socket for better performance.
@@ -18,8 +19,9 @@ namespace Dalamud.Game
///
public WinSockHandlers()
{
- this.ws2SocketHook = Hook.FromSymbol("ws2_32.dll", "socket", new SocketDelegate(this.OnSocket));
- this.ws2SocketHook.Enable();
+ this.ws2SocketHook = HookManager.DirtyLinuxUser ? null
+ : Hook.FromSymbol("ws2_32.dll", "socket", this.OnSocket);
+ this.ws2SocketHook?.Enable();
}
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
@@ -30,7 +32,7 @@ namespace Dalamud.Game
///
public void Dispose()
{
- this.ws2SocketHook.Dispose();
+ this.ws2SocketHook?.Dispose();
}
private IntPtr OnSocket(int af, int type, int protocol)
@@ -47,11 +49,11 @@ namespace Dalamud.Game
// https://linux.die.net/man/7/tcp
// https://assets.extrahop.com/whitepapers/TCP-Optimization-Guide-by-ExtraHop.pdf
var value = new IntPtr(1);
- NativeFunctions.SetSockOpt(socket, SocketOptionLevel.Tcp, SocketOptionName.NoDelay, ref value, 4);
+ _ = NativeFunctions.SetSockOpt(socket, SocketOptionLevel.Tcp, SocketOptionName.NoDelay, ref value, 4);
// Enable tcp_quickack option. This option is undocumented in MSDN but it is supported in Windows 7 and onwards.
value = new IntPtr(1);
- NativeFunctions.SetSockOpt(socket, SocketOptionLevel.Tcp, SocketOptionName.AddMembership, ref value, 4);
+ _ = NativeFunctions.SetSockOpt(socket, SocketOptionLevel.Tcp, SocketOptionName.AddMembership, ref value, 4);
}
}
diff --git a/Dalamud/Hooking/Hook.cs b/Dalamud/Hooking/Hook.cs
index 90c611844..3e06f4726 100644
--- a/Dalamud/Hooking/Hook.cs
+++ b/Dalamud/Hooking/Hook.cs
@@ -1,10 +1,7 @@
using System;
-using System.Reflection;
-using System.Runtime.CompilerServices;
-using System.Runtime.InteropServices;
-using CoreHook;
using Dalamud.Hooking.Internal;
+using Dalamud.Hooking.Internal.Implementations;
namespace Dalamud.Hooking
{
@@ -13,13 +10,9 @@ namespace Dalamud.Hooking
/// This class is basically a thin wrapper around the LocalHook type to provide helper functions.
///
/// Delegate type to represents a function prototype. This must be the same prototype as original function do.
- public sealed class Hook : IDisposable, IDalamudHook where T : Delegate
+ public sealed class Hook : IDisposable where T : Delegate
{
- private readonly IntPtr address;
-
- private readonly T original;
-
- private readonly LocalHook hookInfo;
+ private readonly IDalamudHookImpl hookImpl;
///
/// Initializes a new instance of the class.
@@ -28,11 +21,8 @@ namespace Dalamud.Hooking
/// A memory address to install a hook.
/// Callback function. Delegate must have a same original function prototype.
public Hook(IntPtr address, T detour)
+ : this(address, detour, false)
{
- this.hookInfo = LocalHook.Create(address, detour, null); // Installs a hook here
- this.address = address;
- this.original = Marshal.GetDelegateForFunctionPointer(this.hookInfo.OriginalAddress);
- HookManager.TrackedHooks.Add(new HookInfo() { Delegate = detour, Hook = this, Assembly = Assembly.GetCallingAssembly() });
}
///
@@ -41,61 +31,44 @@ namespace Dalamud.Hooking
///
/// A memory address to install a hook.
/// Callback function. Delegate must have a same original function prototype.
- /// A callback object which can be accessed within the detour.
- [Obsolete("There is no need to specify new YourDelegateType or callbackParam", true)]
- public Hook(IntPtr address, Delegate detour, object callbackParam = null)
- : this(address, detour as T)
+ /// Follow any JMPs to the actual method that needs hooking.
+ ///
+ /// The followJmp parameter is only used when ReloadedHooks are used, which currently is only for Linux users.
+ /// Generally, this is only necessary when hooking Win32 functions.
+ ///
+ public Hook(IntPtr address, T detour, bool followJmp)
{
+ this.hookImpl = HookManager.DirtyLinuxUser
+ ? new ReloadedHookImpl(address, detour, followJmp)
+ : new CoreHookImpl(address, detour);
}
///
/// Gets a memory address of the target function.
///
/// Hook is already disposed.
- public IntPtr Address
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get
- {
- this.CheckDisposed();
- return this.address;
- }
- }
+ public IntPtr Address => this.hookImpl.Address;
///
/// Gets a delegate function that can be used to call the actual function as if function is not hooked yet.
///
/// Hook is already disposed.
- public T Original
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get
- {
- this.CheckDisposed();
- return this.original;
- }
- }
+ public T Original => this.hookImpl.Original;
///
/// Gets a value indicating whether or not the hook is enabled.
///
- public bool IsEnabled
- {
- get
- {
- this.CheckDisposed();
- return this.hookInfo.ThreadACL.IsExclusive;
- }
- }
+ public bool IsEnabled => this.hookImpl.IsEnabled;
///
/// Gets a value indicating whether or not the hook has been disposed.
///
- public bool IsDisposed { get; private set; }
+ public bool IsDisposed => this.hookImpl.IsDisposed;
+
///
/// Creates a hook. Hooking address is inferred by calling to GetProcAddress() function.
- /// Hook is not activated until Enable() method is called.
+ /// The hook is not activated until Enable() method is called.
///
/// A name of the module currently loaded in the memory. (e.g. ws2_32.dll).
/// A name of the exported function name (e.g. send).
@@ -103,65 +76,39 @@ namespace Dalamud.Hooking
/// The hook with the supplied parameters.
public static Hook FromSymbol(string moduleName, string exportName, T detour)
{
- // Get a function address from the symbol name.
- var address = LocalHook.GetProcAddress(moduleName, exportName);
+ if (HookManager.DirtyLinuxUser)
+ {
+ var moduleHandle = NativeFunctions.GetModuleHandleW(moduleName);
+ if (moduleHandle == IntPtr.Zero)
+ throw new Exception($"Could not get a handle to module {moduleName}");
- return new Hook(address, detour);
+ var procAddress = NativeFunctions.GetProcAddress(moduleHandle, exportName);
+ if (procAddress == IntPtr.Zero)
+ throw new Exception($"Could not get the address of {moduleName}::{exportName}");
+
+ return new Hook(procAddress, detour, true);
+ }
+ else
+ {
+ var address = CoreHook.LocalHook.GetProcAddress(moduleName, exportName);
+ return new Hook(address, detour);
+ }
}
- ///
- /// Creates a hook. Hooking address is inferred by calling to GetProcAddress() function.
- /// Hook is not activated until Enable() method is called.
- ///
- /// A name of the module currently loaded in the memory. (e.g. ws2_32.dll).
- /// A name of the exported function name (e.g. send).
- /// Callback function. Delegate must have a same original function prototype.
- /// A callback object which can be accessed within the detour.
- /// The hook with the supplied parameters.
- [Obsolete("There is no need to specify new YourDelegateType or callbackParam", true)]
- public static Hook FromSymbol(string moduleName, string exportName, Delegate detour, object callbackParam = null) => FromSymbol(moduleName, exportName, detour as T);
-
///
/// Remove a hook from the current process.
///
- public void Dispose()
- {
- if (this.IsDisposed)
- {
- return;
- }
-
- this.IsDisposed = true;
- this.hookInfo.Dispose();
- }
+ public void Dispose() => this.hookImpl.Dispose();
///
/// Starts intercepting a call to the function.
///
- public void Enable()
- {
- this.CheckDisposed();
-
- this.hookInfo.ThreadACL.SetExclusiveACL(null);
- }
+ public void Enable() => this.hookImpl.Enable();
///
/// Stops intercepting a call to the function.
///
- public void Disable()
- {
- this.CheckDisposed();
+ public void Disable() => this.hookImpl.Disable();
- this.hookInfo.ThreadACL.SetInclusiveACL(null);
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private void CheckDisposed()
- {
- if (this.IsDisposed)
- {
- throw new ObjectDisposedException("Hook is already disposed.");
- }
- }
}
}
diff --git a/Dalamud/Hooking/Internal/HookManager.cs b/Dalamud/Hooking/Internal/HookManager.cs
index 1651c696e..29d80be47 100644
--- a/Dalamud/Hooking/Internal/HookManager.cs
+++ b/Dalamud/Hooking/Internal/HookManager.cs
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
+using Dalamud.Memory;
+
namespace Dalamud.Hooking.Internal
{
///
@@ -8,16 +10,35 @@ namespace Dalamud.Hooking.Internal
///
internal class HookManager : IDisposable
{
- // private readonly Dalamud dalamud;
+ private static readonly ModuleLog Log = new("HM");
+ private static bool checkLinuxOnce = true;
+ private static bool isRunningLinux = false;
///
/// Initializes a new instance of the class.
///
/// Dalamud instance.
- public HookManager(Dalamud dalamud)
+ internal HookManager(Dalamud dalamud)
{
_ = dalamud;
- // this.dalamud = dalamud;
+ }
+
+ ///
+ /// Gets a value indicating whether the client is running under Linux Wine.
+ ///
+ /// A value indicating whether the game is running under Wine.
+ internal static bool DirtyLinuxUser
+ {
+ get
+ {
+ if (checkLinuxOnce)
+ {
+ var value = Environment.GetEnvironmentVariable("XL_WINEONLINUX");
+ isRunningLinux = value is not null;
+ }
+
+ return isRunningLinux;
+ }
}
///
@@ -25,9 +46,43 @@ namespace Dalamud.Hooking.Internal
///
internal static List TrackedHooks { get; } = new();
+ ///
+ /// Gets a static list of original code for a hooked address.
+ ///
+ internal static List<(IntPtr Address, byte[] Original)> Originals { get; } = new();
+
///
public void Dispose()
{
+ RevertHooks();
+ TrackedHooks.Clear();
+ Originals.Clear();
+ }
+
+ private static unsafe void RevertHooks()
+ {
+ foreach (var (address, originalBytes) in Originals)
+ {
+ var i = 0;
+ var current = (byte*)address;
+ // Find how many bytes have been modified by comparing to the saved original
+ for (; i < originalBytes.Length; i++)
+ {
+ if (current[i] == originalBytes[i])
+ break;
+ }
+
+ if (i > 0)
+ {
+ Log.Debug($"Reverting hook at 0x{address.ToInt64():X}");
+ fixed (byte* original = originalBytes)
+ {
+ MemoryHelper.ChangePermission(address, i, MemoryProtection.ExecuteReadWrite, out var oldPermissions);
+ MemoryHelper.WriteRaw(address, originalBytes);
+ MemoryHelper.ChangePermission(address, i, oldPermissions);
+ }
+ }
+ }
}
}
}
diff --git a/Dalamud/Hooking/Internal/Implementations/CoreHookImpl.cs b/Dalamud/Hooking/Internal/Implementations/CoreHookImpl.cs
new file mode 100644
index 000000000..bfff3eae8
--- /dev/null
+++ b/Dalamud/Hooking/Internal/Implementations/CoreHookImpl.cs
@@ -0,0 +1,133 @@
+using System;
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+namespace Dalamud.Hooking.Internal.Implementations
+{
+ ///
+ /// Manages a hook which can be used to intercept a call to native function.
+ /// This class is basically a thin wrapper around the LocalHook type to provide helper functions.
+ ///
+ /// Delegate type to represents a function prototype. This must be the same prototype as original function do.
+ internal sealed class CoreHookImpl : IDisposable, IDalamudHookImpl where T : Delegate
+ {
+ private readonly IntPtr address;
+
+ private readonly CoreHook.LocalHook hookImpl;
+ private readonly T original;
+
+ ///
+ /// Initializes a new instance of the class.
+ /// Hook is not activated until Enable() method is called.
+ ///
+ /// A memory address to install a hook.
+ /// Callback function. Delegate must have a same original function prototype.
+ public CoreHookImpl(IntPtr address, T detour)
+ {
+ this.address = address;
+
+ this.hookImpl = CoreHook.LocalHook.Create(address, detour, null);
+ this.original = Marshal.GetDelegateForFunctionPointer(this.hookImpl.OriginalAddress);
+
+ HookManager.TrackedHooks.Add(new HookInfo(this, detour, Assembly.GetCallingAssembly()));
+ }
+
+ ///
+ /// Gets a memory address of the target function.
+ ///
+ /// Hook is already disposed.
+ public IntPtr Address
+ {
+ get
+ {
+ this.CheckDisposed();
+ return this.address;
+ }
+ }
+
+ ///
+ /// Gets a delegate function that can be used to call the actual function as if function is not hooked yet.
+ ///
+ /// Hook is already disposed.
+ public T Original
+ {
+ get
+ {
+ this.CheckDisposed();
+ return this.original;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether or not the hook is enabled.
+ ///
+ public bool IsEnabled
+ {
+ get
+ {
+ this.CheckDisposed();
+ return this.hookImpl.ThreadACL.IsExclusive;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether or not the hook has been disposed.
+ ///
+ public bool IsDisposed { get; private set; }
+
+ ///
+ /// Creates a hook. Hooking address is inferred by calling to GetProcAddress() function.
+ /// The hook is not activated until Enable() method is called.
+ ///
+ /// A name of the module currently loaded in the memory. (e.g. ws2_32.dll).
+ /// A name of the exported function name (e.g. send).
+ /// Callback function. Delegate must have a same original function prototype.
+ /// The hook with the supplied parameters.
+ public static IDalamudHookImpl FromSymbol(string moduleName, string exportName, T detour)
+ {
+ var address = CoreHook.LocalHook.GetProcAddress(moduleName, exportName);
+ return new CoreHookImpl(address, detour);
+ }
+
+ ///
+ /// Remove a hook from the current process.
+ ///
+ public void Dispose()
+ {
+ if (this.IsDisposed)
+ return;
+
+ this.IsDisposed = true;
+ this.hookImpl.Dispose();
+ }
+
+ ///
+ /// Starts intercepting a call to the function.
+ ///
+ public void Enable()
+ {
+ this.CheckDisposed();
+ this.hookImpl.ThreadACL.SetExclusiveACL(null);
+ }
+
+ ///
+ /// Stops intercepting a call to the function.
+ ///
+ public void Disable()
+ {
+ this.CheckDisposed();
+ this.hookImpl.ThreadACL.SetExclusiveACL(null);
+ }
+
+ ///
+ /// Check if this object has been disposed already.
+ ///
+ private void CheckDisposed()
+ {
+ if (this.IsDisposed)
+ {
+ throw new ObjectDisposedException("Hook is already disposed.");
+ }
+ }
+ }
+}
diff --git a/Dalamud/Hooking/Internal/Implementations/IDalamudHookImpl.cs b/Dalamud/Hooking/Internal/Implementations/IDalamudHookImpl.cs
new file mode 100644
index 000000000..6290086d2
--- /dev/null
+++ b/Dalamud/Hooking/Internal/Implementations/IDalamudHookImpl.cs
@@ -0,0 +1,28 @@
+using System;
+
+namespace Dalamud.Hooking.Internal.Implementations
+{
+ ///
+ /// Manages a hook which can be used to intercept a call to native function.
+ /// This class is basically a thin wrapper around the LocalHook type to provide helper functions.
+ ///
+ /// Delegate type to represents a function prototype. This must be the same prototype as original function do.
+ internal interface IDalamudHookImpl : IDisposable, IDalamudHook where T : Delegate
+ {
+ ///
+ /// Gets a delegate function that can be used to call the actual function as if function is not hooked yet.
+ ///
+ /// Hook is already disposed.
+ public T Original { get; }
+
+ ///
+ /// Starts intercepting a call to the function.
+ ///
+ public void Enable();
+
+ ///
+ /// Stops intercepting a call to the function.
+ ///
+ public void Disable();
+ }
+}
diff --git a/Dalamud/Hooking/Internal/Implementations/ReloadedHookImpl.cs b/Dalamud/Hooking/Internal/Implementations/ReloadedHookImpl.cs
new file mode 100644
index 000000000..b8b0bebcc
--- /dev/null
+++ b/Dalamud/Hooking/Internal/Implementations/ReloadedHookImpl.cs
@@ -0,0 +1,196 @@
+using System;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+using Dalamud.Memory;
+
+namespace Dalamud.Hooking.Internal.Implementations
+{
+ ///
+ /// Manages a hook which can be used to intercept a call to native function.
+ /// This class is basically a thin wrapper around the LocalHook type to provide helper functions.
+ ///
+ /// Delegate type to represents a function prototype. This must be the same prototype as original function do.
+ internal sealed class ReloadedHookImpl : IDalamudHookImpl where T : Delegate
+ {
+ private readonly IntPtr address;
+ private readonly Reloaded.Hooks.Definitions.IHook hookImpl;
+
+ ///
+ /// Initializes a new instance of the class.
+ /// Hook is not activated until Enable() method is called.
+ ///
+ /// A memory address to install a hook.
+ /// Callback function. Delegate must have a same original function prototype.
+ public ReloadedHookImpl(IntPtr address, T detour)
+ : this(address, detour, false)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ /// Hook is not activated until Enable() method is called.
+ ///
+ /// A memory address to install a hook.
+ /// Callback function. Delegate must have a same original function prototype.
+ /// Follow any JMPs to the actual method that needs hooking.
+ public ReloadedHookImpl(IntPtr address, T detour, bool followJmp)
+ {
+ if (followJmp)
+ {
+ // This is horrible hackery to follow various types of JMP.
+ // It likely needs to stop when entering a reloaded hook trampoline.
+ // I would much rather use Iced to check against a Instruction type.
+ while (true)
+ {
+ var b1 = Marshal.ReadByte(address);
+ if (b1 == 0xE9)
+ {
+ var jumpOffset = Marshal.ReadInt32(address + 1);
+ address += jumpOffset + 5;
+ continue;
+ }
+
+ var b2 = Marshal.ReadByte(address, 1);
+ if (b1 == 0xFF && b2 == 0x25)
+ {
+ address = Marshal.ReadIntPtr(address + 6);
+ continue;
+ }
+
+ break;
+ }
+ }
+
+ var otherHook = HookManager.Originals.FirstOrDefault(o => o.Address == address);
+ if (otherHook == default)
+ {
+ MemoryHelper.ReadRaw(address, 50, out var original);
+ HookManager.Originals.Add((address, original));
+ }
+
+ this.address = address;
+ this.hookImpl = Reloaded.Hooks.ReloadedHooks.Instance.CreateHook(detour, address.ToInt64());
+
+ HookManager.TrackedHooks.Add(new HookInfo(this, detour, Assembly.GetCallingAssembly()));
+ }
+
+ ///
+ /// Gets a memory address of the target function.
+ ///
+ /// Hook is already disposed.
+ public IntPtr Address
+ {
+ get
+ {
+ this.CheckDisposed();
+ return this.address;
+ }
+ }
+
+ ///
+ /// Gets a delegate function that can be used to call the actual function as if function is not hooked yet.
+ ///
+ /// Hook is already disposed.
+ public T Original
+ {
+ get
+ {
+ this.CheckDisposed();
+ return this.hookImpl.OriginalFunction;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether or not the hook is enabled.
+ ///
+ public bool IsEnabled
+ {
+ get
+ {
+ this.CheckDisposed();
+ return this.hookImpl.IsHookEnabled;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether or not the hook has been disposed.
+ ///
+ public bool IsDisposed { get; private set; }
+
+ ///
+ /// Creates a hook. Hooking address is inferred by calling to GetProcAddress() function.
+ /// The hook is not activated until Enable() method is called.
+ ///
+ /// A name of the module currently loaded in the memory. (e.g. ws2_32.dll).
+ /// A name of the exported function name (e.g. send).
+ /// Callback function. Delegate must have a same original function prototype.
+ /// The hook with the supplied parameters.
+ public static IDalamudHookImpl FromSymbol(string moduleName, string exportName, T detour)
+ {
+ var moduleHandle = NativeFunctions.GetModuleHandleW(moduleName);
+ if (moduleHandle == IntPtr.Zero)
+ throw new Exception($"Could not get a handle to module {moduleName}");
+
+ var procAddress = NativeFunctions.GetProcAddress(moduleHandle, exportName);
+ if (procAddress == IntPtr.Zero)
+ throw new Exception($"Could not get the address of {moduleName}::{exportName}");
+
+ return new ReloadedHookImpl(procAddress, detour, true);
+ }
+
+ ///
+ /// Remove a hook from the current process.
+ ///
+ public void Dispose()
+ {
+ if (this.IsDisposed)
+ return;
+
+ this.IsDisposed = true;
+
+ if (this.hookImpl.IsHookEnabled)
+ this.hookImpl.Disable();
+ }
+
+ ///
+ /// Starts intercepting a call to the function.
+ ///
+ public void Enable()
+ {
+ this.CheckDisposed();
+
+ if (!this.hookImpl.IsHookActivated)
+ this.hookImpl.Activate();
+
+ if (!this.hookImpl.IsHookEnabled)
+ this.hookImpl.Enable();
+ }
+
+ ///
+ /// Stops intercepting a call to the function.
+ ///
+ public void Disable()
+ {
+ this.CheckDisposed();
+
+ if (!this.hookImpl.IsHookActivated)
+ return;
+
+ if (this.hookImpl.IsHookEnabled)
+ this.hookImpl.Disable();
+ }
+
+ ///
+ /// Check if this object has been disposed already.
+ ///
+ private void CheckDisposed()
+ {
+ if (this.IsDisposed)
+ {
+ throw new ObjectDisposedException("Hook is already disposed.");
+ }
+ }
+ }
+}
diff --git a/Dalamud/Interface/Internal/InterfaceManager.cs b/Dalamud/Interface/Internal/InterfaceManager.cs
index b4c51d275..9c5fe3f6f 100644
--- a/Dalamud/Interface/Internal/InterfaceManager.cs
+++ b/Dalamud/Interface/Internal/InterfaceManager.cs
@@ -10,6 +10,7 @@ using Dalamud.Game;
using Dalamud.Game.ClientState;
using Dalamud.Game.Internal.DXGI;
using Dalamud.Hooking;
+using Dalamud.Hooking.Internal;
using ImGuiNET;
using ImGuiScene;
using Serilog;
@@ -101,17 +102,17 @@ namespace Dalamud.Interface.Internal
Log.Error(e, "RTSS Free failed");
}
- var user32 = NativeFunctions.GetModuleHandleW("user32.dll");
- var setCursorAddr = NativeFunctions.GetProcAddress(user32, "SetCursor");
+ this.setCursorHook = HookManager.DirtyLinuxUser ? null
+ : Hook.FromSymbol("user32.dll", "SetCursor", this.SetCursorDetour);
+ this.presentHook = new Hook(this.address.Present, this.PresentDetour, true);
+ this.resizeBuffersHook = new Hook(this.address.ResizeBuffers, this.ResizeBuffersDetour, true);
+
+ var setCursorAddress = this.setCursorHook?.Address ?? IntPtr.Zero;
Log.Verbose("===== S W A P C H A I N =====");
- Log.Verbose($"SetCursor address 0x{setCursorAddr.ToInt64():X}");
- Log.Verbose($"Present address 0x{this.address.Present.ToInt64():X}");
- Log.Verbose($"ResizeBuffers address 0x{this.address.ResizeBuffers.ToInt64():X}");
-
- this.setCursorHook = new Hook(setCursorAddr, this.SetCursorDetour);
- this.presentHook = new Hook(this.address.Present, this.PresentDetour);
- this.resizeBuffersHook = new Hook(this.address.ResizeBuffers, this.ResizeBuffersDetour);
+ Log.Verbose($"SetCursor address 0x{setCursorAddress.ToInt64():X}");
+ Log.Verbose($"Present address 0x{this.presentHook.Address.ToInt64():X}");
+ Log.Verbose($"ResizeBuffers address 0x{this.resizeBuffersHook.Address.ToInt64():X}");
}
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]