Module control API: - Three cdecl exports: WeirdUtils_IsModuleActive, WeirdUtils_DisableModule, WeirdUtils_DisableAll - C header include/weirdutils_api.h for runtime DLL discovery - All modules gain pub module_name, isActive(), shared mutex via src/mutex.zig - build.zig refactored to module_list array of ModuleDesc Bigcursor: - D3D9 vtable hooks on SetCursorProperties/ShowCursor - Scale2x/Scale3x pixel-art upscalers in pure Zig (replaces 12K-line hqx C) - Bilinear resampler for fractional scales (1.0-4.0, default 1.2) - Win32 cursor via CreateIconIndirect bypasses D3D9 32x32 limit - FNV-1a hash cache for ~16 cursor bitmaps - Lua API: SetCursorScale(n) / GetCursorScale() - CVar cursorScale for Config.wtf persistence (tenths) Other: - Add dpslog module stub - Docs and README updates
3.8 KiB
Outline Validation Checklist
Build
zig buildsucceeds with no errors (x86 windows-msvc target)- Output DLL present at
zig-out/lib/weirdutils.dll
Existing Feature Regression
- DLL loads without crash (inject into WoW 1.12.1)
/wu versionresponds correctly/wu testcalls C function and returns string- Screenshot hook works (
/wu ss, PNG output) - Interact/loot bindings work (InteractNearest, LootAllCorpses)
- Addon loads on login (green "WeirdUtils loaded" message)
Outline Subsystem
Commands
/wu outlineshows ON/OFF status/wu outline onenables outlines/wu outline offdisables outlines/wuhelp text includes outline commands
Dead Player Outlines
- Dead friendly players in party/raid show cyan outline
- Outline is visible through walls (no depth test)
- Outline disappears when player is resurrected
- Skeleton corpses are NOT outlined
Raid Mark Outlines
- Units with raid icons (Star through Skull) show coloured outlines
- Each icon has a distinct colour
- Outlines update when raid marks change
- Outlines respect terrain occlusion (depth test enabled)
Target Outline
- Current target shows golden amber outline
- Outline changes when target changes
- Outline clears when target is deselected
- Outline respects terrain occlusion
Rendering Quality
- Outlines are clean (no jagged edges or flicker)
- Outline thickness is consistent across distances
- Normal model renders correctly on top of outline
- No visible render state leakage to other models
- Non-outlined units don't cover outline pixels (stencil test)
Performance
- No noticeable FPS drop in normal gameplay
- No FPS drop in 40-player raid with multiple outlines
- Frame rate stable when toggling outlines on/off
Edge Cases
- D3DX DLL not present → outlines gracefully disabled (no crash)
- No targets tracked → zero overhead (fast path)
/wu outline off→ DIP hook passes through immediately- Device Reset (resolution change) → shaders recreated properly
Hook Prologue Verification (Ghidra)
Verified 2026-02-23 using Ghidra MCP against WoW.exe 1.12.1 (build 5875, 4,907,008 bytes).
Method
Raw bytes read from each hook target address via get_bytes. Instruction boundaries decoded to confirm the prologue_size parameter passed to hook.prepare() lands on a clean instruction boundary. A mid-instruction cut would corrupt the trampoline - the copied bytes would decode as a different instruction when followed by the trampoline's JMP.
Results
| Address | Function | Prologue bytes | Decoded | Boundary | Safe | Fixups |
|---|---|---|---|---|---|---|
0x0070b360 |
CM2SceneRenderDraw |
55 8B EC 81 EC 80 00 00 00 |
PUSH EBP; MOV EBP,ESP; SUB ESP,0x80 |
+1,+3,+9 | 9B | none |
0x00710b90 |
CM2Model_ManageRenderListNode |
55 8B EC 8B 45 08 |
PUSH EBP; MOV EBP,ESP; MOV EAX,[EBP+8] |
+1,+3,+6 | 6B | none |
0x0070cb30 |
CM2Scene_DrawBatchProjected |
55 8B EC 83 EC 10 |
PUSH EBP; MOV EBP,ESP; SUB ESP,0x10 |
+1,+3,+6 | 6B | none |
Fix applied
CM2SceneRenderDraw prologue changed from 6 to 9 bytes in src/outline/model_hook.zig. The original 6-byte value would have split the SUB ESP, 0x80 instruction (opcode 81 EC + 4-byte immediate, spanning offset +3 to +9). The trampoline would have executed 81 EC 80 E9 xx xx as SUB ESP, <corrupted>, leading to stack corruption and a crash.
No other changes needed
- All three prologues contain only register/memory instructions (no
E8 CALLorE9 JMP), sorel32_fixupsremains&.{}. - The 4-byte NOP padding (bytes 5-8) at
0x0070b360after the 5-byteE9 JMPis harmless - it is never executed (execution jumps to the detour thunk).