Add bigcursor module stub, rename combatlog to logsessions, bump session timer to 60min

- New bigcursor module skeleton with build integration and mutex
- Rename combatlog module to logsessions (directory, files, build options,
  imports, console prefixes, mutex name, DLL variant, docs)
- Increase log session continuation threshold from 30 to 60 minutes
- Update DLL_README log sessions section
This commit is contained in:
MarcelineVQ
2026-03-04 15:14:04 -08:00
parent 1bbf334958
commit 00749d5745
9 changed files with 141 additions and 64 deletions
+13 -8
View File
@@ -105,9 +105,9 @@ No configuration needed, install and forget.
---
### Combat Log Sessions
### Log Sessions
Organizes combat, raw combat, and chat logs into per-character directories with timestamped filenames:
Organizes the combat, raw combat, and chat logs into per-character directories with timestamped filenames:
```
Logs\<Realm>\<Character>\WoWChatLog_YYYYMMDD_HHMMSS.txt
@@ -115,11 +115,8 @@ Logs\<Realm>\<Character>\WoWCombatLog_YYYYMMDD_HHMMSS.txt
Logs\<Realm>\<Character>\WoWRawCombatLog_YYYYMMDD_HHMMSS.txt (superwow only)
```
Each new game session begins with a marker line (`COMBATLOG_SESSION` or `CHAT_SESSION`) identifying the character and realm. If a log file for the same character was written to within the last 30 minutes, the session continues into the same file instead of creating a new one.
Like any normal log use, logging must be enabled in-game for files to appear:
- **Combat log**: `/combatlog` in chat, or `LoggingCombat(1)` from a macro/addon
- **Chat log**: `/chatlog` in chat, or `LoggingChat(1)` from a macro/addon
Every character login begins with a marker line (`COMBATLOG_SESSION` or `CHAT_SESSION`) identifying the character and realm.
If a log file for the same character was written to within the last 60 minutes, the same logfile will be used instead of creating a new one.
Lua API for addon developers:
@@ -128,7 +125,7 @@ Lua API for addon developers:
No other configuration needed, install and forget.
**DLL:** `combatlog.dll`
**DLL:** `logsessions.dll`
---
@@ -140,6 +137,14 @@ Fixes duplicate floating heal numbers caused by SuperWoW 1.5. Only relevant if y
---
### Big Cursor
Increases the hardware cursor render size for improved visibility. No configuration needed, install and forget.
**DLL:** `bigcursor.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.
+18 -2
View File
@@ -57,7 +57,7 @@ want:
# Example: only transmogfix + customassets + healtextfix
zig build -Doptimize=ReleaseSmall \
-Dscreenshot=false -Dinteract=false -Doutline=false \
-Dworldmarkers=false -Dframecrash=false -Dcombatlog=false \
-Dworldmarkers=false -Dframecrash=false -Dlogsessions=false \
-Dminimapicons=false \
-Dtransmogfix=true -Dcustomassets=true -Dhealtextfix=true
```
@@ -100,7 +100,8 @@ git push origin main
## 3. Write the release notes
Use this template — fill in the sections that apply, delete the rest:
Use this template - fill in the sections that apply, delete the rest.
Use `-` (not em dash) anywhere a dash would be used:
```markdown
## What's New
@@ -143,8 +144,23 @@ tea release create \
--asset zig-out/variants/healtextfix.dll
```
## 5. Hide source archives
Codeberg attaches empty source tar/zip by default. Hide them via API:
```sh
curl -s -X PATCH \
-H "Authorization: token <your-token>" \
-H "Content-Type: application/json" \
-d '{"hide_archive_links":true}' \
"https://codeberg.org/api/v1/repos/MarcelineVQ/WeirdUtils/releases/<release-id>"
```
Get the release ID from `tea release list --repo MarcelineVQ/WeirdUtils --output json`.
## Checklist
- [ ] Built with `ReleaseSmall` (both default and `all-variants`)
- [ ] Remote README updated (if `DLL_README.md` changed)
- [ ] Release created and DLLs uploaded via `tea`
- [ ] Source archives hidden
+19 -15
View File
@@ -14,11 +14,12 @@ pub fn build(b: *std.Build) void {
const enable_outline = b.option(bool, "outline", "Enable outline module") orelse true;
const enable_worldmarkers = b.option(bool, "worldmarkers", "Enable world markers module") orelse true;
const enable_framecrash = b.option(bool, "framecrash", "Enable framecrash fix") orelse false;
const enable_combatlog = b.option(bool, "combatlog", "Enable combat log freshness") orelse true;
const enable_logsessions = b.option(bool, "logsessions", "Enable log session rotation") 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_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;
const enable_bigcursor = b.option(bool, "bigcursor", "Enable big cursor module") orelse true;
// Create build options module
const build_options = b.addOptions();
@@ -27,11 +28,12 @@ pub fn build(b: *std.Build) void {
build_options.addOption(bool, "enable_outline", enable_outline);
build_options.addOption(bool, "enable_worldmarkers", enable_worldmarkers);
build_options.addOption(bool, "enable_framecrash", enable_framecrash);
build_options.addOption(bool, "enable_combatlog", enable_combatlog);
build_options.addOption(bool, "enable_logsessions", enable_logsessions);
build_options.addOption(bool, "enable_minimapicons", enable_minimapicons);
build_options.addOption(bool, "enable_transmogfix", enable_transmogfix);
build_options.addOption(bool, "enable_customassets", enable_customassets);
build_options.addOption(bool, "enable_healtextfix", enable_healtextfix);
build_options.addOption(bool, "enable_bigcursor", enable_bigcursor);
const build_options_module = build_options.createModule();
const zhook_dep = b.dependency("zhook", .{
@@ -60,19 +62,20 @@ 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, customassets: bool, healtextfix: bool };
const Variant = struct { name: []const u8, screenshot: bool, interact: bool, outline: bool, worldmarkers: bool, framecrash: bool, logsessions: bool, minimapicons: bool, transmogfix: bool, customassets: bool, healtextfix: bool, bigcursor: bool };
inline for (&[_]Variant{
.{ .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 },
.{ .name = "full", .screenshot = true, .interact = true, .outline = true, .worldmarkers = true, .framecrash = true, .logsessions = true, .minimapicons = true, .transmogfix = true, .customassets = true, .healtextfix = true, .bigcursor = true },
.{ .name = "screenshot", .screenshot = true, .interact = false, .outline = false, .worldmarkers = false, .framecrash = true, .logsessions = true, .minimapicons = false, .transmogfix = false, .customassets = false, .healtextfix = false, .bigcursor = false },
.{ .name = "interact", .screenshot = false, .interact = true, .outline = false, .worldmarkers = false, .framecrash = true, .logsessions = true, .minimapicons = false, .transmogfix = false, .customassets = false, .healtextfix = false, .bigcursor = false },
.{ .name = "outline", .screenshot = false, .interact = false, .outline = true, .worldmarkers = false, .framecrash = true, .logsessions = true, .minimapicons = false, .transmogfix = false, .customassets = false, .healtextfix = false, .bigcursor = false },
.{ .name = "worldmarkers", .screenshot = false, .interact = false, .outline = false, .worldmarkers = true, .framecrash = true, .logsessions = true, .minimapicons = false, .transmogfix = false, .customassets = false, .healtextfix = false, .bigcursor = false },
.{ .name = "framecrash", .screenshot = false, .interact = false, .outline = false, .worldmarkers = false, .framecrash = true, .logsessions = false, .minimapicons = false, .transmogfix = false, .customassets = false, .healtextfix = false, .bigcursor = false },
.{ .name = "logsessions", .screenshot = false, .interact = false, .outline = false, .worldmarkers = false, .framecrash = false, .logsessions = true, .minimapicons = false, .transmogfix = false, .customassets = false, .healtextfix = false, .bigcursor = false },
.{ .name = "minimapicons", .screenshot = false, .interact = false, .outline = false, .worldmarkers = false, .framecrash = true, .logsessions = false, .minimapicons = true, .transmogfix = false, .customassets = false, .healtextfix = false, .bigcursor = false },
.{ .name = "transmogfix", .screenshot = false, .interact = false, .outline = false, .worldmarkers = false, .framecrash = false, .logsessions = false, .minimapicons = false, .transmogfix = true, .customassets = false, .healtextfix = false, .bigcursor = false },
.{ .name = "customassets", .screenshot = false, .interact = false, .outline = false, .worldmarkers = false, .framecrash = false, .logsessions = false, .minimapicons = false, .transmogfix = false, .customassets = true, .healtextfix = false, .bigcursor = false },
.{ .name = "healtextfix", .screenshot = false, .interact = false, .outline = false, .worldmarkers = false, .framecrash = false, .logsessions = false, .minimapicons = false, .transmogfix = false, .customassets = false, .healtextfix = true, .bigcursor = false },
.{ .name = "bigcursor", .screenshot = false, .interact = false, .outline = false, .worldmarkers = false, .framecrash = false, .logsessions = false, .minimapicons = false, .transmogfix = false, .customassets = false, .healtextfix = false, .bigcursor = true },
}) |variant| {
const opts = b.addOptions();
opts.addOption(bool, "enable_screenshot", variant.screenshot);
@@ -80,11 +83,12 @@ pub fn build(b: *std.Build) void {
opts.addOption(bool, "enable_outline", variant.outline);
opts.addOption(bool, "enable_worldmarkers", variant.worldmarkers);
opts.addOption(bool, "enable_framecrash", variant.framecrash);
opts.addOption(bool, "enable_combatlog", variant.combatlog);
opts.addOption(bool, "enable_logsessions", variant.logsessions);
opts.addOption(bool, "enable_minimapicons", variant.minimapicons);
opts.addOption(bool, "enable_transmogfix", variant.transmogfix);
opts.addOption(bool, "enable_customassets", variant.customassets);
opts.addOption(bool, "enable_healtextfix", variant.healtextfix);
opts.addOption(bool, "enable_bigcursor", variant.bigcursor);
const variant_lib = b.addLibrary(.{
.name = variant.name,
+51
View File
@@ -0,0 +1,51 @@
//! Big cursor module.
//!
//! Increases the hardware cursor render size for improved visibility.
//!
//! TODO: Research cursor rendering pipeline and hook implementation.
const std = @import("std");
const con = @import("../console.zig");
const WINAPI = std.builtin.CallingConvention.winapi;
extern "kernel32" fn CreateMutexA(lpMutexAttributes: ?*anyopaque, bInitialOwner: i32, lpName: [*:0]const u8) callconv(WINAPI) ?*anyopaque;
extern "kernel32" fn ReleaseMutex(hMutex: *anyopaque) callconv(WINAPI) i32;
extern "kernel32" fn CloseHandle(hObject: *anyopaque) callconv(WINAPI) i32;
extern "kernel32" fn GetLastError() callconv(WINAPI) u32;
extern "kernel32" fn GetCurrentProcessId() callconv(WINAPI) u32;
const ERROR_ALREADY_EXISTS: u32 = 183;
var g_mutex: ?*anyopaque = null;
var g_is_hook_owner: bool = false;
pub fn installHooks() void {
con.print("[bigcursor] Module loaded (stub)\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\\WeirdUtils_BigcursorHook_{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]));
if (g_mutex == null) return;
if (GetLastError() == ERROR_ALREADY_EXISTS) {
_ = CloseHandle(g_mutex.?);
g_mutex = null;
g_is_hook_owner = false;
con.print("[bigcursor] Another DLL owns hooks (mutex taken), skipping\n");
return;
}
g_is_hook_owner = true;
}
pub fn removeHooks() void {
if (g_is_hook_owner) {
if (g_mutex) |m| {
_ = ReleaseMutex(m);
_ = CloseHandle(m);
g_mutex = null;
}
}
g_is_hook_owner = false;
}
@@ -1,4 +1,4 @@
//! Combat log session rotation with per-character directories.
//! Log session rotation with per-character directories.
//!
//! Redirects combat log, raw combat log, and chat log to per-character
//! directories: `Logs\<realm>\<character>\<Type>_<timestamp>_<PID>.txt`.
@@ -6,7 +6,7 @@
//! Features:
//! - Early path setup: hooks HandleCharacterSelection to resolve character/realm
//! from the select screen data before world loading begins
//! - Session continuation: reuses files modified < 30 min ago
//! - Session continuation: reuses files modified < 60 min ago
//! - Session marker: writes `COMBATLOG_SESSION: <char> <realm>` on first combat write
//!
//! All DLL-side — no Lua addon needed.
@@ -184,7 +184,7 @@ fn setupSessionDir(realm: []const u8, char_name: []const u8) bool {
g_dir_path[dir_path.len] = 0;
g_dir_path_len = dir_path.len;
con.fmt("[combatlog] dir: {s}\n", .{g_dir_path[0..g_dir_path_len]});
con.fmt("[logsessions] dir: {s}\n", .{g_dir_path[0..g_dir_path_len]});
return true;
}
@@ -193,7 +193,7 @@ fn setupSessionDir(realm: []const u8, char_name: []const u8) bool {
// =============================================================================
/// Scan directory for files matching `<prefix>_*.txt`, return the newest if
/// modified within 30 minutes. Writes full path into result_buf, returns length.
/// modified within 60 minutes. Writes full path into result_buf, returns length.
fn findRecentFile(prefix: []const u8, result_buf: *[260]u8) ?usize {
if (g_dir_path_len == 0) return null;
@@ -235,7 +235,7 @@ fn findRecentFile(prefix: []const u8, result_buf: *[260]u8) ?usize {
var current_ft: FILETIME = undefined;
GetSystemTimeAsFileTime(&current_ft);
const current: u64 = @bitCast(current_ft);
const threshold: u64 = 30 * 60 * 10_000_000; // 30 minutes
const threshold: u64 = 60 * 60 * 10_000_000; // 60 minutes
if (current > newest_time and (current - newest_time) < threshold) {
// Build full path: dir + filename
@@ -252,9 +252,9 @@ fn findRecentFile(prefix: []const u8, result_buf: *[260]u8) ?usize {
/// Resolve a log file path: reuse recent file or generate new timestamped name.
fn resolveLogPath(prefix: []const u8, result_buf: *[260]u8) usize {
// Try to reuse a recent file (modified < 30 min ago)
// Try to reuse a recent file (modified < 60 min ago)
if (findRecentFile(prefix, result_buf)) |len| {
con.fmt("[combatlog] reusing: {s}\n", .{result_buf[0..len]});
con.fmt("[logsessions] reusing: {s}\n", .{result_buf[0..len]});
return len;
}
@@ -273,7 +273,7 @@ fn resolveLogPath(prefix: []const u8, result_buf: *[260]u8) usize {
st.wSecond,
}) catch return 0;
result_buf[path.len] = 0;
con.fmt("[combatlog] new: {s}\n", .{path});
con.fmt("[logsessions] new: {s}\n", .{path});
return path.len;
}
@@ -288,7 +288,7 @@ fn configureSession(char_span: []const u8, realm_span: []const u8) void {
g_session_char_len = sanitizeName(char_span, &g_session_char);
g_session_realm_len = sanitizeName(realm_span, &g_session_realm);
con.fmt("[combatlog] session: {s} on {s}\n", .{
con.fmt("[logsessions] session: {s} on {s}\n", .{
g_session_char[0..g_session_char_len],
g_session_realm[0..g_session_realm_len],
});
@@ -298,7 +298,7 @@ fn configureSession(char_span: []const u8, realm_span: []const u8) void {
g_session_realm[0..g_session_realm_len],
g_session_char[0..g_session_char_len],
)) {
con.print("[combatlog] setup: failed to create directories\n");
con.print("[logsessions] setup: failed to create directories\n");
return;
}
@@ -342,7 +342,7 @@ fn enterWorldDetour() callconv(sc) void {
if (char_name != null and realm_name != null) {
configureSession(std.mem.span(char_name.?), std.mem.span(realm_name.?));
} else {
con.print("[combatlog] enter world: char/realm not available\n");
con.print("[logsessions] enter world: char/realm not available\n");
}
}
@@ -376,18 +376,18 @@ fn initLogDetour(file_path: u32, flags: u32, handle_out: u32) callconv(sc) u32 {
// Redirect based on path suffix
if (g_combat_path_len > 0 and std.mem.endsWith(u8, path_span, "WoWCombatLog.txt")) {
con.fmt("[combatlog] redirect: {s} -> {s}\n", .{ path_span, g_combat_path[0..g_combat_path_len] });
con.fmt("[logsessions] redirect: {s} -> {s}\n", .{ path_span, g_combat_path[0..g_combat_path_len] });
return init_log_hook.callOriginal(.{ @intFromPtr(&g_combat_path), flags, handle_out });
}
if (g_raw_combat_path_len > 0 and std.mem.endsWith(u8, path_span, "WoWRawCombatLog.txt")) {
g_raw_combat_handle_addr = handle_out; // capture SuperWoW's handle address
con.fmt("[combatlog] redirect: {s} -> {s}\n", .{ path_span, g_raw_combat_path[0..g_raw_combat_path_len] });
con.fmt("[logsessions] redirect: {s} -> {s}\n", .{ path_span, g_raw_combat_path[0..g_raw_combat_path_len] });
return init_log_hook.callOriginal(.{ @intFromPtr(&g_raw_combat_path), flags, handle_out });
}
if (g_chat_path_len > 0 and std.mem.endsWith(u8, path_span, "WoWChatLog.txt")) {
con.fmt("[combatlog] redirect: {s} -> {s}\n", .{ path_span, g_chat_path[0..g_chat_path_len] });
con.fmt("[logsessions] redirect: {s} -> {s}\n", .{ path_span, g_chat_path[0..g_chat_path_len] });
return init_log_hook.callOriginal(.{ @intFromPtr(&g_chat_path), flags, handle_out });
}
@@ -448,7 +448,7 @@ fn writeSessionMarker(handle: u32, fmt_str: [*:0]const u8) void {
@intFromPtr(&marker_ptr),
});
con.fmt("[combatlog] marker: {s}\n", .{marker_str});
con.fmt("[logsessions] marker: {s}\n", .{marker_str});
}
// =============================================================================
@@ -459,7 +459,7 @@ fn writeSessionMarker(handle: u32, fmt_str: [*:0]const u8) void {
/// Called from logoutDetour/shutdownDetour in main.zig.
pub fn onShutdown() void {
if (g_paths_configured) {
con.print("[combatlog] session reset\n");
con.print("[logsessions] session reset\n");
}
g_paths_configured = false;
g_combat_marker_written = false;
@@ -505,11 +505,11 @@ pub fn luaGetChatLogPath(L: lua.State) callconv(.c) u32 {
// =============================================================================
pub fn installHooks() void {
con.print("[combatlog] Module loaded\n");
con.print("[logsessions] 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\\WeirdUtils_CombatlogHook_{d}", .{GetCurrentProcessId()}) catch return;
const mutex_name = std.fmt.bufPrint(&mutex_name_buf, "Local\\WeirdUtils_LogSessionsHook_{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]));
@@ -519,7 +519,7 @@ pub fn installHooks() void {
_ = CloseHandle(g_mutex.?);
g_mutex = null;
g_is_hook_owner = false;
con.print("[combatlog] Another DLL owns hooks (mutex taken), skipping\n");
con.print("[logsessions] Another DLL owns hooks (mutex taken), skipping\n");
return;
}
g_is_hook_owner = true;
@@ -527,21 +527,21 @@ pub fn installHooks() void {
// Hook HandleCharacterSelection — sets up paths when player clicks Enter World,
// before the world loading sequence calls InitializeLogBuffer.
if (enter_world_hook.attach(o.FN_HANDLE_CHAR_SELECT, &enterWorldDetour) != .ok) {
con.print("[combatlog] FAILED to hook HandleCharacterSelection!\n");
con.print("[logsessions] FAILED to hook HandleCharacterSelection!\n");
} else {
con.print("[combatlog] hooked HandleCharacterSelection OK\n");
con.print("[logsessions] hooked HandleCharacterSelection OK\n");
}
if (init_log_hook.attach(o.FN_INIT_LOG_BUFFER, &initLogDetour) != .ok) {
con.print("[combatlog] FAILED to hook InitializeLogBuffer!\n");
con.print("[logsessions] FAILED to hook InitializeLogBuffer!\n");
} else {
con.print("[combatlog] hooked InitializeLogBuffer OK\n");
con.print("[logsessions] hooked InitializeLogBuffer OK\n");
}
if (write_log_hook.attach(o.FN_WRITE_FMT_LOG_MSG, &writeLogDetour) != .ok) {
con.print("[combatlog] FAILED to hook WriteFormattedLogMessage!\n");
con.print("[logsessions] FAILED to hook WriteFormattedLogMessage!\n");
} else {
con.print("[combatlog] hooked WriteFormattedLogMessage OK\n");
con.print("[logsessions] hooked WriteFormattedLogMessage OK\n");
}
}
@@ -1,4 +1,4 @@
//! Address constants for combat log session rotation.
//! Address constants for log session rotation.
// =============================================================================
// Combat log path and state
+10 -7
View File
@@ -9,11 +9,12 @@ const build_opts = struct {
const outline = @import("build_options").enable_outline;
const worldmarkers = @import("build_options").enable_worldmarkers;
const framecrash = @import("build_options").enable_framecrash;
const combatlog = @import("build_options").enable_combatlog;
const logsessions = @import("build_options").enable_logsessions;
const minimapicons = @import("build_options").enable_minimapicons;
const transmogfix = @import("build_options").enable_transmogfix;
const customassets = @import("build_options").enable_customassets;
const healtextfix = @import("build_options").enable_healtextfix;
const bigcursor = @import("build_options").enable_bigcursor;
};
// Conditional module imports
@@ -22,11 +23,12 @@ const interact = if (build_opts.interact) @import("interact/interact.zig") else
const outline = if (build_opts.outline) @import("outline/api.zig") else struct {};
const markers = if (build_opts.worldmarkers) @import("markers/markers.zig") else struct {};
const framecrash = if (build_opts.framecrash) @import("framecrash/framecrash.zig") else struct {};
const combatlog = if (build_opts.combatlog) @import("combatlog/combatlog.zig") else struct {};
const logsessions = if (build_opts.logsessions) @import("logsessions/logsessions.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 customassets = if (build_opts.customassets) @import("customassets/customassets.zig") else struct {};
const healtextfix = if (build_opts.healtextfix) @import("healtextfix/healtextfix.zig") else struct {};
const bigcursor = if (build_opts.bigcursor) @import("bigcursor/bigcursor.zig") else struct {};
const WINAPI = std.builtin.CallingConvention.winapi;
const fc: std.builtin.CallingConvention = .{ .x86_fastcall = .{} };
@@ -108,9 +110,9 @@ fn registerLuaFunctions() void {
if (build_opts.outline) {
registerFunction("OutlineCommand", @intFromPtr(&outline.outlineCommand));
}
if (build_opts.combatlog) {
registerFunction("GetCombatLogPath", @intFromPtr(&combatlog.luaGetCombatLogPath));
registerFunction("GetChatLogPath", @intFromPtr(&combatlog.luaGetChatLogPath));
if (build_opts.logsessions) {
registerFunction("GetCombatLogPath", @intFromPtr(&logsessions.luaGetCombatLogPath));
registerFunction("GetChatLogPath", @intFromPtr(&logsessions.luaGetChatLogPath));
}
if (build_opts.worldmarkers and markers.isActive()) {
// User-facing functions stay global
@@ -795,7 +797,7 @@ fn logoutDetour() callconv(sc) void {
// Reset per-session state — only on real logout/disconnect, not /reload.
if (build_opts.worldmarkers) markers.onShutdown();
if (build_opts.combatlog) combatlog.onShutdown();
if (build_opts.logsessions) logsessions.onShutdown();
// Clean up world objects BEFORE game teardown — modules with
// remove_on_shutdown must destroy while game systems are alive.
@@ -836,10 +838,11 @@ const ModuleHooks = struct {
const modules = [_]ModuleHooks{
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.logsessions) .{ .install = logsessions.installHooks, .remove = logsessions.removeHooks } else .{},
if (build_opts.transmogfix) .{ .install = transmogfix.installHooks, .remove = transmogfix.removeHooks } else .{},
if (build_opts.minimapicons) .{ .install = minimapicons.installHooks, .remove = minimapicons.removeHooks } else .{},
if (build_opts.healtextfix) .{ .install = healtextfix.installHooks, .remove = healtextfix.removeHooks } else .{},
if (build_opts.bigcursor) .{ .install = bigcursor.installHooks, .remove = bigcursor.removeHooks } else .{},
if (build_opts.worldmarkers) .{ .install = markers.installHooks, .remove = markers.removeHooks } else .{},
if (build_opts.interact) .{ .install = interact.installHooks, .remove = interact.removeHooks } else .{},
if (build_opts.outline) .{ .remove = outline.cleanup } else .{},
+4 -6
View File
@@ -70,8 +70,7 @@ fn unitGUID(unit_id: [*:0]const u8) u64 {
[hi] "={edx}" (hi),
: [_] "{ecx}" (unit_id),
[func] "r" (@as(u32, ADDR_UnitGUID)),
: .{ .memory = true, .cc = true }
);
: .{ .memory = true, .cc = true });
return @as(u64, hi) << 32 | lo;
}
@@ -86,8 +85,7 @@ fn getObjectByGUID(guid: u64) u32 {
: [lo] "r" (lo),
[hi] "r" (hi),
[func] "r" (@as(u32, ADDR_GetObjectByGUID)),
: .{ .ecx = true, .edx = true, .memory = true, .cc = true }
);
: .{ .ecx = true, .edx = true, .memory = true, .cc = true });
}
fn updateInventoryAlertStates() void {
@@ -765,7 +763,6 @@ fn hookRefreshVisualAppearance(unit: u32, event_data: u32, extra_data: u32, forc
// =============================================================================
fn hookSceneEnd(device: u32) callconv(tc) void {
if (g_enabled and (g_local_pending_count > 0 or g_other_pending_count > 0)) {
processTimeouts(GetTickCount());
}
@@ -782,7 +779,8 @@ 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\\WeirdUtils_TransmogCoalesceHook_{d}", .{GetCurrentProcessId()}) catch return;
// muted name scheme not quite the same because this dll exists in the wild and we want to match it
const mutex_name = std.fmt.bufPrint(&mutex_name_buf, "Local\\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]));