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:
+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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user