Publish source: Unlicense, public README, repo hygiene

The remote was previously a distribution-only point for pre-built DLLs.
This opens the source.

- LICENSE: Unlicense, with a GPL-3.0 carve-out for src/dpslog/WeirdDPSMate
  (a DPSMate fork that keeps its own license)
- README.md replaces the stale internal one with the user-facing docs from
  DLL_README.md, swapping the 'Why No Source Code?' section for build and
  layout notes. DLL_README.md is dropped; one README now serves both.
- RELEASING.md: drop the trim-the-README-per-release dance and the
  remote/WeirdUtils/ distribution clone, both obsolete now
- gitignore agent/editor scratch, build caches, the vendored WSBT addon,
  and the WeirdThreat/uwu-logs checkouts (separate upstream repos)
- Commit outstanding module work: superweirdo, clickthrough portal visuals,
  transform44 decompiles, worldmarkers demo presets, tools/
This commit is contained in:
MarcelineVQ
2026-07-27 21:47:55 -07:00
parent f500147fc7
commit b12fc820ef
42 changed files with 5563 additions and 727 deletions
+1 -1
View File
@@ -1098,7 +1098,7 @@ pub fn calcScaledInverse(this_mat: u32, out: u32, scale: f32) void {
// mat3(offset_vec3*), mat4(scale_float_bits)
// =============================================================================
pub fn transformImpl_SSE(this: u32, mat1: u32, mat2: u32, mat3: u32, mat4: u32) callconv(.{ .x86_thiscall = .{} }) void {
pub export fn transformImpl_SSE(this: u32, mat1: u32, mat2: u32, mat3: u32, mat4: u32) callconv(.{ .x86_thiscall = .{} }) void {
@setEvalBranchQuota(50000);
// =========================================================================
+1 -1
View File
@@ -284,7 +284,7 @@ fn attachmentRecursion64(this: u32, model_hdr: u32, bone_out_base: u32, frame_ct
// scene objects stay on the f64 pipeline.
// =============================================================================
pub fn transformImpl_SSE64(this: u32, mat1: u32, mat2: u32, mat3: u32, mat4: u32) callconv(.{ .x86_thiscall = .{} }) void {
pub export fn transformImpl_SSE64(this: u32, mat1: u32, mat2: u32, mat3: u32, mat4: u32) callconv(.{ .x86_thiscall = .{} }) void {
@setEvalBranchQuota(50000);
// Section 1: Entry checks
+564
View File
@@ -0,0 +1,564 @@
//! transform_capture — snapshot transformMatrix4x4 inputs + outputs for offline
//! bench/parity replay. Enabled via a feature flag in weirdperformance.zig.
//!
//! Design:
//! - Hook transformMatrix4x4 (0x714260).
//! - On entry: compute a path hash from SceneObject flags + model_hdr counts.
//! Skip if already seen.
//! - Walk reachable pointer graph; record each referenced memory page
//! (4KB granularity, keyed by page address). Save INPUT snapshot.
//! - Call the game original via callOriginal.
//! - Save OUTPUT snapshot of all pages we know get written.
//! - Write to disk: one .trxcap file per unique path hash.
//!
//! Replay (bench):
//! - Read .trxcap file.
//! - For each page in the input snapshot, MAP_FIXED at its VA, copy data.
//! - Call transformImpl_SSE64 (or SSE or game's x87).
//! - Diff memory against the saved output snapshot.
//!
//! This captures real game state and gives us true parity coverage, but has a
//! state-pollution avoidance problem in-game: we can't run *both* game x87 and
//! our SSE impls on the same live SceneObject without state bleeding between
//! calls. The solution is to replay offline with each impl starting from a
//! fresh copy of the captured input snapshot.
const std = @import("std");
const hook = @import("zhook");
const logging = @import("../logging.zig");
var log: logging.Logger = .{};
// Win32 file/dir APIs — we run inside a Windows DLL, no libc filesystem.
const HANDLE = *anyopaque;
const INVALID_HANDLE: HANDLE = @ptrFromInt(std.math.maxInt(usize));
extern "kernel32" fn CreateFileA(
name: [*:0]const u8,
desiredAccess: u32,
shareMode: u32,
security: ?*anyopaque,
creationDisposition: u32,
flags: u32,
template: ?HANDLE,
) callconv(.{ .x86_stdcall = .{} }) HANDLE;
extern "kernel32" fn WriteFile(
h: HANDLE,
buf: [*]const u8,
bytes: u32,
written: *u32,
ov: ?*anyopaque,
) callconv(.{ .x86_stdcall = .{} }) i32;
extern "kernel32" fn CloseHandle(h: HANDLE) callconv(.{ .x86_stdcall = .{} }) i32;
extern "kernel32" fn CreateDirectoryA(name: [*:0]const u8, security: ?*anyopaque) callconv(.{ .x86_stdcall = .{} }) i32;
extern "kernel32" fn SetFilePointer(h: HANDLE, lo: i32, hi: ?*i32, method: u32) callconv(.{ .x86_stdcall = .{} }) u32;
const GENERIC_WRITE: u32 = 0x40000000;
const FILE_SHARE_READ: u32 = 0x1;
const CREATE_ALWAYS: u32 = 2;
const OPEN_ALWAYS: u32 = 4;
const FILE_ATTRIBUTE_NORMAL: u32 = 0x80;
const FILE_END: u32 = 2;
fn writeAll(h: HANDLE, data: []const u8) bool {
var written: u32 = 0;
const ok = WriteFile(h, data.ptr, @intCast(data.len), &written, null);
return ok != 0 and written == data.len;
}
pub const module_name: [*:0]const u8 = "transform_capture";
// Hook into game transformMatrix4x4
const TransformFn = fn (u32, u32, u32, u32, u32) callconv(.{ .x86_thiscall = .{} }) void;
var transform_hook: hook.Detour(TransformFn) = .{};
// =============================================================================
// SceneObject offsets (same as bone_sse, duplicated here so we don't couple)
// =============================================================================
const SO_MODEL_DATA_PTR: u32 = 0x010;
const SO_ANIM_CTX_PTR: u32 = 0x02C;
const SO_MODEL_CTR_PTR: u32 = 0x030;
const SO_SYNC_VALUE: u32 = 0x040;
const SO_BONE_RT_BASE: u32 = 0x090;
const SO_BONE_OUT_PTR: u32 = 0x094;
const SO_TEX_ANIM_OUT: u32 = 0x0A0;
const SO_COLOR_ANIM_OUT: u32 = 0x0A8;
const SO_SCALE1: u32 = 0x0AC;
const SO_SCALE2: u32 = 0x0B0;
const SO_SCALE3: u32 = 0x0B4;
const SO_HIERARCHY_PTR: u32 = 0x1C8;
const SO_HIERARCHY_IDX: u32 = 0x1DC;
const SO_FIELD_200: u32 = 0x200;
const SO_PARTICLE1: u32 = 0x3C4;
const SO_PARTICLE2: u32 = 0x3C8;
const SO_PARTICLE3: u32 = 0x3D0;
const SO_PARTICLE4: u32 = 0x3D4;
// =============================================================================
// Memory helpers
// =============================================================================
inline fn ru32(addr: u32) u32 {
return @as(*const u32, @ptrFromInt(addr)).*;
}
inline fn ru16(addr: u32) u16 {
return @as(*align(1) const u16, @ptrFromInt(addr)).*;
}
// =============================================================================
// Page capture infrastructure
// =============================================================================
const PAGE_SIZE: u32 = 0x1000;
const PAGE_MASK: u32 = 0xFFFFF000;
const MAX_CAPTURES: usize = 64; // cap on unique-hash scenarios we record
const MAX_PAGES_PER_CAPTURE: usize = 512; // each capture can touch up to 2MB
/// One captured page's raw bytes plus its VA.
const PageBlock = extern struct {
va: u32,
data: [PAGE_SIZE]u8,
};
/// One captured scenario: input pages + output pages + entry args.
const Capture = struct {
path_hash: u32 = 0,
this: u32 = 0,
mat1: u32 = 0,
mat2: u32 = 0,
mat3: u32 = 0,
mat4: u32 = 0,
input_page_count: u32 = 0,
output_page_count: u32 = 0,
input_pages: [*]PageBlock = undefined,
output_pages: [*]PageBlock = undefined,
};
// One scratch slot — we write captures to disk immediately, so only one in-
// flight capture needs memory at a time.
var scratch: Capture = .{};
var scratch_input_pages: [MAX_PAGES_PER_CAPTURE]PageBlock align(16) = undefined;
var scratch_output_pages: [MAX_PAGES_PER_CAPTURE]PageBlock align(16) = undefined;
// Deduplication: array of hashes we've already dumped.
var seen_hashes: [MAX_CAPTURES]u32 = [_]u32{0} ** MAX_CAPTURES;
var seen_hash_count: usize = 0;
// Re-entry guard (transformMatrix4x4 recurses into itself for attachments).
var in_capture: bool = false;
// Total captures written (== seen_hash_count, kept separate for clarity)
var capture_count: usize = 0;
// Total hook calls seen (for coverage statistics)
var total_calls: u64 = 0;
// Captures-written-per-log-slot so we can track distribution of hash values
var call_counts_per_hash: [MAX_CAPTURES]u32 = [_]u32{0} ** MAX_CAPTURES;
// How many consecutive calls have yielded no new hash (for "coverage saturated" heuristic).
var calls_since_new_hash: u64 = 0;
// Output directory — set at install time. Default = current working dir.
var capture_dir: []const u8 = ".";
// Per-capture scratch: pages we've already saved this iteration.
var seen_pages: [MAX_PAGES_PER_CAPTURE]u32 = [_]u32{0} ** MAX_PAGES_PER_CAPTURE;
var seen_count: usize = 0;
fn seenAdd(page_va: u32) bool {
// Linear scan; MAX_PAGES_PER_CAPTURE is small enough that this is cheap.
for (seen_pages[0..seen_count]) |p| if (p == page_va) return false;
if (seen_count >= MAX_PAGES_PER_CAPTURE) return false;
seen_pages[seen_count] = page_va;
seen_count += 1;
return true;
}
fn seenReset() void {
seen_count = 0;
}
/// Copy one 4KB page from game memory into the destination array.
/// Returns true if the page was new (added to seen list).
fn captureOnePage(addr: u32, dst: [*]PageBlock, idx: *u32) bool {
const page_va = addr & PAGE_MASK;
if (!seenAdd(page_va)) return false;
if (idx.* >= MAX_PAGES_PER_CAPTURE) return false;
dst[idx.*].va = page_va;
@memcpy(&dst[idx.*].data, @as([*]const u8, @ptrFromInt(page_va))[0..PAGE_SIZE]);
idx.* += 1;
return true;
}
/// Capture a memory range (rounded to pages).
fn capturePagesCovering(addr: u32, size: u32, dst: [*]PageBlock, idx: *u32) void {
if (addr == 0 or size == 0) return;
const start_page = addr & PAGE_MASK;
const end_addr = addr +% size;
var cur = start_page;
while (cur < end_addr) : (cur +%= PAGE_SIZE) {
_ = captureOnePage(cur, dst, idx);
}
}
// =============================================================================
// Graph walk — identify all pages reachable from the SceneObject that
// transformMatrix4x4 might read.
// =============================================================================
fn walkAndCapture(this: u32, mat1: u32, mat2: u32, mat3: u32, mat4: u32, dst: [*]PageBlock, idx: *u32) void {
_ = mat4;
// Direct pages
capturePagesCovering(this, 0x400, dst, idx);
capturePagesCovering(mat1, 0x40, dst, idx);
capturePagesCovering(mat2, 0x0C, dst, idx);
capturePagesCovering(mat3, 0x0C, dst, idx);
// Following pointers
const anim_ctx = ru32(this + SO_ANIM_CTX_PTR);
capturePagesCovering(anim_ctx, 0x20, dst, idx);
const model_ctr = ru32(this + SO_MODEL_CTR_PTR);
capturePagesCovering(model_ctr, 0x140, dst, idx);
if (model_ctr == 0) return;
const model_hdr = ru32(model_ctr + 0x130);
capturePagesCovering(model_hdr, 0x200, dst, idx);
if (model_hdr == 0) return;
// model_hdr reference arrays
const gs_durations = ru32(model_hdr + 0x18);
const gs_count = ru32(model_hdr + 0x14);
capturePagesCovering(gs_durations, gs_count * 4, dst, idx);
const anim_lookup = ru32(model_hdr + 0x20);
capturePagesCovering(anim_lookup, 0x1000, dst, idx); // conservative; actual size unknown
const bone_count = ru32(model_hdr + 0x34);
const bone_defs = ru32(model_hdr + 0x38);
capturePagesCovering(bone_defs, bone_count * 0x6C, dst, idx);
// bone_rt / bone_out
const bone_rt = ru32(this + SO_BONE_RT_BASE);
capturePagesCovering(bone_rt, bone_count * 0x118, dst, idx);
const bone_out = ru32(this + SO_BONE_OUT_PTR);
capturePagesCovering(bone_out, bone_count * 0x40, dst, idx);
// AnimData inside bone_defs references keyframe arrays
var bi: u32 = 0;
var bd = bone_defs;
while (bi < bone_count) : ({
bi += 1;
bd += 0x6C;
}) {
// Three animation tracks per bone (trans/rot/scale)
inline for ([_]u32{ 0x0C, 0x28, 0x44 }) |track_off| {
const ad = bd + track_off;
const ranges = ru32(ad + 0x08);
const ranges_count = ru32(ad + 0x04); // nRanges
capturePagesCovering(ranges, ranges_count * 8, dst, idx);
const ts = ru32(ad + 0x10);
const kf_count = ru32(ad + 0x0C);
capturePagesCovering(ts, kf_count * 4, dst, idx);
const kf_base = ru32(ad + 0x18);
// Stride varies by track — trans/scale=12, rot=16, but spline modes
// use 36. Conservative: use 36 × kf_count per track.
capturePagesCovering(kf_base, kf_count * 36, dst, idx);
}
}
// Output-buffer pages (will be WRITTEN but we need to capture input state
// too in case the game reads before writing)
capturePagesCovering(ru32(this + SO_TEX_ANIM_OUT), 0x200, dst, idx);
capturePagesCovering(ru32(this + SO_COLOR_ANIM_OUT), 0x200, dst, idx);
capturePagesCovering(ru32(this + SO_SCALE1), 0x200, dst, idx);
capturePagesCovering(ru32(this + SO_SCALE2), 0x800, dst, idx);
capturePagesCovering(ru32(this + SO_SCALE3), 0x800, dst, idx);
capturePagesCovering(ru32(this + SO_HIERARCHY_PTR), 0x800, dst, idx);
capturePagesCovering(ru32(this + SO_FIELD_200), 0x800, dst, idx);
capturePagesCovering(ru32(this + SO_PARTICLE1), 0x800, dst, idx);
capturePagesCovering(ru32(this + SO_PARTICLE2), 0x800, dst, idx);
capturePagesCovering(ru32(this + SO_PARTICLE3), 0x800, dst, idx);
capturePagesCovering(ru32(this + SO_PARTICLE4), 0x400, dst, idx);
// Attachment data
const attach_count = ru32(model_hdr + 0x104);
const attach_data = ru32(model_hdr + 0x108);
capturePagesCovering(attach_data, attach_count * 0x30, dst, idx);
// Texture/color/word anim data
inline for ([_]struct { cnt: u32, dat: u32, stride: u32 }{
.{ .cnt = 0x54, .dat = 0x58, .stride = 0x34 }, // tex anim
.{ .cnt = 0x64, .dat = 0x68, .stride = 0x28 }, // color anim
.{ .cnt = 0x74, .dat = 0x78, .stride = 0x1C }, // word anim
.{ .cnt = 0xAC, .dat = 0xB0, .stride = 0x54 }, // bone keyframe
.{ .cnt = 0x11C, .dat = 0x120, .stride = 0xD4 }, // ribbon emitter
.{ .cnt = 0x124, .dat = 0x128, .stride = 0x7C }, // particle emitter
.{ .cnt = 0x134, .dat = 0x138, .stride = 0xDC }, // partsec
.{ .cnt = 0x13C, .dat = 0x140, .stride = 0x1F8 }, // partlarge
}) |sec| {
const c = ru32(model_hdr + sec.cnt);
const d = ru32(model_hdr + sec.dat);
capturePagesCovering(d, c * sec.stride, dst, idx);
}
}
// =============================================================================
// Path hash — distinguishes different code paths through transformMatrix4x4.
// Cheap to compute; aim to hit every major branch.
// =============================================================================
fn pathHash(this: u32) u32 {
var h: u32 = 0x811C9DC5;
const model_ctr = ru32(this + SO_MODEL_CTR_PTR);
if (model_ctr == 0) return h;
const model_hdr = ru32(model_ctr + 0x130);
if (model_hdr == 0) return h;
const bone_count = ru32(model_hdr + 0x34);
h = (h ^ bone_count) *% 0x01000193;
const bone_defs = ru32(model_hdr + 0x38);
if (bone_count > 0 and bone_defs != 0) {
// Flags of first few bones (encodes billboard types, etc.)
const max_bones = @min(bone_count, 6);
var bi: u32 = 0;
while (bi < max_bones) : (bi += 1) {
const flags = ru32(bone_defs + bi * 0x6C + 0x04);
h = (h ^ flags) *% 0x01000193;
}
}
// Which post-bone-loop sections are populated?
inline for ([_]u32{ 0x14, 0x54, 0x64, 0x74, 0xAC, 0x104, 0x11C, 0x124, 0x134, 0x13C }) |off| {
const v = ru32(model_hdr + off);
h = (h ^ (if (v == 0) @as(u32, 0) else 1)) *% 0x01000193;
}
// emitter_ctx presence
const emitter_ctx = ru32(this + 0x1CC);
h = (h ^ (if (emitter_ctx == 0) @as(u32, 0) else 1)) *% 0x01000193;
return h;
}
// =============================================================================
// Detour
// =============================================================================
fn transformDetour(this: u32, mat1: u32, mat2: u32, mat3: u32, mat4: u32) callconv(.{ .x86_thiscall = .{} }) void {
// Re-entry guard: transformMatrix4x4 recurses for attachment children.
if (in_capture) {
transform_hook.callOriginal(.{ this, mat1, mat2, mat3, mat4 });
return;
}
total_calls +%= 1;
if (capture_count < MAX_CAPTURES) {
const h = pathHash(this);
var known_idx: ?usize = null;
for (seen_hashes[0..seen_hash_count], 0..) |sh, i| if (sh == h) {
known_idx = i;
break;
};
if (known_idx) |idx| {
call_counts_per_hash[idx] +%= 1;
calls_since_new_hash +%= 1;
} else {
in_capture = true;
defer in_capture = false;
scratch.path_hash = h;
scratch.this = this;
scratch.mat1 = mat1;
scratch.mat2 = mat2;
scratch.mat3 = mat3;
scratch.mat4 = mat4;
scratch.input_page_count = 0;
scratch.output_page_count = 0;
seenReset();
walkAndCapture(this, mat1, mat2, mat3, mat4, &scratch_input_pages, &scratch.input_page_count);
transform_hook.callOriginal(.{ this, mat1, mat2, mat3, mat4 });
var oi: u32 = 0;
var pi: usize = 0;
while (pi < scratch.input_page_count) : (pi += 1) {
if (oi >= MAX_PAGES_PER_CAPTURE) break;
scratch_output_pages[oi].va = scratch_input_pages[pi].va;
@memcpy(&scratch_output_pages[oi].data, @as([*]const u8, @ptrFromInt(scratch_input_pages[pi].va))[0..PAGE_SIZE]);
oi += 1;
}
scratch.output_page_count = oi;
scratch.input_pages = &scratch_input_pages;
scratch.output_pages = &scratch_output_pages;
const dump_err = dumpOneToDir(capture_dir, scratch);
const cov_err = appendCoverageLog(h, scratch.input_page_count);
log.fmt("capture: hash=0x{x:08} pages={d} dump={any} cov={any}\n", .{
h, scratch.input_page_count, dump_err, cov_err,
});
if (seen_hash_count < MAX_CAPTURES) {
seen_hashes[seen_hash_count] = h;
call_counts_per_hash[seen_hash_count] = 1;
seen_hash_count += 1;
}
capture_count += 1;
calls_since_new_hash = 0;
return;
}
} else {
calls_since_new_hash +%= 1;
}
transform_hook.callOriginal(.{ this, mat1, mat2, mat3, mat4 });
}
// =============================================================================
// File format
// =============================================================================
const MAGIC: [4]u8 = .{ 'T', 'R', 'X', 'C' };
const VERSION: u32 = 1;
const FileHeader = extern struct {
magic: [4]u8 = MAGIC,
version: u32 = VERSION,
path_hash: u32,
this: u32,
mat1: u32,
mat2: u32,
mat3: u32,
mat4: u32,
input_page_count: u32,
output_page_count: u32,
};
/// Build a null-terminated path: "<dir>/<filename>".
fn buildPath(buf: []u8, dir: []const u8, filename: []const u8) ![:0]const u8 {
const total = dir.len + 1 + filename.len + 1;
if (total > buf.len) return error.NoSpaceLeft;
@memcpy(buf[0..dir.len], dir);
buf[dir.len] = '/';
@memcpy(buf[dir.len + 1 ..][0..filename.len], filename);
buf[dir.len + 1 + filename.len] = 0;
return buf[0 .. dir.len + 1 + filename.len :0];
}
fn buildDirZ(buf: []u8, dir: []const u8) ![:0]const u8 {
if (dir.len + 1 > buf.len) return error.NoSpaceLeft;
@memcpy(buf[0..dir.len], dir);
buf[dir.len] = 0;
return buf[0..dir.len :0];
}
fn dumpOneToDir(dir_path: []const u8, c: Capture) !void {
var dir_buf: [256]u8 = undefined;
const dir_z = try buildDirZ(&dir_buf, dir_path);
_ = CreateDirectoryA(dir_z.ptr, null); // ignore result: already-exists is fine
var name_buf: [64]u8 = undefined;
const fname = try std.fmt.bufPrint(&name_buf, "{x:08}.trxcap", .{c.path_hash});
var path_buf: [512]u8 = undefined;
const path = try buildPath(&path_buf, dir_path, fname);
const h = CreateFileA(path.ptr, GENERIC_WRITE, FILE_SHARE_READ, null, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, null);
if (h == INVALID_HANDLE) return error.CreateFailed;
defer _ = CloseHandle(h);
const hdr = FileHeader{
.path_hash = c.path_hash,
.this = c.this,
.mat1 = c.mat1,
.mat2 = c.mat2,
.mat3 = c.mat3,
.mat4 = c.mat4,
.input_page_count = c.input_page_count,
.output_page_count = c.output_page_count,
};
if (!writeAll(h, std.mem.asBytes(&hdr))) return error.WriteFailed;
if (!writeAll(h, @as([*]const u8, @ptrCast(c.input_pages))[0 .. c.input_page_count * @sizeOf(PageBlock)])) return error.WriteFailed;
if (!writeAll(h, @as([*]const u8, @ptrCast(c.output_pages))[0 .. c.output_page_count * @sizeOf(PageBlock)])) return error.WriteFailed;
}
/// Append a line to coverage.log whenever we see a new path hash.
fn appendCoverageLog(new_hash: u32, page_count: u32) !void {
var dir_buf: [256]u8 = undefined;
const dir_z = try buildDirZ(&dir_buf, capture_dir);
_ = CreateDirectoryA(dir_z.ptr, null);
var path_buf: [512]u8 = undefined;
const path = try buildPath(&path_buf, capture_dir, "coverage.log");
const h = CreateFileA(path.ptr, GENERIC_WRITE, FILE_SHARE_READ, null, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, null);
if (h == INVALID_HANDLE) return error.CreateFailed;
defer _ = CloseHandle(h);
_ = SetFilePointer(h, 0, null, FILE_END);
var line_buf: [256]u8 = undefined;
const line = try std.fmt.bufPrint(&line_buf, "[#{d}] new hash 0x{x:08} (total_calls={d}, pages={d})\n", .{
seen_hash_count + 1, new_hash, total_calls, page_count,
});
if (!writeAll(h, line)) return error.WriteFailed;
}
// =============================================================================
// Install / Remove
// =============================================================================
/// Install with the given output directory (relative to WoW's current working
/// directory, usually the game install folder). Captures + coverage.log will
/// land there. Pass "." to write them next to WoW.exe.
pub fn install() bool {
return installTo("transform_captures");
}
pub fn installTo(dir: []const u8) bool {
log = logging.Logger.open("transform_capture", .both);
capture_dir = dir;
const ok = transform_hook.attach(0x714260, &transformDetour) == .ok;
log.fmt("install: hook_attach={any} dir={s}\n", .{ ok, dir });
// Smoke-test the write path so we know if file I/O is broken before any
// transform calls arrive.
var dir_buf: [256]u8 = undefined;
const dir_z = buildDirZ(&dir_buf, capture_dir) catch return ok;
const mkdir_ok = CreateDirectoryA(dir_z.ptr, null);
log.fmt("install: mkdir returned {d}\n", .{mkdir_ok});
var path_buf: [512]u8 = undefined;
const path = buildPath(&path_buf, capture_dir, "install_test.txt") catch return ok;
const h = CreateFileA(path.ptr, GENERIC_WRITE, FILE_SHARE_READ, null, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, null);
if (h == INVALID_HANDLE) {
log.fmt("install: CreateFileA FAILED for {s}\n", .{path});
} else {
_ = writeAll(h, "transform_capture install test\n");
_ = CloseHandle(h);
log.fmt("install: wrote install_test.txt ok\n", .{});
}
return ok;
}
pub fn remove() void {
transform_hook.detach();
}
pub fn getCaptureCount() usize {
return capture_count;
}
pub fn getTotalCalls() u64 {
return total_calls;
}
pub fn getCallsSinceNewHash() u64 {
return calls_since_new_hash;
}
+12 -2
View File
@@ -19,6 +19,7 @@ const mod_mutex = @import("../mutex.zig");
const inflate_hook = @import("inflate_hook.zig");
const timer_fix = @import("timer_fix.zig");
const filecache = @import("filecache.zig");
const transform_capture = @import("transform_capture.zig");
pub const module_name: [*:0]const u8 = "weirdperformance";
@@ -364,8 +365,17 @@ pub fn installHooks() void {
log = logging.Logger.open(module_name, .both);
var installed: u32 = 0;
// Bone transform SSE
if (transform_hook.attach(0x714260, &bone_sse64.transformImpl_SSE64) == .ok) installed += 1;
// Bone transform: either the f64 SSE impl for normal operation, OR the
// transform_capture hook for recording game-x87 state to disk.
// Compile-time flag picks one: the two are mutually exclusive because
// capture requires the game's x87 output (bone_sse64 would replace it).
const build_options = @import("build_options");
const capture_mode = @hasDecl(build_options, "transform_capture") and build_options.transform_capture;
if (capture_mode) {
if (transform_capture.install()) installed += 1;
} else {
if (transform_hook.attach(0x714260, &bone_sse64.transformImpl_SSE64) == .ok) installed += 1;
}
// Frustum clip SSE (1.9x speedup)
if (clip_hook.attach(0x6318C0, &clip_sse.clipPolygonToSinglePlane) == .ok) installed += 1;