Use hook.cc.* aliases and migrate remaining hook.fastcall calls

Replace all local calling convention declarations (const fc/tc/sc)
with hook.cc.fastcall/thiscall/stdcall from zhook. Migrate remaining
17 hook.fastcall() inline asm call sites to hook.call() with typed
function pointers.
This commit is contained in:
MarcelineVQ
2026-03-09 11:16:52 -07:00
parent 9c96a94dc1
commit d325c9f684
15 changed files with 217 additions and 248 deletions
+3 -5
View File
@@ -16,8 +16,6 @@ const hook = @import("zhook");
const con = @import("console.zig");
const build_options = @import("build_options");
const fc: std.builtin.CallingConvention = .{ .x86_fastcall = .{} };
const sc: std.builtin.CallingConvention = .{ .x86_stdcall = .{} };
// Build option convenience aliases
const build_opts = struct {
@@ -300,9 +298,9 @@ pub fn findEmbeddedFile(path: [*:0]const u8) ?*const FileEntry {
// DLL-embedded addon to register them in the game's addon hash table.
// =============================================================================
var setup_addons_hook: hook.Detour(fn (u32) callconv(fc) void) = .{};
var setup_addons_hook: hook.Detour(fn (u32) callconv(hook.cc.fastcall) void) = .{};
fn setupAddonsDetour(mgr_ptr: u32) callconv(fc) void {
fn setupAddonsDetour(mgr_ptr: u32) callconv(hook.cc.fastcall) void {
setup_addons_hook.callOriginal(.{mgr_ptr});
inline for (embed_modules) |mod| {
@@ -343,7 +341,7 @@ fn hideAddonFromList(name: [*:0]const u8) void {
}
fn callLoadAddonTOC(addon_name: [*:0]const u8) void {
hook.call(fn ([*:0]const u8) callconv(fc) void, 0x0051c9b0, .{addon_name});
hook.call(fn ([*:0]const u8) callconv(hook.cc.fastcall) void, 0x0051c9b0, .{addon_name});
}
// =============================================================================
+17 -22
View File
@@ -13,9 +13,6 @@ const con = @import("../console.zig");
const mod_mutex = @import("../mutex.zig");
const WINAPI = std.builtin.CallingConvention.winapi;
const sc: std.builtin.CallingConvention = .{ .x86_stdcall = .{} };
const fc: std.builtin.CallingConvention = .{ .x86_fastcall = .{} };
const tc: std.builtin.CallingConvention = .{ .x86_thiscall = .{} };
pub const module_name: [*:0]const u8 = "bigcursor";
@@ -172,21 +169,19 @@ const IconInfo = extern struct {
const CVAR_LOOKUP: usize = 0x0063DEC0;
// RegisterCVar: __fastcall(ECX=name, EDX=help, stack: unk1, default, callback, category, unk2, unk3)
const RegisterCVarFn = *const fn ([*:0]const u8, u32, u32, [*:0]const u8, u32, u32, u32, u32) callconv(fc) u32;
const RegisterCVarFn = *const fn ([*:0]const u8, u32, u32, [*:0]const u8, u32, u32, u32, u32) callconv(hook.cc.fastcall) u32;
const registerCVar: RegisterCVarFn = @ptrFromInt(0x0063DB90);
const CVAR_NAME = "cursorScale";
/// Read CVar integer value (tenths: 15 = 1.5x). Returns f32 scale.
fn readCVarScaleInit() f32 {
const cvar_ptr = hook.fastcall(u32, CVAR_LOOKUP, @intFromPtr(@as([*:0]const u8, CVAR_NAME)), @as(u32, 0));
const cvar_ptr = hook.call(fn ([*:0]const u8) callconv(hook.cc.fastcall) u32, CVAR_LOOKUP, .{CVAR_NAME});
if (cvar_ptr == 0) return g_scale_f;
// CVar struct: integer value at offset +40 bytes (10 pointer-sized fields)
const val = hook.readMem(i32, cvar_ptr + 40);
if (val >= 10 and val <= 40) {
return @as(f32, @floatFromInt(val)) / 10.0;
}
return g_scale_f;
const clamped = std.math.clamp(val, 5, 40);
return @as(f32, @floatFromInt(clamped)) / 10.0;
}
// =============================================================================
@@ -277,17 +272,17 @@ const D3DSURFACE_DESC = extern struct {
};
fn surfaceGetDesc(surface: *anyopaque, desc: *D3DSURFACE_DESC) i32 {
const f: *const fn (*anyopaque, *D3DSURFACE_DESC) callconv(sc) i32 = @ptrFromInt(vtbl(surface)[SVT_GetDesc]);
const f: *const fn (*anyopaque, *D3DSURFACE_DESC) callconv(hook.cc.stdcall) i32 = @ptrFromInt(vtbl(surface)[SVT_GetDesc]);
return f(surface, desc);
}
fn surfaceLockRect(surface: *anyopaque, locked: *D3DLOCKED_RECT, rect: ?*anyopaque, flags: u32) i32 {
const f: *const fn (*anyopaque, *D3DLOCKED_RECT, ?*anyopaque, u32) callconv(sc) i32 = @ptrFromInt(vtbl(surface)[SVT_LockRect]);
const f: *const fn (*anyopaque, *D3DLOCKED_RECT, ?*anyopaque, u32) callconv(hook.cc.stdcall) i32 = @ptrFromInt(vtbl(surface)[SVT_LockRect]);
return f(surface, locked, rect, flags);
}
fn surfaceUnlockRect(surface: *anyopaque) i32 {
const f: *const fn (*anyopaque) callconv(sc) i32 = @ptrFromInt(vtbl(surface)[SVT_UnlockRect]);
const f: *const fn (*anyopaque) callconv(hook.cc.stdcall) i32 = @ptrFromInt(vtbl(surface)[SVT_UnlockRect]);
return f(surface);
}
@@ -468,8 +463,8 @@ fn cacheClear() void {
// Hook: IDirect3DDevice9::SetCursorProperties (VT slot 10)
// =============================================================================
fn hkSetCursorProperties(device: *anyopaque, x_hotspot: u32, y_hotspot: u32, cursor_surface: *anyopaque) callconv(sc) i32 {
const origFn: *const fn (*anyopaque, u32, u32, *anyopaque) callconv(sc) i32 =
fn hkSetCursorProperties(device: *anyopaque, x_hotspot: u32, y_hotspot: u32, cursor_surface: *anyopaque) callconv(hook.cc.stdcall) i32 {
const origFn: *const fn (*anyopaque, u32, u32, *anyopaque) callconv(hook.cc.stdcall) i32 =
@ptrFromInt(orig_set_cursor_props);
if (g_scale_f <= 1.0) return origFn(device, x_hotspot, y_hotspot, cursor_surface);
@@ -532,8 +527,8 @@ fn hkSetCursorProperties(device: *anyopaque, x_hotspot: u32, y_hotspot: u32, cur
// Hook: IDirect3DDevice9::ShowCursor (VT slot 12)
// =============================================================================
fn hkShowCursor(device: *anyopaque, bShow: i32) callconv(sc) i32 {
const origFn: *const fn (*anyopaque, i32) callconv(sc) i32 =
fn hkShowCursor(device: *anyopaque, bShow: i32) callconv(hook.cc.stdcall) i32 {
const origFn: *const fn (*anyopaque, i32) callconv(hook.cc.stdcall) i32 =
@ptrFromInt(orig_show_cursor);
g_cursor_visible = bShow != 0;
@@ -557,19 +552,19 @@ fn hkShowCursor(device: *anyopaque, bShow: i32) callconv(sc) i32 {
// =============================================================================
fn luaPushNumber(L_ptr: usize, n: f64) void {
hook.call(fn (usize, f64) callconv(tc) void, 0x6F3810, .{ L_ptr, n });
hook.call(fn (usize, f64) callconv(hook.cc.thiscall) void, 0x6F3810, .{ L_ptr, n });
}
pub fn luaSetCursorScale(L: *anyopaque) callconv(.c) u32 {
const L_ptr = @intFromPtr(L);
const nargs = hook.fastcall(i32, 0x6F3070, L_ptr, @as(u32, 0)); // lua_gettop
const nargs = hook.call(fn (usize) callconv(hook.cc.fastcall) i32, 0x6F3070, .{L_ptr}); // lua_gettop
if (nargs < 1) return 0;
const val: f32 = @floatCast(hook.fastcall(f64, 0x6F3620, L_ptr, @as(i32, 1))); // lua_tonumber
if (val < 1.0 or val > 4.0) return 0;
const val: f32 = @floatCast(hook.call(fn (usize, i32) callconv(hook.cc.fastcall) f64, 0x6F3620, .{ L_ptr, 1 })); // lua_tonumber
const clamped = std.math.clamp(val, 0.5, 4.0);
if (val != g_scale_f) {
g_scale_f = val;
if (clamped != g_scale_f) {
g_scale_f = clamped;
cacheClear();
g_hcursor = null;
logFmt("[bigcursor] scale set to {d:.2}\n", .{g_scale_f});
+19 -21
View File
@@ -24,8 +24,6 @@ const hook = @import("zhook");
const con = @import("../console.zig");
const WINAPI = std.builtin.CallingConvention.winapi;
const tc: std.builtin.CallingConvention = .{ .x86_thiscall = .{} };
const fc: std.builtin.CallingConvention = .{ .x86_fastcall = .{} };
extern "kernel32" fn IsBadReadPtr(lp: ?*const anyopaque, ucb: usize) callconv(WINAPI) i32;
extern "kernel32" fn CreateMutexA(lpMutexAttributes: ?*anyopaque, bInitialOwner: i32, lpName: [*:0]const u8) callconv(WINAPI) ?*anyopaque;
@@ -94,7 +92,7 @@ const G_PARENT_FRAME_TYPE_ID: usize = 0x00cf0c10;
const CLEANUP_TARGET: usize = 0x767720;
const CleanupFn = fn (u32) callconv(tc) void;
const CleanupFn = fn (u32) callconv(hook.cc.thiscall) void;
var cleanup_hook: hook.Detour(CleanupFn) = .{};
// =============================================================================
@@ -111,7 +109,7 @@ var cleanup_hook: hook.Detour(CleanupFn) = .{};
const DESTROY_UI_TARGET: usize = 0x7645a0;
const DestroyUIFn = fn (u32, u32) callconv(tc) u32;
const DestroyUIFn = fn (u32, u32) callconv(hook.cc.thiscall) u32;
var destroy_ui_hook: hook.Detour(DestroyUIFn) = .{};
// =============================================================================
@@ -125,7 +123,7 @@ var destroy_ui_hook: hook.Detour(DestroyUIFn) = .{};
const PROCESS_UI_TARGET: usize = 0x772ec0;
const ProcessUIFn = fn (u32, u32) callconv(tc) u32;
const ProcessUIFn = fn (u32, u32) callconv(hook.cc.thiscall) u32;
var process_ui_hook: hook.Detour(ProcessUIFn) = .{};
// =============================================================================
@@ -143,7 +141,7 @@ var process_ui_hook: hook.Detour(ProcessUIFn) = .{};
const PAUSE_ANIM_TARGET: usize = 0x767ee0;
const PauseAnimFn = fn (u32, u32, u32) callconv(tc) void;
const PauseAnimFn = fn (u32, u32, u32) callconv(hook.cc.thiscall) void;
var pause_anim_hook: hook.Detour(PauseAnimFn) = .{};
// =============================================================================
@@ -162,7 +160,7 @@ var pause_anim_hook: hook.Detour(PauseAnimFn) = .{};
const SET_ANIM_TARGET: usize = 0x767c70;
const SetAnimFn = fn (u32, u32, u32, u32, u32, u32, u32) callconv(tc) void;
const SetAnimFn = fn (u32, u32, u32, u32, u32, u32, u32) callconv(hook.cc.thiscall) void;
var set_anim_hook: hook.Detour(SetAnimFn) = .{};
// =============================================================================
@@ -286,7 +284,7 @@ fn fmtStaleInfo(relativeTo: u32) struct { name: []const u8, saw_destroy: bool }
/// Detour for cleanup_linked_list_structures. Runs before the original to
/// walk the dying frame's dependency list and destroy referencing anchors.
fn cleanupDetour(frame: u32) callconv(tc) void {
fn cleanupDetour(frame: u32) callconv(hook.cc.thiscall) void {
recordDestruction(frame);
cleanupReverseDependencies(frame);
cleanup_hook.callOriginal(.{frame});
@@ -296,7 +294,7 @@ fn cleanupDetour(frame: u32) callconv(tc) void {
/// called from cleanupGraphicsResources during UI teardown/reload. The original
/// frees frames without walking the dependency list, leaving stale anchors.
/// Signature: void* __thiscall destroyUIElement(void* this, byte free_flag)
fn destroyUIDetour(frame: u32, free_flag: u32) callconv(tc) u32 {
fn destroyUIDetour(frame: u32, free_flag: u32) callconv(hook.cc.thiscall) u32 {
recordDestruction(frame);
if (IsBadReadPtr(@ptrFromInt(frame + 0x24), 4) == 0) {
recordDestruction(frame + 0x24);
@@ -307,7 +305,7 @@ fn destroyUIDetour(frame: u32, free_flag: u32) callconv(tc) u32 {
}
/// Detour for ProcessUIUpdateEvent - third destruction path, called via vtable.
fn processUIDetour(frame: u32, free_flag: u32) callconv(tc) u32 {
fn processUIDetour(frame: u32, free_flag: u32) callconv(hook.cc.thiscall) u32 {
recordDestruction(frame);
if (IsBadReadPtr(@ptrFromInt(frame + 0x24), 4) == 0) {
recordDestruction(frame + 0x24);
@@ -321,7 +319,7 @@ fn processUIDetour(frame: u32, free_flag: u32) callconv(tc) u32 {
/// This tells us whether a stale relativeTo was ever registered through the
/// normal dependency tracking system.
/// Signature: void __thiscall PauseAnimationGroup(ECX=relativeTo_frame, owner_frame, bitmask)
fn pauseAnimDetour(relativeTo_frame: u32, owner_frame: u32, bitmask: u32) callconv(tc) void {
fn pauseAnimDetour(relativeTo_frame: u32, owner_frame: u32, bitmask: u32) callconv(hook.cc.thiscall) void {
// Silently record - queried later by vtable hooks via logRegistrationStatus()
recordRegistration(relativeTo_frame, owner_frame, bitmask);
@@ -330,7 +328,7 @@ fn pauseAnimDetour(relativeTo_frame: u32, owner_frame: u32, bitmask: u32) callco
/// Detour for SetAnimationOrder - validates relativeTo param before anchor creation.
/// Float params (xOfs, yOfs) are passed as raw u32 bit patterns on the stack.
fn setAnimOrderDetour(frame: u32, point_enum: u32, relativeTo: u32, rel_point: u32, x_ofs: u32, y_ofs: u32, param_6: u32) callconv(tc) void {
fn setAnimOrderDetour(frame: u32, point_enum: u32, relativeTo: u32, rel_point: u32, x_ofs: u32, y_ofs: u32, param_6: u32) callconv(hook.cc.thiscall) void {
var fixed_relativeTo = relativeTo;
// Validate relativeTo BEFORE the original creates the anchor.
@@ -481,7 +479,7 @@ fn getLiveUIParent() u32 {
const type_id = readAligned(G_PARENT_FRAME_TYPE_ID);
if (type_id == 0) return 0;
const cframe_base = hook.fastcall(u32, GET_FRAME_FROM_LUA, @intFromPtr(@as([*:0]const u8, "UIParent")), type_id);
const cframe_base = hook.call(fn ([*:0]const u8, u32) callconv(hook.cc.fastcall) u32, GET_FRAME_FROM_LUA, .{ "UIParent", type_id });
if (cframe_base == 0) return 0;
// Validate the returned pointer
@@ -549,8 +547,8 @@ fn isRelativeToValid(relativeTo: u32) bool {
/// Hook for vtable[3] GetRelativeTo. Validates the stored pointer.
/// If stale, NULLs anchor+0x0C and returns 0 (safe "no relativeTo" path).
fn getRelativeToHook(this: u32) callconv(tc) u32 {
const orig: *const fn (u32) callconv(tc) u32 = @ptrFromInt(orig_get_relative_to);
fn getRelativeToHook(this: u32) callconv(hook.cc.thiscall) u32 {
const orig: *const fn (u32) callconv(hook.cc.thiscall) u32 = @ptrFromInt(orig_get_relative_to);
const result = orig(this);
if (result == 0) return 0;
@@ -573,14 +571,14 @@ fn getRelativeToHook(this: u32) callconv(tc) u32 {
/// Hook for vtable[1] GetWidth. Checks anchor+0x0C before calling original.
/// Returns sentinel if relativeTo is NULL or dangling.
/// Signature: f32 __thiscall GetWidth(this, u32 param) - callee cleans 1 stack arg.
fn getWidthHook(this: u32, param: u32) callconv(tc) f32 {
fn getWidthHook(this: u32, param: u32) callconv(hook.cc.thiscall) f32 {
const relativeTo: u32 = readAligned(this + 0x0C);
if (!isRelativeToValid(relativeTo)) {
if (relativeTo != 0) {
// Try to substitute live UIParent instead of NULLing
if (tryFixStaleRelativeTo(this, relativeTo)) {
// Fixed - call original with the healed pointer
const orig: *const fn (u32, u32) callconv(tc) f32 = @ptrFromInt(orig_get_width);
const orig: *const fn (u32, u32) callconv(hook.cc.thiscall) f32 = @ptrFromInt(orig_get_width);
return orig(this, param);
}
const field: *align(1) u32 = @ptrFromInt(this + 0x0C);
@@ -589,19 +587,19 @@ fn getWidthHook(this: u32, param: u32) callconv(tc) f32 {
return @as(*align(1) const f32, @ptrFromInt(SENTINEL_ADDR)).*;
}
const orig: *const fn (u32, u32) callconv(tc) f32 = @ptrFromInt(orig_get_width);
const orig: *const fn (u32, u32) callconv(hook.cc.thiscall) f32 = @ptrFromInt(orig_get_width);
return orig(this, param);
}
/// Hook for vtable[2] GetHeight. Same pattern as GetWidth.
/// Signature: f32 __thiscall GetHeight(this, u32 param) - callee cleans 1 stack arg.
fn getHeightHook(this: u32, param: u32) callconv(tc) f32 {
fn getHeightHook(this: u32, param: u32) callconv(hook.cc.thiscall) f32 {
const relativeTo: u32 = readAligned(this + 0x0C);
if (!isRelativeToValid(relativeTo)) {
if (relativeTo != 0) {
// Try to substitute live UIParent instead of NULLing
if (tryFixStaleRelativeTo(this, relativeTo)) {
const orig: *const fn (u32, u32) callconv(tc) f32 = @ptrFromInt(orig_get_height);
const orig: *const fn (u32, u32) callconv(hook.cc.thiscall) f32 = @ptrFromInt(orig_get_height);
return orig(this, param);
}
const field: *align(1) u32 = @ptrFromInt(this + 0x0C);
@@ -610,7 +608,7 @@ fn getHeightHook(this: u32, param: u32) callconv(tc) f32 {
return @as(*align(1) const f32, @ptrFromInt(SENTINEL_ADDR)).*;
}
const orig: *const fn (u32, u32) callconv(tc) f32 = @ptrFromInt(orig_get_height);
const orig: *const fn (u32, u32) callconv(hook.cc.thiscall) f32 = @ptrFromInt(orig_get_height);
return orig(this, param);
}
+8 -10
View File
@@ -55,8 +55,6 @@ const C3Vector = struct {
// Calling Conventions
// =============================================================================
const sc: std.builtin.CallingConvention = .{ .x86_stdcall = .{} };
const tc: std.builtin.CallingConvention = .{ .x86_thiscall = .{} };
// =============================================================================
// Game API
@@ -65,7 +63,7 @@ const tc: std.builtin.CallingConvention = .{ .x86_thiscall = .{} };
fn getObjectPointer(guid: u64) u32 {
const lo: u32 = @truncate(guid);
const hi: u32 = @truncate(guid >> 32);
return hook.call(fn (u32, u32) callconv(sc) u32, Offsets.FUN_GET_OBJECT_POINTER, .{ lo, hi });
return hook.call(fn (u32, u32) callconv(hook.cc.stdcall) u32, Offsets.FUN_GET_OBJECT_POINTER, .{ lo, hi });
}
fn isInWorld() bool {
@@ -106,11 +104,11 @@ fn isUnitSkinnable(unit: u32) bool {
fn setTarget(guid: u64) void {
const lo: u32 = @truncate(guid);
const hi: u32 = @truncate(guid >> 32);
hook.call(fn (u32, u32) callconv(sc) void, Offsets.FUN_SET_TARGET, .{ lo, hi });
hook.call(fn (u32, u32) callconv(hook.cc.stdcall) void, Offsets.FUN_SET_TARGET, .{ lo, hi });
}
fn rightClickInteract(pointer: u32, autoloot: i32, fun_ptr: usize) void {
hook.call(fn (u32, i32) callconv(tc) void, fun_ptr, .{ pointer, autoloot });
hook.call(fn (u32, i32) callconv(hook.cc.thiscall) void, fun_ptr, .{ pointer, autoloot });
}
// =============================================================================
@@ -134,15 +132,15 @@ fn isBlacklisted(id: u32) bool {
}
// =============================================================================
// Lua helpers (hook.fastcall to avoid broken callconv(fc) codegen)
// Lua helpers
// =============================================================================
fn luaIsNumber(L: *anyopaque, idx: i32) bool {
return hook.fastcall(u32, Offsets.LUA_ISNUMBER, @intFromPtr(L), idx) != 0;
return hook.call(fn (usize, i32) callconv(hook.cc.fastcall) u32, Offsets.LUA_ISNUMBER, .{ @intFromPtr(L), idx }) != 0;
}
fn luaToNumber(L: *anyopaque, idx: i32) f64 {
return hook.fastcall(f64, Offsets.LUA_TONUMBER, @intFromPtr(L), idx);
return hook.call(fn (usize, i32) callconv(hook.cc.fastcall) f64, Offsets.LUA_TONUMBER, .{ @intFromPtr(L), idx });
}
fn luaPrintError(L: *anyopaque, msg: [*:0]const u8) void {
@@ -364,10 +362,10 @@ pub fn lootAllCorpses(_: *anyopaque) callconv(.c) u32 {
// Per-frame hook for processing the loot queue.
// =============================================================================
const SceneEndFn = fn (u32) callconv(tc) void;
const SceneEndFn = fn (u32) callconv(hook.cc.thiscall) void;
var scene_end_hook: hook.Detour(SceneEndFn) = .{};
fn hookSceneEnd(device: u32) callconv(tc) void {
fn hookSceneEnd(device: u32) callconv(hook.cc.thiscall) void {
if (loot_active) {
processLootQueue();
}
+6 -7
View File
@@ -17,7 +17,6 @@ const con = @import("../console.zig");
const lua = @import("../lua.zig");
const o = @import("offsets.zig");
const sc = std.builtin.CallingConvention{ .x86_stdcall = .{} };
const WINAPI = std.builtin.CallingConvention.winapi;
// =============================================================================
@@ -334,12 +333,12 @@ fn configureSession(char_span: []const u8, realm_span: []const u8) void {
// HandleCharacterSelection hook - set up paths before world loading
// =============================================================================
var enter_world_hook: hook.Detour(fn () callconv(sc) void) = .{};
var enter_world_hook: hook.Detour(fn () callconv(hook.cc.stdcall) void) = .{};
/// Fires when the player clicks Enter World on the character select screen.
/// Character name and realm are available from the select screen data.
/// Sets up paths BEFORE the world loading sequence calls InitializeLogBuffer.
fn enterWorldDetour() callconv(sc) void {
fn enterWorldDetour() callconv(hook.cc.stdcall) void {
asm volatile ("" ::: .{ .esi = true, .edi = true, .ebx = true });
if (!g_paths_configured) {
@@ -373,9 +372,9 @@ fn restorePathPointers() void {
// InitializeLogBuffer hook - lazy setup + path redirect
// =============================================================================
var init_log_hook: hook.Detour(fn (u32, u32, u32) callconv(sc) u32) = .{};
var init_log_hook: hook.Detour(fn (u32, u32, u32) callconv(hook.cc.stdcall) u32) = .{};
fn initLogDetour(file_path: u32, flags: u32, handle_out: u32) callconv(sc) u32 {
fn initLogDetour(file_path: u32, flags: u32, handle_out: u32) callconv(hook.cc.stdcall) u32 {
asm volatile ("" ::: .{ .esi = true, .edi = true, .ebx = true });
const path_ptr: [*:0]const u8 = @ptrFromInt(file_path);
@@ -405,9 +404,9 @@ fn initLogDetour(file_path: u32, flags: u32, handle_out: u32) callconv(sc) u32 {
// WriteFormattedLogMessage hook - inject session markers on first write per log
// =============================================================================
var write_log_hook: hook.Detour(fn (u32, u32, u32) callconv(sc) void) = .{};
var write_log_hook: hook.Detour(fn (u32, u32, u32) callconv(hook.cc.stdcall) void) = .{};
fn writeLogDetour(handle: u32, fmt: u32, va_list: u32) callconv(sc) void {
fn writeLogDetour(handle: u32, fmt: u32, va_list: u32) callconv(hook.cc.stdcall) void {
asm volatile ("" ::: .{ .esi = true, .edi = true, .ebx = true });
if (g_session_char_len > 0) {
+26 -27
View File
@@ -1,22 +1,22 @@
//! WoW 1.12.1 Lua C API wrappers (all __fastcall, L in ECX)
const std = @import("std");
const fc: std.builtin.CallingConvention = .{ .x86_fastcall = .{} };
const hook = @import("zhook");
pub const State = *anyopaque;
pub fn getContext() State {
const f: *const fn () callconv(fc) State = @ptrFromInt(0x7040D0);
const f: *const fn () callconv(hook.cc.fastcall) State = @ptrFromInt(0x7040D0);
return f();
}
pub fn gettop(L: State) i32 {
const f: *const fn (State) callconv(fc) i32 = @ptrFromInt(0x6F3070);
const f: *const fn (State) callconv(hook.cc.fastcall) i32 = @ptrFromInt(0x6F3070);
return f(L);
}
pub fn settop(L: State, index: i32) void {
const f: *const fn (State, i32) callconv(fc) void = @ptrFromInt(0x6F3080);
const f: *const fn (State, i32) callconv(hook.cc.fastcall) void = @ptrFromInt(0x6F3080);
f(L, index);
}
@@ -25,42 +25,42 @@ pub fn pop(L: State, n: i32) void {
}
pub fn pushvalue(L: State, index: i32) void {
const f: *const fn (State, i32) callconv(fc) void = @ptrFromInt(0x6F3350);
const f: *const fn (State, i32) callconv(hook.cc.fastcall) void = @ptrFromInt(0x6F3350);
f(L, index);
}
pub fn remove(L: State, index: i32) void {
const f: *const fn (State, i32) callconv(fc) void = @ptrFromInt(0x6F30D0);
const f: *const fn (State, i32) callconv(hook.cc.fastcall) void = @ptrFromInt(0x6F30D0);
f(L, index);
}
pub fn insert(L: State, index: i32) void {
const f: *const fn (State, i32) callconv(fc) void = @ptrFromInt(0x6F31A0);
const f: *const fn (State, i32) callconv(hook.cc.fastcall) void = @ptrFromInt(0x6F31A0);
f(L, index);
}
pub fn typeOf(L: State, index: i32) i32 {
const f: *const fn (State, i32) callconv(fc) i32 = @ptrFromInt(0x6F3400);
const f: *const fn (State, i32) callconv(hook.cc.fastcall) i32 = @ptrFromInt(0x6F3400);
return f(L, index);
}
pub fn typeName(L: State, tp: i32) [*:0]const u8 {
const f: *const fn (State, i32) callconv(fc) [*:0]const u8 = @ptrFromInt(0x6F3480);
const f: *const fn (State, i32) callconv(hook.cc.fastcall) [*:0]const u8 = @ptrFromInt(0x6F3480);
return f(L, tp);
}
pub fn isnumber(L: State, index: i32) bool {
const f: *const fn (State, i32) callconv(fc) u32 = @ptrFromInt(0x6F34D0);
const f: *const fn (State, i32) callconv(hook.cc.fastcall) u32 = @ptrFromInt(0x6F34D0);
return f(L, index) != 0;
}
pub fn isstring(L: State, index: i32) bool {
const f: *const fn (State, i32) callconv(fc) u32 = @ptrFromInt(0x6F3510);
const f: *const fn (State, i32) callconv(hook.cc.fastcall) u32 = @ptrFromInt(0x6F3510);
return f(L, index) != 0;
}
pub fn pushnil(L: State) void {
const f: *const fn (State) callconv(fc) void = @ptrFromInt(0x6F37F0);
const f: *const fn (State) callconv(hook.cc.fastcall) void = @ptrFromInt(0x6F37F0);
f(L);
}
@@ -72,63 +72,62 @@ pub fn pushnumber(L: State, n: f64) void {
}
pub fn pushstring(L: State, s: [*:0]const u8) void {
const f: *const fn (State, [*:0]const u8) callconv(fc) void = @ptrFromInt(0x6F3890);
const f: *const fn (State, [*:0]const u8) callconv(hook.cc.fastcall) void = @ptrFromInt(0x6F3890);
f(L, s);
}
pub fn pushboolean(L: State, b: i32) void {
const f: *const fn (State, i32) callconv(fc) void = @ptrFromInt(0x6F39F0);
const f: *const fn (State, i32) callconv(hook.cc.fastcall) void = @ptrFromInt(0x6F39F0);
f(L, b);
}
pub fn pushcclosure(L: State, func: usize, n: i32) void {
const f: *const fn (State, usize, i32) callconv(fc) void = @ptrFromInt(0x6F3920);
const f: *const fn (State, usize, i32) callconv(hook.cc.fastcall) void = @ptrFromInt(0x6F3920);
@call(.never_tail, f, .{ L, func, n });
}
pub fn tonumber(L: State, index: i32) f64 {
const f: *const fn (State, i32) callconv(fc) f64 = @ptrFromInt(0x6F3620);
const f: *const fn (State, i32) callconv(hook.cc.fastcall) f64 = @ptrFromInt(0x6F3620);
return f(L, index);
}
pub fn tostring(L: State, index: i32) ?[*:0]const u8 {
const f: *const fn (State, i32) callconv(fc) ?[*:0]const u8 = @ptrFromInt(0x6F3690);
const f: *const fn (State, i32) callconv(hook.cc.fastcall) ?[*:0]const u8 = @ptrFromInt(0x6F3690);
return f(L, index);
}
pub fn toboolean(L: State, index: i32) i32 {
const f: *const fn (State, i32) callconv(fc) i32 = @ptrFromInt(0x6F3660);
const f: *const fn (State, i32) callconv(hook.cc.fastcall) i32 = @ptrFromInt(0x6F3660);
return f(L, index);
}
pub fn newtable(L: State) void {
const f: *const fn (State) callconv(fc) void = @ptrFromInt(0x6F3C90);
const f: *const fn (State) callconv(hook.cc.fastcall) void = @ptrFromInt(0x6F3C90);
f(L);
}
pub fn settable(L: State, index: i32) void {
const f: *const fn (State, i32) callconv(fc) void = @ptrFromInt(0x6F3E20);
const f: *const fn (State, i32) callconv(hook.cc.fastcall) void = @ptrFromInt(0x6F3E20);
f(L, index);
}
pub fn gettable(L: State, index: i32) void {
const f: *const fn (State, i32) callconv(fc) void = @ptrFromInt(0x6F3A40);
const f: *const fn (State, i32) callconv(hook.cc.fastcall) void = @ptrFromInt(0x6F3A40);
f(L, index);
}
pub fn next(L: State, index: i32) i32 {
const f: *const fn (State, i32) callconv(fc) i32 = @ptrFromInt(0x6F4450);
const f: *const fn (State, i32) callconv(hook.cc.fastcall) i32 = @ptrFromInt(0x6F4450);
return f(L, index);
}
pub fn pcall(L: State, nargs: i32, nresults: i32, errfunc: i32) i32 {
const f: *const fn (State, i32, i32, i32) callconv(fc) i32 = @ptrFromInt(0x6F41A0);
const f: *const fn (State, i32, i32, i32) callconv(hook.cc.fastcall) i32 = @ptrFromInt(0x6F41A0);
return @call(.never_tail, f, .{ L, nargs, nresults, errfunc });
}
pub fn luaError(L: State, msg: [*:0]const u8) void {
const hook = @import("zhook");
hook.call(fn (*anyopaque, [*:0]const u8) callconv(.c) void, 0x6F4940, .{ L, msg });
hook.call(fn (*anyopaque, [*:0]const u8) callconv(hook.cc.cdecl) void, 0x6F4940, .{ L, msg });
}
pub const LuaReg = extern struct {
@@ -137,11 +136,11 @@ pub const LuaReg = extern struct {
};
pub fn openlib(L: State, libname: ?[*:0]const u8, funcs: [*]const LuaReg, nup: i32) void {
const f: *const fn (State, ?[*:0]const u8, [*]const LuaReg, i32) callconv(fc) void = @ptrFromInt(0x6F4DC0);
const f: *const fn (State, ?[*:0]const u8, [*]const LuaReg, i32) callconv(hook.cc.fastcall) void = @ptrFromInt(0x6F4DC0);
@call(.never_tail, f, .{ L, libname, funcs, nup });
}
pub fn checknumber(L: State, index: i32) f64 {
const f: *const fn (State, i32) callconv(fc) f64 = @ptrFromInt(0x6F4C80);
const f: *const fn (State, i32) callconv(hook.cc.fastcall) f64 = @ptrFromInt(0x6F4C80);
return f(L, index);
}
+33 -36
View File
@@ -33,16 +33,14 @@ const bigcursor = if (build_opts.bigcursor) @import("bigcursor/bigcursor.zig") e
const dpslog = if (build_opts.dpslog) @import("dpslog/dpslog.zig") else struct {};
const WINAPI = std.builtin.CallingConvention.winapi;
const fc: std.builtin.CallingConvention = .{ .x86_fastcall = .{} };
const sc: std.builtin.CallingConvention = .{ .x86_stdcall = .{} };
// =============================================================================
// Lua Protection Bypass
// =============================================================================
var protection_hook: hook.Detour(fn () callconv(sc) void) = .{};
var protection_hook: hook.Detour(fn () callconv(hook.cc.stdcall) void) = .{};
fn luaProtectionDetour() callconv(sc) void {}
fn luaProtectionDetour() callconv(hook.cc.stdcall) void {}
// =============================================================================
// Lua C API wrappers (WoW 1.12.1 - all __fastcall, L in ECX)
@@ -55,11 +53,11 @@ pub const lua = @import("lua.zig");
// =============================================================================
fn registerFunction(name: [*:0]const u8, func_addr: usize) void {
hook.fastcall(void, 0x704120, @intFromPtr(name), func_addr);
hook.call(fn ([*:0]const u8, usize) callconv(hook.cc.fastcall) void, 0x704120, .{ name, func_addr });
}
fn allocateGameBuffer(size: u32) ?[*]u8 {
return hook.call(fn (u32, u32, u32, u32) callconv(sc) ?[*]u8, 0x6462E0, .{
return hook.call(fn (u32, u32, u32, u32) callconv(hook.cc.stdcall) ?[*]u8, 0x6462E0, .{
size, @intFromPtr(@as([*:0]const u8, "weirdutils")), 0, 0,
});
}
@@ -122,7 +120,7 @@ const findEmbeddedFile = addons.findEmbeddedFile;
// Hook: LoadFileWithTextureResourceFallback (0x648620)
// =============================================================================
const LoadFileFn = fn (u32, [*:0]const u8, *?[*]u8, ?*u32, u32, u32, u32) callconv(sc) u32;
const LoadFileFn = fn (u32, [*:0]const u8, *?[*]u8, ?*u32, u32, u32, u32) callconv(hook.cc.stdcall) u32;
var file_hook: hook.Detour(LoadFileFn) = .{};
fn loadFileDetour(
@@ -133,7 +131,7 @@ fn loadFileDetour(
extra_alloc: u32,
flags: u32,
async_ptr: u32,
) callconv(sc) u32 {
) callconv(hook.cc.stdcall) u32 {
if (findEmbeddedFile(path)) |entry| {
const data_len: u32 = @intCast(entry.data.len);
const total = data_len + extra_alloc;
@@ -165,23 +163,22 @@ fn loadFileDetour(
//
// Fake context detection: type==0, handle(+0x04)==NULL, embedded_ptr(+0x30)!=0
const OpenFileFn = fn (u32, [*:0]const u8, u32, *u32) callconv(sc) u32;
const OpenFileFn = fn (u32, [*:0]const u8, u32, *u32) callconv(hook.cc.stdcall) u32;
var open_file_hook: hook.Detour(OpenFileFn) = .{};
const GetFileSizeFn = fn (u32, ?*u32) callconv(sc) u32;
const GetFileSizeFn = fn (u32, ?*u32) callconv(hook.cc.stdcall) u32;
var get_file_size_hook: hook.Detour(GetFileSizeFn) = .{};
const ReadFileFn = fn (u32, [*]u8, u32, ?*u32, u32, u32) callconv(sc) u32;
const ReadFileFn = fn (u32, [*]u8, u32, ?*u32, u32, u32) callconv(hook.cc.stdcall) u32;
var read_file_hook: hook.Detour(ReadFileFn) = .{};
const CleanupFileFn = fn (u32) callconv(sc) void;
const CleanupFileFn = fn (u32) callconv(hook.cc.stdcall) void;
var cleanup_file_handle_hook: hook.Detour(CleanupFileFn) = .{};
const ProcessAsyncFn = fn (u32) callconv(fc) void;
const ProcessAsyncFn = fn (u32) callconv(hook.cc.fastcall) void;
var process_async_hook: hook.Detour(ProcessAsyncFn) = .{};
const tc: std.builtin.CallingConvention = .{ .x86_thiscall = .{} };
const LoadModelFn = fn (u32, u32, u32) callconv(tc) u32;
const LoadModelFn = fn (u32, u32, u32) callconv(hook.cc.thiscall) u32;
var model_load_hook: hook.Detour(LoadModelFn) = .{};
// Windows API imports for async handling
@@ -199,17 +196,17 @@ fn isFakeFileContext(ctx_addr: u32) bool {
/// Call initializeFileContext (0x647290) - __thiscall(ECX=ctx, type)
fn callInitFileContext(ctx: [*]u8, file_type: u32) void {
hook.call(fn (u32, u32) callconv(tc) void, 0x647290, .{ @intFromPtr(ctx), file_type });
hook.call(fn (u32, u32) callconv(hook.cc.thiscall) void, 0x647290, .{ @intFromPtr(ctx), file_type });
}
/// Call cleanupFileContext (0x6472d0) - __thiscall(ECX=ctx)
fn callCleanupFileContext(ctx: [*]u8) void {
hook.call(fn (u32) callconv(tc) void, 0x6472d0, .{@intFromPtr(ctx)});
hook.call(fn (u32) callconv(hook.cc.thiscall) void, 0x6472d0, .{@intFromPtr(ctx)});
}
/// Free a buffer via FreeMemory/SMemFree (0x646430) - __stdcall(ptr, src, flags)
fn freeGameBuffer(ptr: [*]u8) void {
hook.call(fn (u32, u32, u32) callconv(sc) void, 0x646430, .{
hook.call(fn (u32, u32, u32) callconv(hook.cc.stdcall) void, 0x646430, .{
@intFromPtr(ptr), @intFromPtr(@as([*:0]const u8, "weirdutils")), 0xffffffff,
});
}
@@ -221,7 +218,7 @@ fn openFileDetour(
path: [*:0]const u8,
flags: u32,
handle_out: *u32,
) callconv(sc) u32 {
) callconv(hook.cc.stdcall) u32 {
if (findEmbeddedFile(path)) |entry| {
// Allocate and zero-fill 0x60-byte file context
const ctx = allocateGameBuffer(0x60) orelse {
@@ -259,7 +256,7 @@ fn openFileDetour(
fn getFileSizeDetour(
file_ctx: u32,
high_size_out: ?*u32,
) callconv(sc) u32 {
) callconv(hook.cc.stdcall) u32 {
if (isFakeFileContext(file_ctx)) {
if (high_size_out) |h| h.* = 0;
const size = hook.readMem(u32, file_ctx + 0x34);
@@ -279,7 +276,7 @@ fn readFileDetour(
bytes_read_out: ?*u32,
async_ptr: u32,
param6: u32,
) callconv(sc) u32 {
) callconv(hook.cc.stdcall) u32 {
if (isFakeFileContext(ctx)) {
const data_ptr = hook.readMem(u32, ctx + 0x30);
const data_size = hook.readMem(u32, ctx + 0x34);
@@ -308,7 +305,7 @@ fn readFileDetour(
// --- Hook 4: processAsyncFileOperation (0x647350) ---
fn processAsyncDetour(param1: u32) callconv(fc) void {
fn processAsyncDetour(param1: u32) callconv(hook.cc.fastcall) void {
const ctx_addr = hook.readMem(u32, param1 + 0x08);
if (isFakeFileContext(ctx_addr)) {
@@ -360,7 +357,7 @@ fn processAsyncDetour(param1: u32) callconv(fc) void {
// --- Hook 6: CleanupFileHandleResources (0x648730) ---
fn cleanupFileHandleDetour(file_ctx: u32) callconv(sc) void {
fn cleanupFileHandleDetour(file_ctx: u32) callconv(hook.cc.stdcall) void {
const fake = isFakeFileContext(file_ctx);
if (fake) {
const path_ptr = hook.readMem(u32, file_ctx + 0x0C);
@@ -381,7 +378,7 @@ fn cleanupFileHandleDetour(file_ctx: u32) callconv(sc) void {
// --- Hook 5: loadModelFromFileAsync (0x71d4e0) ---
fn loadModelAsyncDetour(model: u32, file_handle: u32, should_use_callback: u32) callconv(tc) u32 {
fn loadModelAsyncDetour(model: u32, file_handle: u32, should_use_callback: u32) callconv(hook.cc.thiscall) u32 {
// file_handle IS the file context address directly (Ghidra shows pointer* but
// the assembly pushes it directly to GetFileSizeFromHandle - no dereference)
@@ -404,7 +401,7 @@ fn loadModelAsyncDetour(model: u32, file_handle: u32, should_use_callback: u32)
// Allocate buffer via setCullMode (0x71f9a0) - same as original path
// setCullMode is __fastcall(ECX=size), returns buffer pointer
const buffer_addr = hook.fastcall(u32, 0x71f9a0, data_size, 0);
const buffer_addr = hook.call(fn (u32) callconv(hook.cc.fastcall) u32, 0x71f9a0, .{data_size});
if (buffer_addr == 0) {
con.print("[file] setCullMode alloc failed\n");
return 0;
@@ -445,7 +442,7 @@ fn loadModelAsyncDetour(model: u32, file_handle: u32, should_use_callback: u32)
// Call processLoadedModelData directly - __fastcall(ECX=model)
con.fmt("[file] calling processLoadedModelData(0x{x})...\n", .{model});
const result = hook.fastcall(u32, 0x71d640, model, 0);
const result = hook.call(fn (u32) callconv(hook.cc.fastcall) u32, 0x71d640, .{model});
con.print("[file] processLoadedModelData returned\n");
con.fmt("[file] result=0x{x}\n", .{result});
@@ -473,10 +470,10 @@ fn loadModelAsyncDetour(model: u32, file_handle: u32, should_use_callback: u32)
// files are invisible to preloadFileWithFlags, so LoadAddonRecursive skips
// Bindings.xml (and SavedVariables) for our addons.
const CheckFileExistenceFn = fn (u32, u32, u32) callconv(fc) u32;
const CheckFileExistenceFn = fn (u32, u32, u32) callconv(hook.cc.fastcall) u32;
var cfe_hook: hook.Detour(CheckFileExistenceFn) = .{};
fn checkFileExistenceDetour(filename_ptr: u32, flags: u32, output_buffer_ptr: u32) callconv(fc) u32 {
fn checkFileExistenceDetour(filename_ptr: u32, flags: u32, output_buffer_ptr: u32) callconv(hook.cc.fastcall) u32 {
if (filename_ptr != 0) {
// Embedded DLL files
const path: [*:0]const u8 = @ptrFromInt(filename_ptr);
@@ -516,9 +513,9 @@ fn removeFileHooks() void {
// Hook: LoadScriptFunctions (0x490250)
// =============================================================================
var lsf_hook: hook.Detour(fn () callconv(sc) void) = .{};
var lsf_hook: hook.Detour(fn () callconv(hook.cc.stdcall) void) = .{};
fn loadScriptFunctionsDetour() callconv(sc) void {
fn loadScriptFunctionsDetour() callconv(hook.cc.stdcall) void {
lsf_hook.callOriginal(.{});
registerLuaFunctions();
}
@@ -528,9 +525,9 @@ fn loadScriptFunctionsDetour() callconv(sc) void {
// Hook: GameEngine_MainInitialize (0x46a400)
// =============================================================================
var engine_init_hook: hook.Detour(fn () callconv(sc) void) = .{};
var engine_init_hook: hook.Detour(fn () callconv(hook.cc.stdcall) void) = .{};
fn engineInitDetour() callconv(sc) void {
fn engineInitDetour() callconv(hook.cc.stdcall) void {
engine_init_hook.callOriginal(.{});
if (build_opts.screenshot) {
@@ -552,9 +549,9 @@ fn engineInitDetour() callconv(sc) void {
// Fires on real character logout/disconnect only - NOT on /reload or map change.
// =============================================================================
var logout_hook: hook.Detour(fn () callconv(sc) void) = .{};
var logout_hook: hook.Detour(fn () callconv(hook.cc.stdcall) void) = .{};
fn logoutDetour() callconv(sc) void {
fn logoutDetour() callconv(hook.cc.stdcall) void {
con.print("[weirdutils] World_HandleLogoutCleanup -- player logout\n");
// Reset per-session state - only on real logout/disconnect, not /reload.
@@ -580,7 +577,7 @@ fn logoutDetour() callconv(sc) void {
// Hook: CGGameUI_Shutdown (0x490BD0)
// =============================================================================
var shutdown_hook: hook.Detour(fn () callconv(sc) void) = .{};
var shutdown_hook: hook.Detour(fn () callconv(hook.cc.stdcall) void) = .{};
// =============================================================================
// Module lifecycle - single table drives install, shutdown, and uninstall.
@@ -615,7 +612,7 @@ const modules = [_]ModuleHooks{
if (build_opts.screenshot) .{ .name = screenshot.module_name, .remove = screenshot.removeHook, .is_active = screenshot.isActive } else .{},
};
fn shutdownDetour() callconv(sc) void {
fn shutdownDetour() callconv(hook.cc.stdcall) void {
con.print("[weirdutils] CGGameUI_Shutdown\n");
// Per-session resets and remove_on_shutdown cleanup live in logoutDetour
// (World_HandleLogoutCleanup) - fires on real logout/disconnect only, not /reload.
+13 -15
View File
@@ -114,9 +114,7 @@ const DespawningEntity = struct {
};
var despawning: [MAX_DESPAWNING]?DespawningEntity = .{null} ** MAX_DESPAWNING;
const fc = std.builtin.CallingConvention{ .x86_fastcall = .{} };
const sc = std.builtin.CallingConvention{ .x86_stdcall = .{} };
const tc = std.builtin.CallingConvention{ .x86_thiscall = .{} };
// =============================================================================
// Permission check - leader or raid officer required
@@ -125,7 +123,7 @@ const tc = std.builtin.CallingConvention{ .x86_thiscall = .{} };
/// Get local player GUID via GetPlayerGUID (0x468550).
/// __fastcall(), no params, returns u64 via EDX:EAX.
fn getPlayerGUID() u64 {
return hook.call(fn () callconv(fc) u64, o.FN_GET_PLAYER_GUID, .{});
return hook.call(fn () callconv(hook.cc.fastcall) u64, o.FN_GET_PLAYER_GUID, .{});
}
/// Look up a player name from the name cache by GUID.
@@ -133,7 +131,7 @@ fn getPlayerGUID() u64 {
fn getNameFromGUID(guid_lo: u32, guid_hi: u32) ?[*:0]const u8 {
if (guid_lo == 0 and guid_hi == 0) return null;
var name_buf: [2]u32 = .{ 0, 0 };
const result = hook.call(fn (u32, u32, u32, u32, u32, u32, u32) callconv(tc) u32, o.FN_NAME_CACHE_LOOKUP, .{
const result = hook.call(fn (u32, u32, u32, u32, u32, u32, u32) callconv(hook.cc.thiscall) u32, o.FN_NAME_CACHE_LOOKUP, .{
o.NAME_CACHE_OBJ, guid_lo, guid_hi, @intFromPtr(&name_buf), 0, 0, 0,
});
return if (result != 0) @ptrFromInt(result) else null;
@@ -247,7 +245,7 @@ fn getCursorTerrainPosition() ?Vec3 {
@as(*align(1) u32, @ptrFromInt(world_frame + o.WF_HIT_TERRAIN_Z)).* = 0;
// UpdateHitTest - __fastcall(ECX=worldFrame)
hook.fastcall(void, o.FN_UPDATE_HIT_TEST, world_frame, 0);
hook.call(fn (u32) callconv(hook.cc.fastcall) void, o.FN_UPDATE_HIT_TEST, .{world_frame});
const hit_type = hook.readMem(u32, world_frame + o.WF_HIT_TYPE);
const x = hook.readMem(f32, world_frame + o.WF_HIT_TERRAIN_X);
@@ -286,7 +284,7 @@ fn getCursorTerrainPosition() ?Vec3 {
/// CreateEntityInstance_WithAttachment - __fastcall, RET 0x14.
fn createEntityInstance(path: [*:0]const u8, pos: *[3]f32, facing: f32, flags: u32, update_now: u32) ?*anyopaque {
const result = hook.call(fn ([*:0]const u8, *[3]f32, f32, u32, u32, u32, u32) callconv(fc) u32, o.FN_CREATE_ENTITY_INSTANCE, .{
const result = hook.call(fn ([*:0]const u8, *[3]f32, f32, u32, u32, u32, u32) callconv(hook.cc.fastcall) u32, o.FN_CREATE_ENTITY_INSTANCE, .{
path, pos, facing, flags, update_now, 0, 0,
});
return if (result != 0) @ptrFromInt(result) else null;
@@ -294,7 +292,7 @@ fn createEntityInstance(path: [*:0]const u8, pos: *[3]f32, facing: f32, flags: u
/// CleanupEntity_ProcessAttachments - __fastcall(ECX=entity), no stack params.
fn cleanupEntity(obj: *anyopaque) void {
hook.call(fn (*anyopaque) callconv(fc) void, o.FN_CLEANUP_ENTITY, .{obj});
hook.call(fn (*anyopaque) callconv(hook.cc.fastcall) void, o.FN_CLEANUP_ENTITY, .{obj});
}
// =============================================================================
@@ -308,7 +306,7 @@ fn playAnimation(entity: *anyopaque, anim_id: u32, queue: bool) void {
const model = hook.readMem(u32, entity_addr + 0x88);
if (model == 0 or model < 0x10000) return;
hook.call(fn (u32, u32, u32, i32, u32, u32, u32, u32) callconv(tc) void, o.FN_PLAY_BONE_ANIMATION, .{
hook.call(fn (u32, u32, u32, i32, u32, u32, u32, u32) callconv(hook.cc.thiscall) void, o.FN_PLAY_BONE_ANIMATION, .{
model, 0xFFFFFFFF, anim_id, -1, 0, @as(u32, @bitCast(@as(f32, 1.0))), 1, @intFromBool(queue),
});
}
@@ -405,7 +403,7 @@ fn spawnEntity(index: usize, pos: Vec3) bool {
/// SetUnitPositionAndOrientation - __fastcall(ECX=positionData, EDX=pos), 1 stack param.
fn setUnitPositionAndOrientation(entity: *anyopaque, pos: *[3]f32, facing: f32) void {
hook.call(fn (*anyopaque, *[3]f32, f32) callconv(fc) void, 0x698e20, .{ entity, pos, facing });
hook.call(fn (*anyopaque, *[3]f32, f32) callconv(hook.cc.fastcall) void, 0x698e20, .{ entity, pos, facing });
}
/// Remove only the live entity for a marker slot (def untouched).
@@ -517,7 +515,7 @@ pub fn luaClearWorldMarker(L: lua.State) callconv(.c) u32 {
}
const nargs = lua.gettop(L);
const lua_type: *const fn (lua.State, i32) callconv(fc) i32 = @ptrFromInt(0x6F3400);
const lua_type: *const fn (lua.State, i32) callconv(hook.cc.fastcall) i32 = @ptrFromInt(0x6F3400);
if (nargs == 0 or lua_type(L, 1) == 0) {
// No args or nil - clear all
@@ -729,12 +727,12 @@ pub fn luaCanSetMarkers(L: lua.State) callconv(.c) u32 {
// World teardown hook
// =============================================================================
var world_cleanup_hook: hook.Detour(fn () callconv(sc) void) = .{};
var world_update_hook: hook.Detour(fn (u32) callconv(fc) void) = .{};
var world_cleanup_hook: hook.Detour(fn () callconv(hook.cc.stdcall) void) = .{};
var world_update_hook: hook.Detour(fn (u32) callconv(hook.cc.fastcall) void) = .{};
/// OnWorldUpdate hook - per-frame tick while world is active.
/// Drives animation state (Hold queue after Stand, despawn cleanup).
fn worldUpdateDetour(frame: u32) callconv(fc) void {
fn worldUpdateDetour(frame: u32) callconv(hook.cc.fastcall) void {
tickAnimations();
world_update_hook.callOriginal(.{frame});
}
@@ -744,7 +742,7 @@ fn worldUpdateDetour(frame: u32) callconv(fc) void {
/// game's teardown runs - the same pattern every native caller uses (e.g.
/// processCinematicExit, DestroyPathObjectIfPresent). This unlinks them from
/// the WDOODADDEF hash table so the atexit handler never touches freed memory.
fn worldCleanupDetour() callconv(sc) void {
fn worldCleanupDetour() callconv(hook.cc.stdcall) void {
con.print("[worldmarkers] >>> worldCleanupDetour FIRING <<<\n");
destroyAllEntities();
world_cleanup_hook.callOriginal(.{});
+22 -25
View File
@@ -19,9 +19,6 @@ const lua = @import("../lua.zig");
const con = @import("../console.zig");
const mod_mutex = @import("../mutex.zig");
const fc: std.builtin.CallingConvention = .{ .x86_fastcall = .{} };
const tc: std.builtin.CallingConvention = .{ .x86_thiscall = .{} };
const sc: std.builtin.CallingConvention = .{ .x86_stdcall = .{} };
pub const module_name: [*:0]const u8 = "minimapicons";
@@ -248,7 +245,7 @@ const CStatus = extern struct {
}
fn deinit(self: *CStatus) void {
const f: *const fn (*CStatus) callconv(tc) void = @ptrFromInt(ADDR.CStatusDestructor);
const f: *const fn (*CStatus) callconv(hook.cc.thiscall) void = @ptrFromInt(ADDR.CStatusDestructor);
f(self);
}
};
@@ -365,7 +362,7 @@ fn isValidPtr(addr: u32) bool {
fn getObjectByGUID(guid_lo: u32, guid_hi: u32) u32 {
if (guid_lo == 0 and guid_hi == 0) return 0;
return hook.call(fn (u32, u32) callconv(sc) u32, ADDR.GetObjectByGUID, .{ guid_lo, guid_hi });
return hook.call(fn (u32, u32) callconv(hook.cc.stdcall) u32, ADDR.GetObjectByGUID, .{ guid_lo, guid_hi });
}
fn getObjectType(obj: u32) u32 {
@@ -461,7 +458,7 @@ fn getSummonedByGUID(obj: u32) u64 {
}
fn getActivePlayerGUID() u64 {
return hook.call(fn () callconv(fc) u64, ADDR.ClntObjMgrGetActivePlayer, .{});
return hook.call(fn () callconv(hook.cc.fastcall) u64, ADDR.ClntObjMgrGetActivePlayer, .{});
}
fn getActivePlayerObject() u32 {
@@ -473,7 +470,7 @@ fn getActivePlayerObject() u32 {
/// UnitReaction: __thiscall(localPlayer_ECX, unit_stack) -> int (>=4 = friendly).
fn unitReaction(local_player: u32, unit: u32) i32 {
if (local_player == 0 or unit == 0) return 0;
return hook.call(fn (u32, u32) callconv(tc) i32, ADDR.UnitReaction, .{ local_player, unit });
return hook.call(fn (u32, u32) callconv(hook.cc.thiscall) i32, ADDR.UnitReaction, .{ local_player, unit });
}
/// Check if a unit passes faction/friendliness filters.
@@ -514,7 +511,7 @@ fn isUnitAllowed(obj: u32, local_player: u32) bool {
/// CallSpellCastHandler: __fastcall(obj_ECX) -> char (bool via virtual dispatch).
/// This is what IsValidInteractionTarget uses for type 0x21 (GO).
fn isGoInteractable(obj: u32) bool {
const result: u8 = @truncate(hook.call(fn (u32) callconv(fc) u32, ADDR.CallSpellCastHandler, .{obj}));
const result: u8 = @truncate(hook.call(fn (u32) callconv(hook.cc.fastcall) u32, ADDR.CallSpellCastHandler, .{obj}));
if (result == 0) {
con.fmt("[minimapicons] GO 0x{x} rejected: not interactable (entry={d})\n", .{ obj, getObjectEntry(obj) });
}
@@ -549,7 +546,7 @@ fn getObjectPosition(obj: u32) ?C3Vector {
if (!isValidPtr(get_pos_fn)) return null;
var pos: C3Vector = undefined;
// __thiscall(obj_ECX, &pos_stack) → C3Vector*
_ = hook.call(fn (u32, *C3Vector) callconv(tc) u32, get_pos_fn, .{ obj, &pos });
_ = hook.call(fn (u32, *C3Vector) callconv(hook.cc.thiscall) u32, get_pos_fn, .{ obj, &pos });
return pos;
}
@@ -569,7 +566,7 @@ fn worldPosToMinimapCoords(
// WorldPosToMinimapFrameCoords: __fastcall(out_ECX, edx, C3Vector cur, float radius,
// float worldX, float worldY, float layoutScale, float unkScale)
// EDX unused — use thiscall (ECX=this, rest on stack)
_ = hook.call(fn (*C2Vector, f32, f32, f32, f32, f32, f32, f32, f32) callconv(tc) u32, ADDR.WorldPosToMinimapCoords, .{
_ = hook.call(fn (*C2Vector, f32, f32, f32, f32, f32, f32, f32, f32) callconv(hook.cc.thiscall) u32, ADDR.WorldPosToMinimapCoords, .{
out, cur.x, cur.y, cur.z, radius, world_x, world_y, layout_scale, unk_scale,
});
}
@@ -583,7 +580,7 @@ fn getFrameUnkScale(info: u32) f32 {
const fn_addr = hook.readMem(u32, vtable + 7 * 4);
if (!isValidPtr(fn_addr)) return 1.0;
// __thiscall(fsp_ECX) → f32 on FPU ST(0)
return hook.call(fn (u32) callconv(tc) f32, fn_addr, .{fsp});
return hook.call(fn (u32) callconv(hook.cc.thiscall) f32, fn_addr, .{fsp});
}
// =============================================================================
@@ -621,7 +618,7 @@ fn loadTexture(path: [*:0]const u8) u32 {
defer status.deinit();
// TextureCreate: __fastcall(filename_ECX, status_EDX, texFlags, unk1, unk2)
const texture = hook.call(fn ([*:0]const u8, *CStatus, u32, u32, u32) callconv(fc) u32, ADDR.TextureCreate, .{
const texture = hook.call(fn ([*:0]const u8, *CStatus, u32, u32, u32) callconv(hook.cc.fastcall) u32, ADDR.TextureCreate, .{
path, &status, g_default_tex_flags, 0, 1,
});
@@ -642,7 +639,7 @@ fn initTexFlags() u32 {
var flags: u32 = 0;
// CGxTexFlags constructor: __thiscall(this, filter=0, wrapU=0, wrapV=0,
// forceMipTracking=0, generateMipMaps=0, renderTarget=0, maxAnisotropy=0, unknownFlag=1)
hook.call(fn (*u32, u32, u32, u32, u32, u32, u32, u32, u32) callconv(tc) void, ADDR.CGxTexFlagsInit, .{
hook.call(fn (*u32, u32, u32, u32, u32, u32, u32, u32, u32) callconv(hook.cc.thiscall) void, ADDR.CGxTexFlagsInit, .{
&flags, 0, 0, 0, 0, 0, 0, 0, 1,
});
return flags;
@@ -658,7 +655,7 @@ fn getGxTex(texture: u32) u32 {
status.init();
defer status.deinit();
const gx_tex = hook.call(fn (u32, u32, *CStatus) callconv(fc) u32, ADDR.TextureGetGxTex, .{
const gx_tex = hook.call(fn (u32, u32, *CStatus) callconv(hook.cc.fastcall) u32, ADDR.TextureGetGxTex, .{
texture, 1, &status,
});
@@ -683,7 +680,7 @@ fn drawMinimapBlip(pos: C2Vector, scale: f32) void {
// GxPrimLockVertexPtrs(count=4, vertices, vertStride=12, normal, 0, color, 0,
// null, 0, texCoords, 8, null, 0)
hook.call(fn (u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32) callconv(fc) void, ADDR.GxPrimLockVertexPtrs, .{
hook.call(fn (u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32) callconv(hook.cc.fastcall) void, ADDR.GxPrimLockVertexPtrs, .{
4, // count (ECX)
@intFromPtr(&vertices), // vertices (EDX)
12, // vertStride
@@ -700,7 +697,7 @@ fn drawMinimapBlip(pos: C2Vector, scale: f32) void {
});
// GxPrimDrawElements(TriangleStrip=4, count=4, indices)
const drawElements: *const fn (u32, u32, u32) callconv(fc) void = @ptrFromInt(ADDR.GxPrimDrawElements);
const drawElements: *const fn (u32, u32, u32) callconv(hook.cc.fastcall) void = @ptrFromInt(ADDR.GxPrimDrawElements);
drawElements(4, 4, ADDR.BlipVertIndices);
// GxPrimUnlockVertexPtrs()
@@ -901,7 +898,7 @@ fn refreshActiveTrackingCache() void {
// ObjectEnumProc: __fastcall(MINIMAPINFO* ECX, unused EDX, u64 guid on stack)
// When guid is u64, MSVC fastcall skips EDX and puts the 8 bytes on stack.
// In Zig fastcall with 4 u32 params: ECX=info, EDX=_edx, stack=guid_lo,guid_hi → RET 8.
const EnumProcFn = fn (u32, u32, u32, u32) callconv(fc) i32;
const EnumProcFn = fn (u32, u32, u32, u32) callconv(hook.cc.fastcall) i32;
var enum_proc_hook: hook.Detour(EnumProcFn) = .{};
fn isInCity() bool {
@@ -912,7 +909,7 @@ fn isInCity() bool {
return false;
}
fn objectEnumProcDetour(info: u32, _edx: u32, guid_lo: u32, guid_hi: u32) callconv(fc) i32 {
fn objectEnumProcDetour(info: u32, _edx: u32, guid_lo: u32, guid_hi: u32) callconv(hook.cc.fastcall) i32 {
if (g_has_active_tracking and !(g_city_toggle and isInCity())) {
if (checkObject(info, guid_lo, guid_hi)) {
return 1; // Skip original — we draw our own icon
@@ -923,10 +920,10 @@ fn objectEnumProcDetour(info: u32, _edx: u32, guid_lo: u32, guid_hi: u32) callco
// RenderObjectBlips: __thiscall(CGMinimapFrame* ECX, DNInfo* stack)
// Hooked via fastcall with explicit EDX placeholder.
const RenderBlipsFn = fn (u32, u32, u32) callconv(fc) void;
const RenderBlipsFn = fn (u32, u32, u32) callconv(hook.cc.fastcall) void;
var render_blips_hook: hook.Detour(RenderBlipsFn) = .{};
fn renderObjectBlipsDetour(thisptr: u32, _edx: u32, dn_info: u32) callconv(fc) void {
fn renderObjectBlipsDetour(thisptr: u32, _edx: u32, dn_info: u32) callconv(hook.cc.fastcall) void {
render_blips_hook.callOriginal(.{ thisptr, _edx, dn_info });
const count = g_blip_count;
@@ -957,7 +954,7 @@ fn renderObjectBlipsDetour(thisptr: u32, _edx: u32, dn_info: u32) callconv(fc) v
continue;
}
const gxRsSet: *const fn (u32, u32) callconv(fc) void = @ptrFromInt(ADDR.GxRsSet);
const gxRsSet: *const fn (u32, u32) callconv(hook.cc.fastcall) void = @ptrFromInt(ADDR.GxRsSet);
gxRsSet(23, gx_tex);
// Draw all blips sharing this texture
@@ -968,10 +965,10 @@ fn renderObjectBlipsDetour(thisptr: u32, _edx: u32, dn_info: u32) callconv(fc) v
}
// ClntObjMgrEnumVisibleObjects: __fastcall(callback ECX, context EDX)
const EnumVisFn = fn (u32, u32) callconv(fc) i32;
const EnumVisFn = fn (u32, u32) callconv(hook.cc.fastcall) i32;
var enum_vis_hook: hook.Detour(EnumVisFn) = .{};
fn enumVisibleObjectsDetour(callback: u32, context: u32) callconv(fc) i32 {
fn enumVisibleObjectsDetour(callback: u32, context: u32) callconv(hook.cc.fastcall) i32 {
// Clear tracked blips when the minimap's own callback is about to enumerate
if (callback == ADDR.ObjectEnumProc) {
g_blip_count = 0;
@@ -1007,7 +1004,7 @@ fn luaStringToSlice(L: lua.State, idx: i32) ?[]const u8 {
}
// SetObjectTypeBlip(typeName [, texturePath [, scale [, includeFilter [, excludeFilter]]]])
pub fn luaSetObjectTypeBlip(L: lua.State) callconv(fc) i32 {
pub fn luaSetObjectTypeBlip(L: lua.State) callconv(hook.cc.fastcall) i32 {
defer refreshActiveTrackingCache();
if (!lua.isstring(L, 1)) {
@@ -1130,7 +1127,7 @@ pub fn luaSetObjectTypeBlip(L: lua.State) callconv(fc) i32 {
}
// SetMinimapCityToggle(enabled) — suppress NPC/GO tracking in capital cities
pub fn luaSetCityToggle(L: lua.State) callconv(fc) i32 {
pub fn luaSetCityToggle(L: lua.State) callconv(hook.cc.fastcall) i32 {
g_city_toggle = lua.isnumber(L, 1) and lua.tonumber(L, 1) != 0;
return 0;
}
+4 -4
View File
@@ -72,13 +72,13 @@ pub fn isEnabled() bool {
/// OutlineCommand("on") → enable outlines
/// OutlineCommand("off") → disable outlines
pub fn outlineCommand(L: *anyopaque) callconv(.c) u32 {
const nargs = hook.fastcall(i32, 0x6F3070, @intFromPtr(L), @as(u32, 0)); // lua_gettop
const nargs = hook.call(fn (usize) callconv(hook.cc.fastcall) i32, 0x6F3070, .{@intFromPtr(L)}); // lua_gettop
if (nargs >= 1) {
// Check if first arg is a string
const is_str = hook.fastcall(u32, 0x6F3510, @intFromPtr(L), @as(u32, 1)); // lua_isstring
const is_str = hook.call(fn (usize, u32) callconv(hook.cc.fastcall) u32, 0x6F3510, .{ @intFromPtr(L), 1 }); // lua_isstring
if (is_str != 0) {
const str_ptr = hook.fastcall(u32, 0x6F3690, @intFromPtr(L), @as(u32, 1)); // lua_tostring
const str_ptr = hook.call(fn (usize, u32) callconv(hook.cc.fastcall) u32, 0x6F3690, .{ @intFromPtr(L), 1 }); // lua_tostring
if (str_ptr != 0) {
const s: [*:0]const u8 = @ptrFromInt(str_ptr);
const span = @import("std").mem.span(s);
@@ -92,7 +92,7 @@ pub fn outlineCommand(L: *anyopaque) callconv(.c) u32 {
}
// Push current state as boolean
hook.fastcall(void, 0x6F39F0, @intFromPtr(L), @as(u32, if (isEnabled()) 1 else 0)); // lua_pushboolean
hook.call(fn (usize, u32) callconv(hook.cc.fastcall) void, 0x6F39F0, .{ @intFromPtr(L), @as(u32, if (isEnabled()) 1 else 0) }); // lua_pushboolean
return 1;
}
+43 -44
View File
@@ -20,7 +20,6 @@ const tracker = @import("tracker.zig");
const model_hook = @import("model_hook.zig");
const WINAPI = std.builtin.CallingConvention.winapi;
const sc: std.builtin.CallingConvention = .{ .x86_stdcall = .{} };
// =============================================================================
// Windows / D3D9 externs
@@ -39,7 +38,7 @@ inline fn vt(obj: *anyopaque) [*]usize {
/// Call a COM method that takes only (self) and returns HRESULT.
fn comCall0(obj: *anyopaque, idx: usize) i32 {
const f: *const fn (*anyopaque) callconv(sc) i32 = @ptrFromInt(vt(obj)[idx]);
const f: *const fn (*anyopaque) callconv(hook.cc.stdcall) i32 = @ptrFromInt(vt(obj)[idx]);
return f(obj);
}
@@ -86,7 +85,7 @@ const D3DXAssembleShaderFn = *const fn (
u32, // Flags
*?*anyopaque, // ppShader (ID3DXBuffer**)
*?*anyopaque, // ppErrorMsgs (ID3DXBuffer**)
) callconv(sc) i32;
) callconv(hook.cc.stdcall) i32;
// =============================================================================
// Render target resources
@@ -161,108 +160,108 @@ const QuadVertex = extern struct {
// =============================================================================
fn deviceSetRS(dev: *anyopaque, state: u32, value: u32) void {
const f: *const fn (*anyopaque, u32, u32) callconv(sc) i32 = @ptrFromInt(vt(dev)[types.VT.SetRenderState]);
const f: *const fn (*anyopaque, u32, u32) callconv(hook.cc.stdcall) i32 = @ptrFromInt(vt(dev)[types.VT.SetRenderState]);
_ = f(dev, state, value);
}
fn deviceGetRS(dev: *anyopaque, state: u32) u32 {
var val: u32 = 0;
const f: *const fn (*anyopaque, u32, *u32) callconv(sc) i32 = @ptrFromInt(vt(dev)[types.VT.GetRenderState]);
const f: *const fn (*anyopaque, u32, *u32) callconv(hook.cc.stdcall) i32 = @ptrFromInt(vt(dev)[types.VT.GetRenderState]);
_ = f(dev, state, &val);
return val;
}
fn deviceSetPtr(dev: *anyopaque, idx: usize, ptr: *anyopaque) void {
const f: *const fn (*anyopaque, *anyopaque) callconv(sc) i32 = @ptrFromInt(vt(dev)[idx]);
const f: *const fn (*anyopaque, *anyopaque) callconv(hook.cc.stdcall) i32 = @ptrFromInt(vt(dev)[idx]);
_ = f(dev, ptr);
}
fn deviceGetPtr(dev: *anyopaque, idx: usize) ?*anyopaque {
var ptr: ?*anyopaque = null;
const f: *const fn (*anyopaque, *?*anyopaque) callconv(sc) i32 = @ptrFromInt(vt(dev)[idx]);
const f: *const fn (*anyopaque, *?*anyopaque) callconv(hook.cc.stdcall) i32 = @ptrFromInt(vt(dev)[idx]);
_ = f(dev, &ptr);
return ptr;
}
/// Set a COM pointer, handling null via optional pointer (ABI-equivalent to passing 0).
fn deviceSetPtrOrNull(dev: *anyopaque, idx: usize, ptr: ?*anyopaque) void {
const f: *const fn (*anyopaque, ?*anyopaque) callconv(sc) i32 = @ptrFromInt(vt(dev)[idx]);
const f: *const fn (*anyopaque, ?*anyopaque) callconv(hook.cc.stdcall) i32 = @ptrFromInt(vt(dev)[idx]);
_ = @call(.never_tail, f, .{ dev, ptr });
}
fn deviceSetPSConstF(dev: *anyopaque, start: u32, data: *const [4]f32) void {
const f: *const fn (*anyopaque, u32, *const [4]f32, u32) callconv(sc) i32 =
const f: *const fn (*anyopaque, u32, *const [4]f32, u32) callconv(hook.cc.stdcall) i32 =
@ptrFromInt(vt(dev)[types.VT.SetPixelShaderConstantF]);
_ = f(dev, start, data, 1);
}
fn deviceGetPSConstF(dev: *anyopaque, start: u32, data: *[4]f32) void {
const f: *const fn (*anyopaque, u32, *[4]f32, u32) callconv(sc) i32 =
const f: *const fn (*anyopaque, u32, *[4]f32, u32) callconv(hook.cc.stdcall) i32 =
@ptrFromInt(vt(dev)[types.VT.GetPixelShaderConstantF]);
_ = f(dev, start, data, 1);
}
fn deviceGetViewport(dev: *anyopaque, vp_out: *types.D3DVIEWPORT9) void {
const f: *const fn (*anyopaque, *types.D3DVIEWPORT9) callconv(sc) i32 =
const f: *const fn (*anyopaque, *types.D3DVIEWPORT9) callconv(hook.cc.stdcall) i32 =
@ptrFromInt(vt(dev)[types.VT.GetViewport]);
_ = f(dev, vp_out);
}
fn deviceSetRenderTarget(dev: *anyopaque, idx: u32, surf: *anyopaque) void {
const f: *const fn (*anyopaque, u32, *anyopaque) callconv(sc) i32 =
const f: *const fn (*anyopaque, u32, *anyopaque) callconv(hook.cc.stdcall) i32 =
@ptrFromInt(vt(dev)[types.VT.SetRenderTarget]);
_ = f(dev, idx, surf);
}
fn deviceGetRenderTarget(dev: *anyopaque, idx: u32) ?*anyopaque {
var surf: ?*anyopaque = null;
const f: *const fn (*anyopaque, u32, *?*anyopaque) callconv(sc) i32 =
const f: *const fn (*anyopaque, u32, *?*anyopaque) callconv(hook.cc.stdcall) i32 =
@ptrFromInt(vt(dev)[types.VT.GetRenderTarget]);
_ = f(dev, idx, &surf);
return surf;
}
fn deviceSetTexture(dev: *anyopaque, stage: u32, tex: ?*anyopaque) void {
const f: *const fn (*anyopaque, u32, ?*anyopaque) callconv(sc) i32 =
const f: *const fn (*anyopaque, u32, ?*anyopaque) callconv(hook.cc.stdcall) i32 =
@ptrFromInt(vt(dev)[types.VT.SetTexture]);
_ = @call(.never_tail, f, .{ dev, stage, tex });
}
fn deviceSetFVF(dev: *anyopaque, fvf: u32) void {
const f: *const fn (*anyopaque, u32) callconv(sc) i32 =
const f: *const fn (*anyopaque, u32) callconv(hook.cc.stdcall) i32 =
@ptrFromInt(vt(dev)[types.VT.SetFVF]);
_ = f(dev, fvf);
}
fn deviceGetSamplerState(dev: *anyopaque, sampler: u32, state_type: u32) u32 {
var val: u32 = 0;
const f: *const fn (*anyopaque, u32, u32, *u32) callconv(sc) i32 =
const f: *const fn (*anyopaque, u32, u32, *u32) callconv(hook.cc.stdcall) i32 =
@ptrFromInt(vt(dev)[types.VT.GetSamplerState]);
_ = f(dev, sampler, state_type, &val);
return val;
}
fn deviceSetSamplerState(dev: *anyopaque, sampler: u32, state_type: u32, value: u32) void {
const f: *const fn (*anyopaque, u32, u32, u32) callconv(sc) i32 =
const f: *const fn (*anyopaque, u32, u32, u32) callconv(hook.cc.stdcall) i32 =
@ptrFromInt(vt(dev)[types.VT.SetSamplerState]);
_ = f(dev, sampler, state_type, value);
}
fn deviceGetStreamSource(dev: *anyopaque, stream: u32, vb_out: *?*anyopaque, offset_out: *u32, stride_out: *u32) void {
const f: *const fn (*anyopaque, u32, *?*anyopaque, *u32, *u32) callconv(sc) i32 =
const f: *const fn (*anyopaque, u32, *?*anyopaque, *u32, *u32) callconv(hook.cc.stdcall) i32 =
@ptrFromInt(vt(dev)[types.VT.GetStreamSource]);
_ = f(dev, stream, vb_out, offset_out, stride_out);
}
fn deviceSetStreamSource(dev: *anyopaque, stream: u32, vb: ?*anyopaque, offset: u32, stride: u32) void {
const f: *const fn (*anyopaque, u32, ?*anyopaque, u32, u32) callconv(sc) i32 =
const f: *const fn (*anyopaque, u32, ?*anyopaque, u32, u32) callconv(hook.cc.stdcall) i32 =
@ptrFromInt(vt(dev)[types.VT.SetStreamSource]);
_ = @call(.never_tail, f, .{ dev, stream, vb, offset, stride });
}
fn deviceGetTexture(dev: *anyopaque, stage: u32) ?*anyopaque {
var tex: ?*anyopaque = null;
const f: *const fn (*anyopaque, u32, *?*anyopaque) callconv(sc) i32 =
const f: *const fn (*anyopaque, u32, *?*anyopaque) callconv(hook.cc.stdcall) i32 =
@ptrFromInt(vt(dev)[types.VT.GetTexture]);
_ = f(dev, stage, &tex);
return tex;
@@ -270,38 +269,38 @@ fn deviceGetTexture(dev: *anyopaque, stage: u32) ?*anyopaque {
fn deviceGetIndices(dev: *anyopaque) ?*anyopaque {
var ib: ?*anyopaque = null;
const f: *const fn (*anyopaque, *?*anyopaque) callconv(sc) i32 =
const f: *const fn (*anyopaque, *?*anyopaque) callconv(hook.cc.stdcall) i32 =
@ptrFromInt(vt(dev)[types.VT.GetIndices]);
_ = f(dev, &ib);
return ib;
}
fn deviceSetIndices(dev: *anyopaque, ib: ?*anyopaque) void {
const f: *const fn (*anyopaque, ?*anyopaque) callconv(sc) i32 =
const f: *const fn (*anyopaque, ?*anyopaque) callconv(hook.cc.stdcall) i32 =
@ptrFromInt(vt(dev)[types.VT.SetIndices]);
_ = @call(.never_tail, f, .{ dev, ib });
}
fn deviceGetVSConstF(dev: *anyopaque, start: u32, data: [*][4]f32, count: u32) void {
const f: *const fn (*anyopaque, u32, [*][4]f32, u32) callconv(sc) i32 =
const f: *const fn (*anyopaque, u32, [*][4]f32, u32) callconv(hook.cc.stdcall) i32 =
@ptrFromInt(vt(dev)[types.VT.GetVertexShaderConstantF]);
_ = f(dev, start, data, count);
}
fn deviceSetVSConstF(dev: *anyopaque, start: u32, data: [*]const [4]f32, count: u32) void {
const f: *const fn (*anyopaque, u32, [*]const [4]f32, u32) callconv(sc) i32 =
const f: *const fn (*anyopaque, u32, [*]const [4]f32, u32) callconv(hook.cc.stdcall) i32 =
@ptrFromInt(vt(dev)[types.VT.SetVertexShaderConstantF]);
_ = f(dev, start, data, count);
}
fn deviceDrawPrimitiveUP(dev: *anyopaque, prim_type: u32, prim_count: u32, data: *const anyopaque, stride: u32) void {
const f: *const fn (*anyopaque, u32, u32, *const anyopaque, u32) callconv(sc) i32 =
const f: *const fn (*anyopaque, u32, u32, *const anyopaque, u32) callconv(hook.cc.stdcall) i32 =
@ptrFromInt(vt(dev)[types.VT.DrawPrimitiveUP]);
_ = f(dev, prim_type, prim_count, data, stride);
}
fn deviceCreateTexture(dev: *anyopaque, w: u32, h: u32, levels: u32, usage: u32, fmt: u32, pool: u32, out: *?*anyopaque) i32 {
const f: *const fn (*anyopaque, u32, u32, u32, u32, u32, u32, *?*anyopaque, u32) callconv(sc) i32 =
const f: *const fn (*anyopaque, u32, u32, u32, u32, u32, u32, *?*anyopaque, u32) callconv(hook.cc.stdcall) i32 =
@ptrFromInt(vt(dev)[types.VT.CreateTexture]);
return f(dev, w, h, levels, usage, fmt, pool, out, 0);
}
@@ -310,7 +309,7 @@ fn deviceCreateTexture(dev: *anyopaque, w: u32, h: u32, levels: u32, usage: u32,
fn textureGetSurfaceLevel(tex: *anyopaque) ?*anyopaque {
// IDirect3DTexture9::GetSurfaceLevel is vtable index 18
var surf: ?*anyopaque = null;
const f: *const fn (*anyopaque, u32, *?*anyopaque) callconv(sc) i32 =
const f: *const fn (*anyopaque, u32, *?*anyopaque) callconv(hook.cc.stdcall) i32 =
@ptrFromInt(vt(tex)[18]);
if (f(tex, 0, &surf) < 0) return null;
return surf;
@@ -318,7 +317,7 @@ fn textureGetSurfaceLevel(tex: *anyopaque) ?*anyopaque {
/// Clear the current render target to a colour.
fn clearRenderTarget(dev: *anyopaque, color: u32) void {
const f: *const fn (*anyopaque, u32, ?*anyopaque, u32, u32, f32, u32) callconv(sc) i32 =
const f: *const fn (*anyopaque, u32, ?*anyopaque, u32, u32, f32, u32) callconv(hook.cc.stdcall) i32 =
@ptrFromInt(vt(dev)[types.VT.Clear]);
_ = f(dev, 0, null, types.D3DCLEAR_TARGET, color, 1.0, 0);
}
@@ -618,10 +617,10 @@ fn assemblePS(device: *anyopaque, assemble: D3DXAssembleShaderFn, src: [*]const
// ID3DXBuffer::GetBufferPointer is vtable[3]
const buf_ptr: *anyopaque = @ptrFromInt(
@as(*const fn (*anyopaque) callconv(sc) usize, @ptrFromInt(vt(code.?)[3]))(code.?),
@as(*const fn (*anyopaque) callconv(hook.cc.stdcall) usize, @ptrFromInt(vt(code.?)[3]))(code.?),
);
var ps_out: ?*anyopaque = null;
const create: *const fn (*anyopaque, *anyopaque, *?*anyopaque) callconv(sc) i32 =
const create: *const fn (*anyopaque, *anyopaque, *?*anyopaque) callconv(hook.cc.stdcall) i32 =
@ptrFromInt(vt(device)[types.VT.CreatePixelShader]);
if (create(device, buf_ptr, &ps_out) < 0) return null;
return ps_out;
@@ -657,7 +656,7 @@ fn buildFullscreenQuad(w: u32, h: u32) [4]QuadVertex {
// EndScene hook
// =============================================================================
fn hkEndScene(device: *anyopaque) callconv(sc) i32 {
fn hkEndScene(device: *anyopaque) callconv(hook.cc.stdcall) i32 {
// One-time: check if depth/stencil surface has stencil bits.
if (need_force_reset) {
need_force_reset = false;
@@ -686,7 +685,7 @@ fn hkEndScene(device: *anyopaque) callconv(sc) i32 {
frame_has_outlines = false;
// Call original EndScene
const f: *const fn (*anyopaque) callconv(sc) i32 = @ptrFromInt(orig_endscene);
const f: *const fn (*anyopaque) callconv(hook.cc.stdcall) i32 = @ptrFromInt(orig_endscene);
return f(device);
}
@@ -694,7 +693,7 @@ fn hkEndScene(device: *anyopaque) callconv(sc) i32 {
// Reset hook - force D24S8 depth/stencil format, release resources
// =============================================================================
fn hkReset(device: *anyopaque, pp: *types.D3DPRESENT_PARAMETERS) callconv(sc) i32 {
fn hkReset(device: *anyopaque, pp: *types.D3DPRESENT_PARAMETERS) callconv(hook.cc.stdcall) i32 {
if (pp.EnableAutoDepthStencil != 0) {
const fmt = pp.AutoDepthStencilFormat;
if (fmt != types.D3DFMT_D24S8 and fmt != types.D3DFMT_D24FS8 and
@@ -709,7 +708,7 @@ fn hkReset(device: *anyopaque, pp: *types.D3DPRESENT_PARAMETERS) callconv(sc) i3
releaseShaders();
releaseResources();
const f: *const fn (*anyopaque, *types.D3DPRESENT_PARAMETERS) callconv(sc) i32 = @ptrFromInt(orig_reset);
const f: *const fn (*anyopaque, *types.D3DPRESENT_PARAMETERS) callconv(hook.cc.stdcall) i32 = @ptrFromInt(orig_reset);
return f(device, pp);
}
@@ -732,8 +731,8 @@ fn hkDIP(
num_verts: u32,
start_idx: u32,
prim_count: u32,
) callconv(sc) i32 {
const OrigDIP = *const fn (*anyopaque, u32, i32, u32, u32, u32, u32) callconv(sc) i32;
) callconv(hook.cc.stdcall) i32 {
const OrigDIP = *const fn (*anyopaque, u32, i32, u32, u32, u32, u32) callconv(hook.cc.stdcall) i32;
const origFn: OrigDIP = @ptrFromInt(orig_dip);
// ---- Cache outline draws for EndScene replay ----
@@ -885,7 +884,7 @@ fn runJfaPipeline(device: *anyopaque) void {
// Depth-stencil surface
var saved_ds: ?*anyopaque = null;
const getDS: *const fn (*anyopaque, *?*anyopaque) callconv(sc) i32 =
const getDS: *const fn (*anyopaque, *?*anyopaque) callconv(hook.cc.stdcall) i32 =
@ptrFromInt(vt(device)[types.VT.GetDepthStencilSurface]);
_ = getDS(device, &saved_ds);
@@ -935,7 +934,7 @@ fn runJfaPipeline(device: *anyopaque) void {
// =====================================================================
if (cached_draw_count > 0) {
const origFn: *const fn (*anyopaque, u32, i32, u32, u32, u32, u32) callconv(sc) i32 =
const origFn: *const fn (*anyopaque, u32, i32, u32, u32, u32, u32) callconv(hook.cc.stdcall) i32 =
@ptrFromInt(orig_dip);
deviceSetRenderTarget(device, 0, rt_silhouette_surf.?);
@@ -985,7 +984,7 @@ fn runJfaPipeline(device: *anyopaque) void {
// Clear stencil marks to avoid affecting next frame's rendering
deviceSetRS(device, types.D3DRS.STENCILENABLE, 0);
const clearFn: *const fn (*anyopaque, u32, ?*anyopaque, u32, u32, f32, u32) callconv(sc) i32 =
const clearFn: *const fn (*anyopaque, u32, ?*anyopaque, u32, u32, f32, u32) callconv(hook.cc.stdcall) i32 =
@ptrFromInt(vt(device)[types.VT.Clear]);
_ = clearFn(device, 0, null, types.D3DCLEAR_STENCIL, 0, 1.0, 0);
}
@@ -1177,28 +1176,28 @@ fn hasStencilBits(fmt: u32) bool {
fn forceD24S8IfNeeded(device: *anyopaque) void {
var pDS: ?*anyopaque = null;
const getDS: *const fn (*anyopaque, *?*anyopaque) callconv(sc) i32 =
const getDS: *const fn (*anyopaque, *?*anyopaque) callconv(hook.cc.stdcall) i32 =
@ptrFromInt(vt(device)[types.VT.GetDepthStencilSurface]);
if (getDS(device, &pDS) < 0) return;
const ds = pDS orelse return;
defer comRelease(ds);
var desc: types.D3DSURFACE_DESC = .{};
const getDesc: *const fn (*anyopaque, *types.D3DSURFACE_DESC) callconv(sc) i32 =
const getDesc: *const fn (*anyopaque, *types.D3DSURFACE_DESC) callconv(hook.cc.stdcall) i32 =
@ptrFromInt(vt(ds)[12]);
if (getDesc(ds, &desc) < 0) return;
if (hasStencilBits(desc.Format)) return;
var pSwap: ?*anyopaque = null;
const getSC: *const fn (*anyopaque, u32, *?*anyopaque) callconv(sc) i32 =
const getSC: *const fn (*anyopaque, u32, *?*anyopaque) callconv(hook.cc.stdcall) i32 =
@ptrFromInt(vt(device)[types.VT.GetSwapChain]);
if (getSC(device, 0, &pSwap) < 0) return;
const swap = pSwap orelse return;
defer comRelease(swap);
var pp: types.D3DPRESENT_PARAMETERS = .{};
const getPP: *const fn (*anyopaque, *types.D3DPRESENT_PARAMETERS) callconv(sc) i32 =
const getPP: *const fn (*anyopaque, *types.D3DPRESENT_PARAMETERS) callconv(hook.cc.stdcall) i32 =
@ptrFromInt(vt(swap)[9]);
if (getPP(swap, &pp) < 0) return;
@@ -1208,7 +1207,7 @@ fn forceD24S8IfNeeded(device: *anyopaque) void {
releaseShaders();
releaseResources();
const resetFn: *const fn (*anyopaque, *types.D3DPRESENT_PARAMETERS) callconv(sc) i32 =
const resetFn: *const fn (*anyopaque, *types.D3DPRESENT_PARAMETERS) callconv(hook.cc.stdcall) i32 =
@ptrFromInt(vt(device)[types.VT.Reset]);
_ = resetFn(device, &pp);
}
+6 -7
View File
@@ -25,15 +25,14 @@ const wow = @import("wow.zig");
// Calling convention constants
// =============================================================================
const tc: std.builtin.CallingConvention = .{ .x86_thiscall = .{} };
// =============================================================================
// Hook state
// =============================================================================
const RenderDrawFn = fn (u32, u32, u32, u32, u32) callconv(tc) void;
const ManageRenderFn = fn (u32, u32) callconv(tc) void;
const DrawBatchFn = fn (u32) callconv(tc) void;
const RenderDrawFn = fn (u32, u32, u32, u32, u32) callconv(hook.cc.thiscall) void;
const ManageRenderFn = fn (u32, u32) callconv(hook.cc.thiscall) void;
const DrawBatchFn = fn (u32) callconv(hook.cc.thiscall) void;
var render_draw_hook: hook.Detour(RenderDrawFn) = .{};
var manage_render_hook: hook.Detour(ManageRenderFn) = .{};
@@ -75,7 +74,7 @@ var reordered_indices: [MAX_REORDER]i32 = undefined;
// through other players and gear (since those aren't in depth when stencil
// is written). The outline composites on top of everything in EndScene.
fn renderDrawDetour(this: u32, view_matrix: u32, batch_data: u32, batch_indices: u32, batch_count: u32) callconv(tc) void {
fn renderDrawDetour(this: u32, view_matrix: u32, batch_data: u32, batch_indices: u32, batch_count: u32) callconv(hook.cc.thiscall) void {
// One-time: install D3D9 hooks now that the game is actively rendering.
if (d3d9_deferred_pending) {
d3d9_deferred_pending = false;
@@ -147,7 +146,7 @@ fn renderDrawDetour(this: u32, view_matrix: u32, batch_data: u32, batch_indices:
// __thiscall(model_ECX, addToList_stack)
// Native thiscall detour - no thunk needed.
fn manageRenderDetour(model: u32, add_to_list: u32) callconv(tc) void {
fn manageRenderDetour(model: u32, add_to_list: u32) callconv(hook.cc.thiscall) void {
// Classify the model when it's being ADDED to the render list
if (add_to_list == 1 and model != 0 and tracker.enabled and tracker.hasTargets()) {
tracker.classifyModel(model);
@@ -165,7 +164,7 @@ fn manageRenderDetour(model: u32, add_to_list: u32) callconv(tc) void {
// wrong ret instructions for functions with ≤2 register params. The naked
// function bridges fastcall → cdecl and calls the implementation function.
fn drawBatchProjDetour(ctx: u32) callconv(tc) void {
fn drawBatchProjDetour(ctx: u32) callconv(hook.cc.thiscall) void {
// Fast path: no tracking enabled or nothing tracked → just call original
if (!tracker.enabled or !tracker.hasTargets()) {
draw_batch_hook.callOriginal(.{ctx});
+3 -6
View File
@@ -14,9 +14,6 @@ const types = @import("types.zig");
// =============================================================================
const WINAPI = std.builtin.CallingConvention.winapi;
const fc: std.builtin.CallingConvention = .{ .x86_fastcall = .{} };
const sc: std.builtin.CallingConvention = .{ .x86_stdcall = .{} };
const tc: std.builtin.CallingConvention = .{ .x86_thiscall = .{} };
extern "kernel32" fn IsBadReadPtr(
lp: ?*const anyopaque,
@@ -145,7 +142,7 @@ pub fn resolveModelOwner(model: u32) u32 {
/// UnitGUID("player") / UnitGUID("target") → 64-bit GUID.
/// __fastcall(unitIdStr_ECX) → EDX:EAX (u64).
pub fn unitGUID(unit_id: [*:0]const u8) u64 {
return hook.call(fn ([*:0]const u8) callconv(fc) u64, o.FN_UNIT_GUID, .{unit_id});
return hook.call(fn ([*:0]const u8) callconv(hook.cc.fastcall) u64, o.FN_UNIT_GUID, .{unit_id});
}
/// Resolve a GUID → object pointer via the object manager hash table.
@@ -154,7 +151,7 @@ pub fn getObjectByGUID(guid: u64) u32 {
if (guid == 0) return 0;
const lo: u32 = @truncate(guid);
const hi: u32 = @truncate(guid >> 32);
return hook.call(fn (u32, u32) callconv(sc) u32, o.FN_GET_OBJECT_BY_GUID, .{ lo, hi });
return hook.call(fn (u32, u32) callconv(hook.cc.stdcall) u32, o.FN_GET_OBJECT_BY_GUID, .{ lo, hi });
}
/// Get the local player's object pointer.
@@ -174,7 +171,7 @@ pub fn getTargetGUID() u64 {
/// Reaction >= 4 means friendly.
pub fn isUnitFriendly(unit: u32, local_player: u32) bool {
if (unit == 0 or local_player == 0) return false;
const reaction = hook.call(fn (u32, u32) callconv(tc) i32, o.FN_UNIT_REACTION, .{ local_player, unit });
const reaction = hook.call(fn (u32, u32) callconv(hook.cc.thiscall) i32, o.FN_UNIT_REACTION, .{ local_player, unit });
return reaction >= 4;
}
+4 -6
View File
@@ -53,23 +53,21 @@ const ERROR_ALREADY_EXISTS: u32 = 183;
// State
// =============================================================================
const tc: std.builtin.CallingConvention = .{ .x86_thiscall = .{} };
const fc: std.builtin.CallingConvention = .{ .x86_fastcall = .{} };
// CVar for compression level persistence (0–9, default 6)
const CVAR_NAME = "screenshotQuality";
const CVAR_LOOKUP: usize = 0x0063DEC0;
const RegisterCVarFn = *const fn ([*:0]const u8, u32, u32, [*:0]const u8, u32, u32, u32, u32) callconv(fc) u32;
const RegisterCVarFn = *const fn ([*:0]const u8, u32, u32, [*:0]const u8, u32, u32, u32, u32) callconv(hook.cc.fastcall) u32;
const registerCVar: RegisterCVarFn = @ptrFromInt(0x0063DB90);
fn readCVarQuality() i32 {
const cvar_ptr = hook.fastcall(u32, CVAR_LOOKUP, @intFromPtr(@as([*:0]const u8, CVAR_NAME)), @as(u32, 0));
const cvar_ptr = hook.call(fn ([*:0]const u8) callconv(hook.cc.fastcall) u32, CVAR_LOOKUP, .{CVAR_NAME});
if (cvar_ptr == 0) return 6;
const val = hook.readMem(i32, cvar_ptr + 40);
return std.math.clamp(val, 0, 9);
}
const TgaWriteFn = fn (u32, u32) callconv(tc) i32;
const TgaWriteFn = fn (u32, u32) callconv(hook.cc.thiscall) i32;
var tga_hook: hook.Detour(TgaWriteFn) = .{};
var screenshot_dir: [260]u8 = undefined;
var screenshot_dir_len: usize = 0;
@@ -153,7 +151,7 @@ fn callOriginal(self: u32, filename: u32) i32 {
// Thunked from __fastcall(self_ECX, _EDX, filename_stack) → cdecl
// =============================================================================
fn tgaWriteDetour(self: u32, filename: u32) callconv(tc) i32 {
fn tgaWriteDetour(self: u32, filename: u32) callconv(hook.cc.thiscall) i32 {
// CVar 0 = disabled, fall through to original TGA write
const quality = readCVarQuality();
if (quality == 0) return callOriginal(self, filename);
+10 -13
View File
@@ -61,13 +61,13 @@ const DISPLAY_INFO_TABLE_PTR: usize = 0x00c0de90;
// =============================================================================
fn unitGUID(unit_id: [*:0]const u8) u64 {
return hook.call(fn ([*:0]const u8) callconv(fc) u64, ADDR_UnitGUID, .{unit_id});
return hook.call(fn ([*:0]const u8) callconv(hook.cc.fastcall) u64, ADDR_UnitGUID, .{unit_id});
}
fn getObjectByGUID(guid: u64) u32 {
const lo: u32 = @truncate(guid);
const hi: u32 = @truncate(guid >> 32);
return hook.call(fn (u32, u32) callconv(sc) u32, ADDR_GetObjectByGUID, .{ lo, hi });
return hook.call(fn (u32, u32) callconv(hook.cc.stdcall) u32, ADDR_GetObjectByGUID, .{ lo, hi });
}
fn updateInventoryAlertStates() void {
@@ -76,11 +76,11 @@ fn updateInventoryAlertStates() void {
}
fn refreshAppearanceAndEquipment(unit: u32) void {
hook.fastcall(void, ADDR_RefreshAppearance, unit, @as(u32, 0));
hook.call(fn (u32) callconv(hook.cc.fastcall) void, ADDR_RefreshAppearance, .{unit});
}
fn refreshEquipmentDisplay(unit: u32) void {
hook.fastcall(void, ADDR_RefreshEquipmentDisplay, unit, @as(u32, 0));
hook.call(fn (u32) callconv(hook.cc.fastcall) void, ADDR_RefreshEquipmentDisplay, .{unit});
}
// =============================================================================
@@ -153,12 +153,9 @@ pub fn isActive() bool {
// Hooks
// =============================================================================
const tc: std.builtin.CallingConvention = .{ .x86_thiscall = .{} };
const fc: std.builtin.CallingConvention = .{ .x86_fastcall = .{} };
const sc: std.builtin.CallingConvention = .{ .x86_stdcall = .{} };
const SetBlockFn = fn (u32, u32, u32) callconv(tc) u32;
const RefreshFn = fn (u32, u32, u32, u32) callconv(tc) void;
const SceneEndFn = fn (u32) callconv(tc) void;
const SetBlockFn = fn (u32, u32, u32) callconv(hook.cc.thiscall) u32;
const RefreshFn = fn (u32, u32, u32, u32) callconv(hook.cc.thiscall) void;
const SceneEndFn = fn (u32) callconv(hook.cc.thiscall) void;
var set_block_hook: hook.Detour(SetBlockFn) = .{};
var refresh_hook: hook.Detour(RefreshFn) = .{};
@@ -473,7 +470,7 @@ fn processTimeouts(now: u32) void {
// Hook 1: SetBlock (0x6142E0)
// =============================================================================
fn hookSetBlock(obj: u32, index: u32, value: u32) callconv(tc) u32 {
fn hookSetBlock(obj: u32, index: u32, value: u32) callconv(hook.cc.thiscall) u32 {
const val = value;
// VISIBLE_ITEM writes
@@ -648,7 +645,7 @@ fn hookSetBlock(obj: u32, index: u32, value: u32) callconv(tc) u32 {
// Hook 2: RefreshVisualAppearance (0x5fb880)
// =============================================================================
fn hookRefreshVisualAppearance(unit: u32, event_data: u32, extra_data: u32, force_update: u32) callconv(tc) void {
fn hookRefreshVisualAppearance(unit: u32, event_data: u32, extra_data: u32, force_update: u32) callconv(hook.cc.thiscall) void {
if (!g_enabled or refresh_hook.inner.trampoline == 0) {
if (refresh_hook.inner.trampoline != 0) {
callOriginalRefresh(unit, event_data, extra_data, force_update);
@@ -754,7 +751,7 @@ fn hookRefreshVisualAppearance(unit: u32, event_data: u32, extra_data: u32, forc
// Hook 3: SceneEnd (0x5a17a0)
// =============================================================================
fn hookSceneEnd(device: u32) callconv(tc) void {
fn hookSceneEnd(device: u32) callconv(hook.cc.thiscall) void {
if (g_enabled and (g_local_pending_count > 0 or g_other_pending_count > 0)) {
processTimeouts(GetTickCount());
}