diff --git a/src/customassets/customassets.zig b/src/customassets/customassets.zig index da88a9c..d5fb2eb 100644 --- a/src/customassets/customassets.zig +++ b/src/customassets/customassets.zig @@ -2,12 +2,13 @@ // customassets - Loose file loading & permissive patch glob // ============================================================================= // -// 1. Patches patch-?.MPQ → patch-*.MPQ so multi-char patch names work -// 2. NOPs two gates in File_FindInArchive so CheckFileExistence runs for -// all files, not just Interface/AddOns -// 3. Indexes loose Data/ files into an O(1) hash set; main.zig's +// 1. Patches patch-?.MPQ -> patch-*.MPQ so multi-char patch names work +// 2. Indexes loose Data/ files into an O(1) hash set; main.zig's // CheckFileExistence hook calls looseFilesLookup() to serve them // +// Note: File_FindInArchive gate NOPs (for CheckFileExistence) moved to +// main.zig core file hooks so all builds with embedded addons get them. +// // ============================================================================= const std = @import("std"); @@ -211,44 +212,6 @@ fn revertGlobPatch() void { } } -// ============================================================================= -// Patches 2 & 3: NOP loose file gates in File_FindInArchive -// ============================================================================= - -var old_jz: [2]u8 = undefined; -var jz_patched: bool = false; -var old_jnz: [2]u8 = undefined; -var jnz_patched: bool = false; - -fn applyLooseFilePatches() void { - const nops = [2]u8{ 0x90, 0x90 }; - - // Gate 1: JZ at 0x654b5c (74 25) - if (hook.readMem(u8, 0x654b5c) == 0x74 and hook.readMem(u8, 0x654b5d) == 0x25) { - old_jz = .{ 0x74, 0x25 }; - hook.writeProtected(0x654b5c, &nops); - jz_patched = true; - } - - // Gate 2: JNZ at 0x654b6a (75 17) - if (hook.readMem(u8, 0x654b6a) == 0x75 and hook.readMem(u8, 0x654b6b) == 0x17) { - old_jnz = .{ 0x75, 0x17 }; - hook.writeProtected(0x654b6a, &nops); - jnz_patched = true; - } -} - -fn revertLooseFilePatches() void { - if (jz_patched) { - hook.writeProtected(0x654b5c, &old_jz); - jz_patched = false; - } - if (jnz_patched) { - hook.writeProtected(0x654b6a, &old_jnz); - jnz_patched = false; - } -} - // ============================================================================= // Init / Cleanup // ============================================================================= @@ -276,14 +239,15 @@ pub fn installHooks() void { log = logging.Logger.open(module_name, .console); applyGlobPatch(); - applyLooseFilePatches(); + // Gate NOPs for CheckFileExistence are now in main.zig (core file hooks) + // so all builds with embedded addons get them. No need to apply here. looseFilesInit(); installed = true; } pub fn removeHooks() void { if (g_is_hook_owner and installed) { - revertLooseFilePatches(); + // Gate reverts are now in main.zig removeFileHooks() revertGlobPatch(); looseFilesCleanup(); installed = false; diff --git a/src/main.zig b/src/main.zig index 3055830..642c3ec 100644 --- a/src/main.zig +++ b/src/main.zig @@ -604,10 +604,50 @@ fn installFileHooks() void { _ = cleanup_file_handle_hook.attach(0x648730, &cleanupFileHandleDetour); _ = model_load_hook.attach(0x71d4e0, &loadModelAsyncDetour); _ = cfe_hook.attach(0x654DD0, &checkFileExistenceDetour); + // NOP two gates in File_FindInArchive that prevent CheckFileExistence + // from running. Without this, preloadFileWithFlags never reaches our + // hook and the game skips Bindings.xml for embedded addons. + // (customassets also does this for loose files; the patches are idempotent.) + nopFileExistenceGates(); log.print("in-memory file hooks installed\n"); } +// File_FindInArchive (0x6549a0) has two conditional jumps that skip the +// call to CheckFileExistence (0x654DD0). NOP them so our hook always runs. +var old_gate1: [2]u8 = undefined; +var gate1_patched: bool = false; +var old_gate2: [2]u8 = undefined; +var gate2_patched: bool = false; + +fn nopFileExistenceGates() void { + const nops = [2]u8{ 0x90, 0x90 }; + // Gate 1: JZ at 0x654b5c (74 25) -- skips CheckFileExistence + if (hook.readMem(u8, 0x654b5c) == 0x74 and hook.readMem(u8, 0x654b5d) == 0x25) { + old_gate1 = .{ 0x74, 0x25 }; + hook.writeProtected(0x654b5c, &nops); + gate1_patched = true; + } + // Gate 2: JNZ at 0x654b6a (75 17) -- skips CheckFileExistence + if (hook.readMem(u8, 0x654b6a) == 0x75 and hook.readMem(u8, 0x654b6b) == 0x17) { + old_gate2 = .{ 0x75, 0x17 }; + hook.writeProtected(0x654b6a, &nops); + gate2_patched = true; + } +} + +fn revertFileExistenceGates() void { + if (gate1_patched) { + hook.writeProtected(0x654b5c, &old_gate1); + gate1_patched = false; + } + if (gate2_patched) { + hook.writeProtected(0x654b6a, &old_gate2); + gate2_patched = false; + } +} + fn removeFileHooks() void { + revertFileExistenceGates(); cfe_hook.detach(); model_load_hook.detach(); process_async_hook.detach();