Repo hygiene: drop internal planning files, clean docs for publication
Untrack TODO.md, RELEASING.md, RELEASE_NOTES.md, ideas/ and docs/CLAUDE_PROPER_OBJECT_REGISTRATION_PLAN.md -- internal planning and agent notes with no reason to be published. Files stay on disk locally. Replace remaining absolute machine paths in docs with relative ones (one referenced a separate private project), and swap decorative emoji for ASCII markers. README: lead with the no-longer-actively-developed notice.
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
# WeirdUtils
|
||||
|
||||
> **This project is no longer actively developed.** The full source is 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. See [Source Code](#source-code) for details and caveats.
|
||||
|
||||
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.
|
||||
|
||||
You may get all features by installing `weirdutils.dll`, or choose any selection of features via individual DLLs.
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
# Unreleased Changes
|
||||
|
||||
Track notable changes here between releases. Clear this file when cutting a new release
|
||||
(move contents into the release notes on Codeberg).
|
||||
|
||||
## What's New
|
||||
|
||||
- **World Markers** - place up to 5 animated Cataclysm-style markers at any position in
|
||||
the world. Slash commands (`/wm 1`, `/cwm`), keybindings, full Lua API, and automatic
|
||||
group sync between WeirdUtils users. Requires party/raid leader or raid assist.
|
||||
|
||||
- **DPSLog (COMBAT_LOG_EVENT)** - WotLK 3.3.5 CLEU parity for vanilla 1.12.1. 37 subevents
|
||||
across 24 hooks with full structured data: source/dest names via name cache, spell names
|
||||
via SpellRec, overkill/overheal from unit descriptors, WotLK-standard field ordering
|
||||
(blocked before absorbed, separate glancing/crushing booleans). Drain Life reclassified
|
||||
as SPELL_PERIODIC_LEECH. SPELL_ENERGIZE with actual amount/powerType via dedicated
|
||||
SMSG_SPELLENERGIZELOG hook. Periodic energize divides by power display factor (rage/10).
|
||||
Includes `GetSpellInfo(spellId)` Lua API (name, rank, icon, castTime, minRange, maxRange)
|
||||
with verified SpellRec/SpellIcon/SpellRange/SpellCastTimes DBC offsets.
|
||||
Embedded tracker addon (`/dpslog`). Full event reference in wiki.
|
||||
|
||||
- **Interact** - smart interaction helpers: "Interact Nearest" right-clicks the closest
|
||||
interactable NPC or object within 5 yards, "Loot All Corpses" bulk loots nearby corpses.
|
||||
Keybindings and Lua API. Already documented in DLL_README but not yet released.
|
||||
|
||||
- **Crash Fix (framecrash)** - prevents crashes from stale UI frame anchor pointers.
|
||||
Already documented in DLL_README but not yet released.
|
||||
|
||||
- **Outlines** - renders glowing colored outlines around units. Already documented in
|
||||
DLL_README but not yet released.
|
||||
|
||||
- **Addon Profiling Stub (addonperf)** - TBC+ addon CPU profiling API stub
|
||||
(GetScriptCPUUsage). Internal/experimental, not in DLL_README.
|
||||
|
||||
- **Minimap Quest Tracking** - shows nearby NPCs with available quests (yellow !) on the
|
||||
minimap. Reads the per-player quest status cached on unit objects by the client's
|
||||
SMSG_QUESTGIVER_STATUS handler. Enabled by default in the tracking dropdown, togglable
|
||||
like other NPC tracking categories.
|
||||
-197
@@ -1,197 +0,0 @@
|
||||
# Releasing WeirdUtils
|
||||
|
||||
> **Remote:** https://codeberg.org/MarcelineVQ/WeirdUtils
|
||||
|
||||
## How the remote repo works
|
||||
|
||||
`main` on the remote holds the full source, the user-facing `README.md`,
|
||||
`include/weirdutils_api.h`, and issue templates under `.gitea/`. Releases
|
||||
attach pre-built DLLs to a tag on `main`.
|
||||
|
||||
This was previously a distribution-only remote - source lived locally and only
|
||||
a trimmed README plus binaries were pushed. That is no longer the case; develop
|
||||
against `main` and push directly.
|
||||
|
||||
The wiki clone lives at `remote/wiki/`. `remote/WeirdUtils/` is the old
|
||||
distribution-only clone and is obsolete - it can be deleted.
|
||||
|
||||
## 0. Sync tags
|
||||
|
||||
`tea` creates tags on Codeberg, so fetch them before comparing against the
|
||||
previous release:
|
||||
|
||||
```sh
|
||||
git fetch --tags
|
||||
```
|
||||
|
||||
## 1. Bump module versions
|
||||
|
||||
Each module has a `version` field in `build.zig`'s `module_list`. This is what
|
||||
`GetWeirdUtilsVersion()` reports to Lua addons. Before building, bump the
|
||||
version for any module that has changed since the last release:
|
||||
|
||||
```zig
|
||||
// build.zig — module_list
|
||||
.{ .name = "minimapicons", .version = "1.1", ... },
|
||||
```
|
||||
|
||||
Only bump modules that actually changed. Use `git log --oneline -- src/<module>/`
|
||||
to check what changed since the last release tag.
|
||||
|
||||
## 2. Build DLLs
|
||||
|
||||
Decide which modules to include in this release. Check `build.zig` for the
|
||||
current list of module flags (`b.option(bool, ...)` declarations) and their
|
||||
defaults. List them with:
|
||||
|
||||
```sh
|
||||
zig build --help 2>&1 | grep 'Enable'
|
||||
```
|
||||
|
||||
`all-variants` builds everything in one command: the combined DLL, the noperf
|
||||
variant, and a standalone DLL per module. `-D` flags control which modules are
|
||||
included across all of them -- disabled modules are excluded from the combined
|
||||
DLL, noperf, and won't get a standalone variant built.
|
||||
|
||||
```sh
|
||||
zig build all-variants -Doptimize=ReleaseSmall \
|
||||
-Dworldmarkers=false -Dinteract=false -Doutline=false \
|
||||
-Dframecrash=false -Ddpslog=false -Daddonperf=false \
|
||||
-Dssemaths=false -Dsilicon=false -Dtransform44=false
|
||||
```
|
||||
|
||||
### Output locations
|
||||
|
||||
| Artifact | Path |
|
||||
|----------|------|
|
||||
| Combined DLL | `zig-out/bin/weirdutils.dll` |
|
||||
| Variant DLLs | `zig-out/variants/<name>.dll` |
|
||||
|
||||
Verify:
|
||||
|
||||
```sh
|
||||
ls -lh zig-out/bin/weirdutils.dll zig-out/variants/*.dll
|
||||
```
|
||||
|
||||
## 3. Update the README
|
||||
|
||||
`README.md` documents every module, not just the released subset - the source
|
||||
is public, so there is nothing to trim. Add a section for any new module.
|
||||
|
||||
Do NOT remove trailing spaces in `README.md` - they are intentional Markdown
|
||||
line breaks.
|
||||
|
||||
## 4. Write the release notes
|
||||
|
||||
Write the release notes to `/tmp/release-notes-vX.Y.Z.md` - NOT inside the
|
||||
project directory. They are a transient artifact consumed by `tea --note-file`
|
||||
and should not pollute the repo root or end up in git status.
|
||||
|
||||
Use this template - fill in the sections that apply, delete the rest.
|
||||
Use `-` (not em dash) anywhere a dash would be used.
|
||||
|
||||
Always include an **Included** section listing every module in this release,
|
||||
not just what changed. Users downloading a release should see the full list
|
||||
of what they're getting:
|
||||
|
||||
```markdown
|
||||
## What's New
|
||||
|
||||
- ...
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
- ...
|
||||
|
||||
## Included
|
||||
|
||||
- **Module Name** - one-line description
|
||||
- ...
|
||||
|
||||
## Notes
|
||||
|
||||
- Place DLLs next to `WoW.exe` and add to `dlls.txt`
|
||||
- `weirdutils.dll` includes all features; individual DLLs are also provided
|
||||
```
|
||||
|
||||
## 5. Create the release and upload DLLs
|
||||
|
||||
Uses `tea` (Gitea/Forgejo CLI) which handles release creation, tagging, and
|
||||
asset upload in one command. The tag is created on the remote automatically.
|
||||
|
||||
Run from a clone of the remote repo, or specify `--repo` explicitly.
|
||||
Only attach the combined DLL and the variant DLLs for modules in this release.
|
||||
|
||||
Only attach DLLs to the release - do NOT attach `weirdutils_api.h` as a
|
||||
release asset. The header belongs in the repo itself (pushed to `main`),
|
||||
not in the release downloads.
|
||||
|
||||
```sh
|
||||
# Example: transmogfix + customassets + healtextfix release
|
||||
tea release create \
|
||||
--repo MarcelineVQ/WeirdUtils \
|
||||
--tag vX.Y.Z \
|
||||
--target main \
|
||||
--title "vX.Y.Z" \
|
||||
--note-file release-notes.md \
|
||||
--asset zig-out/bin/weirdutils.dll \
|
||||
--asset zig-out/variants/transmogfix.dll \
|
||||
--asset zig-out/variants/customassets.dll \
|
||||
--asset zig-out/variants/healtextfix.dll
|
||||
```
|
||||
|
||||
### Replacing a DLL on an existing release
|
||||
|
||||
Delete the old asset by name, then upload the new one:
|
||||
|
||||
```sh
|
||||
tea release assets delete --repo MarcelineVQ/WeirdUtils -y v0.4.0 minimapicons.dll
|
||||
tea release assets create --repo MarcelineVQ/WeirdUtils v0.4.0 zig-out/variants/minimapicons.dll
|
||||
```
|
||||
|
||||
## 6. Source archives
|
||||
|
||||
Codeberg attaches source tar/zip to each release automatically. Now that the
|
||||
repo contains the source, leave them visible - they are a useful artifact.
|
||||
|
||||
To hide them anyway, get your token from the tea config:
|
||||
|
||||
```sh
|
||||
grep 'token:' ~/.config/tea/config.yml | head -1 | awk '{print $2}'
|
||||
```
|
||||
|
||||
Then hide the archives:
|
||||
|
||||
```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 using the API (not `tea release list --output json`, which
|
||||
mangles keys like `tag-_name`):
|
||||
|
||||
```sh
|
||||
tea api repos/MarcelineVQ/WeirdUtils/releases | python3 -c "
|
||||
import sys,json; releases=json.load(sys.stdin)
|
||||
r=[x for x in releases if x['tag_name']=='vX.Y.Z']
|
||||
print(r[0]['id']) if r else print('not found')
|
||||
"
|
||||
```
|
||||
|
||||
## Known Issues
|
||||
|
||||
- **vanillafixes launcher**: Incompatible with WeirdUtils DLL injection. Users
|
||||
should load the DLL via WoW.exe + `dlls.txt` or another loader instead.
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] Remote clone tags synced (`cd remote/WeirdUtils && git fetch --tags`)
|
||||
- [ ] Module versions bumped in `build.zig` for changed modules
|
||||
- [ ] Check `RELEASE_NOTES.md` for unreleased changes — move into release notes
|
||||
- [ ] Built with `ReleaseSmall` (both default and `all-variants`)
|
||||
- [ ] `README.md` has a section for any new module
|
||||
- [ ] `include/weirdutils_api.h` lists all module DLL names
|
||||
- [ ] Release created and DLLs uploaded via `tea`
|
||||
@@ -1,15 +0,0 @@
|
||||
- [x] Setup Atelier #atelier
|
||||
|
||||
Atelier helps you manage AI coding tasks, plans, and TODOs in a visual Kanban board.
|
||||
|
||||
**Getting Started:**
|
||||
- Click **Setup** to configure Atelier with recommended defaults
|
||||
- Or click **Customize** to choose your own settings
|
||||
- Drag items between columns to update their status
|
||||
- Click an item to view and edit its content
|
||||
[Full documentation](https://atelier.dev)
|
||||
[Setup](vscode://Atelier.atelier/customize?agent=claude\&defaults=true) [Customize](vscode://Atelier.atelier/customize?agent=claude)
|
||||
- [ ] add scaling to minimapicons
|
||||
- [ ] Investigate the functions behind wow.exe lua string.find and find out how to optimize them as they seem to be extremely expensive for the work they do, at least when the game is ran via wine
|
||||
- [ ] allow dead player-pets to be selectable in clikthrough
|
||||
- [x] adjust our basic settings for atelier to use opus for work as the larger context window is useful
|
||||
@@ -1,169 +0,0 @@
|
||||
# Proper Object Registration Plan (for Claude)
|
||||
|
||||
## Goal
|
||||
Stop ad-hoc list insertion and implement a **correct, engine-native object creation/registration path** for client-side markers in WoW 1.12.1.
|
||||
|
||||
Success = marker is visible in-world, no crash on create/destroy/toggle, and lifecycle uses native manager functions (not manual pointer surgery as primary path).
|
||||
|
||||
---
|
||||
|
||||
## Current Status Summary (Read First)
|
||||
|
||||
1. **Crash fixed, visibility not fixed**
|
||||
- We eliminated crash caused by callback slot garbage (`obj+0x174`) in manager iteration path.
|
||||
- Markers still invisible in both current test paths.
|
||||
|
||||
2. **What was tried**
|
||||
- Path A: Allocate world object + attach model + manual insert into manager list (`0x00C7CAEC`).
|
||||
- Path B: `CreateGameObject_WithProperties` + fallback manual insertion if unlinked.
|
||||
- Both now create/destroy cleanly, still not rendering.
|
||||
|
||||
3. **Known engine behaviors**
|
||||
- Manager iterator (`AllocateGameObject` region around `0x00683F80`) uses positions at `+0x5C/+0x60/+0x68` and calls render-management functions.
|
||||
- It may call callback pointer at `+0x174` if non-zero.
|
||||
- List pointers can be low-bit tagged.
|
||||
|
||||
4. **Conclusion so far**
|
||||
- List manipulation and crash stability are mostly solved.
|
||||
- Remaining blocker is likely **incorrect native registration/state initialization**, i.e. object not entering correct draw/classification path.
|
||||
|
||||
---
|
||||
|
||||
## Mandatory Context Files
|
||||
|
||||
Read these before coding:
|
||||
|
||||
1. `research/marker-visibility-debug-log.md`
|
||||
2. `docs/M2_MODEL_SYSTEM.md`
|
||||
3. `research/dynamic-object-research.md`
|
||||
4. `src/markers/markers.zig`
|
||||
5. Crash reference: `/media/bigfaststore/games/twmoa_1172/Errors/2026-02-27 07.11.41 Crash.txt`
|
||||
|
||||
Also review the CLI Ghidra skill:
|
||||
- `~/.claude/skills/ghidra-cli-wow-re/SKILL.md`
|
||||
|
||||
---
|
||||
|
||||
## Rules of Engagement
|
||||
|
||||
1. **Do not treat manual list insertion as final architecture.**
|
||||
2. **Do not trust decompiler signatures alone.** Verify calling conventions from bytes/prologue/RET cleanup.
|
||||
3. **Prefer discovering native add/remove APIs** used by real visible objects.
|
||||
4. **Every hypothesis must have an observable test log.**
|
||||
5. **No broad refactors until registration path is proven.**
|
||||
|
||||
---
|
||||
|
||||
## Execution Plan
|
||||
|
||||
## Phase 1 - Identify canonical native registration path
|
||||
|
||||
### Task 1.1: Recover true iterator and linkage semantics
|
||||
- Disassemble around `0x00683F80` (and nearby helpers) in detail.
|
||||
- Map exactly how nodes are traversed and linked (including pointer tagging and indirection via globals like `0x00C7CAE4/0x00C7CAEC`).
|
||||
- Produce pseudocode with verified offsets and conditions.
|
||||
|
||||
**Deliverable:** short technical note: "Manager traversal/link semantics".
|
||||
|
||||
### Task 1.2: Find native insertion/removal helpers
|
||||
- Find xrefs to `0x00C7CAEC` and offsets `+0x16C/+0x170`.
|
||||
- Identify functions that insert/remove objects in production code.
|
||||
- Verify one candidate by callsite context (who calls it and when).
|
||||
|
||||
**Deliverable:** candidate helper function list with addresses + confidence.
|
||||
|
||||
### Task 1.3: Find real creator path for visible world objects
|
||||
- Trace from known visible object creators (game objects/doodads/spell visuals) to registration.
|
||||
- Determine **minimal required function chain** from creation to render visibility.
|
||||
|
||||
**Deliverable:** canonical call chain diagram with function addresses.
|
||||
|
||||
---
|
||||
|
||||
## Phase 2 - Build proper registration wrapper in markers module
|
||||
|
||||
### Task 2.1: Implement native-path wrapper
|
||||
- Add a wrapper that uses discovered native creator/registration APIs.
|
||||
- Manual list insertion may remain only as fallback/diagnostic mode, not default.
|
||||
|
||||
### Task 2.2: Lifecycle correctness
|
||||
- Ensure destroy path uses matching native teardown/unlink APIs.
|
||||
- Prevent double-unlink and callback garbage.
|
||||
|
||||
### Task 2.3: Instrumentation
|
||||
- Keep concise debug logs for:
|
||||
- object pointer
|
||||
- model pointer
|
||||
- registration result path
|
||||
- destroy path confirmation
|
||||
|
||||
**Deliverable:** code in `src/markers/markers.zig` with clear mode labels.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3 - Validation protocol (must pass)
|
||||
|
||||
Run and capture logs for:
|
||||
|
||||
1. `/mark on`
|
||||
2. visually confirm marker appears
|
||||
3. `/mark off`
|
||||
4. repeat 5+ times
|
||||
5. mode switching stress (`testa/testb` if retained)
|
||||
|
||||
Must pass:
|
||||
- no crash
|
||||
- no panic
|
||||
- marker visible at player position
|
||||
- clean destroy every cycle
|
||||
|
||||
If failed:
|
||||
- report exact failing step, log lines, and next smallest probe
|
||||
|
||||
---
|
||||
|
||||
## Expected Outputs from Claude
|
||||
|
||||
1. Updated code implementing native registration path.
|
||||
2. Updated `research/marker-visibility-debug-log.md` with:
|
||||
- what changed
|
||||
- what was validated
|
||||
- remaining unknowns (if any)
|
||||
3. Optional update to `docs/M2_MODEL_SYSTEM.md` for newly confirmed function semantics.
|
||||
4. Final summary:
|
||||
- root cause
|
||||
- final fix
|
||||
- evidence (logs + addresses)
|
||||
|
||||
---
|
||||
|
||||
## Suggested Command Workflow (CLI Ghidra)
|
||||
|
||||
Use headless scripts via:
|
||||
|
||||
```bash
|
||||
<ghidra-scripts>/run-analysis.sh /tmp/analysis.py
|
||||
```
|
||||
|
||||
Focus scripts on:
|
||||
- xrefs to manager globals and list offsets
|
||||
- disassembly around candidate insertion helpers
|
||||
- call graph from visible object creation paths
|
||||
|
||||
---
|
||||
|
||||
## Non-Goals (for this pass)
|
||||
|
||||
- New marker visuals
|
||||
- Effects polish
|
||||
- Optimizations unrelated to visibility/registration correctness
|
||||
- Expanding to many marker types before first visible stable marker
|
||||
|
||||
---
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- Marker visibility achieved using engine-native registration path (or proven minimal wrapper around it)
|
||||
- No crash across repeated create/destroy
|
||||
- Documentation updated with verified semantics
|
||||
- Ad-hoc list surgery no longer required as primary behavior
|
||||
@@ -39,8 +39,8 @@ struct Model {
|
||||
|
||||
| Offset | Type | Name | Description | Verified |
|
||||
|--------|------|------|-------------|----------|
|
||||
| +0x30 | `void*` | dataPtr | Pointer to geometry data container | ✓ |
|
||||
| +0x94 | `BoneMatrix*` | boneArray | **Pointer to bone matrix array** | ✓ |
|
||||
| +0x30 | `void*` | dataPtr | Pointer to geometry data container | [OK] |
|
||||
| +0x94 | `BoneMatrix*` | boneArray | **Pointer to bone matrix array** | [OK] |
|
||||
|
||||
### Nested Structure (Model+0x30)
|
||||
|
||||
@@ -148,8 +148,8 @@ struct MeshData {
|
||||
|
||||
| Offset | Type | Name | Description | Verified |
|
||||
|--------|------|------|-------------|----------|
|
||||
| +0x04 | `uint16_t` | vertexOffset | Start vertex in global array | ✓ |
|
||||
| +0x06 | `uint16_t` | vertexCount | **Loop terminator in applyBoneTransforms** | ✓ |
|
||||
| +0x04 | `uint16_t` | vertexOffset | Start vertex in global array | [OK] |
|
||||
| +0x06 | `uint16_t` | vertexCount | **Loop terminator in applyBoneTransforms** | [OK] |
|
||||
| +0x08 | `uint16_t` | indexOffset | Start index for this submesh | ? |
|
||||
| +0x0A | `uint16_t` | indexCount | Number of indices | ? |
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
# Hooking Quick Reference Guide
|
||||
|
||||
## 🚨 Critical Rules (Break These = Crash)
|
||||
## Critical Rules (Break These = Crash)
|
||||
|
||||
### 1. NEVER Use PUSHA/POPA Around C Function Calls
|
||||
```asm
|
||||
; ❌ WRONG - Will crash
|
||||
; [X] WRONG - Will crash
|
||||
pusha
|
||||
call _MyCFunction ; This modifies EAX/ECX/EDX
|
||||
popa ; This RESTORES old values, breaking everything
|
||||
|
||||
; ✅ RIGHT - Only save non-volatile registers
|
||||
; [OK] RIGHT - Only save non-volatile registers
|
||||
pushl %ebx
|
||||
pushl %esi
|
||||
pushl %edi
|
||||
@@ -26,18 +26,18 @@ popl %ebx
|
||||
typedef void (__fastcall *Func_t)(void* thisPtr, void* edx, int arg);
|
||||
|
||||
static void __fastcall MyHook(void* thisPtr, void* edx, int arg) {
|
||||
// thisPtr from ECX ✅
|
||||
// edx is trash ✅
|
||||
// arg from stack ✅
|
||||
// thisPtr from ECX [OK]
|
||||
// edx is trash [OK]
|
||||
// arg from stack [OK]
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Steal Complete Instructions Only
|
||||
```cpp
|
||||
// ❌ WRONG
|
||||
// [X] WRONG
|
||||
#define STOLEN_BYTES 6 // No idea if this splits an instruction
|
||||
|
||||
// ✅ RIGHT
|
||||
// [OK] RIGHT
|
||||
// Disassemble: PUSH EBP (1) + MOV EBP,ESP (2) + SUB ESP,0x80 (6) = 9 bytes
|
||||
#define STOLEN_BYTES 9 // Documented complete instructions
|
||||
```
|
||||
@@ -54,7 +54,7 @@ static void __fastcall MyHook(void* thisPtr, void* edx, int arg) {
|
||||
|
||||
---
|
||||
|
||||
## 📋 Register Preservation Cheat Sheet
|
||||
## Register Preservation Cheat Sheet
|
||||
|
||||
| Register | Type | Who Saves It? | Can Hook Modify? |
|
||||
|----------|------|---------------|------------------|
|
||||
@@ -69,7 +69,7 @@ static void __fastcall MyHook(void* thisPtr, void* edx, int arg) {
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Calling Convention Quick Reference
|
||||
## Calling Convention Quick Reference
|
||||
|
||||
### __stdcall (WINAPI)
|
||||
- Args: stack (right to left)
|
||||
@@ -93,7 +93,7 @@ static void __fastcall MyHook(void* thisPtr, void* edx, int arg) {
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Safe Hook Templates
|
||||
## Safe Hook Templates
|
||||
|
||||
### Template 1: VTable Hook (Safest)
|
||||
```cpp
|
||||
@@ -165,7 +165,7 @@ __attribute__((naked)) static void NakedHook() {
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ Safety Checklist
|
||||
## Safety Checklist
|
||||
|
||||
### Before Installing Hook:
|
||||
- [ ] Disassemble target to verify prologue
|
||||
@@ -190,7 +190,7 @@ __attribute__((naked)) static void NakedHook() {
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Common Crash Causes
|
||||
## Common Crash Causes
|
||||
|
||||
1. **PUSHA/POPA with C calls** → Volatile register corruption
|
||||
2. **Wrong calling convention** → ECX/EDX clobbered when needed
|
||||
@@ -203,7 +203,7 @@ __attribute__((naked)) static void NakedHook() {
|
||||
|
||||
---
|
||||
|
||||
## 📚 Research Sources
|
||||
## Research Sources
|
||||
|
||||
- [How to Hook Functions - Guided Hacking](https://guidedhacking.com/threads/how-to-hook-functions-code-detouring-guide.14185/)
|
||||
- [Thiscall Hooking - tresp4sser](https://tresp4sser.wordpress.com/2012/10/06/how-to-hook-thiscall-functions/)
|
||||
@@ -213,7 +213,7 @@ __attribute__((naked)) static void NakedHook() {
|
||||
|
||||
---
|
||||
|
||||
## 💡 Key Insight
|
||||
## Key Insight
|
||||
|
||||
**Hooking is NOT about blindly copying bytes.**
|
||||
|
||||
|
||||
+21
-21
@@ -5,62 +5,62 @@
|
||||
|
||||
## Critical Address Fixes
|
||||
|
||||
### ❌ ProcessWorldWithFrustum
|
||||
### [X] ProcessWorldWithFrustum
|
||||
- **Old Address:** `0x00683000` (WRONG - no function at this address)
|
||||
- **Correct Address:** `0x00682fa0`
|
||||
- **Ghidra Signature:** `undefined __fastcall ProcessWorldWithFrustum(float * frustumBounds)`
|
||||
- **First Byte:** `0x57` (PUSH EDI)
|
||||
- **Stolen Bytes:** 9 bytes
|
||||
- **Status:** ✅ FIXED in discovery_hooks.cpp
|
||||
- **Status:** [OK] FIXED in discovery_hooks.cpp
|
||||
|
||||
### ❌ RenderObjectsWithLOD
|
||||
### [X] RenderObjectsWithLOD
|
||||
- **Old Address:** `0x00684600` (WRONG - no function at this address)
|
||||
- **Correct Address:** `0x00684510`
|
||||
- **Ghidra Signature:** `undefined __stdcall RenderObjectsWithLOD(void)`
|
||||
- **First Byte:** `0x55` (PUSH EBP)
|
||||
- **Stolen Bytes:** 8 bytes
|
||||
- **Status:** ✅ FIXED in discovery_hooks.cpp
|
||||
- **Status:** [OK] FIXED in discovery_hooks.cpp
|
||||
|
||||
## Verified Correct Addresses
|
||||
|
||||
### ✅ ScenePresent
|
||||
### [OK] ScenePresent
|
||||
- **Address:** `0x0059a870`
|
||||
- **Ghidra Signature:** `void __fastcall CGxDeviceD3d::ScenePresent(CGxDeviceD3d * device)`
|
||||
- **First Byte:** `0x55` (PUSH EBP)
|
||||
- **Stolen Bytes:** 6 bytes
|
||||
- **Calling Convention:** __fastcall (saves ECX+EDX) ✅ Correct in code
|
||||
- **Calling Convention:** __fastcall (saves ECX+EDX) [OK] Correct in code
|
||||
|
||||
### ✅ CullAndProcessWorldChunks
|
||||
### [OK] CullAndProcessWorldChunks
|
||||
- **Address:** `0x00683040`
|
||||
- **Ghidra Signature:** `undefined __stdcall CullAndProcessWorldChunks(void)`
|
||||
- **First Byte:** `0x55` (PUSH EBP)
|
||||
- **Stolen Bytes:** 8 bytes
|
||||
- **Calling Convention:** __stdcall (no register saves) ✅ Correct in code
|
||||
- **Calling Convention:** __stdcall (no register saves) [OK] Correct in code
|
||||
|
||||
### ✅ ProcessStaticObjectsCulling
|
||||
### [OK] ProcessStaticObjectsCulling
|
||||
- **Address:** `0x00683bf0`
|
||||
- **Ghidra Signature:** `undefined __fastcall ProcessStaticObjectsCulling(int staticObjectManager)`
|
||||
- **First Byte:** `0x55` (PUSH EBP)
|
||||
- **Stolen Bytes:** 9 bytes
|
||||
- **Calling Convention:** __fastcall (saves ECX+EDX) ✅ Correct in code
|
||||
- **Calling Convention:** __fastcall (saves ECX+EDX) [OK] Correct in code
|
||||
|
||||
### ✅ CM2Scene_DrawModelBatch
|
||||
### [OK] CM2Scene_DrawModelBatch
|
||||
- **Address:** `0x0070cf70`
|
||||
- **Ghidra Signature:** `undefined __fastcall CM2Scene_DrawModelBatch(void * renderContext)`
|
||||
- **First Byte:** `0x55` (PUSH EBP)
|
||||
- **Stolen Bytes:** 8 bytes
|
||||
- **Calling Convention:** __fastcall (saves ECX+EDX) ✅ Correct in code
|
||||
- **Calling Convention:** __fastcall (saves ECX+EDX) [OK] Correct in code
|
||||
|
||||
## Calling Convention Summary
|
||||
|
||||
| Function | Ghidra Convention | Code Implementation | Status |
|
||||
|----------|------------------|---------------------|--------|
|
||||
| ScenePresent | __fastcall | __fastcall (ECX+EDX) | ✅ Correct |
|
||||
| ProcessWorldWithFrustum | __fastcall | __fastcall (ECX+EDX) | ✅ Correct |
|
||||
| CullAndProcessWorldChunks | __stdcall | __cdecl (no saves) | ✅ OK (both no reg saves) |
|
||||
| ProcessStaticObjectsCulling | __fastcall | __fastcall (ECX+EDX) | ✅ Correct |
|
||||
| RenderObjectsWithLOD | __stdcall | __cdecl (no saves) | ✅ OK (both no reg saves) |
|
||||
| CM2Scene_DrawModelBatch | __fastcall | __fastcall (ECX+EDX) | ✅ Correct |
|
||||
| ScenePresent | __fastcall | __fastcall (ECX+EDX) | [OK] Correct |
|
||||
| ProcessWorldWithFrustum | __fastcall | __fastcall (ECX+EDX) | [OK] Correct |
|
||||
| CullAndProcessWorldChunks | __stdcall | __cdecl (no saves) | OK (both no reg saves) |
|
||||
| ProcessStaticObjectsCulling | __fastcall | __fastcall (ECX+EDX) | [OK] Correct |
|
||||
| RenderObjectsWithLOD | __stdcall | __cdecl (no saves) | OK (both no reg saves) |
|
||||
| CM2Scene_DrawModelBatch | __fastcall | __fastcall (ECX+EDX) | [OK] Correct |
|
||||
|
||||
## Prologue Byte Analysis
|
||||
|
||||
@@ -87,9 +87,9 @@ The Ghidra MCP `get_bytes` tool only returns 1 byte regardless of the length par
|
||||
|
||||
## Recommendations
|
||||
|
||||
1. ✅ **Address fixes have been applied** to discovery_hooks.cpp
|
||||
2. ⚠️ **Test the DLL** after recompilation to verify hooks work correctly
|
||||
3. ⚠️ **Monitor for crashes** that could indicate incorrect stolen byte counts
|
||||
1. [OK] **Address fixes have been applied** to discovery_hooks.cpp
|
||||
2. [!] **Test the DLL** after recompilation to verify hooks work correctly
|
||||
3. [!] **Monitor for crashes** that could indicate incorrect stolen byte counts
|
||||
4. If crashes occur after address fix, use Ghidra GUI to manually count instruction bytes in prologues
|
||||
|
||||
## Next Steps
|
||||
|
||||
@@ -14,9 +14,9 @@ Any architectural approach is acceptable. The existing 3-pass stencil code is pr
|
||||
|
||||
The depth buffer doesn't distinguish "wall pixel" from "player pixel." After a frame is rendered, there's no way to know which depth values came from static geometry vs units. This makes pure post-process approaches (mask + edge detection in EndScene) unable to satisfy requirements 1 and 2 simultaneously:
|
||||
|
||||
- If you depth-test the mask render against the full depth buffer → walls occlude (✓) but players also occlude (✗)
|
||||
- If you skip depth testing → players don't occlude (✓) but walls also don't occlude (✗)
|
||||
- If you composite in EndScene after all rendering → outlines are on top of everything, including walls (✗)
|
||||
- If you depth-test the mask render against the full depth buffer → walls occlude ([OK]) but players also occlude ([X])
|
||||
- If you skip depth testing → players don't occlude ([OK]) but walls also don't occlude ([X])
|
||||
- If you composite in EndScene after all rendering → outlines are on top of everything, including walls ([X])
|
||||
|
||||
**You need depth information from BEFORE players/NPCs have rendered, but AFTER terrain/WMOs have rendered.** This is only available at a specific point during the frame - when M2 model batches begin processing.
|
||||
|
||||
@@ -26,9 +26,9 @@ The depth buffer doesn't distinguish "wall pixel" from "player pixel." After a f
|
||||
|
||||
**How it satisfies the requirements:**
|
||||
|
||||
1. **Batch reordering** moves outline targets to render first in the M2 batch list. At that point, only terrain + WMO depth exists in the depth buffer. The outline pass (pass 2) uses `ZENABLE=TRUE` → terrain/WMOs occlude the outline. ✓
|
||||
2. **Stencil bit** (`STENCIL_BIT_OUTLINE=0x02`) is written where outline pixels are drawn. Subsequent player/NPC batches test against this bit and fail where outline exists → players can't paint over outlines. ✓
|
||||
3. **Dead players** use `ZENABLE=FALSE` in pass 2 → outline ignores all depth → visible through walls. ✓
|
||||
1. **Batch reordering** moves outline targets to render first in the M2 batch list. At that point, only terrain + WMO depth exists in the depth buffer. The outline pass (pass 2) uses `ZENABLE=TRUE` → terrain/WMOs occlude the outline. [OK]
|
||||
2. **Stencil bit** (`STENCIL_BIT_OUTLINE=0x02`) is written where outline pixels are drawn. Subsequent player/NPC batches test against this bit and fail where outline exists → players can't paint over outlines. [OK]
|
||||
3. **Dead players** use `ZENABLE=FALSE` in pass 2 → outline ignores all depth → visible through walls. [OK]
|
||||
|
||||
**Downsides:**
|
||||
- 3 DIP calls per outline target (stencil mark + outline + normal redraw)
|
||||
@@ -79,9 +79,9 @@ This is what the current code already does, just cleaned up.
|
||||
|
||||
Given the requirements, the 3-pass stencil with batch reordering is the only approach that works without exotic depth buffer tricks. The key properties it provides:
|
||||
|
||||
1. **Outline renders when only static-world depth exists** (via batch reordering) → walls/terrain occlude ✓
|
||||
2. **Stencil bit prevents later units from overwriting outline** → players don't occlude ✓
|
||||
3. **Per-category depth disable** → dead player outlines through walls ✓
|
||||
1. **Outline renders when only static-world depth exists** (via batch reordering) → walls/terrain occlude [OK]
|
||||
2. **Stencil bit prevents later units from overwriting outline** → players don't occlude [OK]
|
||||
3. **Per-category depth disable** → dead player outlines through walls [OK]
|
||||
|
||||
## Improvements to Implement
|
||||
|
||||
|
||||
+53
-55
@@ -49,7 +49,7 @@ Functions in rendering vtable:
|
||||
|
||||
### World Rendering Orchestration (FOUND!)
|
||||
|
||||
#### **ProcessWorldWithFrustum** ⭐ MAIN WORLD ORCHESTRATOR
|
||||
#### **ProcessWorldWithFrustum** MAIN WORLD ORCHESTRATOR
|
||||
- **Address**: `0x00683000` (called at 0x0068302f)
|
||||
- **Signature**: `void __fastcall ProcessWorldWithFrustum(float *frustumBounds)`
|
||||
- **Role**: **PRIMARY WORLD RENDERING ORCHESTRATOR** - Coordinates terrain, objects, and models
|
||||
@@ -65,8 +65,7 @@ Functions in rendering vtable:
|
||||
- **`CullObjectsToRenderList(manager, 2)`** - Cull objects type 2
|
||||
- `ProcessStaticObjectsCulling()` - Cull static world objects
|
||||
4. `PopFrustumStack()` - Restore frustum state
|
||||
5. **`CullAndProcessWorldChunks()`** - Terrain chunk rendering ⭐
|
||||
- **Notes**: This is the function that needs to be hooked for complete render order control!
|
||||
5. **`CullAndProcessWorldChunks()`** - Terrain chunk rendering - **Notes**: This is the function that needs to be hooked for complete render order control!
|
||||
|
||||
#### **CullObjectsToRenderList**
|
||||
- **Address**: `0x00683ab0`
|
||||
@@ -161,7 +160,7 @@ Functions in rendering vtable:
|
||||
|
||||
### Terrain/Water Rendering
|
||||
|
||||
#### **CullAndProcessWorldChunks** ⭐ TERRAIN RENDERER
|
||||
#### **CullAndProcessWorldChunks** TERRAIN RENDERER
|
||||
- **Address**: `0x00683040`
|
||||
- **Called by**: `ProcessWorldWithFrustum` (after object culling)
|
||||
- **Signature**: `void CullAndProcessWorldChunks(void)`
|
||||
@@ -219,7 +218,7 @@ Functions in rendering vtable:
|
||||
#### **renderMeshWithLOD**
|
||||
- **Called by**: `RenderObjectsWithLOD` (0x00684510)
|
||||
- **Role**: **ACTUAL MESH DRAWING FUNCTION** - Likely calls DrawIndexedPrimitive
|
||||
- **Status**: ⚠️ Function body not yet located - CRITICAL to find!
|
||||
- **Status**: [!] Function body not yet located - CRITICAL to find!
|
||||
- **Importance**: This is where the final D3D drawing happens
|
||||
|
||||
#### **Function Pointer Rendering (CM2Scene_DrawModelBatch)**
|
||||
@@ -283,7 +282,7 @@ WoW uses multiple linked lists to manage culled objects that need to be rendered
|
||||
- **Data Address**: `DAT_00c7cb18` + offset (indexed by renderListIndex * 0xc)
|
||||
- **Indices**: 0, 1, 2 (likely: terrain objects, WMOs, M2 models)
|
||||
- **Populated by**: `CullObjectsToRenderList` (0x00683ab0)
|
||||
- **Processed by**: Unknown (⚠️ still need to find)
|
||||
- **Processed by**: Unknown ([!] still need to find)
|
||||
|
||||
#### **Static Object Render Lists (ProcessStaticObjectsCulling)**
|
||||
- **List 1 (Normal)**:
|
||||
@@ -303,7 +302,7 @@ WoW uses multiple linked lists to manage culled objects that need to be rendered
|
||||
#### **LOD Object Render List (RenderObjectsWithLOD)**
|
||||
- **List Head**: `PTR_00c7cae0`
|
||||
- **Offset**: `PTR_00c7cad8`
|
||||
- **Populated by**: Unknown (⚠️ likely populated during culling phase)
|
||||
- **Populated by**: Unknown ([!] likely populated during culling phase)
|
||||
- **Processed by**: `RenderObjectsWithLOD` (0x00684510)
|
||||
- **Notes**: Objects are rendered with LOD (Level of Detail) based on distance
|
||||
|
||||
@@ -336,32 +335,32 @@ WoW uses multiple linked lists to manage culled objects that need to be rendered
|
||||
- **Setup**: Per-stage configuration via `SetTextureStage()`
|
||||
- **Transforms**: Applied to stages 0 and 1 during model batch rendering
|
||||
|
||||
## COMPLETE RENDERING PIPELINE DISCOVERED! ✅
|
||||
## COMPLETE RENDERING PIPELINE DISCOVERED! [OK]
|
||||
|
||||
### What We Found
|
||||
|
||||
#### ✅ **Main World Orchestrator**: `ProcessWorldWithFrustum` (0x00683000)
|
||||
#### [OK] **Main World Orchestrator**: `ProcessWorldWithFrustum` (0x00683000)
|
||||
- Coordinates all world rendering
|
||||
- Culls objects to 3 render lists (indices 0, 1, 2)
|
||||
- Renders terrain chunks last via `CullAndProcessWorldChunks`
|
||||
|
||||
#### ✅ **Terrain Renderer**: `CullAndProcessWorldChunks` (0x00683040)
|
||||
#### [OK] **Terrain Renderer**: `CullAndProcessWorldChunks` (0x00683040)
|
||||
- Primary terrain chunk culling and rendering
|
||||
- Processes ADT tiles within camera frustum
|
||||
- **Currently renders AFTER objects** (called last in ProcessWorldWithFrustum)
|
||||
|
||||
#### ✅ **Object Culling**: `CullObjectsToRenderList` (0x00683ab0)
|
||||
#### [OK] **Object Culling**: `CullObjectsToRenderList` (0x00683ab0)
|
||||
- Called 3 times with indices: 1, 0, 2
|
||||
- Likely represents: terrain objects, WMOs, and M2 models
|
||||
- Frustum culls and adds to render lists
|
||||
|
||||
#### ✅ **Static Object Culling**: `ProcessStaticObjectsCulling` (0x00683bf0)
|
||||
#### [OK] **Static Object Culling**: `ProcessStaticObjectsCulling` (0x00683bf0)
|
||||
- Handles WMO groups and static doodads
|
||||
- Respects render flags
|
||||
|
||||
### ✅ **NEW DISCOVERIES** - Render List Processing Found!
|
||||
### [OK] **NEW DISCOVERIES** - Render List Processing Found!
|
||||
|
||||
#### ⭐ **RenderObjectsWithLOD** - RENDER LIST PROCESSOR
|
||||
#### **RenderObjectsWithLOD** - RENDER LIST PROCESSOR
|
||||
- **Address**: `0x00684510`
|
||||
- **Role**: **PRIMARY RENDER LIST PROCESSOR** - Iterates through object render list and draws them
|
||||
- **Signature**: `void RenderObjectsWithLOD(void)`
|
||||
@@ -376,14 +375,13 @@ WoW uses multiple linked lists to manage culled objects that need to be rendered
|
||||
- Sets render target
|
||||
- Calls `ProcessObjectGeometry()` to prepare geometry
|
||||
- Calls `UpdateObjectPosition()` to update transform
|
||||
- **Calls `renderMeshWithLOD()` to actually draw the mesh** ⭐
|
||||
5. Checks LOD distance (`_DAT_00867958`) to cull distant objects
|
||||
- **Calls `renderMeshWithLOD()` to actually draw the mesh** 5. Checks LOD distance (`_DAT_00867958`) to cull distant objects
|
||||
6. Calls `EndRender()` to finish rendering
|
||||
- **Notes**: This is the missing link between culling and rendering!
|
||||
- **Calls**: `renderMeshWithLOD()`, `ProcessObjectGeometry()`, `UpdateObjectPosition()`
|
||||
- **Status**: No xrefs found - likely called via function pointer or vtable
|
||||
|
||||
#### ⭐ **executeRenderCommands** - WMO/STATIC OBJECT RENDERER
|
||||
#### **executeRenderCommands** - WMO/STATIC OBJECT RENDERER
|
||||
- **Called by**: `ProcessStaticObjectsCulling` (at end of function)
|
||||
- **Role**: Renders static objects (WMOs, buildings, doodads) from the culled list
|
||||
- **Implementation**: Found inside `ProcessStaticObjectsCulling` as a loop:
|
||||
@@ -399,24 +397,24 @@ WoW uses multiple linked lists to manage culled objects that need to be rendered
|
||||
|
||||
### Still To Investigate
|
||||
|
||||
#### 🟡 **Main Frame/Game Loop**
|
||||
#### [~] **Main Frame/Game Loop**
|
||||
- **Status**: Need to find what calls `ProcessWorldWithFrustum`
|
||||
- **Question**: What is the top-level orchestrator above ProcessWorldWithFrustum?
|
||||
- **Hypothesis**: Called from main game loop, possibly via callback or vtable
|
||||
- **Search Strategy**: Look for functions that reference ScenePresent vtable or main loop
|
||||
|
||||
#### 🟡 **RenderObjectsWithLOD Caller**
|
||||
#### [~] **RenderObjectsWithLOD Caller**
|
||||
- **Status**: No xrefs found to this function
|
||||
- **Question**: Where/how is `RenderObjectsWithLOD` called?
|
||||
- **Hypothesis**: Called via function pointer after ProcessWorldWithFrustum completes
|
||||
- **Likely**: Part of render list processing phase between culling and present
|
||||
|
||||
#### 🟡 **renderMeshWithLOD Implementation**
|
||||
#### [~] **renderMeshWithLOD Implementation**
|
||||
- **Status**: Called by RenderObjectsWithLOD, but function not yet located
|
||||
- **Role**: Actual mesh drawing function - likely calls DrawIndexedPrimitive
|
||||
- **Need**: Find this function to understand final rendering stage
|
||||
|
||||
#### 🟡 **Connection to M2Scene**
|
||||
#### [~] **Connection to M2Scene**
|
||||
- **Status**: M2 model rendering chain is clear, but connection to world rendering unclear
|
||||
- **Question**: How does `CM2Scene_ExecuteRenderPass` get called in relation to `ProcessWorldWithFrustum`?
|
||||
- **Need**: Find the higher-level function that calls both
|
||||
@@ -544,41 +542,41 @@ Once terrain/WMO renderers are found:
|
||||
|
||||
## Summary
|
||||
|
||||
### ✅ MAJOR PROGRESS - Rendering Pipeline Nearly Complete!
|
||||
### [OK] MAJOR PROGRESS - Rendering Pipeline Nearly Complete!
|
||||
|
||||
This analysis has successfully discovered most of the WoW rendering architecture:
|
||||
|
||||
#### **Primary Discoveries**
|
||||
1. **✅ World Orchestrator**: `ProcessWorldWithFrustum` (0x00683000)
|
||||
1. **[OK] World Orchestrator**: `ProcessWorldWithFrustum` (0x00683000)
|
||||
- Coordinates frustum culling for all world objects
|
||||
- Processes 32 object managers with 3 render list types
|
||||
- Calls terrain renderer last
|
||||
|
||||
2. **✅ Terrain Renderer**: `CullAndProcessWorldChunks` (0x00683040)
|
||||
2. **[OK] Terrain Renderer**: `CullAndProcessWorldChunks` (0x00683040)
|
||||
- Primary ADT terrain chunk culling and rendering
|
||||
- Called AFTER object culling (currently renders terrain depth last)
|
||||
|
||||
3. **✅ Object Culling**: `CullObjectsToRenderList` (0x00683ab0)
|
||||
3. **[OK] Object Culling**: `CullObjectsToRenderList` (0x00683ab0)
|
||||
- Frustum culls objects to 3 render lists (indices: 1, 0, 2)
|
||||
- Adds to global render lists at PTR_00c7cb14/PTR_00c7cb18
|
||||
- Likely: terrain objects, WMOs, M2 models
|
||||
|
||||
4. **✅ Static Culling**: `ProcessStaticObjectsCulling` (0x00683bf0)
|
||||
4. **[OK] Static Culling**: `ProcessStaticObjectsCulling` (0x00683bf0)
|
||||
- Handles WMO groups and static doodads
|
||||
- **Contains inline WMO renderer** (`executeRenderCommands` loop at end)
|
||||
- Adds to render lists at PTR_00c7cadc/PTR_00c7cb54
|
||||
|
||||
5. **⭐ NEW - Render List Processor**: `RenderObjectsWithLOD` (0x00684510)
|
||||
5. **NEW - Render List Processor**: `RenderObjectsWithLOD` (0x00684510)
|
||||
- **CRITICAL DISCOVERY**: This processes the culled object render list!
|
||||
- Iterates through objects at PTR_00c7cae0
|
||||
- Calls `renderMeshWithLOD()` for actual drawing
|
||||
- Includes LOD distance culling
|
||||
|
||||
6. **✅ M2 Model Pipeline**: Complete chain from `CM2Scene_ExecuteRenderPass` → `CM2SceneRenderDraw` → `RenderBatches` → `CM2Scene_DrawModelBatch`
|
||||
6. **[OK] M2 Model Pipeline**: Complete chain from `CM2Scene_ExecuteRenderPass` → `CM2SceneRenderDraw` → `RenderBatches` → `CM2Scene_DrawModelBatch`
|
||||
|
||||
7. **✅ Render Lists Mapped**: Documented 7+ global render list structures and their usage
|
||||
7. **[OK] Render Lists Mapped**: Documented 7+ global render list structures and their usage
|
||||
|
||||
### 🎯 Updated Rendering Flow
|
||||
### Updated Rendering Flow
|
||||
|
||||
Based on discoveries, the likely flow is:
|
||||
|
||||
@@ -597,7 +595,7 @@ Based on discoveries, the likely flow is:
|
||||
- Cursor overlay
|
||||
- ISceneEnd → D3D Present
|
||||
|
||||
### 🎯 Key Insight for Selective Occlusion
|
||||
### Key Insight for Selective Occlusion
|
||||
|
||||
The render list processing happens AFTER culling. To control occlusion:
|
||||
|
||||
@@ -613,10 +611,10 @@ The render list processing happens AFTER culling. To control occlusion:
|
||||
4. Normal objects/players
|
||||
|
||||
### Remaining Critical Questions
|
||||
- ⚠️ **What calls RenderObjectsWithLOD?** (no xrefs found - likely function pointer or callback system)
|
||||
- ✅ **Found rendering mechanism**: Function pointer at `renderContext+0x40+0x11c` in CM2Scene_DrawModelBatch performs actual D3D draw
|
||||
- ⚠️ **Main game loop?** (what calls ProcessWorldWithFrustum? - no direct callers found, likely callback/vtable)
|
||||
- ⚠️ **Render order?** (do objects render before or after terrain?)
|
||||
- [!] **What calls RenderObjectsWithLOD?** (no xrefs found - likely function pointer or callback system)
|
||||
- [OK] **Found rendering mechanism**: Function pointer at `renderContext+0x40+0x11c` in CM2Scene_DrawModelBatch performs actual D3D draw
|
||||
- [!] **Main game loop?** (what calls ProcessWorldWithFrustum? - no direct callers found, likely callback/vtable)
|
||||
- [!] **Render order?** (do objects render before or after terrain?)
|
||||
|
||||
### Latest Discoveries (Session 2)
|
||||
|
||||
@@ -639,7 +637,7 @@ We now have MOST of the critical hook points for selective occlusion! The missin
|
||||
|
||||
---
|
||||
|
||||
## 🎯 COMPLETE HOOK POINT MAPPING (Session 3)
|
||||
## COMPLETE HOOK POINT MAPPING (Session 3)
|
||||
|
||||
### Terrain Rendering Pipeline
|
||||
**Culling**: `CullAndProcessWorldChunks` (0x00683040)
|
||||
@@ -702,7 +700,7 @@ We now have MOST of the critical hook points for selective occlusion! The missin
|
||||
|
||||
---
|
||||
|
||||
## 📋 RECOMMENDED HOOK STRATEGY FOR SELECTIVE OCCLUSION
|
||||
## RECOMMENDED HOOK STRATEGY FOR SELECTIVE OCCLUSION
|
||||
|
||||
### Option A: High-Level (Easiest)
|
||||
Hook `ProcessWorldWithFrustum` (0x00683000) to reorder rendering:
|
||||
@@ -730,27 +728,27 @@ Hook D3D device:
|
||||
|
||||
---
|
||||
|
||||
## ✅ FINAL DISCOVERY STATUS
|
||||
## [OK] FINAL DISCOVERY STATUS
|
||||
|
||||
### Completed ✅
|
||||
- ✅ Terrain culling function (CullAndProcessWorldChunks)
|
||||
- ✅ WMO culling AND rendering (ProcessStaticObjectsCulling with inline rendering)
|
||||
- ✅ Object culling (CullObjectsToRenderList)
|
||||
- ✅ Object processing (RenderObjectsWithLOD)
|
||||
- ✅ Function pointer rendering system (CM2Scene_DrawModelBatch)
|
||||
- ✅ Main world orchestrator (ProcessWorldWithFrustum)
|
||||
- ✅ Callback registration system (UpdateRenderCallback)
|
||||
- ✅ All render list structures mapped (7+ globals)
|
||||
### Completed [OK]
|
||||
- [OK] Terrain culling function (CullAndProcessWorldChunks)
|
||||
- [OK] WMO culling AND rendering (ProcessStaticObjectsCulling with inline rendering)
|
||||
- [OK] Object culling (CullObjectsToRenderList)
|
||||
- [OK] Object processing (RenderObjectsWithLOD)
|
||||
- [OK] Function pointer rendering system (CM2Scene_DrawModelBatch)
|
||||
- [OK] Main world orchestrator (ProcessWorldWithFrustum)
|
||||
- [OK] Callback registration system (UpdateRenderCallback)
|
||||
- [OK] All render list structures mapped (7+ globals)
|
||||
|
||||
### Architecture Understanding ✅
|
||||
- ✅ WoW uses callback-based rendering (no direct function calls found)
|
||||
- ✅ Rendering is event-driven through vtables and function pointers
|
||||
- ✅ Terrain rendering likely invoked through callback (not found statically)
|
||||
- ✅ Static analysis limited by callback architecture
|
||||
### Architecture Understanding [OK]
|
||||
- [OK] WoW uses callback-based rendering (no direct function calls found)
|
||||
- [OK] Rendering is event-driven through vtables and function pointers
|
||||
- [OK] Terrain rendering likely invoked through callback (not found statically)
|
||||
- [OK] Static analysis limited by callback architecture
|
||||
|
||||
### Remaining Unknowns (Acceptable)
|
||||
- ⚠️ Exact terrain render list processor (likely callback - can find at runtime)
|
||||
- ⚠️ Main game loop that invokes ProcessWorldWithFrustum (callback system)
|
||||
- ⚠️ Exact render order (can determine through runtime hooking)
|
||||
- [!] Exact terrain render list processor (likely callback - can find at runtime)
|
||||
- [!] Main game loop that invokes ProcessWorldWithFrustum (callback system)
|
||||
- [!] Exact render order (can determine through runtime hooking)
|
||||
|
||||
**Conclusion**: We have ALL the hook points needed for selective occlusion implementation. Runtime debugging can fill in the remaining details if needed.
|
||||
|
||||
+14
-14
@@ -120,11 +120,11 @@ pDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
|
||||
### Implementation Status [COMPLETED]
|
||||
|
||||
All objectives achieved - see "Working Implementation" at end of document:
|
||||
- ✅ Hook DrawIndexedPrimitive to detect corpse/target/raid-marked models
|
||||
- ✅ Stencil-based outline rendering in EndScene
|
||||
- ✅ Custom vertex shader for bone-animated outline expansion
|
||||
- ✅ Per-category visual effects (dark halo for dead, colored outline for marks/target)
|
||||
- ✅ Through-wall visibility with proper body/outline layering
|
||||
- [OK] Hook DrawIndexedPrimitive to detect corpse/target/raid-marked models
|
||||
- [OK] Stencil-based outline rendering in EndScene
|
||||
- [OK] Custom vertex shader for bone-animated outline expansion
|
||||
- [OK] Per-category visual effects (dark halo for dead, colored outline for marks/target)
|
||||
- [OK] Through-wall visibility with proper body/outline layering
|
||||
|
||||
### D3D9 "Chams" / Wallhack Technique
|
||||
|
||||
@@ -1780,9 +1780,9 @@ c7: 0.000 1.000 0.000 0.000 <- Y identity
|
||||
**Attempted:** Scale c0.x, c1.y, c2.x, c3.y by `g_outlineThickness` (1.2x)
|
||||
|
||||
**Result:**
|
||||
- Model silhouette appears larger ✓
|
||||
- But silhouette MOVES when view angle changes ✗
|
||||
- Silhouette covers corpse instead of outlining it ✗
|
||||
- Model silhouette appears larger [OK]
|
||||
- But silhouette MOVES when view angle changes [X]
|
||||
- Silhouette covers corpse instead of outlining it [X]
|
||||
|
||||
**Root Cause:** Scaling these projection-space constants distorts the view transformation, not the model. The model center isn't at origin in view space, so scaling pushes it in different directions based on camera angle.
|
||||
|
||||
@@ -2260,9 +2260,9 @@ Self-outline prevention: Local player is excluded from target and raid mark outl
|
||||
|
||||
### Status
|
||||
|
||||
- ✅ Full bone transform shader working
|
||||
- ✅ Stencil-based outline rendering
|
||||
- ✅ Per-category visual effects (outline vs dark halo)
|
||||
- ✅ Distance-based thickness scaling with min/max clamping
|
||||
- ✅ Through-wall visibility
|
||||
- ✅ Self-outline prevention
|
||||
- [OK] Full bone transform shader working
|
||||
- [OK] Stencil-based outline rendering
|
||||
- [OK] Per-category visual effects (outline vs dark halo)
|
||||
- [OK] Distance-based thickness scaling with min/max clamping
|
||||
- [OK] Through-wall visibility
|
||||
- [OK] Self-outline prevention
|
||||
|
||||
@@ -47,7 +47,7 @@ Consider alternative approaches:
|
||||
- **Screen-space dilation** (iterative morphological expand of silhouette) - simpler, no distance field needed
|
||||
- **Gaussian blur difference** - blur silhouette, subtract original, threshold
|
||||
- **Sobel/edge detection** on the silhouette RT
|
||||
- Docs in `/media/storage/projects/zig/weirdutils/docs/` describe these alternatives
|
||||
- Docs in `docs/` describe these alternatives
|
||||
- Reference articles: ameye.dev "5 ways to draw an outline", Ben Golus "Quest for Very Wide Outlines"
|
||||
|
||||
## Key Files
|
||||
@@ -58,7 +58,7 @@ Consider alternative approaches:
|
||||
- `reference/c_overlay/d3d9_hook.cpp` - C reference (uses 3-pass shell extrusion, not JFA)
|
||||
|
||||
## Build / Environment
|
||||
- `zig build` from `/media/storage/projects/zig/weirdutils/`
|
||||
- `zig build` from the repo root
|
||||
- Zig 0.15, target x86-windows-msvc (32-bit DLL injected into WoW 3.3.5)
|
||||
- Linux host (Arch, kernel 6.16.1), game via Wine/DXVK
|
||||
- Git last commit: `661e156` (game object occlusion fix)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Outline Port Notes
|
||||
|
||||
Pure Zig reimplementation of the WoW 1.12.1 unit outline system, ported from the Idris C/C++ reference at `/media/storage/projects/idris/dlls`.
|
||||
Pure Zig reimplementation of the WoW 1.12.1 unit outline system, ported from the earlier Idris C/C++ reference implementation (`reference/c_overlay/`, not included in this repo).
|
||||
|
||||
## Hooked Functions
|
||||
|
||||
|
||||
@@ -1,273 +0,0 @@
|
||||
# Ground-Projected Raid Markers
|
||||
|
||||
**Goal:** Render placeable raid markers (like Wrath+) that project onto the ground as area indicators.
|
||||
|
||||
**Status:** Research complete, implementation pending
|
||||
|
||||
---
|
||||
|
||||
## What We Have
|
||||
|
||||
### D3D9 Rendering Infrastructure
|
||||
- **File:** `src/outline/d3d9_hook.zig`
|
||||
- Render target management (silhouette RT, JFA ping-pong buffers)
|
||||
- Shader assembly via D3DX (PS 3.0)
|
||||
- Fullscreen quad rendering with `DrawPrimitiveUP`
|
||||
- Complete state save/restore around custom passes
|
||||
- Hooks: EndScene, DrawIndexedPrimitive, Reset
|
||||
|
||||
### Model Rendering Hooks
|
||||
- **File:** `src/outline/model_hook.zig`
|
||||
- `CM2SceneRenderDraw` hook - batch reordering for depth control
|
||||
- `CM2Scene_DrawBatchProjected` hook - per-batch interception
|
||||
- Access to render context and model pointers
|
||||
- Batch reordering ensures outline targets render when only terrain+WMO depth exists
|
||||
|
||||
### Render Pipeline Documentation
|
||||
- **File:** `docs/RENDER_ARCHITECTURE.md`
|
||||
- Terrain renderer: `CullAndProcessWorldChunks` (0x00683040)
|
||||
- WMO renderer: `ProcessStaticObjectsCulling` (0x00683bf0)
|
||||
- M2 model pipeline fully mapped
|
||||
- Depth buffer control via hook ordering
|
||||
|
||||
### Outline Rendering Research
|
||||
- **File:** `docs/outline-rendering-research.md`
|
||||
- JFA (Jump Flood Algorithm) for distance-field outlines
|
||||
- Stencil + blur techniques (Valve L4D style)
|
||||
- Screen-space dilation approaches
|
||||
- All SM 3.0 compatible
|
||||
|
||||
### UnitXP SP3 Reference
|
||||
- **Path:** `/media/storage/projects/UnitXP_SP3_Orig/UnitXP_SP3/`
|
||||
- **Key file:** `Vanilla1121_functions.h`
|
||||
- `vanilla1121_worldToScreen(C3Vector& world)` - world → screen projection
|
||||
- `CWorld_Intersect()` - raycast through world geometry
|
||||
- `vanilla1121_unitPosition()` - unit world position
|
||||
- `vanilla1121_getCameraPosition()` - camera world position
|
||||
|
||||
### WoWee Terrain Renderer Reference
|
||||
- **Path:** `/media/storage/projects/WoWee/src/rendering/terrain_renderer.cpp`
|
||||
- Modern OpenGL terrain rendering with multi-layer textures
|
||||
- Frustum culling, LOD, shadow mapping
|
||||
- Good reference for understanding terrain data structures
|
||||
|
||||
---
|
||||
|
||||
## What's Missing
|
||||
|
||||
### 1. Terrain Height Query
|
||||
```c
|
||||
// Need: Get terrain Z at world (X, Y)
|
||||
float GetTerrainHeight(float worldX, float worldY);
|
||||
```
|
||||
|
||||
**Options:**
|
||||
- Reverse engineer WoW's internal terrain height function
|
||||
- Use `CWorld_Intersect()` with vertical ray: `(x, y, +1000) → (x, y, -1000)`
|
||||
- ADT tile parsing (complex, requires MPQ access)
|
||||
|
||||
### 2. Terrain Normal Query
|
||||
```c
|
||||
// Need: Get terrain normal at world (X, Y) for quad orientation
|
||||
C3Vector GetTerrainNormal(float worldX, float worldY);
|
||||
```
|
||||
|
||||
**Options:**
|
||||
- Sample height at 4 neighboring points, compute normal
|
||||
- Reverse engineer WoW's internal function
|
||||
|
||||
### 3. Depth Buffer as Texture (for projected decals)
|
||||
```c
|
||||
// Need: Access depth buffer as readable texture
|
||||
IDirect3DTexture9* GetDepthAsTexture();
|
||||
```
|
||||
|
||||
**D3D9 approaches:**
|
||||
- `INTZ` / `RAWZ` format hack (requires driver support)
|
||||
- `D3DFMT_D24S8` → `D3DFMT_D24X8` with `INTZ` fourcc
|
||||
- Render depth to separate RT during terrain pass
|
||||
- See: https://aras-p.info/texts/D3D9GPUHacks.html
|
||||
|
||||
---
|
||||
|
||||
## Implementation Options
|
||||
|
||||
### Option A: Screen-Space Marker (Simplest)
|
||||
|
||||
**Complexity:** Low
|
||||
**Visual Quality:** Basic (icon on screen, no ground conformity)
|
||||
|
||||
**Implementation:**
|
||||
1. Hook raid target array at `0x00B71368` (8 GUIDs)
|
||||
2. Get marked unit's world position via `vanilla1121_unitPosition()`
|
||||
3. Project to screen via `vanilla1121_worldToScreen()`
|
||||
4. Draw icon sprite in EndScene at screen position
|
||||
5. Optional: Depth test against terrain depth buffer
|
||||
|
||||
**Pros:**
|
||||
- Minimal implementation
|
||||
- Uses existing infrastructure
|
||||
- No terrain queries needed
|
||||
|
||||
**Cons:**
|
||||
- Marker doesn't conform to terrain
|
||||
- Looks like a floating icon, not ground projection
|
||||
- No "area on the ground" effect
|
||||
|
||||
---
|
||||
|
||||
### Option B: Projected Ground Decal (Recommended)
|
||||
|
||||
**Complexity:** Medium
|
||||
**Visual Quality:** Good (conforms to terrain, area indicator)
|
||||
|
||||
**Implementation:**
|
||||
1. Store marker world positions (from raid target array or click events)
|
||||
2. In EndScene, after terrain+WMO depth written:
|
||||
3. For each active marker:
|
||||
- Bind depth buffer as texture (INTZ hack or separate RT)
|
||||
- Render fullscreen quad with decal shader
|
||||
- Shader reconstructs world position from depth
|
||||
- If within marker radius, output marker color/texture
|
||||
- Distance fade at edges for soft boundary
|
||||
|
||||
**Decal Shader (HLSL SM 3.0):**
|
||||
```hlsl
|
||||
// Uniforms set by CPU
|
||||
float3 MarkerCenter; // World position
|
||||
float MarkerRadius; // In world units
|
||||
float4 MarkerColor; // RGBA
|
||||
float2 ScreenSize; // For UV calculation
|
||||
|
||||
// Depth buffer bound to s0
|
||||
sampler2D DepthSampler : register(s0);
|
||||
|
||||
float4 DecalPS(float2 uv : TEXCOORD0) : COLOR
|
||||
{
|
||||
// Read depth, reconstruct world position
|
||||
float depth = tex2D(DepthSampler, uv).r;
|
||||
float3 worldPos = ReconstructWorldPosition(uv, depth);
|
||||
|
||||
// Distance from marker center (XZ plane)
|
||||
float2 offset = worldPos.xz - MarkerCenter.xz;
|
||||
float dist = length(offset);
|
||||
|
||||
// Soft falloff
|
||||
float alpha = saturate(1.0 - (dist / MarkerRadius));
|
||||
alpha = smoothstep(0.0, 0.3, alpha); // Soft edge
|
||||
|
||||
// Output
|
||||
return float4(MarkerColor.rgb, MarkerColor.a * alpha);
|
||||
}
|
||||
```
|
||||
|
||||
**Pros:**
|
||||
- Conforms to terrain contours
|
||||
- Looks like ground projection
|
||||
- Can support multiple markers
|
||||
- Soft edges for aesthetic
|
||||
|
||||
**Cons:**
|
||||
- Requires depth buffer access (driver-dependent)
|
||||
- More complex shader setup
|
||||
- Per-marker render pass overhead
|
||||
|
||||
---
|
||||
|
||||
### Option C: World-Space Quad (Best Conformity)
|
||||
|
||||
**Complexity:** High
|
||||
**Visual Quality:** Best (actual geometry on terrain)
|
||||
|
||||
**Implementation:**
|
||||
1. Create marker quad mesh (4 vertices, 2 triangles)
|
||||
2. Hook `CM2SceneRenderDraw` to inject custom geometry
|
||||
3. For each marker:
|
||||
- Query terrain height at marker position
|
||||
- Query terrain normal for orientation
|
||||
- Position quad at terrain height, orient to normal
|
||||
- Add to render batch with marker texture
|
||||
4. Render as part of M2 batch with depth testing
|
||||
|
||||
**Pros:**
|
||||
- Perfect terrain conformity
|
||||
- Actual geometry, not shader trick
|
||||
- Proper depth testing with other objects
|
||||
|
||||
**Cons:**
|
||||
- Requires terrain height/normal queries
|
||||
- More complex geometry management
|
||||
- Hook injection into render pipeline
|
||||
|
||||
---
|
||||
|
||||
## Recommended Path
|
||||
|
||||
### Phase 1: Proof of Concept (Screen-Space)
|
||||
1. Implement basic screen-space marker rendering
|
||||
2. Hook raid target array, project positions
|
||||
3. Draw simple circle/icon at screen position
|
||||
4. Verify hook integration works
|
||||
|
||||
**Estimated effort:** 1-2 hours
|
||||
|
||||
### Phase 2: Ground Decal (Primary Target)
|
||||
1. Implement INTZ depth texture hack
|
||||
2. Write decal projection shader
|
||||
3. Add marker position storage
|
||||
4. Render projected circles on terrain
|
||||
|
||||
**Estimated effort:** 4-6 hours
|
||||
|
||||
### Phase 3: Polish
|
||||
1. Add marker textures (skull, cross, square, etc.)
|
||||
2. Soft edge falloff
|
||||
3. Multiple marker colors
|
||||
4. Optional: Click-to-place interface
|
||||
|
||||
**Estimated effort:** 2-3 hours
|
||||
|
||||
---
|
||||
|
||||
## Key Files to Create/Modify
|
||||
|
||||
### New Files
|
||||
```
|
||||
src/marker/
|
||||
├── marker_tracker.zig # Track active markers, positions
|
||||
├── marker_decal.zig # Decal rendering shader + pipeline
|
||||
├── marker_hooks.zig # Raid target array hooks
|
||||
└── marker_types.zig # Data structures
|
||||
```
|
||||
|
||||
### Modified Files
|
||||
```
|
||||
src/outline/d3d9_hook.zig # Add depth texture creation
|
||||
src/outline/types.zig # Add depth texture format constants
|
||||
src/main.zig # Initialize marker system
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## External References
|
||||
|
||||
### D3D9 Depth Buffer Hacks
|
||||
- https://aras-p.info/texts/D3D9GPUHacks.html
|
||||
- INTZ / RAWZ format for reading depth as texture
|
||||
|
||||
### Decal Rendering Techniques
|
||||
- Valve L4D Glow Effect (stencil + blur): https://developer.valvesoftware.com/wiki/L4D_Glow_Effect
|
||||
- Unreal Engine deferred decals: https://docs.unrealengine.com/4.27/en-US/RenderingAndGraphics/DeferredRendering/
|
||||
|
||||
### WoW 1.12.1 Internals
|
||||
- wowdev.wiki for ADT terrain format
|
||||
- UnitXP_SP3 for function signatures and calling conventions
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- Raid markers in Wrath+ use ground-projected circles with icon in center
|
||||
- Our outline system already has the render target / shader infrastructure
|
||||
- Depth buffer access is the main technical challenge for Option B
|
||||
- Option A is good for quick testing, Option B is the production target
|
||||
Reference in New Issue
Block a user