The marker cleanup hook on CleanupWorldAndEntities was never installed —
markers.installHooks() was missing from install() in main.zig. Entities
created via WorldMarker were never cleaned up before the game's atexit
handler iterated the hash table over freed heap memory.
Key changes:
- Add markers.installHooks() call (the actual crash fix)
- Replace manual install/uninstall/shutdown lists with a single modules
table that drives all three phases — prevents this class of bug
- Gate marker Lua functions, addon, and keybindings behind isActive()
so they're skipped when another DLL owns the hooks
- Add world_cleanup_hook.detach() to removeHooks() (was missing)
- Migrate from vendored libs/hook to external zhook dependency
- Unify installHooks return types to void across all modules
- Add diagnostic logging to marker cleanup (temporary, for testing)
5 marker slots (Yellow, Cyan, Green, Purple, Red) with placement
by coords, unit ID, or cursor position (stubbed pending terrain
offset research). Key bindings for all 5 markers + clear all.
Assetfix: hook CheckFileExistence to serve loose Data\ files. The original
flags|1 approach failed because game paths contain backslashes, causing
CheckFileExistence to skip BuildFilePath and check the raw path (no Data\
prefix). Fix: write the correct Data\-prefixed disk path to the output buffer
directly and return 1, bypassing the original function for hash map hits.
This preserves hook chaining (filename argument is never transformed).
Also adds transmogfix (transmog update coalescing), minimapicons (stub),
new build options for all three modules, mutex-based multi-DLL safety,
embed .skin data into .m2 models, and various module improvements.
The manual callCleanupFileContext + freeGameBuffer path crashed inside SMemFree
(EIP=0x23, heap metadata corruption). The original CleanupFileHandleResources
handles fake contexts correctly (NULL-safe on handle fields), so always delegate
to the trampoline instead.
Also adds ECX to callCleanupFileContext clobber list, adds M2 file handle cleanup
before processLoadedModelData (matching onModelLoadComplete ordering), and adds
debug logging throughout the async model load path.
Hook 5 intercepts loadModelFromFileAsync (0x71d4e0) to synchronously load M2
model data for fake in-memory file contexts. processLoadedModelData returns 1
and entities are created, but file context cleanup is currently skipped (leaks
0x60 bytes per load) due to a crash in the cleanup path, and there's a later
EIP=0 crash during the game main loop that needs investigation.
Also adds: combatlog module stub, framecrash anchor vtable hooks (GetRelativeTo
and GetWidth/GetHeight crash guards), embedded Raid_UI_FX model assets with
skins and textures, WU_XYZ debug model.
Markers are now created via the game's high-level entity factory (0x6707c0)
which handles spatial registration, render setup, and lifecycle internally.
Removed ~300 lines of dead code: manual game object list insertion, A/B mode
switching, parity scanning, and model attachment wrappers.
Creation works (markers visible). Destruction still crashes — needs correct
destructor for entities created via CreateWorldUnit path (not DestroyWorldObjectAndRelease).
Markers: client-side world object system using CreateGameObject. Places
M2 models at arbitrary world positions via Lua commands (/mark test,
/mark pos). Includes embedded addon, xyz.m2/blp assets served from DLL
memory, position helpers from unit movement struct, and object lifecycle
management (create, cleanup, reposition, alpha, animation).
Framecrash: stub module with reference to crash at 0x007A2452.
Console: debug output via AllocConsole/WriteConsoleA, compiles out
entirely in non-Debug builds. Used by markers and file serve logging.
Build: markers added to feature flag matrix and all-variants step.
Main: conditional markers import, Lua function registration, embedded
addon + asset file serving, console init/deinit lifecycle.
Outline README: added misc planned features and debug mode notes.
- Each module (outline, interact, screenshot) now has its own addon/
subdir with .toc, .lua, and Bindings.xml
- Core WeirdUtils addon lives in src/core/addon/
- build.zig supports compile-time feature gating via -D options
- main.zig uses build_options for conditional imports and addon embedding
- Module addons only load when their module is compiled in
- 'zig build all-variants' produces full.dll + 3 single-module DLLs
Port the model outline hook system (CM2SceneRenderDraw, ManageRenderListNode,
DrawBatchProjected) from the C++ idris DLL to pure Zig. Includes D3D9 vtable
hooks for stencil-based outline rendering with per-category colors and thickness.
Fix GetObjectByGUID calling convention: was using fastcall (ECX/EDX) but Ghidra
confirms it's __stdcall with stack params and RET 8. Fix lua_pushcclosure address
from 0x6F3B80 (mid-body of another function) to 0x6F3920.
Guard resolveModelOwner in DrawBatchProj with hasTargets() check and cache unit
model status during ManageRenderListNode to avoid raw pointer chasing at render
time. Add IsBadReadPtr validation in resolveModelOwner to match C++ IsValidReadPtr
pattern for page-level memory safety.
Add InteractNearest() and LootAllCorpses() as registered Lua C functions,
ported from the standalone interact DLL. Uses the existing Lua protection
bypass instead of NOP-padding trampolines.
- interact.zig: game API wrappers, object scanning, loot queue with
SceneEnd hook for per-frame processing
- Bindings.xml: keybinding definitions loaded via LoadUIBindingsFromFile
(called explicitly since our virtual addon bypasses LoadAddonRecursive)
- Lua addon: BINDING_HEADER_WEIRDUTILS, Interact() wrapper, /wu interact
PNG encoder: replace literals-only fixed Huffman with stb-style LZ77
matching. Hash-table chain depth scales with compression level (1-9),
giving a smooth tradeoff from fast/light to slow/best. Lazy matching
cancels a match if the next position finds a longer one. Falls back
to store blocks if compressed output is larger than raw.
Screenshot: move hook install from loadScriptFunctionsDetour to
engineInitDetour (GameEngine_MainInitialize at 0x46a400) so it fires
after all DLL_PROCESS_ATTACH hooks. Restore original CTgaFile::Write
prologue before hooking to avoid chaining through UnitXP. Capture
compression level at enqueue time so each queued shot reflects the
quality setting at the moment it was taken. Counter uses hex suffix
(0-F, max 16/sec) and resets each second.