All embedded addons now register through LoadAddonTOC (no more
separate hidden loading path). For addons marked hidden, walk the
addon linked list after registration and set the +0x29 exclusion
byte so DeserializeAddonData skips them when building the flat
display array. Removes the LoadAddonsRecursively hook since the
game's native LoadAddonRecursive handles all addon loading.
Move embedded file table, addon registration hooks, and loading logic
from main.zig into addons.zig. Each module's is_active callback gates
addon loading on mutex ownership so individual DLLs only load addons
for modules they control. Addon hooks are skipped entirely at comptime
when no addon-bearing modules are compiled in.
Also consolidates CheckFileExistence hook — customassets no longer hooks
it directly, instead exporting looseFilesLookup for main.zig's hook.
Move the CheckFileExistence (0x654DD0) hook from customassets into
main.zig's core file hooks. Embedded files now pass the game's
preloadFileWithFlags check, so LoadAddonRecursive handles Bindings.xml
loading naturally — respecting addon enabled/disabled state. Only hidden
addons still need explicit binding loading.
Remove the embedded Screenshot addon (TOC + Lua) and WeirdUtilsScreenshot()
Lua function. Compression quality is now controlled entirely via a CVar
(saved to config.wtf), read fresh on each screenshot. CVar 0 disables PNG
and falls through to original TGA.
Prefix all addon folder names with WeirdUtils_ to avoid collisions
with real addons. Rename SavedVariables global from UI_MinimapIcons
to WeirdUtils_MinimapIconsSettings.
Hook SetupAddonProcessing (0x51C740) to call LoadAddonTOC for each
embedded addon after the game's directory scan. This registers them in
the internal addon hash table so the game handles SavedVariables
loading/saving, file loading, and ADDON_LOADED events natively.
- Add SavedVariablesPerCharacter: UI_MinimapIcons to .toc
- Add VARIABLES_LOADED handler to load/initialize toggle state
- Add default=1 for Innkeeper, Repair, Brainwasher categories
- Write UI_MinimapIcons on toggle for save-on-logout persistence
- Remove manual callLoadFileListWithIncludes (game handles it now)
- Keep explicit Bindings.xml loading (preloadFileWithFlags bypass)
Convert custom icon PNGs to BLP2 DXT5 with correct header layout
(mipSizes at 0x54). Fix hasActiveTracking() to check g_go_id_tracking
so brainwasher works standalone. Support per-category scale in Lua.
Add gameobject entry-ID tracking (go_entry) for Goblin Brainwashing
Device (1000333) with INV_Gizmo_01 icon. Extend TypeMapping to 3-way
enum (npc_flag, go_type, go_entry). Override Field Repair Bot 75B
(50041) to match reagent vendor filters despite having no subname.
Heap-allocated pipe-delimited subname filters for vendor subcategories:
Poison, Reagent, Trade Goods, Ammunition — each with 6-locale support.
Fix same-priority same-flag classification to first-match-wins ordering.
Rename Vendor to General Vendor, fix Reagent icon path to Reagents.blp.
Read creature subname/title from the creature cache (obj+0xB30 → entry+0x10)
to distinguish NPC subtypes sharing the same flag. SetObjectTypeBlip now
accepts include and exclude subname filters (args 4 and 5).
Class Trainer filters by player class name (e.g. "Druid"), Profession
Trainer excludes the player class. Entries with include filters take
priority over exclude-only, which take priority over unfiltered catch-all.
Consolidate vendor subcategories into single Vendor entry.
Hook ObjectEnumProc, RenderObjectBlips, and EnumVisibleObjects to classify
visible objects by NPC flags / GO type and render custom blip icons.
Includes Lua API (SetObjectTypeBlip), texture caching, WMO indoor/outdoor
filtering, and coordinate projection. Consolidate vendor subcategories
into single entry since vanilla only has one NPC_FLAG_VENDOR. Spell
tracking entries now toggle off when clicked while active.
Fix: queryMapObjIDs was __fastcall (not __thiscall) — pushing 3 stack
args but RET 0x8 only cleaned 2, corrupting ESP and crashing.
Take over native MiniMapTrackingFrame to show a tracking spell
dropdown (Hunter Track X, Sense Undead/Demons, Find Herbs/Minerals)
plus NPC tracking categories with icons extracted from the 3.3.5
client (Auctioneer, Flight Master, Mailbox, Repair, etc.).
Public functions (WorldMarker, ClearWorldMarker) no longer show chat
messages — return values indicate status. New WorldMarkers.UI_WorldMarker
and UI_ClearWorldMarker wrappers provide user-facing feedback for
slash commands and keybindings. Update README with new API signatures.
Rename SetMarkerDef → SetMarkerSync, ClearMarkerDef → ClearMarkerSync
to clarify their purpose. Remove GetMarkerDef from the table since
GetWorldMarker is now a public global.
- WorldMarker() now returns x,y,z,areaId on success (was just 1)
- New global GetWorldMarker(index) for querying marker positions
- Rename CanSetWorldMarkers → CanSetWorldMarker (singular)
- Addon uses WorldMarker return values directly instead of GetMarkerDef
build.zig scans module addon/ and assets/ directories, passes file
lists as build options. main.zig uses comptime helpers to @embedFile
each path and build the AddonPrefix table automatically. Adding or
removing files no longer requires editing main.zig.
Also renames markers addon files to match WoW addon name
(Markers.toc → WorldMarkers.toc, Markers.lua → WorldMarkers.lua)
and auto-generates loadAddonsDetour from the same module metadata.
DLL now returns -1 for placement failures (no terrain, unit not found)
instead of nil. Lua side shows a throttled (2s cooldown) failure message
separate from the permission denied message.
The WeirdUtils core addon only provided /wu slash commands, a version
global, and a test function. Remove it along with the WeirdUtils
dependency from all module addon TOC files -- the DLL controls addon
load order directly so the dependency was cosmetic.
The combat/chat log path global updates are logsessions-specific and
don't belong in the core addon. Created a dedicated LogSessions addon
with its own TOC, embedded file entries, and conditional loading.
UpdateWorldPosition (called during entity creation) snaps X/Y to the
terrain chunk grid, causing markers to appear offset from the requested
position. After creation, re-apply the exact position via
SetUnitPositionAndOrientation (0x698e20) to override the grid snap.
UpdateHitTest returns hit type 2 (object) when the cursor is over a
unit/game object, with the intersection point landing at the camera
position rather than the object. Detect object hits via the hit type
field and resolve the object's actual world position from its GUID
stored in the HitTestResult.
Register Lua functions to expose current log paths. At PLAYER_LOGIN,
overwrite COMBATLOGENABLED/CHATLOGENABLED globals with actual redirected
paths (English locale only). Update DLL_README with Lua API and logging
enable instructions.
Write CHAT_SESSION and COMBATLOG_SESSION markers on first write to each
log type. Raw combat log handle address captured dynamically from
SuperWoW's InitializeLogBuffer call.
Read character name from select screen data and realm from CVar when
the player clicks Enter World, before InitializeLogBuffer fires.
Removes GUID/name-cache fallback — no more early passthrough to wrong paths.
Organize combat/raw/chat logs into Logs\<realm>\<char>\ directories.
Lazy path setup on first InitializeLogBuffer call resolves character
and realm names, creates directory tree, and reuses files modified
within 30 minutes for session continuation. Chat log added to the
redirection system. Session marker now includes realm name.
Paths not yet redirected before login — needs earlier hook point.
SuperWoWhook.dll calls InitializeLogBuffer directly with hardcoded
"Logs\WoWCombatLog.txt", bypassing the path pointer table. Hook
InitializeLogBuffer itself to intercept the path argument and
substitute our timestamped filename, regardless of caller. Also
redirects WoWRawCombatLog.txt to a matching timestamped file.
TODO: session marker (COMBATLOG_SESSION) needs to be written as the
first line in each log file -- currently it appears after SuperWoW's
initial writes (COMBATANT_INFO, ZONE_INFO) because the write hook
fires too late.
TODO: consolidate small/empty WoWCombatLog_*.txt files on startup.
SetMarkerDefSync checked canSetMarkers() on the local player, which
blocked non-leaders from receiving sync data. Now SF messages use the
same SetMarkerDef (senderHasPermission) check as P messages.
Protocol change: sync responder sends SF: for each marker then SD to
signal completion. Requester enters sync mode with a 5s fallback timer,
locks to first SF sender, and exits on SD. Non-syncing clients ignore
SF/SD. Consolidated login delay and sync timeout into one timer frame.
Instead of hooking EnableChatLogging or SignalEvent (which fire too late
or before the log file is open), hook WriteFormattedLogMessage directly.
On the first write to the combat log, prepend COMBATLOG_SESSION with the
player name resolved via the name cache. This guarantees the session
marker is the very first line in the file.
Also add World_HandleLogoutCleanup (0x491180) hook to reset per-session
state on real logout/disconnect (not /reload), and move remove_on_shutdown
cleanup from shutdownDetour to logoutDetour.
Switch from GetObjectPtr→GetUnitName (object manager, not populated
at LoggingCombat time) to RetrieveNPCDataFromCache (name cache,
available early). Remove the SignalEvent probe hook that was only
for discovery.
Ghidra disasm confirms RET 0xC (callee cleans 3 args). Third arg is a
va_list pointer, not the variadic args directly. For %s, vsprintf reads
*(char**)va_list -- previous code passed &name_buf causing vsprintf to
interpret "Munj" (0x6A6E754D) as a char* and crash at strlen.
Fix: pass &name_ptr (pointer to the char*) and remove caller stack
cleanup. Added detailed research to RESEARCH.md.
The old code called GetObjectByGUID (0x464870) as __fastcall with the
GUID in ECX/EDX, but the function is actually __stdcall with the u64
GUID on the stack. This ABI mismatch caused a crash.
Switched to perfboost's two-step approach with correct conventions:
1. GetObjectPtr (0x464870) — __stdcall(u64 guid) → object ptr
2. CGUnit_C::GetUnitName (0x609210) — __thiscall(ECX=unit, 0) → char*
Also added ESI/EDI/EBX clobber barrier in the EnableChatLogging detour
and copy name to stack buffer before log write.
- 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
Root cause: hook.fastcall used "r" constraints + explicit MOV to set
ECX/EDX. LLVM can allocate "r" inputs to clobbered registers, causing
cross-assignment (ecx_in→EDX, edx_in→ECX) or function address stomping
when func lands in ECX/EDX. Debug works by luck (trivial regalloc);
Release optimizes aggressively and hits the conflicts.
Fix: explicit "{ecx}", "{edx}", "{eax}" register constraints in zhook
fastcall — compiler places values directly, no MOV needed, no ambiguity.
Also fix 9 inline asm blocks across main.zig, interact.zig,
screenshot.zig, markers.zig missing ECX/EDX clobbers after CALL
instructions. Without clobbers the optimizer assumes registers retain
input values after the call — stale reuse in release builds.
Other changes in this commit:
- Rename markers→worldmarkers (build flag, DLL, Lua table)
- Rename assetfix→looseassets
- lua.zig: add .never_tail to pushcclosure, pcall, openlib, pushnumber
- Move internal marker functions into WorldMarkers Lua table via openlib
- Remove unused GetCurrentAreaId function
- Fix cleanup_file_handle_hook.original() → .callOriginal()
Replace Lua-side permission checks with direct memory reads in the DLL.
All marker mutation functions now verify sender identity against the
raid roster / party leader GUID without touching Lua state.
- Add getPlayerGUID (0x468550), getNameFromGUID (name cache at 0xc0e228)
- Add canSetMarkers: checks local player is leader/officer via roster
- Add senderHasPermission: verifies sender name against roster ranks
- Add senderInGroup: weaker check for sync relay (any rank)
- WorldMarker/ClearWorldMarker return 1/nil for addon feedback
- SetMarkerDef/ClearMarkerDef take sender name param, verify DLL-side
- SetMarkerDefSync: dual check (local=leader + sender in group)
- CanSetMarkers() Lua function for addon broadcast decisions
- Remove Lua-side canSetMarkers/senderHasPermission from Markers.lua
- Add offsets: LEADER_GUID, RAID_ROSTER_ARRAY, RAID_MEMBER_COUNT, etc.
Move Lua C API wrappers from main.zig's lua struct into src/lua.zig
so both main.zig and markers.zig import from the same source. Remove
duplicate lapi struct from markers.zig.
Add DLL-side permission gating on WorldMarker/ClearWorldMarker: calls
WoW's IsPartyLeader (0x4e9130) and IsRaidOfficer (0x4bb910) C
functions directly. Requires party leader, raid leader, or raid
assist to place or clear markers. SetMarkerDef/ClearMarkerDef remain
ungated (addon validates sender before calling).
Marker definitions (position + area ID) now persist across zone
transitions. Entities are destroyed on map change but respawned
automatically when the player approaches within 200y. Definitions
are cleared on logout/exit via onShutdown hook.
Group sync via addon messages (WMark prefix, colon-delimited protocol)
with permission checks (raid leader/assist, party leader). Includes
sync request/response for late joiners and roster change broadcasting.
Fix lua_pushnumber calling convention: function is __thiscall (ECX=L,
f64 on stack, ret 8), not __fastcall. The patched inreg fastcall was
placing the f64's low dword in EDX, corrupting values.
Other fixes:
- Remove Bindings.xml from .toc files (explicit binding loader needed)
- Remove remove_on_shutdown for markers (was killing hooks on logout)
- Remove diagnostic entity check code (culling behavior understood)
- New Lua APIs: SetMarkerDef, ClearMarkerDef, GetMarkerDef
- Rename binding labels to marker colors
- SF first-responder lock: after sending SR/LSR, only accept SF
messages from the first player to respond, ignore duplicates
from other members to prevent entity flicker.
- Roster change debounce: retriggerable 5s timer with up to 5
one-second extensions (10s max). Only fires on group size
increase. Party events skipped when in raid.
- Area ID check in DLL respawn: markers only spawn when the
player is in the same zone (area_id match against 0xB4E314).
- New GetCurrentAreaId() Lua function for addon zone awareness.