Migrate inline asm call sites to hook.call()
Replace hand-written inline asm blocks with hook.call() typed function pointer dispatch across 10 files. Also migrates 4 D3D9 COM vtable NULL-dispatch blocks using ?*anyopaque optional pointers. Net removal: ~350 lines of inline asm replaced by single-line calls.
This commit is contained in:
+1
-6
@@ -343,12 +343,7 @@ fn hideAddonFromList(name: [*:0]const u8) void {
|
||||
}
|
||||
|
||||
fn callLoadAddonTOC(addon_name: [*:0]const u8) void {
|
||||
asm volatile (
|
||||
\\call *%[func]
|
||||
:
|
||||
: [_] "{ecx}" (@intFromPtr(addon_name)),
|
||||
[func] "{eax}" (@as(u32, 0x0051c9b0)),
|
||||
: .{ .eax = true, .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
hook.call(fn ([*:0]const u8) callconv(fc) void, 0x0051c9b0, .{addon_name});
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
@@ -15,6 +15,7 @@ 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";
|
||||
|
||||
@@ -556,17 +557,7 @@ fn hkShowCursor(device: *anyopaque, bShow: i32) callconv(sc) i32 {
|
||||
// =============================================================================
|
||||
|
||||
fn luaPushNumber(L_ptr: usize, n: f64) void {
|
||||
const raw: [2]u32 = @bitCast(n);
|
||||
asm volatile (
|
||||
\\push %[hi]
|
||||
\\push %[lo]
|
||||
\\call *%[func]
|
||||
:
|
||||
: [_] "{ecx}" (L_ptr),
|
||||
[lo] "r" (raw[0]),
|
||||
[hi] "r" (raw[1]),
|
||||
[func] "r" (@as(u32, 0x6F3810)),
|
||||
: .{ .eax = true, .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
hook.call(fn (usize, f64) callconv(tc) void, 0x6F3810, .{ L_ptr, n });
|
||||
}
|
||||
|
||||
pub fn luaSetCursorScale(L: *anyopaque) callconv(.c) u32 {
|
||||
|
||||
+11
-41
@@ -51,23 +51,21 @@ const C3Vector = struct {
|
||||
z: f32,
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
// Calling Conventions
|
||||
// =============================================================================
|
||||
|
||||
const sc: std.builtin.CallingConvention = .{ .x86_stdcall = .{} };
|
||||
const tc: std.builtin.CallingConvention = .{ .x86_thiscall = .{} };
|
||||
|
||||
// =============================================================================
|
||||
// Game API
|
||||
// =============================================================================
|
||||
|
||||
fn getObjectPointer(guid: u64) u32 {
|
||||
// __stdcall(u32 guidLow, u32 guidHigh): both on stack, callee cleans (ret 8)
|
||||
const lo: u32 = @truncate(guid);
|
||||
const hi: u32 = @truncate(guid >> 32);
|
||||
return asm volatile (
|
||||
\\push %[hi]
|
||||
\\push %[lo]
|
||||
\\call *%[func]
|
||||
: [ret] "={eax}" (-> u32),
|
||||
: [lo] "r" (lo),
|
||||
[hi] "r" (hi),
|
||||
[func] "r" (@as(u32, Offsets.FUN_GET_OBJECT_POINTER)),
|
||||
: .{ .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
return hook.call(fn (u32, u32) callconv(sc) u32, Offsets.FUN_GET_OBJECT_POINTER, .{ lo, hi });
|
||||
}
|
||||
|
||||
fn isInWorld() bool {
|
||||
@@ -106,30 +104,13 @@ fn isUnitSkinnable(unit: u32) bool {
|
||||
}
|
||||
|
||||
fn setTarget(guid: u64) void {
|
||||
// __stdcall(uint64_t guid): push hi, push lo, callee cleans 8
|
||||
const lo: u32 = @truncate(guid);
|
||||
const hi: u32 = @truncate(guid >> 32);
|
||||
asm volatile (
|
||||
\\push %[hi]
|
||||
\\push %[lo]
|
||||
\\call *%[func]
|
||||
:
|
||||
: [lo] "r" (lo),
|
||||
[hi] "r" (hi),
|
||||
[func] "r" (@as(u32, Offsets.FUN_SET_TARGET)),
|
||||
: .{ .eax = true, .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
hook.call(fn (u32, u32) callconv(sc) void, Offsets.FUN_SET_TARGET, .{ lo, hi });
|
||||
}
|
||||
|
||||
fn rightClickInteract(pointer: u32, autoloot: i32, fun_ptr: usize) void {
|
||||
// __thiscall: ECX=this (pointer), push autoloot, callee cleans 4
|
||||
asm volatile (
|
||||
\\push %[autoloot]
|
||||
\\call *%[func]
|
||||
:
|
||||
: [_] "{ecx}" (pointer),
|
||||
[autoloot] "r" (autoloot),
|
||||
[func] "r" (fun_ptr),
|
||||
: .{ .eax = true, .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
hook.call(fn (u32, i32) callconv(tc) void, fun_ptr, .{ pointer, autoloot });
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
@@ -165,17 +146,7 @@ fn luaToNumber(L: *anyopaque, idx: i32) f64 {
|
||||
}
|
||||
|
||||
fn luaPrintError(L: *anyopaque, msg: [*:0]const u8) void {
|
||||
// __cdecl(lua_State*, const char*): both on stack, caller cleans
|
||||
asm volatile (
|
||||
\\push %[msg]
|
||||
\\push %[L]
|
||||
\\call *%[func]
|
||||
\\add $8, %%esp
|
||||
:
|
||||
: [L] "r" (@intFromPtr(L)),
|
||||
[msg] "r" (@intFromPtr(msg)),
|
||||
[func] "r" (@as(u32, Offsets.LUA_ERROR)),
|
||||
: .{ .eax = true, .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
hook.call(fn (*anyopaque, [*:0]const u8) callconv(.c) void, Offsets.LUA_ERROR, .{ L, msg });
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
@@ -393,7 +364,6 @@ pub fn lootAllCorpses(_: *anyopaque) callconv(.c) u32 {
|
||||
// Per-frame hook for processing the loot queue.
|
||||
// =============================================================================
|
||||
|
||||
const tc: std.builtin.CallingConvention = .{ .x86_thiscall = .{} };
|
||||
const SceneEndFn = fn (u32) callconv(tc) void;
|
||||
var scene_end_hook: hook.Detour(SceneEndFn) = .{};
|
||||
|
||||
|
||||
+2
-10
@@ -127,16 +127,8 @@ pub fn pcall(L: State, nargs: i32, nresults: i32, errfunc: i32) i32 {
|
||||
}
|
||||
|
||||
pub fn luaError(L: State, msg: [*:0]const u8) void {
|
||||
asm volatile (
|
||||
\\push %[msg]
|
||||
\\push %[L]
|
||||
\\call *%[func]
|
||||
\\add $8, %%esp
|
||||
:
|
||||
: [L] "r" (@intFromPtr(L)),
|
||||
[msg] "r" (@intFromPtr(msg)),
|
||||
[func] "r" (@as(u32, 0x6F4940)),
|
||||
: .{ .eax = true, .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
const hook = @import("zhook");
|
||||
hook.call(fn (*anyopaque, [*:0]const u8) callconv(.c) void, 0x6F4940, .{ L, msg });
|
||||
}
|
||||
|
||||
pub const LuaReg = extern struct {
|
||||
|
||||
+8
-35
@@ -59,17 +59,9 @@ fn registerFunction(name: [*:0]const u8, func_addr: usize) void {
|
||||
}
|
||||
|
||||
fn allocateGameBuffer(size: u32) ?[*]u8 {
|
||||
return asm volatile (
|
||||
\\push $0
|
||||
\\push $0
|
||||
\\push %[src]
|
||||
\\push %[size]
|
||||
\\call *%[func]
|
||||
: [ret] "={eax}" (-> ?[*]u8),
|
||||
: [size] "r" (size),
|
||||
[src] "r" (@intFromPtr(@as([*:0]const u8, "weirdutils"))),
|
||||
[func] "r" (@as(u32, 0x6462E0)),
|
||||
: .{ .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
return hook.call(fn (u32, u32, u32, u32) callconv(sc) ?[*]u8, 0x6462E0, .{
|
||||
size, @intFromPtr(@as([*:0]const u8, "weirdutils")), 0, 0,
|
||||
});
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
@@ -207,38 +199,19 @@ fn isFakeFileContext(ctx_addr: u32) bool {
|
||||
|
||||
/// Call initializeFileContext (0x647290) - __thiscall(ECX=ctx, type)
|
||||
fn callInitFileContext(ctx: [*]u8, file_type: u32) void {
|
||||
asm volatile (
|
||||
\\push %[ftype]
|
||||
\\call *%[func]
|
||||
:
|
||||
: [_] "{ecx}" (@intFromPtr(ctx)),
|
||||
[ftype] "r" (file_type),
|
||||
[func] "r" (@as(u32, 0x647290)),
|
||||
: .{ .eax = true, .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
hook.call(fn (u32, u32) callconv(tc) void, 0x647290, .{ @intFromPtr(ctx), file_type });
|
||||
}
|
||||
|
||||
/// Call cleanupFileContext (0x6472d0) - __thiscall(ECX=ctx)
|
||||
fn callCleanupFileContext(ctx: [*]u8) void {
|
||||
asm volatile (
|
||||
\\call *%[func]
|
||||
:
|
||||
: [_] "{ecx}" (@intFromPtr(ctx)),
|
||||
[func] "r" (@as(u32, 0x6472d0)),
|
||||
: .{ .eax = true, .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
hook.call(fn (u32) callconv(tc) void, 0x6472d0, .{@intFromPtr(ctx)});
|
||||
}
|
||||
|
||||
/// Free a buffer via FreeMemory/SMemFree (0x646430) - __stdcall(ptr, src, flags)
|
||||
fn freeGameBuffer(ptr: [*]u8) void {
|
||||
asm volatile (
|
||||
\\push $0xffffffff
|
||||
\\push %[src]
|
||||
\\push %[ptr]
|
||||
\\call *%[func]
|
||||
:
|
||||
: [ptr] "r" (@intFromPtr(ptr)),
|
||||
[src] "r" (@intFromPtr(@as([*:0]const u8, "weirdutils"))),
|
||||
[func] "r" (@as(u32, 0x646430)),
|
||||
: .{ .eax = true, .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
hook.call(fn (u32, u32, u32) callconv(sc) void, 0x646430, .{
|
||||
@intFromPtr(ptr), @intFromPtr(@as([*:0]const u8, "weirdutils")), 0xffffffff,
|
||||
});
|
||||
}
|
||||
|
||||
// --- Hook 1: openFileWithOptions (0x6477c0) ---
|
||||
|
||||
+14
-93
@@ -116,22 +116,16 @@ 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
|
||||
// =============================================================================
|
||||
|
||||
/// Get local player GUID via GetPlayerGUID (0x468550).
|
||||
/// __fastcall(), no params, returns EAX(low):EDX(high).
|
||||
/// __fastcall(), no params, returns u64 via EDX:EAX.
|
||||
fn getPlayerGUID() u64 {
|
||||
var lo: u32 = undefined;
|
||||
var hi: u32 = undefined;
|
||||
asm volatile ("call *%[func]"
|
||||
: [_] "={eax}" (lo),
|
||||
[_] "={edx}" (hi),
|
||||
: [func] "r" (o.FN_GET_PLAYER_GUID),
|
||||
: .{ .ecx = true, .memory = true, .cc = true });
|
||||
return (@as(u64, hi) << 32) | lo;
|
||||
return hook.call(fn () callconv(fc) u64, o.FN_GET_PLAYER_GUID, .{});
|
||||
}
|
||||
|
||||
/// Look up a player name from the name cache by GUID.
|
||||
@@ -139,27 +133,9 @@ 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 stack_args = [6]u32{
|
||||
guid_lo,
|
||||
guid_hi,
|
||||
@intFromPtr(&name_buf),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
};
|
||||
const result: u32 = asm volatile (
|
||||
\\ push 20(%[a])
|
||||
\\ push 16(%[a])
|
||||
\\ push 12(%[a])
|
||||
\\ push 8(%[a])
|
||||
\\ push 4(%[a])
|
||||
\\ push (%[a])
|
||||
\\ call *%[func]
|
||||
: [ret] "={eax}" (-> u32),
|
||||
: [_] "{ecx}" (@as(u32, o.NAME_CACHE_OBJ)),
|
||||
[a] "r" (&stack_args),
|
||||
[func] "r" (@as(u32, o.FN_NAME_CACHE_LOOKUP)),
|
||||
: .{ .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
const result = hook.call(fn (u32, u32, u32, u32, u32, u32, u32) callconv(tc) 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;
|
||||
}
|
||||
|
||||
@@ -310,39 +286,15 @@ 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 facing_bits: u32 = @bitCast(facing);
|
||||
const stack_args = [5]u32{
|
||||
facing_bits,
|
||||
flags,
|
||||
update_now,
|
||||
0,
|
||||
0,
|
||||
};
|
||||
|
||||
const result: u32 = asm volatile (
|
||||
\\ push 16(%[a])
|
||||
\\ push 12(%[a])
|
||||
\\ push 8(%[a])
|
||||
\\ push 4(%[a])
|
||||
\\ push (%[a])
|
||||
\\ call *%[func]
|
||||
: [ret] "={eax}" (-> u32),
|
||||
: [_] "{ecx}" (@intFromPtr(path)),
|
||||
[_] "{edx}" (@intFromPtr(pos)),
|
||||
[a] "r" (&stack_args),
|
||||
[func] "r" (o.FN_CREATE_ENTITY_INSTANCE),
|
||||
: .{ .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
|
||||
const result = hook.call(fn ([*:0]const u8, *[3]f32, f32, u32, u32, u32, u32) callconv(fc) u32, o.FN_CREATE_ENTITY_INSTANCE, .{
|
||||
path, pos, facing, flags, update_now, 0, 0,
|
||||
});
|
||||
return if (result != 0) @ptrFromInt(result) else null;
|
||||
}
|
||||
|
||||
/// CleanupEntity_ProcessAttachments - __fastcall(ECX=entity), no stack params.
|
||||
fn cleanupEntity(obj: *anyopaque) void {
|
||||
asm volatile ("call *%[func]"
|
||||
:
|
||||
: [_] "{ecx}" (@intFromPtr(obj)),
|
||||
[func] "r" (o.FN_CLEANUP_ENTITY),
|
||||
: .{ .eax = true, .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
hook.call(fn (*anyopaque) callconv(fc) void, o.FN_CLEANUP_ENTITY, .{obj});
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
@@ -356,31 +308,9 @@ 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;
|
||||
|
||||
const speed_bits: u32 = @bitCast(@as(f32, 1.0));
|
||||
const stack_args = [7]u32{
|
||||
0xFFFFFFFF, // boneIndex: all bones
|
||||
anim_id,
|
||||
@bitCast(@as(i32, -1)), // seqIndex: random
|
||||
0, // animData: NULL
|
||||
speed_bits, // speed: 1.0
|
||||
1, // blendMode: smooth blend
|
||||
@intFromBool(queue),
|
||||
};
|
||||
|
||||
asm volatile (
|
||||
\\ push 24(%[a])
|
||||
\\ push 20(%[a])
|
||||
\\ push 16(%[a])
|
||||
\\ push 12(%[a])
|
||||
\\ push 8(%[a])
|
||||
\\ push 4(%[a])
|
||||
\\ push (%[a])
|
||||
\\ call *%[func]
|
||||
:
|
||||
: [_] "{ecx}" (model),
|
||||
[a] "r" (&stack_args),
|
||||
[func] "r" (o.FN_PLAY_BONE_ANIMATION),
|
||||
: .{ .eax = true, .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
hook.call(fn (u32, u32, u32, i32, u32, u32, u32, u32) callconv(tc) void, o.FN_PLAY_BONE_ANIMATION, .{
|
||||
model, 0xFFFFFFFF, anim_id, -1, 0, @as(u32, @bitCast(@as(f32, 1.0))), 1, @intFromBool(queue),
|
||||
});
|
||||
}
|
||||
|
||||
/// Clean up despawning entities whose Decay animation has finished.
|
||||
@@ -475,16 +405,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 {
|
||||
const facing_bits: u32 = @bitCast(facing);
|
||||
asm volatile (
|
||||
\\ push %[facing]
|
||||
\\ call *%[func]
|
||||
:
|
||||
: [_] "{ecx}" (@intFromPtr(entity)),
|
||||
[_] "{edx}" (@intFromPtr(pos)),
|
||||
[facing] "r" (facing_bits),
|
||||
[func] "r" (@as(u32, 0x698e20)),
|
||||
: .{ .eax = true, .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
hook.call(fn (*anyopaque, *[3]f32, f32) callconv(fc) void, 0x698e20, .{ entity, pos, facing });
|
||||
}
|
||||
|
||||
/// Remove only the live entity for a marker slot (def untouched).
|
||||
|
||||
@@ -21,6 +21,7 @@ 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";
|
||||
|
||||
@@ -364,15 +365,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 asm volatile (
|
||||
\\push %[hi]
|
||||
\\push %[lo]
|
||||
\\call *%[func]
|
||||
: [ret] "={eax}" (-> u32),
|
||||
: [lo] "r" (guid_lo),
|
||||
[hi] "r" (guid_hi),
|
||||
[func] "r" (@as(u32, ADDR.GetObjectByGUID)),
|
||||
: .{ .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
return hook.call(fn (u32, u32) callconv(sc) u32, ADDR.GetObjectByGUID, .{ guid_lo, guid_hi });
|
||||
}
|
||||
|
||||
fn getObjectType(obj: u32) u32 {
|
||||
@@ -468,14 +461,7 @@ fn getSummonedByGUID(obj: u32) u64 {
|
||||
}
|
||||
|
||||
fn getActivePlayerGUID() u64 {
|
||||
var lo: u32 = undefined;
|
||||
var hi: u32 = undefined;
|
||||
asm volatile ("call *%[func]"
|
||||
: [_] "={eax}" (lo),
|
||||
[_] "={edx}" (hi),
|
||||
: [func] "r" (@as(u32, ADDR.ClntObjMgrGetActivePlayer)),
|
||||
: .{ .ecx = true, .memory = true, .cc = true });
|
||||
return (@as(u64, hi) << 32) | lo;
|
||||
return hook.call(fn () callconv(fc) u64, ADDR.ClntObjMgrGetActivePlayer, .{});
|
||||
}
|
||||
|
||||
fn getActivePlayerObject() u32 {
|
||||
@@ -487,14 +473,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 asm volatile (
|
||||
\\push %[unit]
|
||||
\\call *%[func]
|
||||
: [ret] "={eax}" (-> i32),
|
||||
: [_] "{ecx}" (local_player),
|
||||
[unit] "r" (unit),
|
||||
[func] "r" (@as(u32, ADDR.UnitReaction)),
|
||||
: .{ .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
return hook.call(fn (u32, u32) callconv(tc) i32, ADDR.UnitReaction, .{ local_player, unit });
|
||||
}
|
||||
|
||||
/// Check if a unit passes faction/friendliness filters.
|
||||
@@ -535,11 +514,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(asm volatile ("call *%[func]"
|
||||
: [ret] "={eax}" (-> u32),
|
||||
: [_] "{ecx}" (obj),
|
||||
[func] "r" (@as(u32, ADDR.CallSpellCastHandler)),
|
||||
: .{ .ecx = true, .edx = true, .memory = true, .cc = true }));
|
||||
const result: u8 = @truncate(hook.call(fn (u32) callconv(fc) u32, ADDR.CallSpellCastHandler, .{obj}));
|
||||
if (result == 0) {
|
||||
con.fmt("[minimapicons] GO 0x{x} rejected: not interactable (entry={d})\n", .{ obj, getObjectEntry(obj) });
|
||||
}
|
||||
@@ -574,14 +549,7 @@ fn getObjectPosition(obj: u32) ?C3Vector {
|
||||
if (!isValidPtr(get_pos_fn)) return null;
|
||||
var pos: C3Vector = undefined;
|
||||
// __thiscall(obj_ECX, &pos_stack) → C3Vector*
|
||||
asm volatile (
|
||||
\\push %[out]
|
||||
\\call *%[func]
|
||||
:
|
||||
: [_] "{ecx}" (obj),
|
||||
[out] "r" (@intFromPtr(&pos)),
|
||||
[func] "r" (get_pos_fn),
|
||||
: .{ .eax = true, .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
_ = hook.call(fn (u32, *C3Vector) callconv(tc) u32, get_pos_fn, .{ obj, &pos });
|
||||
return pos;
|
||||
}
|
||||
|
||||
@@ -600,32 +568,10 @@ fn worldPosToMinimapCoords(
|
||||
) void {
|
||||
// WorldPosToMinimapFrameCoords: __fastcall(out_ECX, edx, C3Vector cur, float radius,
|
||||
// float worldX, float worldY, float layoutScale, float unkScale)
|
||||
// 8 stack params (32 bytes), callee cleanup
|
||||
const args = [8]u32{
|
||||
@bitCast(cur.x),
|
||||
@bitCast(cur.y),
|
||||
@bitCast(cur.z),
|
||||
@bitCast(radius),
|
||||
@bitCast(world_x),
|
||||
@bitCast(world_y),
|
||||
@bitCast(layout_scale),
|
||||
@bitCast(unk_scale),
|
||||
};
|
||||
_ = asm volatile (
|
||||
\\pushl 28(%[args])
|
||||
\\pushl 24(%[args])
|
||||
\\pushl 20(%[args])
|
||||
\\pushl 16(%[args])
|
||||
\\pushl 12(%[args])
|
||||
\\pushl 8(%[args])
|
||||
\\pushl 4(%[args])
|
||||
\\pushl (%[args])
|
||||
\\call *%[func]
|
||||
: [ret] "={eax}" (-> u32),
|
||||
: [_] "{ecx}" (@intFromPtr(out)),
|
||||
[args] "r" (&args),
|
||||
[func] "r" (@as(u32, ADDR.WorldPosToMinimapCoords)),
|
||||
: .{ .eax = true, .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
// 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, .{
|
||||
out, cur.x, cur.y, cur.z, radius, world_x, world_y, layout_scale, unk_scale,
|
||||
});
|
||||
}
|
||||
|
||||
fn getFrameUnkScale(info: u32) f32 {
|
||||
@@ -637,12 +583,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 asm volatile (
|
||||
\\call *%[func]
|
||||
: [ret] "={st}" (-> f32),
|
||||
: [_] "{ecx}" (fsp),
|
||||
[func] "r" (fn_addr),
|
||||
: .{ .eax = true, .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
return hook.call(fn (u32) callconv(tc) f32, fn_addr, .{fsp});
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
@@ -680,17 +621,9 @@ fn loadTexture(path: [*:0]const u8) u32 {
|
||||
defer status.deinit();
|
||||
|
||||
// TextureCreate: __fastcall(filename_ECX, status_EDX, texFlags, unk1, unk2)
|
||||
const texture: u32 = asm volatile (
|
||||
\\pushl $1
|
||||
\\pushl $0
|
||||
\\push %[flags]
|
||||
\\call *%[func]
|
||||
: [ret] "={eax}" (-> u32),
|
||||
: [_] "{ecx}" (@intFromPtr(path)),
|
||||
[_] "{edx}" (@intFromPtr(&status)),
|
||||
[flags] "r" (g_default_tex_flags),
|
||||
[func] "r" (@as(u32, ADDR.TextureCreate)),
|
||||
: .{ .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
const texture = hook.call(fn ([*:0]const u8, *CStatus, u32, u32, u32) callconv(fc) u32, ADDR.TextureCreate, .{
|
||||
path, &status, g_default_tex_flags, 0, 1,
|
||||
});
|
||||
|
||||
if (!status.ok() or texture == 0) {
|
||||
con.print("[minimapicons] Failed to load texture\n");
|
||||
@@ -709,20 +642,9 @@ 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)
|
||||
asm volatile (
|
||||
\\pushl $1
|
||||
\\pushl $0
|
||||
\\pushl $0
|
||||
\\pushl $0
|
||||
\\pushl $0
|
||||
\\pushl $0
|
||||
\\pushl $0
|
||||
\\pushl $0
|
||||
\\call *%[func]
|
||||
:
|
||||
: [_] "{ecx}" (@intFromPtr(&flags)),
|
||||
[func] "r" (@as(u32, ADDR.CGxTexFlagsInit)),
|
||||
: .{ .eax = true, .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
hook.call(fn (*u32, u32, u32, u32, u32, u32, u32, u32, u32) callconv(tc) void, ADDR.CGxTexFlagsInit, .{
|
||||
&flags, 0, 0, 0, 0, 0, 0, 0, 1,
|
||||
});
|
||||
return flags;
|
||||
}
|
||||
|
||||
@@ -736,15 +658,9 @@ fn getGxTex(texture: u32) u32 {
|
||||
status.init();
|
||||
defer status.deinit();
|
||||
|
||||
const gx_tex: u32 = asm volatile (
|
||||
\\push %[status]
|
||||
\\call *%[func]
|
||||
: [ret] "={eax}" (-> u32),
|
||||
: [_] "{ecx}" (texture),
|
||||
[_] "{edx}" (@as(u32, 1)),
|
||||
[status] "r" (@intFromPtr(&status)),
|
||||
[func] "r" (@as(u32, ADDR.TextureGetGxTex)),
|
||||
: .{ .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
const gx_tex = hook.call(fn (u32, u32, *CStatus) callconv(fc) u32, ADDR.TextureGetGxTex, .{
|
||||
texture, 1, &status,
|
||||
});
|
||||
|
||||
if (!status.ok()) return 0;
|
||||
return gx_tex;
|
||||
@@ -767,7 +683,9 @@ fn drawMinimapBlip(pos: C2Vector, scale: f32) void {
|
||||
|
||||
// GxPrimLockVertexPtrs(count=4, vertices, vertStride=12, normal, 0, color, 0,
|
||||
// null, 0, texCoords, 8, null, 0)
|
||||
const lock_args = [11]u32{
|
||||
hook.call(fn (u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32) callconv(fc) void, ADDR.GxPrimLockVertexPtrs, .{
|
||||
4, // count (ECX)
|
||||
@intFromPtr(&vertices), // vertices (EDX)
|
||||
12, // vertStride
|
||||
@as(u32, ADDR.BlipNormal), // normal
|
||||
0, // normalStride
|
||||
@@ -779,26 +697,7 @@ fn drawMinimapBlip(pos: C2Vector, scale: f32) void {
|
||||
8, // texStride
|
||||
0, // texCoords2 (null)
|
||||
0, // tex2Stride
|
||||
};
|
||||
asm volatile (
|
||||
\\pushl 40(%[args])
|
||||
\\pushl 36(%[args])
|
||||
\\pushl 32(%[args])
|
||||
\\pushl 28(%[args])
|
||||
\\pushl 24(%[args])
|
||||
\\pushl 20(%[args])
|
||||
\\pushl 16(%[args])
|
||||
\\pushl 12(%[args])
|
||||
\\pushl 8(%[args])
|
||||
\\pushl 4(%[args])
|
||||
\\pushl (%[args])
|
||||
\\call *%[func]
|
||||
:
|
||||
: [_] "{ecx}" (@as(u32, 4)), // count
|
||||
[_] "{edx}" (@intFromPtr(&vertices)), // vertices
|
||||
[args] "r" (&lock_args),
|
||||
[func] "r" (@as(u32, ADDR.GxPrimLockVertexPtrs)),
|
||||
: .{ .eax = true, .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
});
|
||||
|
||||
// GxPrimDrawElements(TriangleStrip=4, count=4, indices)
|
||||
const drawElements: *const fn (u32, u32, u32) callconv(fc) void = @ptrFromInt(ADDR.GxPrimDrawElements);
|
||||
|
||||
+12
-68
@@ -184,21 +184,10 @@ fn deviceGetPtr(dev: *anyopaque, idx: usize) ?*anyopaque {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/// Set a COM pointer, handling the null case by passing 0 via raw write.
|
||||
/// Set a COM pointer, handling null via optional pointer (ABI-equivalent to passing 0).
|
||||
fn deviceSetPtrOrNull(dev: *anyopaque, idx: usize, ptr: ?*anyopaque) void {
|
||||
if (ptr) |p| {
|
||||
deviceSetPtr(dev, idx, p);
|
||||
} else {
|
||||
const func_addr = vt(dev)[idx];
|
||||
asm volatile (
|
||||
\\push $0
|
||||
\\push %[self]
|
||||
\\call *%[func]
|
||||
:
|
||||
: [self] "r" (@intFromPtr(dev)),
|
||||
[func] "r" (func_addr),
|
||||
: .{ .eax = true, .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
}
|
||||
const f: *const fn (*anyopaque, ?*anyopaque) callconv(sc) i32 = @ptrFromInt(vt(dev)[idx]);
|
||||
_ = @call(.never_tail, f, .{ dev, ptr });
|
||||
}
|
||||
|
||||
fn deviceSetPSConstF(dev: *anyopaque, start: u32, data: *const [4]f32) void {
|
||||
@@ -233,25 +222,10 @@ fn deviceGetRenderTarget(dev: *anyopaque, idx: u32) ?*anyopaque {
|
||||
return surf;
|
||||
}
|
||||
|
||||
/// Set texture on a sampler stage. Handles NULL via inline asm.
|
||||
fn deviceSetTexture(dev: *anyopaque, stage: u32, tex: ?*anyopaque) void {
|
||||
if (tex) |t| {
|
||||
const f: *const fn (*anyopaque, u32, *anyopaque) callconv(sc) i32 =
|
||||
@ptrFromInt(vt(dev)[types.VT.SetTexture]);
|
||||
_ = f(dev, stage, t);
|
||||
} else {
|
||||
const func_addr = vt(dev)[types.VT.SetTexture];
|
||||
asm volatile (
|
||||
\\push $0
|
||||
\\push %[stage]
|
||||
\\push %[self]
|
||||
\\call *%[func]
|
||||
:
|
||||
: [self] "r" (@intFromPtr(dev)),
|
||||
[stage] "r" (stage),
|
||||
[func] "r" (func_addr),
|
||||
: .{ .eax = true, .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
}
|
||||
const f: *const fn (*anyopaque, u32, ?*anyopaque) callconv(sc) i32 =
|
||||
@ptrFromInt(vt(dev)[types.VT.SetTexture]);
|
||||
_ = @call(.never_tail, f, .{ dev, stage, tex });
|
||||
}
|
||||
|
||||
fn deviceSetFVF(dev: *anyopaque, fvf: u32) void {
|
||||
@@ -281,27 +255,9 @@ fn deviceGetStreamSource(dev: *anyopaque, stream: u32, vb_out: *?*anyopaque, off
|
||||
}
|
||||
|
||||
fn deviceSetStreamSource(dev: *anyopaque, stream: u32, vb: ?*anyopaque, offset: u32, stride: u32) void {
|
||||
if (vb) |v| {
|
||||
const f: *const fn (*anyopaque, u32, *anyopaque, u32, u32) callconv(sc) i32 =
|
||||
@ptrFromInt(vt(dev)[types.VT.SetStreamSource]);
|
||||
_ = f(dev, stream, v, offset, stride);
|
||||
} else {
|
||||
// Pass NULL VB via inline asm (Zig's *anyopaque is non-null)
|
||||
const args = [_]u32{ stream, 0, offset, stride };
|
||||
const func_addr = vt(dev)[types.VT.SetStreamSource];
|
||||
asm volatile (
|
||||
\\push 12(%[a])
|
||||
\\push 8(%[a])
|
||||
\\push 4(%[a])
|
||||
\\push (%[a])
|
||||
\\push %[self]
|
||||
\\call *%[func]
|
||||
:
|
||||
: [self] "r" (@intFromPtr(dev)),
|
||||
[a] "r" (&args),
|
||||
[func] "r" (func_addr),
|
||||
: .{ .eax = true, .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
}
|
||||
const f: *const fn (*anyopaque, u32, ?*anyopaque, u32, u32) callconv(sc) i32 =
|
||||
@ptrFromInt(vt(dev)[types.VT.SetStreamSource]);
|
||||
_ = @call(.never_tail, f, .{ dev, stream, vb, offset, stride });
|
||||
}
|
||||
|
||||
fn deviceGetTexture(dev: *anyopaque, stage: u32) ?*anyopaque {
|
||||
@@ -321,21 +277,9 @@ fn deviceGetIndices(dev: *anyopaque) ?*anyopaque {
|
||||
}
|
||||
|
||||
fn deviceSetIndices(dev: *anyopaque, ib: ?*anyopaque) void {
|
||||
if (ib) |i| {
|
||||
const f: *const fn (*anyopaque, *anyopaque) callconv(sc) i32 =
|
||||
@ptrFromInt(vt(dev)[types.VT.SetIndices]);
|
||||
_ = f(dev, i);
|
||||
} else {
|
||||
const func_addr = vt(dev)[types.VT.SetIndices];
|
||||
asm volatile (
|
||||
\\push $0
|
||||
\\push %[self]
|
||||
\\call *%[func]
|
||||
:
|
||||
: [self] "r" (@intFromPtr(dev)),
|
||||
[func] "r" (func_addr),
|
||||
: .{ .eax = true, .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
}
|
||||
const f: *const fn (*anyopaque, ?*anyopaque) callconv(sc) 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 {
|
||||
|
||||
+7
-28
@@ -14,6 +14,9 @@ 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,
|
||||
@@ -140,35 +143,18 @@ pub fn resolveModelOwner(model: u32) u32 {
|
||||
// =============================================================================
|
||||
|
||||
/// UnitGUID("player") / UnitGUID("target") → 64-bit GUID.
|
||||
/// __fastcall(unitIdStr_ECX) → EAX:EDX.
|
||||
/// __fastcall(unitIdStr_ECX) → EDX:EAX (u64).
|
||||
pub fn unitGUID(unit_id: [*:0]const u8) u64 {
|
||||
var lo: u32 = undefined;
|
||||
var hi: u32 = undefined;
|
||||
asm volatile ("call *%[func]"
|
||||
: [_] "={eax}" (lo),
|
||||
[_] "={edx}" (hi),
|
||||
: [_] "{ecx}" (@intFromPtr(unit_id)),
|
||||
[func] "r" (@as(u32, o.FN_UNIT_GUID)),
|
||||
: .{ .memory = true, .cc = true });
|
||||
return (@as(u64, hi) << 32) | lo;
|
||||
return hook.call(fn ([*:0]const u8) callconv(fc) u64, o.FN_UNIT_GUID, .{unit_id});
|
||||
}
|
||||
|
||||
/// Resolve a GUID → object pointer via the object manager hash table.
|
||||
/// Ghidra-verified: __stdcall(guidLow, guidHigh) with RET 8.
|
||||
/// NOT __fastcall - params are read from stack, not registers.
|
||||
pub fn getObjectByGUID(guid: u64) u32 {
|
||||
if (guid == 0) return 0;
|
||||
const lo: u32 = @truncate(guid);
|
||||
const hi: u32 = @truncate(guid >> 32);
|
||||
return asm volatile (
|
||||
\\push %[hi]
|
||||
\\push %[lo]
|
||||
\\call *%[func]
|
||||
: [ret] "={eax}" (-> u32),
|
||||
: [lo] "r" (lo),
|
||||
[hi] "r" (hi),
|
||||
[func] "r" (@as(u32, o.FN_GET_OBJECT_BY_GUID)),
|
||||
: .{ .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
return hook.call(fn (u32, u32) callconv(sc) u32, o.FN_GET_OBJECT_BY_GUID, .{ lo, hi });
|
||||
}
|
||||
|
||||
/// Get the local player's object pointer.
|
||||
@@ -188,14 +174,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: i32 = asm volatile (
|
||||
\\push %[unit]
|
||||
\\call *%[func]
|
||||
: [ret] "={eax}" (-> i32),
|
||||
: [_] "{ecx}" (local_player),
|
||||
[unit] "r" (unit),
|
||||
[func] "r" (@as(u32, o.FN_UNIT_REACTION)),
|
||||
: .{ .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
const reaction = hook.call(fn (u32, u32) callconv(tc) i32, o.FN_UNIT_REACTION, .{ local_player, unit });
|
||||
return reaction >= 4;
|
||||
}
|
||||
|
||||
|
||||
@@ -61,31 +61,13 @@ const DISPLAY_INFO_TABLE_PTR: usize = 0x00c0de90;
|
||||
// =============================================================================
|
||||
|
||||
fn unitGUID(unit_id: [*:0]const u8) u64 {
|
||||
// __fastcall(ECX=str) → u64 in EDX:EAX
|
||||
var lo: u32 = undefined;
|
||||
var hi: u32 = undefined;
|
||||
asm volatile (
|
||||
\\call *%[func]
|
||||
: [lo] "={eax}" (lo),
|
||||
[hi] "={edx}" (hi),
|
||||
: [_] "{ecx}" (unit_id),
|
||||
[func] "r" (@as(u32, ADDR_UnitGUID)),
|
||||
: .{ .memory = true, .cc = true });
|
||||
return @as(u64, hi) << 32 | lo;
|
||||
return hook.call(fn ([*:0]const u8) callconv(fc) u64, ADDR_UnitGUID, .{unit_id});
|
||||
}
|
||||
|
||||
fn getObjectByGUID(guid: u64) u32 {
|
||||
const lo: u32 = @truncate(guid);
|
||||
const hi: u32 = @truncate(guid >> 32);
|
||||
return asm volatile (
|
||||
\\push %[hi]
|
||||
\\push %[lo]
|
||||
\\call *%[func]
|
||||
: [ret] "={eax}" (-> u32),
|
||||
: [lo] "r" (lo),
|
||||
[hi] "r" (hi),
|
||||
[func] "r" (@as(u32, ADDR_GetObjectByGUID)),
|
||||
: .{ .ecx = true, .edx = true, .memory = true, .cc = true });
|
||||
return hook.call(fn (u32, u32) callconv(sc) u32, ADDR_GetObjectByGUID, .{ lo, hi });
|
||||
}
|
||||
|
||||
fn updateInventoryAlertStates() void {
|
||||
@@ -172,6 +154,8 @@ pub fn isActive() bool {
|
||||
// =============================================================================
|
||||
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user