diff --git a/Daybreak.API/Interop/GWFastCall.cs b/Daybreak.API/Interop/GWFastCall.cs index bb688ade..8aa37278 100644 --- a/Daybreak.API/Interop/GWFastCall.cs +++ b/Daybreak.API/Interop/GWFastCall.cs @@ -11,37 +11,109 @@ public abstract class GWFastCall(GWAddressCache target) public GWAddressCache Cache { get; } = target; public abstract void Initialize(); - protected unsafe static nint BuildFastCallThunk(nuint target) + /// + /// Builds a thunk for __fastcall with 1 parameter (ECX only). + /// + protected unsafe static nint BuildFastCallThunk1(nuint target) { if (target is 0) { return 0; } - var code = stackalloc byte[13] + var code = stackalloc byte[10] { 0x58, // pop eax (ret) - 0x59, // pop ecx (ctx) - 0x5A, // pop edx (edxVal) - 0x5B, // pop ebx (wparam) - 0x53, // push ebx (wparam) + 0x59, // pop ecx (arg1) 0x50, // push eax (ret) - 0xB8,0,0,0,0, // mov eax, TARGET - 0xFF,0xE0 // jmp eax - }; // 13 bytes total + 0xB8, 0, 0, 0, 0, // mov eax, TARGET + 0xFF, 0xE0 // jmp eax + }; // 10 bytes total - const int TARGET_OFFSET = 7; + const int TARGET_OFFSET = 4; Unsafe.WriteUnaligned(ref code[TARGET_OFFSET], (uint)target); - var mem = NativeMethods.VirtualAlloc(0, 13, NativeMethods.MEM_COMMIT | NativeMethods.MEM_RESERVE, NativeMethods.PAGE_EXECUTE_READWRITE); + var mem = NativeMethods.VirtualAlloc(0, 10, NativeMethods.MEM_COMMIT | NativeMethods.MEM_RESERVE, NativeMethods.PAGE_EXECUTE_READWRITE); if (mem is 0) { throw new Win32Exception(Marshal.GetLastWin32Error(), "VirtualAlloc"); } - Buffer.MemoryCopy(code, (void*)mem, 13, 13); + Buffer.MemoryCopy(code, (void*)mem, 10, 10); return mem; } + + /// + /// Builds a thunk for __fastcall with 2+ parameters (ECX and EDX). + /// + protected unsafe static nint BuildFastCallThunk2(nuint target) + { + if (target is 0) + { + return 0; + } + + var code = stackalloc byte[11] + { + 0x58, // pop eax (ret) + 0x59, // pop ecx (ctx) + 0x5A, // pop edx (edxVal) + 0x50, // push eax (ret) + 0xB8,0,0,0,0, // mov eax, TARGET + 0xFF,0xE0 // jmp eax + }; // 11 bytes total + + const int TARGET_OFFSET = 5; + Unsafe.WriteUnaligned(ref code[TARGET_OFFSET], (uint)target); + + var mem = NativeMethods.VirtualAlloc(0, 11, NativeMethods.MEM_COMMIT | NativeMethods.MEM_RESERVE, NativeMethods.PAGE_EXECUTE_READWRITE); + if (mem is 0) + { + throw new Win32Exception(Marshal.GetLastWin32Error(), "VirtualAlloc"); + } + + Buffer.MemoryCopy(code, (void*)mem, 11, 11); + return mem; + } +} + +public unsafe sealed class GWFastCall(GWAddressCache target) : GWFastCall(target) +{ + private delegate* unmanaged[Stdcall] func = null; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe T2? Invoke(T1 t1) + { + if (this.func is null) + { + this.Initialize(); + } + + if (this.func is not null) + { + if (typeof(T2) == typeof(Void)) + { + ((delegate* unmanaged[Stdcall])this.func)(t1); + return default; + } + + return this.func(t1); + } + + return default; + } + + public override void Initialize() + { + if (this.Cache.GetAddress() is not nuint addr || + addr is 0) + { + return; + } + + var call = BuildFastCallThunk1(addr); + this.func = (delegate* unmanaged[Stdcall])call; + } } public unsafe sealed class GWFastCall(GWAddressCache target) : GWFastCall(target) @@ -78,7 +150,7 @@ public unsafe sealed class GWFastCall(GWAddressCache target) : GWFas return; } - var call = BuildFastCallThunk(addr); + var call = BuildFastCallThunk2(addr); this.func = (delegate* unmanaged[Stdcall])call; } } @@ -117,7 +189,7 @@ public unsafe sealed class GWFastCall(GWAddressCache target) : G return; } - var call = BuildFastCallThunk(addr); + var call = BuildFastCallThunk2(addr); this.func = (delegate* unmanaged[Stdcall])call; } } @@ -156,7 +228,7 @@ public unsafe sealed class GWFastCall(GWAddressCache target) return; } - var call = BuildFastCallThunk(addr); + var call = BuildFastCallThunk2(addr); this.func = (delegate* unmanaged[Stdcall])call; } } @@ -195,7 +267,7 @@ public unsafe sealed class GWFastCall(GWAddressCache tar return; } - var call = BuildFastCallThunk(addr); + var call = BuildFastCallThunk2(addr); this.func = (delegate* unmanaged[Stdcall])call; } } @@ -234,7 +306,7 @@ public unsafe sealed class GWFastCall(GWAddressCache return; } - var call = BuildFastCallThunk(addr); + var call = BuildFastCallThunk2(addr); this.func = (delegate* unmanaged[Stdcall])call; } }