From 4af033d01d843077e3567e666d677ff73e908b72 Mon Sep 17 00:00:00 2001 From: MarcelineVQ Date: Tue, 3 Mar 2026 10:26:17 -0800 Subject: [PATCH] =?UTF-8?q?Rename=20dataassets=E2=86=92customassets,=20Mar?= =?UTF-8?q?kers=E2=86=92WorldMarkers,=20prefix=20all=20mutexes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - dataassets module renamed to customassets everywhere (build flag, source, DLL variant name, docs) - Markers addon renamed to WorldMarkers (addon path, .toc, .lua, Bindings.xml header, Lua globals, debug log prefix, mutex name) - All 9 module mutexes now use WeirdUtils_ prefix to avoid collisions with other DLLs in the same process --- DLL_README.md | 10 +- README.md | 14 +- RELEASING.md | 150 +++++++++++++ build.zig | 30 +-- src/combatlog/combatlog.zig | 199 +++++++++++++++++- .../customassets.zig} | 10 +- src/framecrash/framecrash.zig | 2 +- src/interact/interact.zig | 2 +- src/main.zig | 16 +- src/markers/MPQ_FILESYSTEM_RESEARCH.md | 14 +- src/markers/addon/Bindings.xml | 12 +- src/markers/addon/Markers.lua | 10 +- src/markers/addon/Markers.toc | 6 +- src/markers/markers.zig | 68 +++--- src/minimapicons/minimapicons.zig | 2 +- src/outline/api.zig | 2 +- src/screenshot/screenshot.zig | 2 +- src/transmogfix/transmogfix.zig | 2 +- 18 files changed, 448 insertions(+), 103 deletions(-) create mode 100644 RELEASING.md rename src/{dataassets/dataassets.zig => customassets/customassets.zig} (96%) diff --git a/DLL_README.md b/DLL_README.md index e3d373a..a3e6865 100644 --- a/DLL_README.md +++ b/DLL_README.md @@ -102,7 +102,7 @@ Patch archives are sorted case-insensitively by filename — last in the sort ge No configuration needed, install and forget. -**DLL:** `dataassets.dll` +**DLL:** `customassets.dll` --- @@ -111,3 +111,11 @@ No configuration needed, install and forget. Fixes duplicate floating heal numbers caused by SuperWoW 1.5. Only relevant if you use SuperWoW. No configuration needed, install and forget. **DLL:** `healtextfix.dll` + +--- + +## Why No Source Code? + +This project is distributed as pre-built DLLs only. The source code is not and will not be made publicly available. + +These DLLs work by hooking deeply into the game client's internals — memory layout, function addresses, rendering pipeline, input handling, and more. While every feature here is built for legitimate quality-of-life use, the underlying techniques touch on too many core mechanisms that are trivially abusable. Publishing the source would be handing a candy store to bad actors: the same hooks and patterns used to render a raid marker or fix a crash can be repurposed for cheats, exploits, and in particular automation with minimal effort. diff --git a/README.md b/README.md index 8068c6b..b8ffac4 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Users can pick the full package or grab only the features they want. ```zig // build.zig options (planned) -const enable_dataassets = b.option(bool, "dataassets", "Enable loose file loading & permissive patch glob") orelse true; +const enable_customassets = b.option(bool, "customassets", "Enable loose file loading & permissive patch glob") orelse true; const enable_transmogfix = b.option(bool, "transmogfix", "Enable transmog coalesce fix") orelse true; const enable_interact = b.option(bool, "interact", "Enable interact helpers") orelse true; const enable_outline = b.option(bool, "outline", "Enable outline rendering") orelse true; @@ -52,14 +52,14 @@ const enable_outline = b.option(bool, "outline", "Enable outline rendering") ore zig build # Single-feature builds — one DLL per feature for individual distribution -zig build -Ddataassets=true -Dtransmogfix=false -Dinteract=false -Doutline=false -zig build -Ddataassets=false -Dtransmogfix=true -Dinteract=false -Doutline=false +zig build -Dcustomassets=true -Dtransmogfix=false -Dinteract=false -Doutline=false +zig build -Dcustomassets=false -Dtransmogfix=true -Dinteract=false -Doutline=false # etc. ``` Release artifacts: - `weirdutils.dll` — everything -- `dataassets.dll` — just asset/MPQ fixes +- `customassets.dll` — just asset/MPQ fixes - `transmogfix.dll` — just transmog coalesce - `interact.dll` — just interact/loot helpers - `outline.dll` — just outline rendering @@ -69,14 +69,14 @@ All built from this repo, all sharing the same hook library and codebase. ### Per-Feature Named Mutex A user might load the full DLL alongside one of the smaller single-feature DLLs -(e.g. they use `weirdutils.dll` for everything but also have `dataassets.dll` from +(e.g. they use `weirdutils.dll` for everything but also have `customassets.dll` from before they switched). Each feature module claims a **named mutex** on load — if it's already held, that module skips hook installation. This way any combination of DLLs coexists safely with no duplicate hooks. ```zig // Each module creates a process-specific named mutex on init -const mutex = CreateMutexA(null, 1, "Local\\WeirdUtils_DataAssets_{pid}"); +const mutex = CreateMutexA(null, 1, "Local\\WeirdUtils_CustomAssetsHook_{pid}"); if (GetLastError() == ERROR_ALREADY_EXISTS) { // Another DLL already owns this feature's hooks — skip CloseHandle(mutex); @@ -127,7 +127,7 @@ gh release create v1.0 --repo YourName/WeirdUtils \ --title "v1.0" --notes "Release notes" \ ./zig-out/lib/weirdutils.dll \ ./builds/outline.dll \ - ./builds/dataassets.dll + ./builds/customassets.dll ``` ## Project Structure diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 0000000..25b4495 --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,150 @@ +# Releasing WeirdUtils + +> **Remote:** https://codeberg.org/MarcelineVQ/WeirdUtils + +## How the remote repo works + +This project is developed entirely locally. The remote repo is **only** a +distribution point for releases — no source code is pushed. + +The remote `main` branch contains a single file: `README.md` (built from the +local `DLL_README.md`). This must be set up once when creating the repo: + +```sh +tea repo create --name WeirdUtils --description "Vanilla WoW 1.12.1 utility DLLs" --login MarcelineVQ +``` + +Codeberg disables releases on new repos by default. Enable via API: + +```sh +curl -s -X PATCH \ + -H "Authorization: token " \ + -H "Content-Type: application/json" \ + -d '{"has_releases":true}' \ + "https://codeberg.org/api/v1/repos/MarcelineVQ/WeirdUtils" +``` + +Then push the initial README: + +```sh +# In a temporary directory: +git init && git remote add origin ssh://git@codeberg.org/MarcelineVQ/WeirdUtils.git && git checkout -b main +cp /path/to/weirdutils/DLL_README.md README.md +git add README.md +git commit -m "Add README" +git push origin main +``` + +After that, the remote `main` only needs updating when `DLL_README.md` changes. + +## 1. Build DLLs + +Decide which modules to include in this release. Check `build.zig` for the +current list of module flags (`b.option(bool, ...)` declarations) and their +defaults. List them with: + +```sh +zig build --help 2>&1 | grep 'Enable' +``` + +### Combined DLL + +Build `weirdutils.dll` with only the modules for this release. Explicitly +disable everything not being included — defaults may enable modules you don't +want: + +```sh +# Example: only transmogfix + customassets + healtextfix +zig build -Doptimize=ReleaseSmall \ + -Dscreenshot=false -Dinteract=false -Doutline=false \ + -Dworldmarkers=false -Dframecrash=false -Dcombatlog=false \ + -Dminimapicons=false \ + -Dtransmogfix=true -Dcustomassets=true -Dhealtextfix=true +``` + +### Individual variant DLLs + +```sh +zig build all-variants -Doptimize=ReleaseSmall +``` + +This builds all variants — you only attach the ones for this release. + +### Output locations + +| Artifact | Path | +|----------|------| +| Combined DLL | `zig-out/bin/weirdutils.dll` | +| Variant DLLs | `zig-out/variants/.dll` | + +Verify: + +```sh +ls -lh zig-out/bin/weirdutils.dll zig-out/variants/*.dll +``` + +## 2. Update the remote README + +The remote README should match the features in this release. Start from +`DLL_README.md` and remove the sections for modules not being released — +keep the header, install instructions, and included feature sections exactly +as they are. + +```sh +# from a clone or worktree of the remote repo +# edit README.md: remove sections for modules not in this release +git add README.md +git commit -m "Update README for vX.Y.Z" +git push origin main +``` + +## 3. Write the release notes + +Use this template — fill in the sections that apply, delete the rest: + +```markdown +## What's New + +- ... + +## Bug Fixes + +- ... + +## Module Changes + +- ... + +## Notes + +- Place DLLs next to `WoW.exe` and add to `dlls.txt` +- `weirdutils.dll` includes all features; individual DLLs are also provided +``` + +## 4. Create the release and upload DLLs + +Uses `tea` (Gitea/Forgejo CLI) which handles release creation, tagging, and +asset upload in one command. The tag is created on the remote automatically. + +Run from a clone of the remote repo, or specify `--repo` explicitly. +Only attach the combined DLL and the variant DLLs for modules in this release: + +```sh +# Example: transmogfix + customassets + healtextfix release +tea release create \ + --repo MarcelineVQ/WeirdUtils \ + --tag vX.Y.Z \ + --target main \ + --title "vX.Y.Z" \ + --note-file release-notes.md \ + --asset zig-out/bin/weirdutils.dll \ + --asset zig-out/variants/transmogfix.dll \ + --asset zig-out/variants/customassets.dll \ + --asset zig-out/variants/healtextfix.dll +``` + +## Checklist + +- [ ] Built with `ReleaseSmall` (both default and `all-variants`) +- [ ] Remote README updated (if `DLL_README.md` changed) +- [ ] Release created and DLLs uploaded via `tea` diff --git a/build.zig b/build.zig index 30f05c0..4be757d 100644 --- a/build.zig +++ b/build.zig @@ -17,7 +17,7 @@ pub fn build(b: *std.Build) void { const enable_combatlog = b.option(bool, "combatlog", "Enable combat log freshness") orelse true; const enable_minimapicons = b.option(bool, "minimapicons", "Enable custom minimap icons") orelse true; const enable_transmogfix = b.option(bool, "transmogfix", "Enable transmog update coalescing") orelse true; - const enable_dataassets = b.option(bool, "dataassets", "Enable loose file loading & permissive patch glob") orelse true; + const enable_customassets = b.option(bool, "customassets", "Enable loose file loading & permissive patch glob") orelse true; const enable_healtextfix = b.option(bool, "healtextfix", "Enable SuperWoW heal text fix") orelse true; // Create build options module @@ -30,7 +30,7 @@ pub fn build(b: *std.Build) void { build_options.addOption(bool, "enable_combatlog", enable_combatlog); build_options.addOption(bool, "enable_minimapicons", enable_minimapicons); build_options.addOption(bool, "enable_transmogfix", enable_transmogfix); - build_options.addOption(bool, "enable_dataassets", enable_dataassets); + build_options.addOption(bool, "enable_customassets", enable_customassets); build_options.addOption(bool, "enable_healtextfix", enable_healtextfix); const build_options_module = build_options.createModule(); @@ -60,19 +60,19 @@ pub fn build(b: *std.Build) void { const build_all_step = b.step("all-variants", "Build all DLL variants"); // Helper to create a single-module build - const Variant = struct { name: []const u8, screenshot: bool, interact: bool, outline: bool, worldmarkers: bool, framecrash: bool, combatlog: bool, minimapicons: bool, transmogfix: bool, dataassets: bool, healtextfix: bool }; + const Variant = struct { name: []const u8, screenshot: bool, interact: bool, outline: bool, worldmarkers: bool, framecrash: bool, combatlog: bool, minimapicons: bool, transmogfix: bool, customassets: bool, healtextfix: bool }; inline for (&[_]Variant{ - .{ .name = "full", .screenshot = true, .interact = true, .outline = true, .worldmarkers = true, .framecrash = true, .combatlog = true, .minimapicons = true, .transmogfix = true, .dataassets = true, .healtextfix = true }, - .{ .name = "screenshot", .screenshot = true, .interact = false, .outline = false, .worldmarkers = false, .framecrash = true, .combatlog = true, .minimapicons = false, .transmogfix = false, .dataassets = false, .healtextfix = false }, - .{ .name = "interact", .screenshot = false, .interact = true, .outline = false, .worldmarkers = false, .framecrash = true, .combatlog = true, .minimapicons = false, .transmogfix = false, .dataassets = false, .healtextfix = false }, - .{ .name = "outline", .screenshot = false, .interact = false, .outline = true, .worldmarkers = false, .framecrash = true, .combatlog = true, .minimapicons = false, .transmogfix = false, .dataassets = false, .healtextfix = false }, - .{ .name = "worldmarkers", .screenshot = false, .interact = false, .outline = false, .worldmarkers = true, .framecrash = true, .combatlog = true, .minimapicons = false, .transmogfix = false, .dataassets = false, .healtextfix = false }, - .{ .name = "framecrash", .screenshot = false, .interact = false, .outline = false, .worldmarkers = false, .framecrash = true, .combatlog = false, .minimapicons = false, .transmogfix = false, .dataassets = false, .healtextfix = false }, - .{ .name = "combatlog", .screenshot = false, .interact = false, .outline = false, .worldmarkers = false, .framecrash = false, .combatlog = true, .minimapicons = false, .transmogfix = false, .dataassets = false, .healtextfix = false }, - .{ .name = "minimapicons", .screenshot = false, .interact = false, .outline = false, .worldmarkers = false, .framecrash = true, .combatlog = false, .minimapicons = true, .transmogfix = false, .dataassets = false, .healtextfix = false }, - .{ .name = "transmogfix", .screenshot = false, .interact = false, .outline = false, .worldmarkers = false, .framecrash = false, .combatlog = false, .minimapicons = false, .transmogfix = true, .dataassets = false, .healtextfix = false }, - .{ .name = "dataassets", .screenshot = false, .interact = false, .outline = false, .worldmarkers = false, .framecrash = false, .combatlog = false, .minimapicons = false, .transmogfix = false, .dataassets = true, .healtextfix = false }, - .{ .name = "healtextfix", .screenshot = false, .interact = false, .outline = false, .worldmarkers = false, .framecrash = false, .combatlog = false, .minimapicons = false, .transmogfix = false, .dataassets = false, .healtextfix = true }, + .{ .name = "full", .screenshot = true, .interact = true, .outline = true, .worldmarkers = true, .framecrash = true, .combatlog = true, .minimapicons = true, .transmogfix = true, .customassets = true, .healtextfix = true }, + .{ .name = "screenshot", .screenshot = true, .interact = false, .outline = false, .worldmarkers = false, .framecrash = true, .combatlog = true, .minimapicons = false, .transmogfix = false, .customassets = false, .healtextfix = false }, + .{ .name = "interact", .screenshot = false, .interact = true, .outline = false, .worldmarkers = false, .framecrash = true, .combatlog = true, .minimapicons = false, .transmogfix = false, .customassets = false, .healtextfix = false }, + .{ .name = "outline", .screenshot = false, .interact = false, .outline = true, .worldmarkers = false, .framecrash = true, .combatlog = true, .minimapicons = false, .transmogfix = false, .customassets = false, .healtextfix = false }, + .{ .name = "worldmarkers", .screenshot = false, .interact = false, .outline = false, .worldmarkers = true, .framecrash = true, .combatlog = true, .minimapicons = false, .transmogfix = false, .customassets = false, .healtextfix = false }, + .{ .name = "framecrash", .screenshot = false, .interact = false, .outline = false, .worldmarkers = false, .framecrash = true, .combatlog = false, .minimapicons = false, .transmogfix = false, .customassets = false, .healtextfix = false }, + .{ .name = "combatlog", .screenshot = false, .interact = false, .outline = false, .worldmarkers = false, .framecrash = false, .combatlog = true, .minimapicons = false, .transmogfix = false, .customassets = false, .healtextfix = false }, + .{ .name = "minimapicons", .screenshot = false, .interact = false, .outline = false, .worldmarkers = false, .framecrash = true, .combatlog = false, .minimapicons = true, .transmogfix = false, .customassets = false, .healtextfix = false }, + .{ .name = "transmogfix", .screenshot = false, .interact = false, .outline = false, .worldmarkers = false, .framecrash = false, .combatlog = false, .minimapicons = false, .transmogfix = true, .customassets = false, .healtextfix = false }, + .{ .name = "customassets", .screenshot = false, .interact = false, .outline = false, .worldmarkers = false, .framecrash = false, .combatlog = false, .minimapicons = false, .transmogfix = false, .customassets = true, .healtextfix = false }, + .{ .name = "healtextfix", .screenshot = false, .interact = false, .outline = false, .worldmarkers = false, .framecrash = false, .combatlog = false, .minimapicons = false, .transmogfix = false, .customassets = false, .healtextfix = true }, }) |variant| { const opts = b.addOptions(); opts.addOption(bool, "enable_screenshot", variant.screenshot); @@ -83,7 +83,7 @@ pub fn build(b: *std.Build) void { opts.addOption(bool, "enable_combatlog", variant.combatlog); opts.addOption(bool, "enable_minimapicons", variant.minimapicons); opts.addOption(bool, "enable_transmogfix", variant.transmogfix); - opts.addOption(bool, "enable_dataassets", variant.dataassets); + opts.addOption(bool, "enable_customassets", variant.customassets); opts.addOption(bool, "enable_healtextfix", variant.healtextfix); const variant_lib = b.addLibrary(.{ diff --git a/src/combatlog/combatlog.zig b/src/combatlog/combatlog.zig index 0ceec0c..d716dfe 100644 --- a/src/combatlog/combatlog.zig +++ b/src/combatlog/combatlog.zig @@ -1,12 +1,15 @@ -//! Combat log freshness module. +//! Combat log session rotation. //! -//! Appends a timestamp to the combat log filename so each client session -//! gets a fresh log file (e.g. WoWCombatLog_20260227_123456.txt). +//! Redirects the combat log to a per-session file named with timestamp + PID +//! (e.g. `Logs\WoWCombatLog_20260303_193045_1234.txt`), and writes a +//! `COMBATLOG_SESSION,` marker line when combat logging is enabled. //! -//! TODO: Find and hook the combat log open/create function. +//! All DLL-side — no Lua addon needed. const std = @import("std"); +const hook = @import("zhook"); const con = @import("../console.zig"); +const o = @import("offsets.zig"); const WINAPI = std.builtin.CallingConvention.winapi; extern "kernel32" fn CreateMutexA(lpMutexAttributes: ?*anyopaque, bInitialOwner: i32, lpName: [*:0]const u8) callconv(WINAPI) ?*anyopaque; @@ -16,15 +19,186 @@ extern "kernel32" fn GetLastError() callconv(WINAPI) u32; extern "kernel32" fn GetCurrentProcessId() callconv(WINAPI) u32; const ERROR_ALREADY_EXISTS: u32 = 183; +const SYSTEMTIME = extern struct { + wYear: u16, + wMonth: u16, + wDayOfWeek: u16, + wDay: u16, + wHour: u16, + wMinute: u16, + wSecond: u16, + wMilliseconds: u16, +}; +extern "kernel32" fn GetLocalTime(lpSystemTime: *SYSTEMTIME) callconv(WINAPI) void; + var g_mutex: ?*anyopaque = null; var g_is_hook_owner: bool = false; +// ============================================================================= +// Path redirect +// ============================================================================= + +/// Static buffer for the redirected combat log path. Must outlive the process. +var g_path_buf: [260]u8 = undefined; + +/// Saved original path pointer so we can restore on unload. +var g_original_path_ptr: u32 = 0; + +fn setupPathRedirect() void { + // Save the original pointer value + g_original_path_ptr = hook.readMem(u32, o.COMBAT_LOG_PATH_PTR); + + var st: SYSTEMTIME = undefined; + GetLocalTime(&st); + const pid = GetCurrentProcessId(); + + const path = std.fmt.bufPrint(&g_path_buf, "Logs\\WoWCombatLog_{d:0>4}{d:0>2}{d:0>2}_{d:0>2}{d:0>2}{d:0>2}_{d}.txt", .{ + st.wYear, st.wMonth, st.wDay, + st.wHour, st.wMinute, st.wSecond, + pid, + }) catch { + con.print("[combatlog] path format error\n"); + return; + }; + g_path_buf[path.len] = 0; + + // Overwrite the pointer at 0x00843610 to point to our buffer. + // .data section is RW, no VirtualProtect needed. + const ptr_bytes: [4]u8 = @bitCast(@intFromPtr(&g_path_buf)); + hook.writeMem(o.COMBAT_LOG_PATH_PTR, &ptr_bytes); + + con.fmt("[combatlog] path: {s}\n", .{path}); +} + +fn restorePathPointer() void { + if (g_original_path_ptr != 0) { + const ptr_bytes: [4]u8 = @bitCast(g_original_path_ptr); + hook.writeMem(o.COMBAT_LOG_PATH_PTR, &ptr_bytes); + g_original_path_ptr = 0; + } +} + +// ============================================================================= +// Player identity (same inline asm patterns as markers module) +// ============================================================================= + +/// Get local player GUID via GetPlayerGUID (0x468550). +/// __fastcall(), no params, returns EAX(low):EDX(high). +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; +} + +/// Look up a player name from the name cache by GUID. +/// Calls RetrieveNPCDataFromCache — __thiscall(ECX=cache), 6 stack params, RET 0x18. +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 }); + return if (result != 0) @ptrFromInt(result) else null; +} + +// ============================================================================= +// Session marker +// ============================================================================= + +var g_session_marker_written: bool = false; + +/// Write a COMBATLOG_SESSION line with the player's name to the combat log. +/// Called once after combat logging is first enabled for this session. +fn maybeWriteSessionMarker() void { + // Read combat log handle — 0 means log not active yet + const combat_handle = hook.readMem(u32, o.COMBAT_LOG_HANDLE); + if (combat_handle == 0) return; + + const player_guid = getPlayerGUID(); + if (player_guid == 0) return; + + const guid_lo: u32 = @truncate(player_guid); + const guid_hi: u32 = @truncate(player_guid >> 32); + const name = getNameFromGUID(guid_lo, guid_hi) orelse { + // TODO: If player name is unavailable at this point (e.g. + // LoggingCombat enabled before login), could hook OnWorldUpdate for a + // per-frame retry until name is available. + con.print("[combatlog] session marker: player name not yet available\n"); + return; + }; + + // WriteFormattedLogMessage — __cdecl(handle, fmt, va_list) + // Third arg is a va_list (pointer to the arg list), NOT the arg itself. + const fmt_str: [*:0]const u8 = "COMBATLOG_SESSION,%s"; + var va_args = [1]u32{@intFromPtr(name)}; + const cdecl_args = [3]u32{ + combat_handle, + @intFromPtr(fmt_str), + @intFromPtr(&va_args), + }; + asm volatile ( + \\ push 8(%[a]) + \\ push 4(%[a]) + \\ push (%[a]) + \\ call *%[func] + \\ add $12, %%esp + : + : [a] "r" (&cdecl_args), + [func] "r" (@as(u32, o.FN_WRITE_FMT_LOG_MSG)), + : .{ .eax = true, .ecx = true, .edx = true, .memory = true, .cc = true }); + + g_session_marker_written = true; + con.fmt("[combatlog] session: {s}\n", .{std.mem.span(name)}); +} + +// ============================================================================= +// EnableChatLogging hook +// ============================================================================= + +const fc = std.builtin.CallingConvention{ .x86_fastcall = .{} }; + +var enable_logging_hook: hook.Detour(fn (u32, u32) callconv(fc) u32) = .{}; + +fn enableChatLoggingDetour(lua_state: u32, index: u32) callconv(fc) u32 { + const result = enable_logging_hook.callOriginal(.{ lua_state, index }); + // index 1 = combat log + if (index == 1 and !g_session_marker_written) { + maybeWriteSessionMarker(); + } + return result; +} + +// ============================================================================= +// Install / remove hooks +// ============================================================================= + pub fn installHooks() void { - con.print("[combatlog] Module loaded (stub)\n"); + con.print("[combatlog] Module loaded\n"); // Multi-DLL safety: only one instance per process should hook var mutex_name_buf: [64]u8 = undefined; - const mutex_name = std.fmt.bufPrint(&mutex_name_buf, "Local\\CombatlogHook_{d}", .{GetCurrentProcessId()}) catch return; + const mutex_name = std.fmt.bufPrint(&mutex_name_buf, "Local\\WeirdUtils_CombatlogHook_{d}", .{GetCurrentProcessId()}) catch return; mutex_name_buf[mutex_name.len] = 0; g_mutex = CreateMutexA(null, 1, @ptrCast(mutex_name_buf[0..mutex_name.len :0])); @@ -38,10 +212,23 @@ pub fn installHooks() void { return; } g_is_hook_owner = true; + + // Redirect combat log path to timestamped+PID filename + setupPathRedirect(); + + // Hook EnableChatLogging to inject session marker on combat log enable + if (enable_logging_hook.attach(o.FN_ENABLE_CHAT_LOGGING, &enableChatLoggingDetour) != .ok) { + con.print("[combatlog] FAILED to hook EnableChatLogging!\n"); + } else { + con.print("[combatlog] hooked EnableChatLogging OK\n"); + } } pub fn removeHooks() void { if (g_is_hook_owner) { + enable_logging_hook.detach(); + restorePathPointer(); + if (g_mutex) |m| { _ = ReleaseMutex(m); _ = CloseHandle(m); diff --git a/src/dataassets/dataassets.zig b/src/customassets/customassets.zig similarity index 96% rename from src/dataassets/dataassets.zig rename to src/customassets/customassets.zig index c853fb6..bbb4cf8 100644 --- a/src/dataassets/dataassets.zig +++ b/src/customassets/customassets.zig @@ -1,5 +1,5 @@ // ============================================================================= -// dataassets - Loose file loading & permissive patch glob +// customassets - Loose file loading & permissive patch glob // ============================================================================= // // 1. Patches patch-?.MPQ → patch-*.MPQ so multi-char patch names work @@ -192,7 +192,7 @@ fn checkFileExistenceDetour(filename_ptr: u32, flags: u32, output_buffer_ptr: u3 if (filename_ptr != 0) { if (looseFilesLookup(filename_ptr)) |disk_path| { const raw: [*]const u8 = @ptrFromInt(filename_ptr); - con.fmt("[dataassets] loose hit: \"{s}\"\n", .{raw[0..cStrLen(raw)]}); + con.fmt("[customassets] loose hit: \"{s}\"\n", .{raw[0..cStrLen(raw)]}); if (output_buffer_ptr != 0) { const disk_len = cStrLen(disk_path); @@ -281,11 +281,11 @@ var g_mutex: ?*anyopaque = null; var g_is_hook_owner: bool = false; pub fn installHooks() void { - con.print("[dataassets] Module loaded\n"); + con.print("[customassets] Module loaded\n"); // Multi-DLL safety: only one instance per process should hook var mutex_name_buf: [64]u8 = undefined; - const mutex_name = std.fmt.bufPrint(&mutex_name_buf, "Local\\DataAssetsHook_{d}", .{GetCurrentProcessId()}) catch return; + const mutex_name = std.fmt.bufPrint(&mutex_name_buf, "Local\\WeirdUtils_CustomAssetsHook_{d}", .{GetCurrentProcessId()}) catch return; mutex_name_buf[mutex_name.len] = 0; g_mutex = CreateMutexA(null, 1, @ptrCast(mutex_name_buf[0..mutex_name.len :0])); @@ -295,7 +295,7 @@ pub fn installHooks() void { _ = CloseHandle(g_mutex.?); g_mutex = null; g_is_hook_owner = false; - con.print("[dataassets] Another DLL owns hooks (mutex taken), skipping\n"); + con.print("[customassets] Another DLL owns hooks (mutex taken), skipping\n"); return; } g_is_hook_owner = true; diff --git a/src/framecrash/framecrash.zig b/src/framecrash/framecrash.zig index 08bd57a..77bd159 100644 --- a/src/framecrash/framecrash.zig +++ b/src/framecrash/framecrash.zig @@ -627,7 +627,7 @@ fn restoreVtableSlot(slot_addr: usize, saved: *usize) void { pub fn installHooks() void { // Multi-DLL safety: only one instance per process should hook var mutex_name_buf: [64]u8 = undefined; - const mutex_name = std.fmt.bufPrint(&mutex_name_buf, "Local\\FramecrashHook_{d}", .{GetCurrentProcessId()}) catch return; + const mutex_name = std.fmt.bufPrint(&mutex_name_buf, "Local\\WeirdUtils_FramecrashHook_{d}", .{GetCurrentProcessId()}) catch return; mutex_name_buf[mutex_name.len] = 0; g_mutex = CreateMutexA(null, 1, @ptrCast(mutex_name_buf[0..mutex_name.len :0])); diff --git a/src/interact/interact.zig b/src/interact/interact.zig index 1b371fe..1ab0d2f 100644 --- a/src/interact/interact.zig +++ b/src/interact/interact.zig @@ -406,7 +406,7 @@ pub fn installHooks() void { // Multi-DLL safety: only one instance per process should hook var mutex_name_buf: [64]u8 = undefined; - const mutex_name = std.fmt.bufPrint(&mutex_name_buf, "Local\\InteractHook_{d}", .{GetCurrentProcessId()}) catch return; + const mutex_name = std.fmt.bufPrint(&mutex_name_buf, "Local\\WeirdUtils_InteractHook_{d}", .{GetCurrentProcessId()}) catch return; mutex_name_buf[mutex_name.len] = 0; g_mutex = CreateMutexA(null, 1, @ptrCast(mutex_name_buf[0..mutex_name.len :0])); diff --git a/src/main.zig b/src/main.zig index 5bcb583..04dd2c6 100644 --- a/src/main.zig +++ b/src/main.zig @@ -12,7 +12,7 @@ const build_opts = struct { const combatlog = @import("build_options").enable_combatlog; const minimapicons = @import("build_options").enable_minimapicons; const transmogfix = @import("build_options").enable_transmogfix; - const dataassets = @import("build_options").enable_dataassets; + const customassets = @import("build_options").enable_customassets; const healtextfix = @import("build_options").enable_healtextfix; }; @@ -25,7 +25,7 @@ const framecrash = if (build_opts.framecrash) @import("framecrash/framecrash.zig const combatlog = if (build_opts.combatlog) @import("combatlog/combatlog.zig") else struct {}; const minimapicons = if (build_opts.minimapicons) @import("minimapicons/minimapicons.zig") else struct {}; const transmogfix = if (build_opts.transmogfix) @import("transmogfix/transmogfix.zig") else struct {}; -const dataassets = if (build_opts.dataassets) @import("dataassets/dataassets.zig") else struct {}; +const customassets = if (build_opts.customassets) @import("customassets/customassets.zig") else struct {}; const healtextfix = if (build_opts.healtextfix) @import("healtextfix/healtextfix.zig") else struct {}; const WINAPI = std.builtin.CallingConvention.winapi; @@ -168,8 +168,8 @@ const outline_files = if (build_opts.outline) [_]FileEntry{ } else [_]FileEntry{}; const markers_files = if (build_opts.worldmarkers) [_]FileEntry{ - .{ .name = "Markers.toc", .data = @embedFile("markers/addon/Markers.toc") }, - .{ .name = "Markers.lua", .data = @embedFile("markers/addon/Markers.lua") }, + .{ .name = "WorldMarkers.toc", .data = @embedFile("markers/addon/Markers.toc") }, + .{ .name = "WorldMarkers.lua", .data = @embedFile("markers/addon/Markers.lua") }, .{ .name = "Bindings.xml", .data = @embedFile("markers/addon/Bindings.xml") }, } else [_]FileEntry{}; @@ -216,7 +216,7 @@ const addon_prefixes = [_]AddonPrefix{ .{ .prefix = "Interface\\AddOns\\Screenshot\\", .files = &screenshot_files }, .{ .prefix = "Interface\\AddOns\\Interact\\", .files = &interact_files }, .{ .prefix = "Interface\\AddOns\\Outline\\", .files = &outline_files }, - .{ .prefix = "Interface\\AddOns\\Markers\\", .files = &markers_files }, + .{ .prefix = "Interface\\AddOns\\WorldMarkers\\", .files = &markers_files }, .{ .prefix = "Spells\\", .files = &markers_spells_assets }, .{ .prefix = "Spells\\", .files = &markers_xyz_model }, .{ .prefix = "World\\Expansion01\\Doodads\\Zulaman\\Doors\\", .files = &markers_world_assets }, @@ -718,12 +718,12 @@ fn loadAddonsDetour(error_handler: u32) callconv(fc) void { } if (build_opts.worldmarkers and markers.isActive()) { callLoadFileListWithIncludes( - "Interface\\AddOns\\Markers\\Markers.toc", + "Interface\\AddOns\\WorldMarkers\\WorldMarkers.toc", &md5ctx, error_handler, ); callLoadUIBindingsFromFile( - "Interface\\AddOns\\Markers\\Bindings.xml", + "Interface\\AddOns\\WorldMarkers\\Bindings.xml", &md5ctx, error_handler, ); @@ -803,7 +803,7 @@ const ModuleHooks = struct { /// Order matters: modules are installed top-to-bottom, removed bottom-to-top. /// Modules with remove_on_shutdown run their remove during shutdownDetour too. const modules = [_]ModuleHooks{ - if (build_opts.dataassets) .{ .install = dataassets.installHooks, .remove = dataassets.removeHooks } else .{}, + if (build_opts.customassets) .{ .install = customassets.installHooks, .remove = customassets.removeHooks } else .{}, if (build_opts.framecrash) .{ .install = framecrash.installHooks, .remove = framecrash.removeHooks } else .{}, if (build_opts.combatlog) .{ .install = combatlog.installHooks, .remove = combatlog.removeHooks } else .{}, if (build_opts.transmogfix) .{ .install = transmogfix.installHooks, .remove = transmogfix.removeHooks } else .{}, diff --git a/src/markers/MPQ_FILESYSTEM_RESEARCH.md b/src/markers/MPQ_FILESYSTEM_RESEARCH.md index 17960c1..6f6ee87 100644 --- a/src/markers/MPQ_FILESYSTEM_RESEARCH.md +++ b/src/markers/MPQ_FILESYSTEM_RESEARCH.md @@ -410,7 +410,7 @@ The hash table uses a specific encryption algorithm (documented in wowdev.wiki a ### Data Assets Synergy -The [dataassets project](/media/storage/projects/zig/dataassets/) already implements the glob pattern patch (`0x82edc2: '?' → '*'`) for multi-character patch names. If combined with dataassets, the temp file could use any name like `patch-weirdutils.MPQ`. +The [customassets project](/media/storage/projects/zig/customassets/) already implements the glob pattern patch (`0x82edc2: '?' → '*'`) for multi-character patch names. If combined with customassets, the temp file could use any name like `patch-weirdutils.MPQ`. --- @@ -532,11 +532,11 @@ If we could insert entries into this hash table mapping our asset paths to disk --- -## Approach D: CheckFileExistence Hook (dataassets Pattern) +## Approach D: CheckFileExistence Hook (customassets Pattern) ### Concept -The [dataassets project](/media/storage/projects/zig/dataassets/) takes a different approach entirely: +The [customassets project](/media/storage/projects/zig/customassets/) takes a different approach entirely: 1. **NOP two gates** in `File_FindInArchive` (0x654b5c, 0x654b6a) that restrict `CheckFileExistence` to only "Interface/AddOns" paths 2. **Hook `CheckFileExistence`** (0x654DD0) to check a hash map of loose disk files @@ -544,7 +544,7 @@ The [dataassets project](/media/storage/projects/zig/dataassets/) takes a differ This works for **disk-based** loose files but not for in-memory embedded data. However, combined with writing temp files to disk, it provides a clean single-hook solution. -### Key Addresses (from dataassets) +### Key Addresses (from customassets) | Target | Address | Patch | |--------|---------|-------| @@ -564,7 +564,7 @@ This works for **disk-based** loose files but not for in-memory embedded data. H | Fake contexts | ~20 | **0** | 0 | 1 | 0 | 0 | | Async handling | Manual | **Native** | Native | Uncertain | Native | Native | | Build complexity | Low | **Low** | Medium (MPQ builder) | Medium (MPQ builder) | High (struct RE) | Low | -| RE work needed | Done | **Minimal** (verify path redirect) | Archive_OpenUnified conv | Same as A + async verify | Full struct layout | Already done (dataassets) | +| RE work needed | Done | **Minimal** (verify path redirect) | Archive_OpenUnified conv | Same as A + async verify | Full struct layout | Already done (customassets) | | Risk | Proven but fragile | **Low** (real OS handles) | Low (native I/O) | Medium (async path?) | High (struct mismatch) | Low | | Cleanup needed | None | **Auto** (DELETE_ON_CLOSE) | Delete temp file | None | Remove from array | Delete temp files | | Purely in-memory | Yes | Mostly (cache-backed) | No | Yes | Yes | No | @@ -597,7 +597,7 @@ If a single-file solution is preferred over ~20 temp files, building a real MPQ ### Worth Combining With: Data Assets Integration -The dataassets project's `CheckFileExistence` hook and glob pattern patch could complement either approach, especially for supporting user-provided loose asset files alongside our embedded ones. +The customassets project's `CheckFileExistence` hook and glob pattern patch could complement either approach, especially for supporting user-provided loose asset files alongside our embedded ones. --- @@ -687,6 +687,6 @@ The dataassets project's `CheckFileExistence` hook and glob pattern patch could | Archive search critsec | 0xc54008 | Critical section for archive ops | | Archive search state | 0xc53ff0 | Used by File_FindInArchive | | Patch glob "patch-?.MPQ" | 0x82edbc | Glob pattern for patch discovery | -| Patch glob char | 0x82edc2 | The '?' byte (dataassets patches to '*') | +| Patch glob char | 0x82edc2 | The '?' byte (customassets patches to '*') | | Data path format | 0x82edc8 | `"Data\%s"` format string | | SArchive RTTI | 0x82e248 | `".PAVSArchive@@"` | diff --git a/src/markers/addon/Bindings.xml b/src/markers/addon/Bindings.xml index 55700d8..b279f80 100644 --- a/src/markers/addon/Bindings.xml +++ b/src/markers/addon/Bindings.xml @@ -1,20 +1,20 @@ - + WorldMarker(1) - + WorldMarker(2) - + WorldMarker(3) - + WorldMarker(4) - + WorldMarker(5) - + ClearWorldMarker() diff --git a/src/markers/addon/Markers.lua b/src/markers/addon/Markers.lua index 0a835ec..d1a974b 100644 --- a/src/markers/addon/Markers.lua +++ b/src/markers/addon/Markers.lua @@ -1,9 +1,9 @@ --- Markers addon (embedded in DLL, loaded from memory) --- Part of WeirdUtils - only loaded when markers module is compiled +-- WorldMarkers addon (embedded in DLL, loaded from memory) +-- Part of WeirdUtils - only loaded when worldmarkers module is compiled -MARKERS_VERSION = 4 +WORLDMARKERS_VERSION = 4 -BINDING_HEADER_MARKERS = "Markers" +BINDING_HEADER_WORLDMARKERS = "World Markers" -- ============================================================================= -- Debug logging @@ -343,7 +343,7 @@ frame:RegisterEvent("RAID_ROSTER_UPDATE") frame:SetScript("OnEvent", function() if event == "PLAYER_LOGIN" then - DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00Markers|r v" .. MARKERS_VERSION .. " loaded") + DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00WorldMarkers|r v" .. WORLDMARKERS_VERSION .. " loaded") lastGroupSize = getGroupSize() scheduleSyncRequest() elseif event == "CHAT_MSG_ADDON" then diff --git a/src/markers/addon/Markers.toc b/src/markers/addon/Markers.toc index 4e443dd..fc034cc 100644 --- a/src/markers/addon/Markers.toc +++ b/src/markers/addon/Markers.toc @@ -1,7 +1,7 @@ ## Interface: 11200 -## Title: Markers -## Notes: Client-side raid markers and indicators +## Title: WorldMarkers +## Notes: Client-side world markers ## Version: 1.0 ## Dependencies: WeirdUtils -Markers.lua +WorldMarkers.lua diff --git a/src/markers/markers.zig b/src/markers/markers.zig index 97ae50a..f91f557 100644 --- a/src/markers/markers.zig +++ b/src/markers/markers.zig @@ -38,7 +38,7 @@ const ERROR_ALREADY_EXISTS: u32 = 183; var g_mutex: ?*anyopaque = null; var g_is_hook_owner: bool = false; -/// True if this DLL instance owns the markers hooks and Lua API is safe to use. +/// True if this DLL instance owns the world markers hooks and Lua API is safe to use. pub fn isActive() bool { return g_is_hook_owner; } @@ -466,7 +466,7 @@ fn spawnEntity(index: usize, pos: Vec3) bool { var position = [3]f32{ pos.x, pos.y, pos.z + MARKER_Z_OFFSET }; const obj = createEntityInstance(MODEL_PATHS[index], &position, 0.0, 0, 1) orelse { - con.fmt("[markers] failed to create marker {d}\n", .{index + 1}); + con.fmt("[worldmarkers] failed to create marker {d}\n", .{index + 1}); return false; }; @@ -474,7 +474,7 @@ fn spawnEntity(index: usize, pos: Vec3) bool { marker_created_tick[index] = GetTickCount(); hold_queued[index] = false; - con.fmt("[markers] marker {d} spawned at {d:.1}, {d:.1}, {d:.1} @0x{x}\n", .{ + con.fmt("[worldmarkers] marker {d} spawned at {d:.1}, {d:.1}, {d:.1} @0x{x}\n", .{ index + 1, pos.x, pos.y, pos.z, @intFromPtr(obj), }); return true; @@ -511,7 +511,7 @@ fn clearAllMarkers() void { } marker_defs[i] = EMPTY_DEF; } - if (any) con.print("[markers] all markers cleared\n"); + if (any) con.print("[worldmarkers] all markers cleared\n"); } // ============================================================================= @@ -522,20 +522,20 @@ fn clearAllMarkers() void { /// Returns 1 on success, nil on permission denied. pub fn luaWorldMarker(L: lua.State) callconv(.c) u32 { if (!canSetMarkers()) { - con.print("[markers] WorldMarker: no permission\n"); + con.print("[worldmarkers] WorldMarker: no permission\n"); return 0; // nil — addon shows user message } const nargs = lua.gettop(L); if (nargs < 1 or !lua.isnumber(L, 1)) { - con.print("[markers] WorldMarker: expected index (1-5)\n"); + con.print("[worldmarkers] WorldMarker: expected index (1-5)\n"); return 0; } const raw_index = @as(i32, @intFromFloat(lua.tonumber(L, 1))); if (raw_index < 1 or raw_index > NUM_MARKERS) { - con.print("[markers] WorldMarker: index must be 1-5\n"); + con.print("[worldmarkers] WorldMarker: index must be 1-5\n"); return 0; } const index: usize = @intCast(raw_index - 1); @@ -547,17 +547,17 @@ pub fn luaWorldMarker(L: lua.State) callconv(.c) u32 { _ = placeMarker(index, .{ .x = x, .y = y, .z = z }); } else if (nargs >= 2 and lua.isstring(L, 2)) { const unit_id = lua.tostring(L, 2) orelse { - con.print("[markers] WorldMarker: invalid unit string\n"); + con.print("[worldmarkers] WorldMarker: invalid unit string\n"); return 0; }; const pos = resolveUnitPosition(unit_id) orelse { - con.fmt("[markers] WorldMarker: unit '{s}' not found\n", .{std.mem.span(unit_id)}); + con.fmt("[worldmarkers] WorldMarker: unit '{s}' not found\n", .{std.mem.span(unit_id)}); return 0; }; _ = placeMarker(index, pos); } else { const pos = getCursorTerrainPosition() orelse { - con.print("[markers] no terrain under cursor\n"); + con.print("[worldmarkers] no terrain under cursor\n"); return 0; }; _ = placeMarker(index, pos); @@ -571,7 +571,7 @@ pub fn luaWorldMarker(L: lua.State) callconv(.c) u32 { /// Returns 1 on success, nil on permission denied. pub fn luaClearWorldMarker(L: lua.State) callconv(.c) u32 { if (!canSetMarkers()) { - con.print("[markers] ClearWorldMarker: no permission\n"); + con.print("[worldmarkers] ClearWorldMarker: no permission\n"); return 0; } @@ -586,13 +586,13 @@ pub fn luaClearWorldMarker(L: lua.State) callconv(.c) u32 { } if (!lua.isnumber(L, 1)) { - con.print("[markers] ClearWorldMarker: expected index (1-5) or nil\n"); + con.print("[worldmarkers] ClearWorldMarker: expected index (1-5) or nil\n"); return 0; } const raw_index = @as(i32, @intFromFloat(lua.tonumber(L, 1))); if (raw_index < 1 or raw_index > NUM_MARKERS) { - con.print("[markers] ClearWorldMarker: index must be 1-5\n"); + con.print("[worldmarkers] ClearWorldMarker: index must be 1-5\n"); return 0; } @@ -636,7 +636,7 @@ fn tickAnimations() void { const addr = @intFromPtr(entity); const refcount = hook.readMem(u16, addr + 0x0E); if (refcount <= 1) { - con.fmt("[markers] zombie detected [{d}] @0x{x} rc={d}, destroying\n", .{ i + 1, addr, refcount }); + con.fmt("[worldmarkers] zombie detected [{d}] @0x{x} rc={d}, destroying\n", .{ i + 1, addr, refcount }); cleanupEntity(entity); marker_entities[i] = null; hold_queued[i] = false; @@ -664,7 +664,7 @@ fn tickAnimations() void { const dist_sq = dx * dx + dy * dy + dz * dz; if (dist_sq < RESPAWN_DISTANCE_SQ) { - con.fmt("[markers] respawning [{d}] dist={d:.0}\n", .{ i + 1, @sqrt(dist_sq) }); + con.fmt("[worldmarkers] respawning [{d}] dist={d:.0}\n", .{ i + 1, @sqrt(dist_sq) }); _ = spawnEntity(i, marker_defs[i].pos); } } @@ -684,7 +684,7 @@ pub fn luaSetMarkerDef(L: lua.State) callconv(.c) u32 { const sender = lua.tostring(L, 6) orelse return 0; if (!senderHasPermission(sender)) { - con.fmt("[markers] SetMarkerDef: sender '{s}' denied\n", .{std.mem.span(sender)}); + con.fmt("[worldmarkers] SetMarkerDef: sender '{s}' denied\n", .{std.mem.span(sender)}); return 0; } @@ -706,7 +706,7 @@ pub fn luaSetMarkerDef(L: lua.State) callconv(.c) u32 { .active = true, }; - con.fmt("[markers] SetMarkerDef [{d}] at {d:.1},{d:.1},{d:.1} area={d}\n", .{ index + 1, x, y, z, area_id }); + con.fmt("[worldmarkers] SetMarkerDef [{d}] at {d:.1},{d:.1},{d:.1} area={d}\n", .{ index + 1, x, y, z, area_id }); return 0; } @@ -720,13 +720,13 @@ pub fn luaSetMarkerDefSync(L: lua.State) callconv(.c) u32 { if (!lua.isnumber(L, 1) or !lua.isnumber(L, 2) or !lua.isnumber(L, 3) or !lua.isnumber(L, 4) or !lua.isnumber(L, 5) or !lua.isstring(L, 6)) return 0; if (!canSetMarkers()) { - con.print("[markers] SetMarkerDefSync: local player not leader/assist\n"); + con.print("[worldmarkers] SetMarkerDefSync: local player not leader/assist\n"); return 0; } const sender = lua.tostring(L, 6) orelse return 0; if (!senderInGroup(sender)) { - con.fmt("[markers] SetMarkerDefSync: sender '{s}' not in group\n", .{std.mem.span(sender)}); + con.fmt("[worldmarkers] SetMarkerDefSync: sender '{s}' not in group\n", .{std.mem.span(sender)}); return 0; } @@ -747,7 +747,7 @@ pub fn luaSetMarkerDefSync(L: lua.State) callconv(.c) u32 { .active = true, }; - con.fmt("[markers] SetMarkerDefSync [{d}] at {d:.1},{d:.1},{d:.1} area={d}\n", .{ index + 1, x, y, z, area_id }); + con.fmt("[worldmarkers] SetMarkerDefSync [{d}] at {d:.1},{d:.1},{d:.1} area={d}\n", .{ index + 1, x, y, z, area_id }); return 0; } @@ -762,7 +762,7 @@ pub fn luaClearMarkerDef(L: lua.State) callconv(.c) u32 { // ClearMarkerDef(senderName) — clear all const sender = lua.tostring(L, 1) orelse return 0; if (!senderHasPermission(sender)) { - con.fmt("[markers] ClearMarkerDef: sender '{s}' denied\n", .{std.mem.span(sender)}); + con.fmt("[worldmarkers] ClearMarkerDef: sender '{s}' denied\n", .{std.mem.span(sender)}); return 0; } clearAllMarkers(); @@ -773,7 +773,7 @@ pub fn luaClearMarkerDef(L: lua.State) callconv(.c) u32 { // ClearMarkerDef(index, senderName) — clear one const sender = lua.tostring(L, 2) orelse return 0; if (!senderHasPermission(sender)) { - con.fmt("[markers] ClearMarkerDef: sender '{s}' denied\n", .{std.mem.span(sender)}); + con.fmt("[worldmarkers] ClearMarkerDef: sender '{s}' denied\n", .{std.mem.span(sender)}); return 0; } const raw_index = @as(i32, @intFromFloat(lua.tonumber(L, 1))); @@ -837,7 +837,7 @@ fn worldUpdateDetour(frame: u32) callconv(fc) void { /// processCinematicExit, DestroyPathObjectIfPresent). This unlinks them from /// the WDOODADDEF hash table so the atexit handler never touches freed memory. fn worldCleanupDetour() callconv(sc) void { - con.print("[markers] >>> worldCleanupDetour FIRING <<<\n"); + con.print("[worldmarkers] >>> worldCleanupDetour FIRING <<<\n"); destroyAllEntities(); world_cleanup_hook.callOriginal(.{}); } @@ -850,7 +850,7 @@ fn destroyAllEntities() void { for (&marker_entities, 0..) |*slot, i| { if (slot.*) |existing| { const addr = @intFromPtr(existing); - con.fmt("[markers] destroying marker[{d}] @0x{x}\n", .{ i, addr }); + con.fmt("[worldmarkers] destroying marker[{d}] @0x{x}\n", .{ i, addr }); cleanupEntity(existing); slot.* = null; count += 1; @@ -862,7 +862,7 @@ fn destroyAllEntities() void { for (&despawning, 0..) |*slot, i| { if (slot.*) |d| { const addr = @intFromPtr(d.entity); - con.fmt("[markers] destroying despawn[{d}] @0x{x}\n", .{ i, addr }); + con.fmt("[worldmarkers] destroying despawn[{d}] @0x{x}\n", .{ i, addr }); cleanupEntity(d.entity); slot.* = null; count += 1; @@ -870,7 +870,7 @@ fn destroyAllEntities() void { } if (count > 0) { - con.fmt("[markers] world cleanup: destroyed {d} entities\n", .{count}); + con.fmt("[worldmarkers] world cleanup: destroyed {d} entities\n", .{count}); } } @@ -879,10 +879,10 @@ fn destroyAllEntities() void { // ============================================================================= pub fn installHooks() void { - con.print("[markers] Module loaded\n"); + con.print("[worldmarkers] Module loaded\n"); var mutex_name_buf: [64]u8 = undefined; - const mutex_name = std.fmt.bufPrint(&mutex_name_buf, "Local\\MarkersHook_{d}", .{GetCurrentProcessId()}) catch return; + const mutex_name = std.fmt.bufPrint(&mutex_name_buf, "Local\\WeirdUtils_WorldMarkersHook_{d}", .{GetCurrentProcessId()}) catch return; mutex_name_buf[mutex_name.len] = 0; g_mutex = CreateMutexA(null, 1, @ptrCast(mutex_name_buf[0..mutex_name.len :0])); @@ -892,24 +892,24 @@ pub fn installHooks() void { _ = CloseHandle(g_mutex.?); g_mutex = null; g_is_hook_owner = false; - con.print("[markers] Another DLL owns markers (mutex taken), skipping\n"); + con.print("[worldmarkers] Another DLL owns world markers (mutex taken), skipping\n"); return; } g_is_hook_owner = true; // Hook OnWorldUpdate for per-frame animation tick (runs every frame while world is active). if (world_update_hook.attach(o.FN_ON_WORLD_UPDATE, &worldUpdateDetour) != .ok) { - con.print("[markers] FAILED to hook OnWorldUpdate!\n"); + con.print("[worldmarkers] FAILED to hook OnWorldUpdate!\n"); } else { - con.print("[markers] hooked OnWorldUpdate OK\n"); + con.print("[worldmarkers] hooked OnWorldUpdate OK\n"); } // Hook CleanupWorldAndEntities to destroy our entities before world teardown. // This fires on map change, logout, AND exit — before heaps are destroyed. if (world_cleanup_hook.attach(o.FN_CLEANUP_WORLD_AND_ENTITIES, &worldCleanupDetour) != .ok) { - con.print("[markers] FAILED to hook CleanupWorldAndEntities!\n"); + con.print("[worldmarkers] FAILED to hook CleanupWorldAndEntities!\n"); } else { - con.print("[markers] hooked CleanupWorldAndEntities OK\n"); + con.print("[worldmarkers] hooked CleanupWorldAndEntities OK\n"); } } @@ -918,7 +918,7 @@ pub fn installHooks() void { /// worldCleanupDetour which fires after shutdown. pub fn onShutdown() void { for (&marker_defs) |*d| d.* = EMPTY_DEF; - con.print("[markers] defs cleared (shutdown)\n"); + con.print("[worldmarkers] defs cleared (shutdown)\n"); } pub fn removeHooks() void { diff --git a/src/minimapicons/minimapicons.zig b/src/minimapicons/minimapicons.zig index 49d10e3..5a461ff 100644 --- a/src/minimapicons/minimapicons.zig +++ b/src/minimapicons/minimapicons.zig @@ -27,7 +27,7 @@ pub fn installHooks() void { // Multi-DLL safety: only one instance per process should hook var mutex_name_buf: [64]u8 = undefined; - const mutex_name = std.fmt.bufPrint(&mutex_name_buf, "Local\\MinimapiconsHook_{d}", .{GetCurrentProcessId()}) catch return; + const mutex_name = std.fmt.bufPrint(&mutex_name_buf, "Local\\WeirdUtils_MinimapiconsHook_{d}", .{GetCurrentProcessId()}) catch return; mutex_name_buf[mutex_name.len] = 0; g_mutex = CreateMutexA(null, 1, @ptrCast(mutex_name_buf[0..mutex_name.len :0])); diff --git a/src/outline/api.zig b/src/outline/api.zig index 7e69f63..56c8d19 100644 --- a/src/outline/api.zig +++ b/src/outline/api.zig @@ -30,7 +30,7 @@ pub fn init() bool { // Multi-DLL safety: only one instance per process should hook var mutex_name_buf: [64]u8 = undefined; - const mutex_name = std.fmt.bufPrint(&mutex_name_buf, "Local\\OutlineHook_{d}", .{GetCurrentProcessId()}) catch return false; + const mutex_name = std.fmt.bufPrint(&mutex_name_buf, "Local\\WeirdUtils_OutlineHook_{d}", .{GetCurrentProcessId()}) catch return false; mutex_name_buf[mutex_name.len] = 0; g_mutex = CreateMutexA(null, 1, @ptrCast(mutex_name_buf[0..mutex_name.len :0])); diff --git a/src/screenshot/screenshot.zig b/src/screenshot/screenshot.zig index a9dfb51..1a2764f 100644 --- a/src/screenshot/screenshot.zig +++ b/src/screenshot/screenshot.zig @@ -345,7 +345,7 @@ pub fn installHook() void { // Multi-DLL safety: only one instance per process should hook var mutex_name_buf: [64]u8 = undefined; - const mutex_name = std.fmt.bufPrint(&mutex_name_buf, "Local\\ScreenshotHook_{d}", .{GetCurrentProcessId()}) catch return; + const mutex_name = std.fmt.bufPrint(&mutex_name_buf, "Local\\WeirdUtils_ScreenshotHook_{d}", .{GetCurrentProcessId()}) catch return; mutex_name_buf[mutex_name.len] = 0; g_mutex = CreateMutexA(null, 1, @ptrCast(mutex_name_buf[0..mutex_name.len :0])); diff --git a/src/transmogfix/transmogfix.zig b/src/transmogfix/transmogfix.zig index c55003a..e37c533 100644 --- a/src/transmogfix/transmogfix.zig +++ b/src/transmogfix/transmogfix.zig @@ -782,7 +782,7 @@ pub fn installHooks() void { // Multi-DLL safety: only one instance per process should hook var mutex_name_buf: [64]u8 = undefined; - const mutex_name = std.fmt.bufPrint(&mutex_name_buf, "Local\\TransmogCoalesceHook_{d}", .{GetCurrentProcessId()}) catch return; + const mutex_name = std.fmt.bufPrint(&mutex_name_buf, "Local\\WeirdUtils_TransmogCoalesceHook_{d}", .{GetCurrentProcessId()}) catch return; mutex_name_buf[mutex_name.len] = 0; g_mutex = CreateMutexA(null, 1, @ptrCast(mutex_name_buf[0..mutex_name.len :0]));