Publish source: Unlicense, public README, repo hygiene

The remote was previously a distribution-only point for pre-built DLLs.
This opens the source.

- LICENSE: Unlicense, with a GPL-3.0 carve-out for src/dpslog/WeirdDPSMate
  (a DPSMate fork that keeps its own license)
- README.md replaces the stale internal one with the user-facing docs from
  DLL_README.md, swapping the 'Why No Source Code?' section for build and
  layout notes. DLL_README.md is dropped; one README now serves both.
- RELEASING.md: drop the trim-the-README-per-release dance and the
  remote/WeirdUtils/ distribution clone, both obsolete now
- gitignore agent/editor scratch, build caches, the vendored WSBT addon,
  and the WeirdThreat/uwu-logs checkouts (separate upstream repos)
- Commit outstanding module work: superweirdo, clickthrough portal visuals,
  transform44 decompiles, worldmarkers demo presets, tools/
This commit is contained in:
MarcelineVQ
2026-07-27 21:47:55 -07:00
parent f500147fc7
commit b12fc820ef
42 changed files with 5563 additions and 727 deletions
+358 -145
View File
@@ -1,178 +1,391 @@
# WeirdUtils
All-in-one WoW 1.12.1 (build 5875) utility DLL. Injected as a 32-bit DLL into the
game process via Wine/DXVK on Linux. Provides screen-space outlines, screenshots,
interaction helpers, and an embedded addon with Lua API + keybindings.
This package provides many pre-built DLLs for enhancing the vanilla 1.12 client WoW gameplay experience, aimed in particular at ease of use and accessibility but also bug fixes.
## Current Features
You may get all features by installing `weirdutils.dll`, or choose any selection of features via individual DLLs.
On Turtle WoW, place your chosen DLLs next to your `WoW.exe` and add them to your `dlls.txt`. For other versions you will need some sort of DLL loader.
| Module | Description |
---
## Features
### World Markers
Place up to 5 animated colored markers (Cataclysm style) at any position in the world, useful for raid positioning, pull planning, or route marking. Requires party/raid leader or raid assist.
- `/worldmarker 1` through `/worldmarker 5` (or `/wm 1`) -- place a marker where your cursor is pointing
- `/worldmarker 1 target` -- place a marker on a unit (player, target, mouseover, etc.)
- `/clearworldmarker` (or `/cwm`) -- remove all markers
- `/clearworldmarker 2` -- remove a specific marker
Keybindings for placing each marker and clearing all markers are available in the Key Bindings menu.
Markers automatically sync with group members who also have WeirdUtils installed. When a leader/assist places or clears a marker, all group members see it. Markers persist across zone transitions and respawn when you return to the area.
Lua API for addon developers:
- `WorldMarker(index)` -- place marker at cursor (returns x,y,z,areaId on success, nil if no permission, -1 on failure)
- `WorldMarker(index, "unit")` -- place marker at a unit's position
- `WorldMarker(index, x, y, z)` -- place marker at world coordinates
- `ClearWorldMarker(index)` / `ClearWorldMarker()` -- remove one or all markers (returns 1 on success, nil if no permission)
- `GetWorldMarker(index)` -- returns x,y,z,areaId for an active marker, nil if empty
- `CanSetWorldMarker()` -- returns 1 if the local player is party/raid leader or raid assist, nil otherwise
**DLL:** `worldmarkers.dll`
---
### Outlines
Renders glowing colored outlines around units, improving visibility in crowded encounters.
- `/outlines` or `/ol` -- toggle outlines on or off
A keybinding is available in the Key Bindings menu.
**DLL:** `outline.dll`
---
### Interact
Smart interaction helpers for faster farming and dungeon runs:
- **Interact Nearest** -- right-clicks the closest interactable NPC or object within 5 yards
- **Loot All Corpses** -- bulk loots all nearby corpses in sequence
Best used via keybindings (available in the Key Bindings menu) or macros:
```
/run InteractNearest(1)
/run LootAllCorpses()
```
**DLL:** `interact.dll`
---
### PNG Screenshots
Saves screenshots as compressed PNG files instead of the default uncompressed TGA format. Runs on a background thread with no frame drops.
Controlled via the `screenshotQuality` CVar (saved to config.wtf):
- `/script SetCVar("screenshotQuality", "6")` -- set compression level (1 = fast, 9 = smallest, default 6)
- `/script SetCVar("screenshotQuality", "0")` -- disable PNG, use original TGA format
**DLL:** `pngscreenshots.dll`
---
### Crash Fix
Prevents a class of crashes caused by stale UI frame anchor pointers. No configuration needed, install and forget.
**DLL:** `framecrash.dll`
---
### Transmog Fix
Eliminates FPS drops caused by rapid equipment visual updates when transmogged items lose durability. No configuration needed, install and forget.
**DLL:** `transmogfix.dll`
---
### Custom Data/ Assets
Enables loading loose game asset files (models, textures, etc.) from the `Data/` directory without repacking MPQ archives. Place files in `Data/` mirroring the game's internal paths (e.g. `Data/Character/Troll/Female/TrollFemale.m2`) and they will be used instead of the MPQ version.
Also allows multi-character patch archive names (e.g. `patch-12.mpq`, `patch-jimbo.mpq`).
Patch archives are sorted case-insensitively by filename - last in the sort gets highest priority, and all patches override the base archives.
No configuration needed, install and forget.
**DLL:** `customassets.dll`
---
### Utility Minimap Trackings
Adds TBC/WotLK-style minimap tracking icons for NPC types, game objects, and quest givers.
Replaces the native tracking dropdown with a combined menu showing both spell tracking and NPC category tracking.
Can be disabled from the normal AddOn menu. Preferences saved per-character.
- Click the minimap tracking icon to open the dropdown
- Check/uncheck NPC categories to toggle their minimap icons
- Spell tracking (Hunter tracking, Find Herbs, etc.) remains available alongside NPC tracking
- "Hide in Cities" toggle suppresses NPC icons in capital cities
Tracks various npc types and useful objects like Oranges and Brainwasher and Mailbox.
**DLL:** `minimapicons.dll`
---
### Clickthrough
Smart cursor targeting that prioritizes useful interactions. Instead of always selecting the nearest object under the cursor, the module finds the most useful target along the ray in priority order.
- Lootable corpses first, then interactable game objects/portals, then interactable NPCs, then normal selection
- Dead non-lootable corpses can still be selected when nothing more useful is behind them
- Disabled inside battlegrounds to prevent targeting objectives through enemy players
Can replace SuperWoW's `Clickthrough()` toggle with always-on smart targeting that doesn't require a manual toggle and preserves the ability to select dead bodies when needed. Disable corpse-clickthrough in SuperAPI if you want this.
No configuration needed, install and forget.
**DLL:** `clickthrough.dll`
---
### Log Sessions
Organizes the combat, raw combat, and chat logs into per-character, per-day files:
```
Logs\<Realm>\<Character>\WoWChatLog_YYYY_MM_DD.txt
Logs\<Realm>\<Character>\WoWCombatLog_YYYY_MM_DD.txt
Logs\<Realm>\<Character>\WoWRawCombatLog_YYYY_MM_DD.txt (superwow only)
```
Every character login begins with a marker line (`COMBATLOG_SESSION` or `CHAT_SESSION`) identifying the character and realm.
If today's log file for that character already exists, it is appended to instead of creating a new one, so a day of play stays in one file even across multiple logins or `/reload`s.
Lua API for addon developers:
- `GetCombatLogPath()` -- returns the current combat log file path
- `GetChatLogPath()` -- returns the current chat log file path
No other configuration needed, install and forget.
**DLL:** `logsessions.dll`
---
### DPSLog (Combat Log Events)
Provides WotLK 3.3.5-style `COMBAT_LOG_EVENT_UNFILTERED` for the vanilla client. Fires a single unified event with structured arguments instead of vanilla's fragmented localized text events. Enables modern DPS meter addons without expensive string parsing.
37 subevents covering all combat interactions: damage (spell, melee, periodic, environmental, damage shield, damage split), healing (direct, periodic, overheal tracking), misses (all types), auras (applied, removed, refreshed, broken, dose changes), casts (start, success, failed, interrupted), power (energize, drain, leech), dispels, extra attacks, deaths, and kills.
Each event includes source/destination GUIDs, names, unit flags, raid flags, spell info, and all WotLK-standard suffix fields. Booleans (critical, glancing, crushing) use WotLK semantics: `nil` for false, `"1"` for true. Names are `nil` when the client can't resolve the unit (despawned, out-of-range). Null GUIDs use `0x80000000` flags.
When `/combatlog` is active, also writes structured WotLK-style CSV to `Logs\WeirdCombatLog.txt` (replaces vanilla's combat log file with parseable data).
Also provides:
- `CombatLogGetCurrentEventInfo()` -- WotLK-style lazy arg retrieval (call from event handler):
```lua
local f = CreateFrame("Frame")
f:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
f:SetScript("OnEvent", function()
local sub, srcGUID, srcName, srcFlags, srcRaidFlags,
dstGUID, dstName, dstFlags, dstRaidFlags = CombatLogGetCurrentEventInfo()
-- suffix args follow (spellId, spellName, etc.) -- see wiki for full layout
end)
```
- `GetSpellInfo(spellId)` -- the TBC/WotLK spell lookup API:
```lua
local name, rank, icon, castTime, minRange, maxRange, spellId = GetSpellInfo(133)
```
- `UnitCastingInfo("unit")` -- TBC/WotLK cast bar query (works on any visible unit):
```lua
local name, rank, text, icon, startTime, endTime, isTradeSkill, castID, notInterruptible = UnitCastingInfo("target")
if name then
-- startTime/endTime are in milliseconds (compare with GetTime()*1000)
end
```
- `UnitChannelInfo("unit")` -- TBC/WotLK channel bar query:
```lua
local name, rank, text, icon, startTime, endTime, isTradeSkill, notInterruptible = UnitChannelInfo("target")
```
See the [DPSLog wiki page](https://codeberg.org/gwenael/WeirdUtils/wiki/DPSLog) for full event reference and addon developer guide.
**DLL:** `dpslog.dll`
---
### SuperWoW Heal Text Fix
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`
---
### Big Cursor
Upscales the hardware cursor for improved visibility without losing sharpness. Supports fractional scales from 1.0 (off) to 4.0.
- `/script SetCursorScale(1.2)` -- set cursor scale (default 1.2x)
- `/script SetCursorScale(1)` -- disable (use original 32x32 cursor)
This value is saved to the `cursorScale` CVar in tenths: `/script SetCVar("cursorScale", "15")` for 1.5x.
Lua API for addon developers:
- `SetCursorScale(n)` -- set scale factor (1.0-4.0), takes effect on next cursor change
- `GetCursorScale()` -- returns current scale factor
**DLL:** `bigcursor.dll`
---
### Performance
Engine-level optimizations that reduce CPU time on math, rendering helpers, file lookups, and data decompression.
- **SIMD Math** -- replaces 20+ internal math functions with SSE/AVX equivalents covering skeletal animation, particle rendering, frustum culling, collision detection, text glyph caching, and float-to-integer conversion
- **Data Decompression** -- swaps the game's 2004-era zlib with a modern library (2.2x faster). Loading screen times reduced by at least 13%
- **MPQ File Cache** -- caches archive file lookups so repeat file opens skip the archive chain walk. Saving 50-160ms every 15 seconds during heavy gameplay
- **Timer Calibration** -- recalibrates the client's RDTSC against the OS high-resolution counter for accurate animation timing, and raises the OS timer resolution to 0.5ms. Ported from [VanillaFixes](https://github.com/hannesmann/vanillafixes)
- **Lua Runtime** -- custom slab allocator (O(1) free/realloc), incremental/generational GC (turns the ~1s stop-the-world freeze into ~9ms chunks), faster string interning (~40% on `luaS_newlstr`), and a literal prefilter on `string.find`/`gfind`/`gsub` that kills the O(n²) backtracking addon combat log parsers inflict on every chat message
Most noticeable in cities, raids, during zone transitions, and in addon-heavy setups.
**DLL:** `weirdperformance.dll`
---
## Source Code
This project is no longer actively developed. The full source is now published here,
in the public domain (see `LICENSE`), so it can be forked, salvaged, or learned from
rather than bit-rotting on a private disk.
Earlier releases shipped as pre-built DLLs only. That is no longer the case - the
binaries on the releases page and the source in this repo are the same project.
Fair warning to anyone building on this: these DLLs hook deeply into the client's
internals - memory layout, function addresses, rendering pipeline, input handling.
Much of it is specific to 1.12.1 build 5875 and will not survive a different client
build. Some modules (`interact`, `clickthrough`) intercept input and hit-testing;
what you do with them on someone else's server is between you and that server's admins.
### Building
Requires Zig 0.16 and a checkout of [zhook](https://codeberg.org/marcelinevq/zhook)
as a sibling directory - `build.zig.zon` refers to it by relative path:
```sh
git clone https://codeberg.org/marcelinevq/zhook
git clone https://codeberg.org/MarcelineVQ/WeirdUtils
cd WeirdUtils
zig build # zig-out/bin/weirdutils.dll
zig build all-variants -Doptimize=ReleaseSmall # + one DLL per module
```
Target is `x86-windows-msvc` (32-bit DLL); it is developed on Linux with the game
running under Wine/DXVK. Per-module build flags are listed by `zig build --help`.
### Layout
| Path | What |
|---|---|
| **Outline** | JFA-based screen-space outlines for targets, raid marks, dead players. See [src/outline/README.md](src/outline/README.md). |
| **Screenshot** | Hooks CTgaFile::Write for screenshot capture. |
| **Interact** | Nearest NPC/object interaction, bulk looting with queue processing. |
| **Markers** | World-space raid markers (5 colors) using M2 model entities. Proximity respawn, group sync, animated spawn/despawn. Lua API + slash commands (`/wm`, `/cwm`). |
| **Framecrash** | Anchor vtable guards - prevents crashes from dangling relativeTo pointers and NULL frame refs. |
| **DPSLog** | WotLK-style `COMBAT_LOG_EVENT` for vanilla 1.12.1. Fires structured events with full WotLK 3.3.5 CLEU parity: spell prefix (spellId, spellName, spellSchool), source/dest names, overkill, overheal, all damage/heal/miss/aura/cast subevents. Includes `GetSpellInfo(spellId)` Lua API. Embedded tracker addon. See [wiki/DPSLog.md](wiki/DPSLog.md). |
| **Minimap Icons** | Minimap icon fixes. |
| **Transmogfix** | Coalesces transmog durability update packets to prevent death frame drops. |
| **Data Assets** | Loose file loading, permissive MPQ glob patterns, pre-indexed file hash set. |
| **Healtextfix** | Heal text display fix. |
| **Embedded Addon** | Virtual addons loaded from DLL memory - .toc, .lua, .xml, .m2, .blp served via file I/O hooks (LoadFile + Storm layer). No on-disk addon folder needed. |
| **Lua Protection Bypass** | Stubs the Lua callback address validator to allow C function registration. |
| `src/main.zig` | DLL entry, module init, Lua API registration |
| `src/<module>/` | One directory per feature module; embedded addons live in `addon/` subdirs |
| `src/offsets.zig`, `src/wow.zig` | Client addresses and memory access wrappers |
| `build.zig` | Module list, versions, per-module build flags, variant DLL generation |
| `docs/`, `src/*/RESEARCH.md` | Reverse-engineering notes for the subsystems being hooked |
## Consolidation Plan
`src/dpslog/WeirdDPSMate/` is a fork of DPSMate and stays under GPL-3.0 - see its
own `LICENSE`. Everything else is unlicensed/public domain.
WeirdUtils replaces the standalone utility DLLs in the parent directory. All
development happens here - shared code, shared hooking infrastructure, one build
system. The standalone DLLs are being retired.
---
| Standalone DLL | Purpose | Integration Status |
## Developer Notes
### Runtime Module Control API
WeirdUtils exports three functions for querying and disabling modules at runtime in case other devs find their dll's in conflict.
#### Exported Functions
| Function | Signature | Description |
|---|---|---|
| `../assetfix` | Loose file loading, permissive MPQ glob patterns, pre-indexed file hash set | Not started |
| `../transmogfix` | Death frame drop fix - coalesces transmog durability update packets | Not started |
| `../interact` | Nearest interact + bulk loot | Partially integrated |
| `WeirdUtils_IsModuleActive` | `int __cdecl (const char *name)` | Returns 1 if the module is compiled in and currently hooked, 0 otherwise |
| `WeirdUtils_DisableModule` | `int __cdecl (const char *name)` | Unhooks the named module. Returns 1 if found, 0 otherwise |
| `WeirdUtils_DisableAll` | `int __cdecl (void)` | Unhooks all modules and core hooks. Returns count of modules disabled |
### Compile-Time Feature Gating
Module names are case-insensitive and match the released dll names:
Each module is gated behind a build flag. The same codebase produces both the
all-in-one DLL and individual feature DLLs - just different compile flags.
Users can pick the full package or grab only the features they want.
`customassets`, `framecrash`, `logsessions`, `transmogfix`, `minimapicons`, `healtextfix`, `bigcursor`, `worldmarkers`, `interact`, `outline`, `pngscreenshots`, `clickthrough`, `dpslog`, `weirdperformance`
```zig
// build.zig options (planned)
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;
There is no re-enable API.
#### C/C++ Header
A header-only `include/weirdutils_api.h` is provided that handles DLL discovery and runtime resolution automatically. No .lib file needed:
```c
#include "weirdutils_api.h"
// Returns 0 if WeirdUtils isn't loaded - safe to call unconditionally
if (WeirdUtils_IsModuleActive("transmogfix"))
WeirdUtils_DisableModule("transmogfix");
```
```sh
# Full build - all features in one DLL
zig build
The header tries all known DLL names (`weirdutils.dll`, `worldmarkers.dll`, etc.) via `GetModuleHandleA`, so it works regardless of which DLL variant is loaded.
# Single-feature builds - one DLL per feature for individual distribution
zig build -Dcustomassets=true -Dtransmogfix=false -Dinteract=false -Doutline=false
zig build -Dcustomassets=false -Dtransmogfix=true -Dinteract=false -Doutline=false
# etc.
```
#### Raw GetProcAddress
Release artifacts:
- `weirdutils.dll` - everything
- `customassets.dll` - just asset/MPQ fixes
- `transmogfix.dll` - just transmog coalesce
- `interact.dll` - just interact/loot helpers
- `outline.dll` - just outline rendering
If you prefer not to use the header:
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 `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_CustomAssetsHook_{pid}");
if (GetLastError() == ERROR_ALREADY_EXISTS) {
// Another DLL already owns this feature's hooks - skip
CloseHandle(mutex);
return;
```c
HMODULE hMod = GetModuleHandleA("weirdutils.dll");
if (hMod) {
typedef int (__cdecl *IsActiveFn)(const char *);
IsActiveFn isActive = (IsActiveFn)GetProcAddress(hMod, "WeirdUtils_IsModuleActive");
if (isActive && isActive("transmogfix")) {
typedef int (__cdecl *DisableFn)(const char *);
DisableFn disable = (DisableFn)GetProcAddress(hMod, "WeirdUtils_DisableModule");
if (disable) disable("transmogfix");
}
}
// First to load wins - install hooks
```
This is per-feature, not per-DLL. The full DLL claims one mutex per enabled
feature. A single-feature DLL claims one mutex. Whichever loads first owns the
hooks; the duplicate gracefully becomes a no-op.
### Version Query API
## Planned: Ground-Projected Markers
WeirdUtils registers a Lua global table and query function for addon developers to detect which modules are loaded and their versions. Available from the login screen onward.
World-space markers projected onto terrain, similar to raid markers but driven
programmatically. Use cases:
#### `GetWeirdUtilsVersion()`
- Visual range indicators (spell range circles, aggro radius)
- Waypoint markers for navigation
- Area-of-effect visualization
- Custom raid positioning markers
Returns the `WeirdUtils` table containing all enabled modules and their version strings:
Implementation will require:
- Projecting screen-space or world-space coordinates onto the terrain mesh
- Rendering textured quads or circles that conform to terrain height
- Integration with the D3D9 hook pipeline (rendered during EndScene or as
additional geometry injected into the scene)
## Distribution
This repo is private (source not published to avoid empowering bad actors).
Distribution uses a separate **public release repo** that contains only a
user-facing README and binary releases - no source code.
- **This repo** (private): all source, development, docs
- **Public repo** (e.g. `WeirdUtils`): README with feature descriptions +
GitHub Releases with DLL downloads
Release workflow:
```sh
# Build all variants from this repo
zig build # weirdutils.dll (full)
zig build -Doutline=true -Deverything-else=false # outline.dll
# ... etc for each single-feature build
# Publish to the public repo
gh release create v1.0 --repo YourName/WeirdUtils \
--title "v1.0" --notes "Release notes" \
./zig-out/lib/weirdutils.dll \
./builds/outline.dll \
./builds/customassets.dll
```lua
local modules = GetWeirdUtilsVersion()
for name, version in pairs(modules) do
print(name .. " v" .. version) -- e.g. "dpslog v1.0"
end
```
## Project Structure
#### `GetWeirdUtilsVersion("modulename")`
```
weirdutils/
build.zig Build configuration
src/
main.zig DLL entry, Lua API, file I/O hook, embedded addon
screenshot.zig Screenshot capture hook
interact.zig Interact + loot helpers
png.zig PNG encoding for screenshots
outline/ Outline subsystem (see src/outline/README.md)
api.zig Public API, Lua command handler
d3d9_hook.zig D3D9 vtable hooks, JFA pipeline, shaders
model_hook.zig M2 batch reordering, rendering_outline flag
tracker.zig Per-frame object/model tracking
types.zig D3D9 constants, outline colors, categories
offsets.zig WoW memory addresses and struct offsets
wow.zig Game memory access wrappers
addon/ Embedded addon files (.toc, .lua, .xml)
libs/
hook/ Shared x86 inline hooking library (trampoline, fastcall thunks)
docs/ Design docs, research notes, shader analysis
reference/ C reference implementations
Returns the version string for a specific module, or `nil` if not loaded:
```lua
if GetWeirdUtilsVersion("dpslog") then
-- DPSLog is available, register for COMBAT_LOG_EVENT_UNFILTERED
end
local ver = GetWeirdUtilsVersion("minimapicons") -- "1.0" or nil
```
## Build
The `WeirdUtils` table is additive -- if multiple independent DLLs are loaded (e.g. `dpslog.dll` and `minimapicons.dll` separately), each adds its own modules to the shared table.
```sh
cd /media/storage/projects/zig/weirdutils
zig build
```
---
Target: x86-windows-msvc (32-bit DLL), Zig 0.16 (patched: fastcall inreg fix).
Host: Linux (Arch), game runs via Wine/DXVK.
### Module Mutexes
## Hook Installation Order
Each module also holds a named mutex while active: `Local\WeirdUtils_<name>_<PID>` (e.g. `Local\WeirdUtils_framecrash_12345`). The exception is transmogfix, which uses `Local\TransmogCoalesceHook_<PID>` for legacy reasons.
Hooks are installed in a specific sequence to handle dependencies:
1. **DLL_PROCESS_ATTACH** - Lua protection bypass, file I/O hook, LoadScriptFunctions,
LoadAddonsRecursively, interact hooks, GameEngine_MainInitialize, CGGameUI_Shutdown
2. **GameEngine_MainInitialize** (one-shot) - screenshot hook, outline model hooks
3. **First model hook callback** (deferred) - D3D9 vtable hooks (EndScene, DIP, Reset)
D3D9 hooks are deferred because creating a dummy device during engine init
corrupts the d3d9 proxy's state.
If you see the mutex, the module is loaded - and can use the Runtime Module Control API to disable it. If you don't see it, the module isn't active and you're free to hook those functions yourself.