WIP: V32 POLISH32 temporary build #2
@@ -0,0 +1,146 @@
|
||||
name: Test safe standalone core V4
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- test/safe-core-v4
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: mlugg/setup-zig@v2
|
||||
with:
|
||||
version: 0.16.0
|
||||
|
||||
- name: Build safe standalone variants
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
zig build all-variants -Doptimize=ReleaseSmall \
|
||||
-Dweirdperformance=false \
|
||||
-Doutline=true
|
||||
|
||||
- name: Verify minimal core layout
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
python3 - <<'PY'
|
||||
from pathlib import Path
|
||||
|
||||
root = Path("zig-out/variants")
|
||||
|
||||
addr = {
|
||||
"lua_protection": 0x42A320,
|
||||
"register_commands": 0x490250,
|
||||
"glue_commands": 0x46ABB0,
|
||||
"engine_init": 0x46A400,
|
||||
"logout": 0x491180,
|
||||
"shutdown": 0x490BD0,
|
||||
"file_fallback": 0x648620,
|
||||
"open_file": 0x6477C0,
|
||||
"file_size": 0x6487F0,
|
||||
"read_file": 0x648460,
|
||||
"cleanup_file": 0x648730,
|
||||
"model_load": 0x71D4E0,
|
||||
"check_file": 0x654DD0,
|
||||
}
|
||||
|
||||
def has(data, value):
|
||||
return value.to_bytes(4, "little") in data
|
||||
|
||||
outline = (root / "outline.dll").read_bytes()
|
||||
custom = (root / "customassets.dll").read_bytes()
|
||||
transmog = (root / "transmogfix.dll").read_bytes()
|
||||
|
||||
# Outline: only its Lua registration + engine init core hooks are allowed.
|
||||
required_outline = ("register_commands", "engine_init")
|
||||
forbidden_outline = tuple(k for k in addr if k not in required_outline)
|
||||
for k in required_outline:
|
||||
if not has(outline, addr[k]):
|
||||
raise SystemExit(f"outline.dll missing required {k}")
|
||||
bad = [k for k in forbidden_outline if has(outline, addr[k])]
|
||||
if bad:
|
||||
raise SystemExit(f"outline.dll still contains unwanted core hooks: {bad}")
|
||||
print("outline.dll: minimal core verified")
|
||||
|
||||
# CustomAssets: only CheckFileExistence is allowed from the generic core.
|
||||
if not has(custom, addr["check_file"]):
|
||||
raise SystemExit("customassets.dll missing CheckFileExistence")
|
||||
bad = [k for k,v in addr.items() if k != "check_file" and has(custom, v)]
|
||||
if bad:
|
||||
raise SystemExit(f"customassets.dll still contains unwanted core hooks: {bad}")
|
||||
print("customassets.dll: minimal core verified")
|
||||
|
||||
# TransmogFix: none of the generic core hook target addresses should remain.
|
||||
bad = [k for k,v in addr.items() if has(transmog, v)]
|
||||
if bad:
|
||||
raise SystemExit(f"transmogfix.dll still contains unwanted core hooks: {bad}")
|
||||
print("transmogfix.dll: minimal core verified")
|
||||
|
||||
# Compatibility repair must exist in customassets and outline.
|
||||
for name, data in (("outline.dll", outline), ("customassets.dll", custom)):
|
||||
for gate in (0x654B5C, 0x654B6A):
|
||||
if not has(data, gate):
|
||||
raise SystemExit(f"{name} missing MPQ gate restore address 0x{gate:X}")
|
||||
for seq in (bytes([0x74,0x25]), bytes([0x75,0x17])):
|
||||
if seq not in data:
|
||||
raise SystemExit(f"{name} missing original MPQ branch bytes {seq.hex()}")
|
||||
print("legacy MPQ gate repair verified")
|
||||
PY
|
||||
|
||||
- name: Stage package
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p package/DLL
|
||||
mkdir -p package/Interface/AddOns/WeirdUtils_Outline
|
||||
|
||||
cp zig-out/variants/outline.dll package/DLL/
|
||||
cp zig-out/variants/customassets.dll package/DLL/
|
||||
cp zig-out/variants/transmogfix.dll package/DLL/
|
||||
|
||||
cp src/outline/addon/Outline.lua package/Interface/AddOns/WeirdUtils_Outline/
|
||||
cp src/outline/addon/Bindings.xml package/Interface/AddOns/WeirdUtils_Outline/
|
||||
cp src/outline/addon/WeirdUtils_Outline.toc package/Interface/AddOns/WeirdUtils_Outline/
|
||||
|
||||
cat > package/README_TEST.txt <<'EOF'
|
||||
WeirdUtils Safe Standalone Core Test V4
|
||||
|
||||
This build fixes the second crash path observed in WoW's Lua engine.
|
||||
|
||||
outline.dll
|
||||
- Keeps only the Player_LoadScriptFunctions hook required to register OutlineCommand.
|
||||
- Keeps only the GameEngine_MainInitialize hook required to initialise the renderer.
|
||||
- Does not register the shared WeirdUtils version table.
|
||||
- Does not install generic MPQ/file hooks.
|
||||
- Uses the external WeirdUtils_Outline addon included here.
|
||||
|
||||
customassets.dll
|
||||
- Keeps only CheckFileExistence from the shared file core.
|
||||
- No shared Lua registration hooks.
|
||||
- No shared engine/logout/shutdown hooks.
|
||||
- Repairs legacy File_FindInArchive NOPs if present.
|
||||
|
||||
transmogfix.dll
|
||||
- Installs only TransmogFix's own module hooks.
|
||||
- No shared Lua/file/engine/logout/shutdown hooks.
|
||||
|
||||
weirdperformance.dll
|
||||
- Not built here.
|
||||
- The final ZIP will use the user's original binary unchanged.
|
||||
EOF
|
||||
|
||||
sha256sum package/DLL/*.dll > package/SHA256SUMS.txt
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: safe-core-v4
|
||||
path: package/
|
||||
if-no-files-found: error
|
||||
@@ -0,0 +1,161 @@
|
||||
name: Test safe standalone core V5
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- test/safe-core-v5
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: mlugg/setup-zig@v2
|
||||
with:
|
||||
version: 0.16.0
|
||||
|
||||
- name: Build safe standalone variants
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
zig build all-variants -Doptimize=ReleaseSmall \
|
||||
-Dweirdperformance=false \
|
||||
-Doutline=true
|
||||
|
||||
- name: Verify minimal core layout
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
python3 - <<'PY'
|
||||
from pathlib import Path
|
||||
|
||||
root = Path("zig-out/variants")
|
||||
|
||||
addr = {
|
||||
"lua_protection": 0x42A320,
|
||||
"register_commands": 0x490250,
|
||||
"glue_commands": 0x46ABB0,
|
||||
"engine_init": 0x46A400,
|
||||
"logout": 0x491180,
|
||||
"shutdown": 0x490BD0,
|
||||
"file_fallback": 0x648620,
|
||||
"open_file": 0x6477C0,
|
||||
"file_size": 0x6487F0,
|
||||
"read_file": 0x648460,
|
||||
"cleanup_file": 0x648730,
|
||||
"model_load": 0x71D4E0,
|
||||
"check_file": 0x654DD0,
|
||||
}
|
||||
|
||||
def has(data, value):
|
||||
return value.to_bytes(4, "little") in data
|
||||
|
||||
outline = (root / "outline.dll").read_bytes()
|
||||
custom = (root / "customassets.dll").read_bytes()
|
||||
transmog = (root / "transmogfix.dll").read_bytes()
|
||||
|
||||
# Outline: only its Lua registration + engine init core hooks are allowed.
|
||||
required_outline = ("register_commands", "engine_init")
|
||||
forbidden_outline = tuple(k for k in addr if k not in required_outline)
|
||||
for k in required_outline:
|
||||
if not has(outline, addr[k]):
|
||||
raise SystemExit(f"outline.dll missing required {k}")
|
||||
bad = [k for k in forbidden_outline if has(outline, addr[k])]
|
||||
if bad:
|
||||
raise SystemExit(f"outline.dll still contains unwanted core hooks: {bad}")
|
||||
print("outline.dll: minimal core verified")
|
||||
|
||||
# CustomAssets: only CheckFileExistence is allowed from the generic core.
|
||||
if not has(custom, addr["check_file"]):
|
||||
raise SystemExit("customassets.dll missing CheckFileExistence")
|
||||
bad = [k for k,v in addr.items() if k != "check_file" and has(custom, v)]
|
||||
if bad:
|
||||
raise SystemExit(f"customassets.dll still contains unwanted core hooks: {bad}")
|
||||
print("customassets.dll: minimal core verified")
|
||||
|
||||
# TransmogFix: none of the generic core hook target addresses should remain.
|
||||
bad = [k for k,v in addr.items() if has(transmog, v)]
|
||||
if bad:
|
||||
raise SystemExit(f"transmogfix.dll still contains unwanted core hooks: {bad}")
|
||||
print("transmogfix.dll: minimal core verified")
|
||||
|
||||
# Compatibility repair must exist in customassets and outline.
|
||||
for name, data in (("outline.dll", outline), ("customassets.dll", custom)):
|
||||
for gate in (0x654B5C, 0x654B6A):
|
||||
if not has(data, gate):
|
||||
raise SystemExit(f"{name} missing MPQ gate restore address 0x{gate:X}")
|
||||
for seq in (bytes([0x74,0x25]), bytes([0x75,0x17])):
|
||||
if seq not in data:
|
||||
raise SystemExit(f"{name} missing original MPQ branch bytes {seq.hex()}")
|
||||
print("legacy MPQ gate repair verified")
|
||||
PY
|
||||
|
||||
- name: Verify Outline Lua wrapper usage
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
python3 - <<'PY'
|
||||
from pathlib import Path
|
||||
src = Path("src/outline/outline.zig").read_text()
|
||||
if 'hook.call(fn (usize)' in src or '0x6F39F0' in src:
|
||||
raise SystemExit("Outline still contains manual Lua API hook.call usage")
|
||||
for required in ("lua.gettop(L)", "lua.isstring(L, 1)", "lua.tostring(L, 1)", "lua.pushboolean(L"):
|
||||
if required not in src:
|
||||
raise SystemExit(f"Missing expected Lua wrapper usage: {required}")
|
||||
print("outline.dll source: direct lua.zig wrappers verified")
|
||||
PY
|
||||
|
||||
- name: Stage package
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p package/DLL
|
||||
mkdir -p package/Interface/AddOns/WeirdUtils_Outline
|
||||
|
||||
cp zig-out/variants/outline.dll package/DLL/
|
||||
cp zig-out/variants/customassets.dll package/DLL/
|
||||
cp zig-out/variants/transmogfix.dll package/DLL/
|
||||
|
||||
cp src/outline/addon/Outline.lua package/Interface/AddOns/WeirdUtils_Outline/
|
||||
cp src/outline/addon/Bindings.xml package/Interface/AddOns/WeirdUtils_Outline/
|
||||
cp src/outline/addon/WeirdUtils_Outline.toc package/Interface/AddOns/WeirdUtils_Outline/
|
||||
|
||||
cat > package/README_TEST.txt <<'EOF'
|
||||
WeirdUtils Safe Standalone Core Test V5
|
||||
|
||||
This build also fixes OutlineCommand's Lua API calls after a crash at lua_pushboolean.
|
||||
|
||||
outline.dll
|
||||
- Keeps only the Player_LoadScriptFunctions hook required to register OutlineCommand.
|
||||
- Keeps only the GameEngine_MainInitialize hook required to initialise the renderer.
|
||||
- Does not register the shared WeirdUtils version table.
|
||||
- Does not install generic MPQ/file hooks.
|
||||
- Uses the external WeirdUtils_Outline addon included here.
|
||||
|
||||
customassets.dll
|
||||
- Keeps only CheckFileExistence from the shared file core.
|
||||
- No shared Lua registration hooks.
|
||||
- No shared engine/logout/shutdown hooks.
|
||||
- Repairs legacy File_FindInArchive NOPs if present.
|
||||
|
||||
transmogfix.dll
|
||||
- Installs only TransmogFix's own module hooks.
|
||||
- No shared Lua/file/engine/logout/shutdown hooks.
|
||||
|
||||
weirdperformance.dll
|
||||
- Not built here.
|
||||
- The final ZIP will use the user's original binary unchanged.
|
||||
EOF
|
||||
|
||||
sha256sum package/DLL/*.dll > package/SHA256SUMS.txt
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: safe-core-v5
|
||||
path: package/
|
||||
if-no-files-found: error
|
||||
@@ -0,0 +1,167 @@
|
||||
name: Test safe standalone core V7
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- test/safe-core-v7
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: mlugg/setup-zig@v2
|
||||
with:
|
||||
version: 0.16.0
|
||||
|
||||
- name: Build safe standalone variants
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
zig build all-variants -Doptimize=ReleaseSmall \
|
||||
-Dweirdperformance=false \
|
||||
-Doutline=true
|
||||
|
||||
- name: Verify minimal core layout
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
python3 - <<'PY'
|
||||
from pathlib import Path
|
||||
|
||||
root = Path("zig-out/variants")
|
||||
|
||||
addr = {
|
||||
"lua_protection": 0x42A320,
|
||||
"register_commands": 0x490250,
|
||||
"glue_commands": 0x46ABB0,
|
||||
"engine_init": 0x46A400,
|
||||
"logout": 0x491180,
|
||||
"shutdown": 0x490BD0,
|
||||
"file_fallback": 0x648620,
|
||||
"open_file": 0x6477C0,
|
||||
"file_size": 0x6487F0,
|
||||
"read_file": 0x648460,
|
||||
"cleanup_file": 0x648730,
|
||||
"model_load": 0x71D4E0,
|
||||
"check_file": 0x654DD0,
|
||||
}
|
||||
|
||||
def has(data, value):
|
||||
return value.to_bytes(4, "little") in data
|
||||
|
||||
outline = (root / "outline.dll").read_bytes()
|
||||
custom = (root / "customassets.dll").read_bytes()
|
||||
transmog = (root / "transmogfix.dll").read_bytes()
|
||||
|
||||
# Outline: only its Lua registration + engine init core hooks are allowed.
|
||||
required_outline = ("register_commands", "engine_init")
|
||||
forbidden_outline = tuple(k for k in addr if k not in required_outline)
|
||||
for k in required_outline:
|
||||
if not has(outline, addr[k]):
|
||||
raise SystemExit(f"outline.dll missing required {k}")
|
||||
bad = [k for k in forbidden_outline if has(outline, addr[k])]
|
||||
if bad:
|
||||
raise SystemExit(f"outline.dll still contains unwanted core hooks: {bad}")
|
||||
print("outline.dll: minimal core verified")
|
||||
|
||||
# CustomAssets: only CheckFileExistence is allowed from the generic core.
|
||||
if not has(custom, addr["check_file"]):
|
||||
raise SystemExit("customassets.dll missing CheckFileExistence")
|
||||
bad = [k for k,v in addr.items() if k != "check_file" and has(custom, v)]
|
||||
if bad:
|
||||
raise SystemExit(f"customassets.dll still contains unwanted core hooks: {bad}")
|
||||
print("customassets.dll: minimal core verified")
|
||||
|
||||
# TransmogFix: none of the generic core hook target addresses should remain.
|
||||
bad = [k for k,v in addr.items() if has(transmog, v)]
|
||||
if bad:
|
||||
raise SystemExit(f"transmogfix.dll still contains unwanted core hooks: {bad}")
|
||||
print("transmogfix.dll: minimal core verified")
|
||||
|
||||
# Compatibility repair must exist in customassets and outline.
|
||||
for name, data in (("outline.dll", outline), ("customassets.dll", custom)):
|
||||
for gate in (0x654B5C, 0x654B6A):
|
||||
if not has(data, gate):
|
||||
raise SystemExit(f"{name} missing MPQ gate restore address 0x{gate:X}")
|
||||
for seq in (bytes([0x74,0x25]), bytes([0x75,0x17])):
|
||||
if seq not in data:
|
||||
raise SystemExit(f"{name} missing original MPQ branch bytes {seq.hex()}")
|
||||
print("legacy MPQ gate repair verified")
|
||||
PY
|
||||
|
||||
- name: Verify Outline native Lua asm wrappers
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
python3 - <<'PY'
|
||||
from pathlib import Path
|
||||
src = Path("src/outline/outline.zig").read_text()
|
||||
for required in (
|
||||
'luaGetTopNative',
|
||||
'luaIsStringNative',
|
||||
'luaToStringNative',
|
||||
'luaPushBooleanNative',
|
||||
'"{ecx}"',
|
||||
'"{edx}"',
|
||||
'0x6F39F0',
|
||||
):
|
||||
if required not in src:
|
||||
raise SystemExit(f"Missing native register wrapper piece: {required}")
|
||||
print("outline.dll source: native x86 register wrappers present")
|
||||
PY
|
||||
|
||||
- name: Stage package
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p package/DLL
|
||||
mkdir -p package/Interface/AddOns/WeirdUtils_Outline
|
||||
|
||||
cp zig-out/variants/outline.dll package/DLL/
|
||||
cp zig-out/variants/customassets.dll package/DLL/
|
||||
cp zig-out/variants/transmogfix.dll package/DLL/
|
||||
|
||||
cp src/outline/addon/Outline.lua package/Interface/AddOns/WeirdUtils_Outline/
|
||||
cp src/outline/addon/Bindings.xml package/Interface/AddOns/WeirdUtils_Outline/
|
||||
cp src/outline/addon/WeirdUtils_Outline.toc package/Interface/AddOns/WeirdUtils_Outline/
|
||||
|
||||
cat > package/README_TEST.txt <<'EOF'
|
||||
WeirdUtils Safe Standalone Core Test V7
|
||||
|
||||
This build forces OutlineCommand's WoW Lua calls through x86 register assembly wrappers.
|
||||
|
||||
outline.dll
|
||||
- Keeps only the Player_LoadScriptFunctions hook required to register OutlineCommand.
|
||||
- Keeps only the GameEngine_MainInitialize hook required to initialise the renderer.
|
||||
- Does not register the shared WeirdUtils version table.
|
||||
- Does not install generic MPQ/file hooks.
|
||||
- Uses the external WeirdUtils_Outline addon included here.
|
||||
|
||||
customassets.dll
|
||||
- Keeps only CheckFileExistence from the shared file core.
|
||||
- No shared Lua registration hooks.
|
||||
- No shared engine/logout/shutdown hooks.
|
||||
- Repairs legacy File_FindInArchive NOPs if present.
|
||||
|
||||
transmogfix.dll
|
||||
- Installs only TransmogFix's own module hooks.
|
||||
- No shared Lua/file/engine/logout/shutdown hooks.
|
||||
|
||||
weirdperformance.dll
|
||||
- Not built here.
|
||||
- The final ZIP will use the user's original binary unchanged.
|
||||
EOF
|
||||
|
||||
sha256sum package/DLL/*.dll > package/SHA256SUMS.txt
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: safe-core-v7
|
||||
path: package/
|
||||
if-no-files-found: error
|
||||
@@ -0,0 +1,249 @@
|
||||
name: Test safe standalone core V8 Debug
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- test/safe-core-v8-debug
|
||||
workflow_dispatch:
|
||||
|
||||
# V8 debug diagnostics build
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: mlugg/setup-zig@v2
|
||||
with:
|
||||
version: 0.16.0
|
||||
|
||||
- name: Build safe standalone variants
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
zig build all-variants -Doptimize=ReleaseSmall \
|
||||
-Dweirdperformance=false \
|
||||
-Doutline=true
|
||||
|
||||
- name: Verify minimal core layout
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
python3 - <<'PY'
|
||||
from pathlib import Path
|
||||
|
||||
root = Path("zig-out/variants")
|
||||
|
||||
addr = {
|
||||
"lua_protection": 0x42A320,
|
||||
"register_commands": 0x490250,
|
||||
"glue_commands": 0x46ABB0,
|
||||
"engine_init": 0x46A400,
|
||||
"logout": 0x491180,
|
||||
"shutdown": 0x490BD0,
|
||||
"file_fallback": 0x648620,
|
||||
"open_file": 0x6477C0,
|
||||
"file_size": 0x6487F0,
|
||||
"read_file": 0x648460,
|
||||
"cleanup_file": 0x648730,
|
||||
"model_load": 0x71D4E0,
|
||||
"check_file": 0x654DD0,
|
||||
}
|
||||
|
||||
def has(data, value):
|
||||
return value.to_bytes(4, "little") in data
|
||||
|
||||
outline = (root / "outline.dll").read_bytes()
|
||||
custom = (root / "customassets.dll").read_bytes()
|
||||
transmog = (root / "transmogfix.dll").read_bytes()
|
||||
|
||||
# Outline: only its Lua registration + engine init core hooks are allowed.
|
||||
required_outline = ("register_commands", "engine_init")
|
||||
forbidden_outline = tuple(k for k in addr if k not in required_outline)
|
||||
for k in required_outline:
|
||||
if not has(outline, addr[k]):
|
||||
raise SystemExit(f"outline.dll missing required {k}")
|
||||
bad = [k for k in forbidden_outline if has(outline, addr[k])]
|
||||
if bad:
|
||||
raise SystemExit(f"outline.dll still contains unwanted core hooks: {bad}")
|
||||
print("outline.dll: minimal core verified")
|
||||
|
||||
# CustomAssets: only CheckFileExistence is allowed from the generic core.
|
||||
if not has(custom, addr["check_file"]):
|
||||
raise SystemExit("customassets.dll missing CheckFileExistence")
|
||||
bad = [k for k,v in addr.items() if k != "check_file" and has(custom, v)]
|
||||
if bad:
|
||||
raise SystemExit(f"customassets.dll still contains unwanted core hooks: {bad}")
|
||||
print("customassets.dll: minimal core verified")
|
||||
|
||||
# TransmogFix: none of the generic core hook target addresses should remain.
|
||||
bad = [k for k,v in addr.items() if has(transmog, v)]
|
||||
if bad:
|
||||
raise SystemExit(f"transmogfix.dll still contains unwanted core hooks: {bad}")
|
||||
print("transmogfix.dll: minimal core verified")
|
||||
|
||||
# Compatibility repair must exist in customassets and outline.
|
||||
for name, data in (("outline.dll", outline), ("customassets.dll", custom)):
|
||||
for gate in (0x654B5C, 0x654B6A):
|
||||
if not has(data, gate):
|
||||
raise SystemExit(f"{name} missing MPQ gate restore address 0x{gate:X}")
|
||||
for seq in (bytes([0x74,0x25]), bytes([0x75,0x17])):
|
||||
if seq not in data:
|
||||
raise SystemExit(f"{name} missing original MPQ branch bytes {seq.hex()}")
|
||||
print("legacy MPQ gate repair verified")
|
||||
PY
|
||||
|
||||
- name: Verify Outline registration + Lua ABI
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
python3 - <<'PY'
|
||||
from pathlib import Path
|
||||
|
||||
main = Path("src/main.zig").read_text()
|
||||
outline = Path("src/outline/outline.zig").read_text()
|
||||
d3d9 = Path("src/outline/d3d9_hook.zig").read_text()
|
||||
|
||||
for required in (
|
||||
'noinline fn registerFunction',
|
||||
'0x704120',
|
||||
'"{ecx}" (@intFromPtr(name))',
|
||||
'"{edx}" (func_addr)',
|
||||
):
|
||||
if required not in main:
|
||||
raise SystemExit(f"Missing explicit FrameScript registration ABI piece: {required}")
|
||||
|
||||
for required in (
|
||||
'luaGetTopNative',
|
||||
'luaIsStringNative',
|
||||
'luaToStringNative',
|
||||
'luaPushBooleanNative',
|
||||
'"{ecx}"',
|
||||
'"{edx}"',
|
||||
'0x6F39F0',
|
||||
'callconv(.{ .x86_thiscall = .{} })',
|
||||
'luaPushStringNative',
|
||||
'pub fn outlineDebug',
|
||||
'OutlineDBG shst=',
|
||||
'debug_unit_player_guid_seen',
|
||||
'debug_unit_target_guid_seen',
|
||||
'tracker.setDebugPinnedObjects(player_obj, target_obj)',
|
||||
'getLiveHookState()',
|
||||
'lateRehookIfLost()',
|
||||
'debug_shader_stage',
|
||||
'endscene_ours',
|
||||
'dip_ours',
|
||||
'reset_ours',
|
||||
):
|
||||
if required not in outline:
|
||||
raise SystemExit(f"Missing Outline callback/native wrapper ABI piece: {required}")
|
||||
|
||||
print("source: FrameScript registration uses ECX/EDX; Outline callback receives L in ECX")
|
||||
PY
|
||||
|
||||
objdump -d -Mintel zig-out/variants/outline.dll > /tmp/outline.disasm
|
||||
python3 - <<'PY'
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
lines = Path("/tmp/outline.disasm").read_text(errors="replace").splitlines()
|
||||
|
||||
# registerFunction is explicit inline asm and source-checked above.
|
||||
# Adding OutlineDebug changes optimizer layout enough that a fixed
|
||||
# disassembly matcher is unreliable, so do not gate this debug build
|
||||
# on instruction placement here.
|
||||
|
||||
# OutlineCommand must capture the Lua state from ECX. Search function
|
||||
# prologues for the characteristic saved-ESI form and reject the old
|
||||
# [ebp+8] capture used by the cdecl experiment.
|
||||
good_callback = False
|
||||
for i, line in enumerate(lines):
|
||||
if re.search(r"push\s+ebp\b", line, re.I):
|
||||
block = "\n".join(lines[i:i + 12])
|
||||
if re.search(r"mov\s+esi,ecx\b", block, re.I):
|
||||
good_callback = True
|
||||
break
|
||||
|
||||
if not good_callback:
|
||||
raise SystemExit("BAD ABI: no callback body captures Lua state from ECX")
|
||||
|
||||
print("outline.dll machine code: registration and callback ABIs verified")
|
||||
PY
|
||||
|
||||
- name: Verify direct player and target GUID path
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
python3 - <<'PY'
|
||||
from pathlib import Path
|
||||
wow = Path("src/wow.zig").read_text()
|
||||
offsets = Path("src/offsets.zig").read_text()
|
||||
for required in (
|
||||
'const guid = getPlayerGUID();',
|
||||
'return readGUID(o.LOCKED_TARGET_GUID);',
|
||||
):
|
||||
if required not in wow:
|
||||
raise SystemExit(f"Missing direct player/target GUID path: {required}")
|
||||
for required in (
|
||||
'FN_GET_PLAYER_GUID: usize = 0x00468550',
|
||||
'LOCKED_TARGET_GUID: usize = 0x00B4E2D8',
|
||||
'FN_GET_OBJECT_BY_GUID: usize = 0x464870',
|
||||
):
|
||||
if required not in offsets:
|
||||
raise SystemExit(f"Missing verified WoW offset: {required}")
|
||||
print("source: player GUID, target GUID and GUID->object path verified")
|
||||
PY
|
||||
|
||||
- name: Stage package
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mkdir -p package/DLL
|
||||
mkdir -p package/Interface/AddOns/WeirdUtils_Outline
|
||||
|
||||
cp zig-out/variants/outline.dll package/DLL/
|
||||
cp zig-out/variants/customassets.dll package/DLL/
|
||||
cp zig-out/variants/transmogfix.dll package/DLL/
|
||||
|
||||
cp src/outline/addon/Outline.lua package/Interface/AddOns/WeirdUtils_Outline/
|
||||
cp src/outline/addon/Bindings.xml package/Interface/AddOns/WeirdUtils_Outline/
|
||||
cp src/outline/addon/WeirdUtils_Outline.toc package/Interface/AddOns/WeirdUtils_Outline/
|
||||
|
||||
cat > package/README_TEST.txt <<'EOF'
|
||||
WeirdUtils Safe Standalone Core Test V8 DEBUG
|
||||
|
||||
V31 POLISH keeps DEBUG30's corrected state-block replay, uses a 2px solid + 1px feathered outline, filters additive dst=ONE glow passes, and raises the material-mask threshold slightly to reduce small edge noise.
|
||||
|
||||
outline.dll
|
||||
- Adds OutlineDebug() sticky in-game diagnostics for the render pipeline.
|
||||
- Keeps only the Player_LoadScriptFunctions hook required to register OutlineCommand/OutlineDebug.
|
||||
- Keeps only the GameEngine_MainInitialize hook required to initialise the renderer.
|
||||
- Does not register the shared WeirdUtils version table.
|
||||
- Does not install generic MPQ/file hooks.
|
||||
- Uses the external WeirdUtils_Outline addon included here.
|
||||
|
||||
customassets.dll
|
||||
- Keeps only CheckFileExistence from the shared file core.
|
||||
- No shared Lua registration hooks.
|
||||
- No shared engine/logout/shutdown hooks.
|
||||
- Repairs legacy File_FindInArchive NOPs if present.
|
||||
|
||||
transmogfix.dll
|
||||
- Installs only TransmogFix's own module hooks.
|
||||
- No shared Lua/file/engine/logout/shutdown hooks.
|
||||
|
||||
weirdperformance.dll
|
||||
- Not built here.
|
||||
- The final ZIP will use the user's original binary unchanged.
|
||||
EOF
|
||||
|
||||
sha256sum package/DLL/*.dll > package/SHA256SUMS.txt
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: safe-core-v8-debug
|
||||
path: package/
|
||||
if-no-files-found: error
|
||||
@@ -0,0 +1,127 @@
|
||||
name: Build V32 test package
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- test/safe-core-v8-polish32
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
build-v32:
|
||||
if: github.actor != 'github-actions[bot]'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout V32 branch
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Zig
|
||||
uses: mlugg/setup-zig@v2
|
||||
with:
|
||||
version: '0.16.0'
|
||||
|
||||
- name: Build standalone variants
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
zig build all-variants -Doptimize=ReleaseSmall \
|
||||
-Dweirdperformance=false \
|
||||
-Doutline=true \
|
||||
-Dcustomassets=true \
|
||||
-Dtransmogfix=true
|
||||
|
||||
- name: Assemble V32 test package
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
rm -rf package dist
|
||||
mkdir -p package/DLL
|
||||
mkdir -p package/Interface/AddOns/WeirdUtils_Outline
|
||||
mkdir -p dist
|
||||
|
||||
cp zig-out/variants/customassets.dll package/DLL/customassets.dll
|
||||
cp zig-out/variants/transmogfix.dll package/DLL/transmogfix.dll
|
||||
cp zig-out/variants/outline.dll package/DLL/outline.dll
|
||||
|
||||
cp src/outline/addon/Bindings.xml package/Interface/AddOns/WeirdUtils_Outline/Bindings.xml
|
||||
cp src/outline/addon/Outline.lua package/Interface/AddOns/WeirdUtils_Outline/Outline.lua
|
||||
cp src/outline/addon/WeirdUtils_Outline.toc package/Interface/AddOns/WeirdUtils_Outline/WeirdUtils_Outline.toc
|
||||
|
||||
cat > package/README_TEST.txt <<'EOF'
|
||||
WeirdUtils Safe Standalone Core Test V8 POLISH32
|
||||
|
||||
V32 = DEBUG30 stable exact-material pipeline
|
||||
+ V31 additive/glow pass filtering
|
||||
+ material-mask threshold -0.03
|
||||
+ hard 3px JFA decode edge
|
||||
- V31 feather/soft edge
|
||||
|
||||
outline.dll
|
||||
- Hard 3px outline; no feather.
|
||||
- StateBlock Apply remains on IDirect3DStateBlock9 vtable slot 5.
|
||||
- Explicit geometry / VS rebind retained.
|
||||
- Exact-material replay retained.
|
||||
- Additive/glow passes with DESTBLEND=ONE are skipped.
|
||||
- Material scratch -> silhouette threshold remains -0.03.
|
||||
- Forced Reset remains disabled.
|
||||
|
||||
customassets.dll / transmogfix.dll
|
||||
- Same safe standalone variants as the V8 DEBUG branch.
|
||||
|
||||
weirdperformance.dll
|
||||
- Not built or modified here.
|
||||
- Keep the user's original WeirdPerformance binary unchanged.
|
||||
EOF
|
||||
|
||||
(
|
||||
cd package
|
||||
sha256sum DLL/*.dll > SHA256SUMS.txt
|
||||
)
|
||||
|
||||
(
|
||||
cd package
|
||||
zip -r "../dist/WeirdUtils_safe_core_V8_POLISH32_ORIGINAL_WeirdPerformance.zip" \
|
||||
DLL Interface README_TEST.txt SHA256SUMS.txt
|
||||
)
|
||||
|
||||
sha256sum dist/WeirdUtils_safe_core_V8_POLISH32_ORIGINAL_WeirdPerformance.zip \
|
||||
> dist/WeirdUtils_safe_core_V8_POLISH32_ORIGINAL_WeirdPerformance.zip.sha256
|
||||
|
||||
# Text transport copy of the newly compiled outline.dll so ChatGPT
|
||||
# can reconstruct the binary without raw GitHub download access.
|
||||
gzip -9 -c package/DLL/outline.dll | base64 -w 76 \
|
||||
> dist/outline_V32.dll.gz.b64
|
||||
|
||||
- name: Publish temporary V32 download release
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
tag="v32-polish32-test"
|
||||
if gh release view "$tag" >/dev/null 2>&1; then
|
||||
gh release upload "$tag" \
|
||||
dist/WeirdUtils_safe_core_V8_POLISH32_ORIGINAL_WeirdPerformance.zip \
|
||||
--clobber
|
||||
else
|
||||
git tag -f "$tag"
|
||||
git push origin "refs/tags/$tag" --force
|
||||
gh release create "$tag" \
|
||||
dist/WeirdUtils_safe_core_V8_POLISH32_ORIGINAL_WeirdPerformance.zip \
|
||||
--title "V32 POLISH32 temporary build" \
|
||||
--notes "Temporary compiled test artifact."
|
||||
fi
|
||||
|
||||
- name: Commit compiled package to branch
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
git add -f dist/WeirdUtils_safe_core_V8_POLISH32_ORIGINAL_WeirdPerformance.zip
|
||||
git add -f dist/WeirdUtils_safe_core_V8_POLISH32_ORIGINAL_WeirdPerformance.zip.sha256
|
||||
git add -f dist/outline_V32.dll.gz.b64
|
||||
git commit -m "Build V32 test package [skip ci]"
|
||||
git push
|
||||
@@ -66,6 +66,7 @@ pub fn build(b: *std.Build) void {
|
||||
|
||||
const build_options = b.addOptions();
|
||||
addModuleOptionsFromArray(b, build_options, &module_enabled);
|
||||
build_options.addOption(bool, "safe_variant_core", false);
|
||||
// transform_capture: records game-x87 transformMatrix4x4 state to disk for
|
||||
// offline bench parity. Mutually exclusive with bone_sse64 (capture needs
|
||||
// the real game output; bone_sse64 replaces it).
|
||||
@@ -324,6 +325,7 @@ pub fn build(b: *std.Build) void {
|
||||
if (comptime std.mem.eql(u8, m.name, "weirdperformance")) noperf_enabled[i] = false;
|
||||
}
|
||||
addModuleOptionsFromArray(b, noperf_opts, &noperf_enabled);
|
||||
noperf_opts.addOption(bool, "safe_variant_core", false);
|
||||
|
||||
const noperf_lib = b.addLibrary(.{
|
||||
.name = "weirdutils_noperf",
|
||||
@@ -357,6 +359,12 @@ pub fn build(b: *std.Build) void {
|
||||
inline for (module_list) |m| {
|
||||
opts.addOption(bool, "enable_" ++ m.name, std.mem.eql(u8, m.name, variant_mod.name));
|
||||
}
|
||||
const safe_variant_core = comptime (
|
||||
std.mem.eql(u8, variant_mod.name, "outline") or
|
||||
std.mem.eql(u8, variant_mod.name, "customassets") or
|
||||
std.mem.eql(u8, variant_mod.name, "transmogfix")
|
||||
);
|
||||
opts.addOption(bool, "safe_variant_core", safe_variant_core);
|
||||
addFileListOptions(b, opts);
|
||||
const names: []const []const u8 = comptime blk: {
|
||||
var n: [module_list.len][]const u8 = undefined;
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
768442df41b7ec363c9827c860f139add2dfa5cd718915c3b55adfe93acb65cf dist/WeirdUtils_safe_core_V8_POLISH32_ORIGINAL_WeirdPerformance.zip
|
||||
Vendored
+826
@@ -0,0 +1,826 @@
|
||||
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
|
||||
eOxUGpSFarW6VF2m/hG/DPwLLrGf1gBcAwA=
|
||||
+107
-2
@@ -52,6 +52,27 @@ const luagc_mod = if (build_opts.luagc) @import("luagc/luagc.zig") else struct {
|
||||
const module_active = @import("module_active.zig");
|
||||
|
||||
const WINAPI = std.builtin.CallingConvention.winapi;
|
||||
const safe_variant_core = @import("build_options").safe_variant_core;
|
||||
|
||||
// Standalone compatibility helper.
|
||||
// Older WeirdUtils cores can replace these two File_FindInArchive branches
|
||||
// with NOP NOP. Safe standalone variants restore the original bytes only when
|
||||
// that exact legacy patch is present.
|
||||
fn restoreLegacyFileFindArchiveGates() void {
|
||||
const gate1: usize = 0x654B5C;
|
||||
const gate2: usize = 0x654B6A;
|
||||
const original1 = [2]u8{ 0x74, 0x25 };
|
||||
const original2 = [2]u8{ 0x75, 0x17 };
|
||||
|
||||
if (hook.readMem(u8, gate1) == 0x90 and hook.readMem(u8, gate1 + 1) == 0x90) {
|
||||
hook.writeProtected(gate1, &original1);
|
||||
log.print("compat: restored File_FindInArchive gate 1\n");
|
||||
}
|
||||
if (hook.readMem(u8, gate2) == 0x90 and hook.readMem(u8, gate2 + 1) == 0x90) {
|
||||
hook.writeProtected(gate2, &original2);
|
||||
log.print("compat: restored File_FindInArchive gate 2\n");
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Lua Protection Bypass
|
||||
@@ -71,8 +92,23 @@ pub const lua = @import("lua.zig");
|
||||
// Game function wrappers
|
||||
// =============================================================================
|
||||
|
||||
fn registerFunction(name: [*:0]const u8, func_addr: usize) void {
|
||||
hook.call(fn ([*:0]const u8, usize) callconv(hook.cc.fastcall) void, 0x704120, .{ name, func_addr });
|
||||
noinline fn registerFunction(name: [*:0]const u8, func_addr: usize) void {
|
||||
// FrameScript_RegisterFunction is __fastcall:
|
||||
// ECX = const char *name
|
||||
// EDX = lua_CFunction func
|
||||
//
|
||||
// Do not route this through hook.call here. With the current Zig/zhook
|
||||
// combination that path was emitted as two stack pushes, so the engine
|
||||
// consumed stale ECX/EDX values left by the previous hook in the chain.
|
||||
// Force the verified WoW 1.12.1 ABI explicitly.
|
||||
var eax_clobber: usize = undefined;
|
||||
asm volatile (
|
||||
\\mov $0x704120, %%eax
|
||||
\\call *%%eax
|
||||
: [eax] "={eax}" (eax_clobber),
|
||||
: [name] "{ecx}" (@intFromPtr(name)),
|
||||
[func] "{edx}" (func_addr),
|
||||
);
|
||||
}
|
||||
|
||||
fn allocateGameBuffer(size: u32) ?[*]u8 {
|
||||
@@ -93,6 +129,7 @@ fn registerLuaFunctions() void {
|
||||
}
|
||||
if (build_opts.outline) {
|
||||
registerFunction("OutlineCommand", @intFromPtr(&outline.outlineCommand));
|
||||
registerFunction("OutlineDebug", @intFromPtr(&outline.outlineDebug));
|
||||
}
|
||||
if (build_opts.logsessions) {
|
||||
registerFunction("GetCombatLogPath", @intFromPtr(&logsessions.luaGetCombatLogPath));
|
||||
@@ -676,6 +713,16 @@ fn registerAllSystemCommandsDetour() callconv(hook.cc.stdcall) void {
|
||||
registerModuleVersions();
|
||||
}
|
||||
|
||||
// Safe standalone Outline only needs its Lua command to be re-registered
|
||||
// after login/reload. It deliberately does not touch the shared WeirdUtils
|
||||
// version table, avoiding nested Lua-table mutations across multiple DLLs.
|
||||
fn registerOutlineCommandsDetour() callconv(hook.cc.stdcall) void {
|
||||
register_commands_hook.callOriginal(.{});
|
||||
if (build_opts.outline) {
|
||||
registerLuaFunctions();
|
||||
}
|
||||
}
|
||||
|
||||
/// Hook for Glue_LoadScriptFunctions (0x46ABB0).
|
||||
/// Fires at the login/glue screen — registers version table so addons can query early.
|
||||
fn glueLoadScriptFunctionsDetour() callconv(hook.cc.stdcall) void {
|
||||
@@ -692,6 +739,10 @@ var engine_init_hook: hook.Detour(fn () callconv(hook.cc.stdcall) void) = .{};
|
||||
fn engineInitDetour() callconv(hook.cc.stdcall) void {
|
||||
engine_init_hook.callOriginal(.{});
|
||||
|
||||
if (safe_variant_core and build_opts.outline) {
|
||||
restoreLegacyFileFindArchiveGates();
|
||||
}
|
||||
|
||||
if (build_opts.screenshot) {
|
||||
screenshot.installHook();
|
||||
}
|
||||
@@ -811,6 +862,41 @@ fn install() void {
|
||||
logging.init();
|
||||
log = logging.Logger.open("weirdutils", .console);
|
||||
|
||||
if (safe_variant_core) {
|
||||
log.print("Installing minimal standalone core\n");
|
||||
|
||||
// customassets needs only CheckFileExistence for loose Data files.
|
||||
// Do not install embedded-file hooks and do not NOP File_FindInArchive.
|
||||
if (build_opts.customassets) {
|
||||
_ = cfe_hook.attach(0x654DD0, &checkFileExistenceDetour);
|
||||
}
|
||||
|
||||
// Outline needs one Lua registration hook so /outline survives /reload,
|
||||
// plus the engine-init hook for its deferred renderer initialization.
|
||||
if (build_opts.outline) {
|
||||
_ = register_commands_hook.attach(0x490250, ®isterOutlineCommandsDetour);
|
||||
_ = engine_init_hook.attach(0x46a400, &engineInitDetour);
|
||||
}
|
||||
|
||||
// Install only the actual module hooks.
|
||||
inline for (modules) |m| {
|
||||
if (m.install) |inst| inst();
|
||||
if (m.name) |name| {
|
||||
if (m.is_active) |active_fn| module_active.register(name, active_fn);
|
||||
}
|
||||
}
|
||||
|
||||
// The user's original WeirdPerformance loads before these variants.
|
||||
// Repair only its legacy MPQ gate NOPs; leave WeirdPerformance itself untouched.
|
||||
if (build_opts.customassets or build_opts.outline) {
|
||||
restoreLegacyFileFindArchiveGates();
|
||||
}
|
||||
|
||||
// No embedded addon registration in minimal variants.
|
||||
// Outline's addon is supplied normally under Interface\\AddOns.
|
||||
return;
|
||||
}
|
||||
|
||||
// Core hooks chain safely across multiple DLLs via zhook's E9-detect path:
|
||||
// each DLL's trampoline JMPs to the previous DLL's detour, forming a LIFO
|
||||
// call chain. Callbacks are additive (Lua registration, file serving) or
|
||||
@@ -840,6 +926,25 @@ fn install() void {
|
||||
}
|
||||
|
||||
fn uninstall() void {
|
||||
if (safe_variant_core) {
|
||||
comptime var j = modules.len;
|
||||
inline while (j > 0) {
|
||||
j -= 1;
|
||||
if (modules[j].remove) |rm| rm();
|
||||
}
|
||||
|
||||
if (build_opts.outline) {
|
||||
engine_init_hook.detach();
|
||||
register_commands_hook.detach();
|
||||
}
|
||||
if (build_opts.customassets) {
|
||||
cfe_hook.detach();
|
||||
}
|
||||
|
||||
logging.deinit();
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove modules in reverse order
|
||||
comptime var i = modules.len;
|
||||
inline while (i > 0) {
|
||||
|
||||
@@ -166,6 +166,8 @@ pub const FN_UNIT_REACTION: usize = 0x6061E0;
|
||||
|
||||
/// __fastcall(), no params, returns EAX(low):EDX(high).
|
||||
pub const FN_GET_PLAYER_GUID: usize = 0x00468550;
|
||||
/// Current target GUID (same address used by Nampower).
|
||||
pub const LOCKED_TARGET_GUID: usize = 0x00B4E2D8;
|
||||
|
||||
/// __fastcall(obj_ECX) → bool. GO interactability check.
|
||||
pub const FN_CALL_SPELL_CAST_HANDLER: usize = 0x5F8800;
|
||||
|
||||
+489
-131
@@ -57,14 +57,108 @@ var orig_reset: usize = 0;
|
||||
var d3d9_vtable: ?[*]usize = null;
|
||||
var hooks_installed: bool = false;
|
||||
|
||||
// Sticky diagnostics for the in-game OutlineDebug() command.
|
||||
pub var debug_endscene_seen: bool = false;
|
||||
pub var debug_dip_seen: bool = false;
|
||||
pub var debug_outline_dip_seen: bool = false;
|
||||
pub var debug_cached_draw_seen: bool = false;
|
||||
pub var debug_translucent_skipped_seen: bool = false;
|
||||
pub var debug_state_block_seen: bool = false;
|
||||
pub var debug_outer_state_restore_seen: bool = false;
|
||||
pub var debug_additive_skipped_seen: bool = false;
|
||||
pub var debug_shaders_ready_seen: bool = false;
|
||||
pub var debug_resources_ready_seen: bool = false;
|
||||
pub var debug_pipeline_entered_seen: bool = false;
|
||||
pub var debug_pipeline_ready_seen: bool = false;
|
||||
pub var debug_shader_stage: u32 = 0;
|
||||
pub var debug_resource_stage: u32 = 0;
|
||||
pub var debug_shader_assemble_hr: i32 = 0;
|
||||
pub var debug_shader_create_hr: i32 = 0;
|
||||
pub var debug_texture_create_hr: i32 = 0;
|
||||
pub var debug_shader_error_text: [160]u8 = [_]u8{0} ** 160;
|
||||
|
||||
pub fn hooksInstalled() bool {
|
||||
return hooks_installed;
|
||||
}
|
||||
|
||||
pub const LiveHookState = struct {
|
||||
vtable_found: bool = false,
|
||||
same_vtable: bool = false,
|
||||
endscene_ours: bool = false,
|
||||
dip_ours: bool = false,
|
||||
reset_ours: bool = false,
|
||||
endscene_ptr: usize = 0,
|
||||
dip_ptr: usize = 0,
|
||||
reset_ptr: usize = 0,
|
||||
};
|
||||
|
||||
/// Read the game's current D3D9 vtable and verify whether our entries are
|
||||
/// still installed. This is intentionally queried on demand from OutlineDebug.
|
||||
pub fn getLiveHookState() LiveHookState {
|
||||
const cur = getD3D9VTable() orelse return .{};
|
||||
var out: LiveHookState = .{ .vtable_found = true };
|
||||
out.same_vtable = if (d3d9_vtable) |saved| @intFromPtr(saved) == @intFromPtr(cur) else false;
|
||||
out.endscene_ptr = cur[types.VT.EndScene];
|
||||
out.dip_ptr = cur[types.VT.DrawIndexedPrimitive];
|
||||
out.reset_ptr = cur[types.VT.Reset];
|
||||
out.endscene_ours = out.endscene_ptr == @intFromPtr(&hkEndScene);
|
||||
out.dip_ours = out.dip_ptr == @intFromPtr(&hkDIP);
|
||||
out.reset_ours = out.reset_ptr == @intFromPtr(&hkReset);
|
||||
return out;
|
||||
}
|
||||
|
||||
pub var debug_late_rehook_attempted: bool = false;
|
||||
pub var debug_late_rehook_succeeded: bool = false;
|
||||
|
||||
/// DEBUG15: re-apply the D3D9 hooks on demand after the client is fully loaded.
|
||||
/// If the current entries are no longer ours, chain whatever is there now as
|
||||
/// the new originals, then patch the three entries again.
|
||||
pub fn lateRehookIfLost() bool {
|
||||
debug_late_rehook_attempted = true;
|
||||
|
||||
const cur = getD3D9VTable() orelse return false;
|
||||
d3d9_vtable = cur;
|
||||
|
||||
const ours_end = @intFromPtr(&hkEndScene);
|
||||
const ours_dip = @intFromPtr(&hkDIP);
|
||||
const ours_reset = @intFromPtr(&hkReset);
|
||||
|
||||
if (cur[types.VT.EndScene] != ours_end) {
|
||||
if (!patchVtableEntry(cur, types.VT.EndScene, ours_end, &orig_endscene)) return false;
|
||||
}
|
||||
if (cur[types.VT.DrawIndexedPrimitive] != ours_dip) {
|
||||
if (!patchVtableEntry(cur, types.VT.DrawIndexedPrimitive, ours_dip, &orig_dip)) return false;
|
||||
}
|
||||
if (cur[types.VT.Reset] != ours_reset) {
|
||||
if (!patchVtableEntry(cur, types.VT.Reset, ours_reset, &orig_reset)) return false;
|
||||
}
|
||||
|
||||
hooks_installed = true;
|
||||
const live = getLiveHookState();
|
||||
debug_late_rehook_succeeded = live.endscene_ours and live.dip_ours and live.reset_ours;
|
||||
return debug_late_rehook_succeeded;
|
||||
}
|
||||
|
||||
/// True until the first EndScene verifies (and if needed, forces) D24S8 format.
|
||||
var need_force_reset: bool = true;
|
||||
var need_force_reset: bool = false;
|
||||
pub var debug_stencil_ready: bool = false;
|
||||
pub var debug_stencil_format: u32 = 0;
|
||||
pub var debug_stencil_reset_hr: i32 = 0;
|
||||
|
||||
pub fn requestStencilCheck() void {
|
||||
// DEBUG23: intentionally disabled. Forcing IDirect3DDevice9::Reset on this
|
||||
// client can hang the render thread while audio/game logic keeps running.
|
||||
need_force_reset = false;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Shader resources
|
||||
// =============================================================================
|
||||
|
||||
var outline_ps: ?*anyopaque = null; // flat-color PS (silhouettes)
|
||||
var outline_ps: ?*anyopaque = null; // flat-color PS (solid silhouettes)
|
||||
var outline_alpha_ps: ?*anyopaque = null; // texture-alpha-aware silhouette PS
|
||||
var outline_rgb_ps: ?*anyopaque = null; // additive/modulated texture coverage PS
|
||||
var material_mask_ps: ?*anyopaque = null; // exact-material scratch -> binary mask
|
||||
var jfa_init_ps: ?*anyopaque = null; // JFA seed init PS
|
||||
var jfa_prop_ps: ?*anyopaque = null; // JFA propagation PS
|
||||
var jfa_decode_ps: ?*anyopaque = null; // JFA decode + composite PS
|
||||
@@ -91,7 +185,9 @@ const D3DXAssembleShaderFn = *const fn (
|
||||
// Render target resources
|
||||
// =============================================================================
|
||||
|
||||
var rt_silhouette_tex: ?*anyopaque = null; // A8R8G8B8 silhouette texture
|
||||
var rt_material_tex: ?*anyopaque = null; // A8R8G8B8 exact-material scratch
|
||||
var rt_material_surf: ?*anyopaque = null;
|
||||
var rt_silhouette_tex: ?*anyopaque = null; // A8R8G8B8 normalized silhouette mask
|
||||
var rt_silhouette_surf: ?*anyopaque = null;
|
||||
var rt_jfa_a_tex: ?*anyopaque = null; // G16R16F JFA ping texture
|
||||
var rt_jfa_a_surf: ?*anyopaque = null;
|
||||
@@ -132,6 +228,18 @@ const CachedDraw = struct {
|
||||
ib: ?*anyopaque = null,
|
||||
vertex_decl: ?*anyopaque = null,
|
||||
vertex_shader: ?*anyopaque = null,
|
||||
tex: [4]?*anyopaque = .{ null, null, null, null },
|
||||
pixel_shader: ?*anyopaque = null,
|
||||
state_block: ?*anyopaque = null,
|
||||
alpha_op: [4]u32 = .{ 1, 1, 1, 1 },
|
||||
alpha_arg1: [4]u32 = .{ 0, 0, 0, 0 },
|
||||
alpha_arg2: [4]u32 = .{ 0, 0, 0, 0 },
|
||||
alpha_test_enable: u32 = 0,
|
||||
alpha_ref: u32 = 0,
|
||||
alpha_func: u32 = types.D3DCMP_ALWAYS,
|
||||
alpha_blend_enable: u32 = 0,
|
||||
src_blend: u32 = types.D3DBLEND_ONE,
|
||||
dst_blend: u32 = types.D3DBLEND_ZERO,
|
||||
// Per-model outline info
|
||||
color: u32 = 0,
|
||||
category: types.ModelCategory = .none,
|
||||
@@ -207,6 +315,12 @@ fn deviceGetViewport(dev: *anyopaque, vp_out: *types.D3DVIEWPORT9) void {
|
||||
_ = f(dev, vp_out);
|
||||
}
|
||||
|
||||
fn deviceSetViewport(dev: *anyopaque, vp_in: *const types.D3DVIEWPORT9) void {
|
||||
const f: *const fn (*anyopaque, *const types.D3DVIEWPORT9) callconv(hook.cc.stdcall) i32 =
|
||||
@ptrFromInt(vt(dev)[types.VT.SetViewport]);
|
||||
_ = f(dev, vp_in);
|
||||
}
|
||||
|
||||
fn deviceSetRenderTarget(dev: *anyopaque, idx: u32, surf: *anyopaque) void {
|
||||
const f: *const fn (*anyopaque, u32, *anyopaque) callconv(hook.cc.stdcall) i32 =
|
||||
@ptrFromInt(vt(dev)[types.VT.SetRenderTarget]);
|
||||
@@ -227,6 +341,38 @@ fn deviceSetTexture(dev: *anyopaque, stage: u32, tex: ?*anyopaque) void {
|
||||
_ = @call(.never_tail, f, .{ dev, stage, tex });
|
||||
}
|
||||
|
||||
fn deviceGetTSS(dev: *anyopaque, stage: u32, state_type: u32) u32 {
|
||||
var val: u32 = 0;
|
||||
const f: *const fn (*anyopaque, u32, u32, *u32) callconv(hook.cc.stdcall) i32 =
|
||||
@ptrFromInt(vt(dev)[types.VT.GetTextureStageState]);
|
||||
_ = f(dev, stage, state_type, &val);
|
||||
return val;
|
||||
}
|
||||
|
||||
fn deviceSetTSS(dev: *anyopaque, stage: u32, state_type: u32, value: u32) void {
|
||||
const f: *const fn (*anyopaque, u32, u32, u32) callconv(hook.cc.stdcall) i32 =
|
||||
@ptrFromInt(vt(dev)[types.VT.SetTextureStageState]);
|
||||
_ = f(dev, stage, state_type, value);
|
||||
}
|
||||
|
||||
fn deviceCreateStateBlock(dev: *anyopaque) ?*anyopaque {
|
||||
var out: ?*anyopaque = null;
|
||||
const f: *const fn (*anyopaque, u32, *?*anyopaque) callconv(hook.cc.stdcall) i32 =
|
||||
@ptrFromInt(vt(dev)[types.VT.CreateStateBlock]);
|
||||
if (f(dev, types.D3DSBT_ALL, &out) < 0) return null;
|
||||
return out;
|
||||
}
|
||||
|
||||
fn stateBlockApply(sb: *anyopaque) bool {
|
||||
// IDirect3DStateBlock9 vtable:
|
||||
// 0 QI, 1 AddRef, 2 Release, 3 GetDevice, 4 Capture, 5 Apply.
|
||||
// DEBUG28/29 accidentally called Capture here, so no captured state was
|
||||
// ever restored. Use the real Apply slot.
|
||||
const f: *const fn (*anyopaque) callconv(hook.cc.stdcall) i32 =
|
||||
@ptrFromInt(vt(sb)[5]);
|
||||
return f(sb) >= 0;
|
||||
}
|
||||
|
||||
fn deviceSetFVF(dev: *anyopaque, fvf: u32) void {
|
||||
const f: *const fn (*anyopaque, u32) callconv(hook.cc.stdcall) i32 =
|
||||
@ptrFromInt(vt(dev)[types.VT.SetFVF]);
|
||||
@@ -302,7 +448,9 @@ fn deviceDrawPrimitiveUP(dev: *anyopaque, prim_type: u32, prim_count: u32, data:
|
||||
fn deviceCreateTexture(dev: *anyopaque, w: u32, h: u32, levels: u32, usage: u32, fmt: u32, pool: u32, out: *?*anyopaque) i32 {
|
||||
const f: *const fn (*anyopaque, u32, u32, u32, u32, u32, u32, *?*anyopaque, u32) callconv(hook.cc.stdcall) i32 =
|
||||
@ptrFromInt(vt(dev)[types.VT.CreateTexture]);
|
||||
return f(dev, w, h, levels, usage, fmt, pool, out, 0);
|
||||
const hr = f(dev, w, h, levels, usage, fmt, pool, out, 0);
|
||||
if (hr < 0) debug_texture_create_hr = hr;
|
||||
return hr;
|
||||
}
|
||||
|
||||
/// Get surface level 0 from a texture. Returns AddRef'd surface or null.
|
||||
@@ -348,53 +496,73 @@ fn ensureResources(device: *anyopaque) void {
|
||||
var vp: types.D3DVIEWPORT9 = .{};
|
||||
deviceGetViewport(device, &vp);
|
||||
if (vp.Width == 0 or vp.Height == 0) return;
|
||||
debug_resource_stage = 1; // viewport valid
|
||||
|
||||
// Check if resources already match current dimensions
|
||||
if (vp.Width == resource_width and vp.Height == resource_height and
|
||||
rt_silhouette_tex != null) return;
|
||||
rt_material_tex != null and rt_silhouette_tex != null) return;
|
||||
|
||||
// Release old and create new
|
||||
releaseResources();
|
||||
resource_width = vp.Width;
|
||||
resource_height = vp.Height;
|
||||
|
||||
// Silhouette RT (A8R8G8B8)
|
||||
// Exact-material scratch RT (A8R8G8B8)
|
||||
if (deviceCreateTexture(device, vp.Width, vp.Height, 1, types.D3DUSAGE_RENDERTARGET, types.D3DFMT_A8R8G8B8, types.D3DPOOL_DEFAULT, &rt_material_tex) < 0) {
|
||||
releaseResources();
|
||||
return;
|
||||
}
|
||||
rt_material_surf = textureGetSurfaceLevel(rt_material_tex.?);
|
||||
if (rt_material_surf == null) {
|
||||
releaseResources();
|
||||
return;
|
||||
}
|
||||
|
||||
// Normalized silhouette RT (A8R8G8B8)
|
||||
if (deviceCreateTexture(device, vp.Width, vp.Height, 1, types.D3DUSAGE_RENDERTARGET, types.D3DFMT_A8R8G8B8, types.D3DPOOL_DEFAULT, &rt_silhouette_tex) < 0) {
|
||||
releaseResources();
|
||||
return;
|
||||
}
|
||||
debug_resource_stage = 2; // silhouette texture ready
|
||||
rt_silhouette_surf = textureGetSurfaceLevel(rt_silhouette_tex.?);
|
||||
if (rt_silhouette_surf == null) {
|
||||
releaseResources();
|
||||
return;
|
||||
}
|
||||
debug_resource_stage = 3; // silhouette surface ready
|
||||
|
||||
// JFA A RT (G16R16F)
|
||||
if (deviceCreateTexture(device, vp.Width, vp.Height, 1, types.D3DUSAGE_RENDERTARGET, types.D3DFMT_G16R16F, types.D3DPOOL_DEFAULT, &rt_jfa_a_tex) < 0) {
|
||||
releaseResources();
|
||||
return;
|
||||
}
|
||||
debug_resource_stage = 4; // JFA A texture ready
|
||||
rt_jfa_a_surf = textureGetSurfaceLevel(rt_jfa_a_tex.?);
|
||||
if (rt_jfa_a_surf == null) {
|
||||
releaseResources();
|
||||
return;
|
||||
}
|
||||
debug_resource_stage = 5; // JFA A surface ready
|
||||
|
||||
// JFA B RT (G16R16F)
|
||||
if (deviceCreateTexture(device, vp.Width, vp.Height, 1, types.D3DUSAGE_RENDERTARGET, types.D3DFMT_G16R16F, types.D3DPOOL_DEFAULT, &rt_jfa_b_tex) < 0) {
|
||||
releaseResources();
|
||||
return;
|
||||
}
|
||||
debug_resource_stage = 6; // JFA B texture ready
|
||||
rt_jfa_b_surf = textureGetSurfaceLevel(rt_jfa_b_tex.?);
|
||||
if (rt_jfa_b_surf == null) {
|
||||
releaseResources();
|
||||
return;
|
||||
}
|
||||
debug_resource_stage = 7; // all RT surfaces ready
|
||||
|
||||
debug_resources_ready_seen = true;
|
||||
}
|
||||
|
||||
fn releaseResources() void {
|
||||
inline for (.{
|
||||
&rt_silhouette_surf, &rt_jfa_a_surf, &rt_jfa_b_surf,
|
||||
&rt_material_surf, &rt_silhouette_surf, &rt_jfa_a_surf, &rt_jfa_b_surf,
|
||||
}) |surf_ptr| {
|
||||
if (surf_ptr.*) |s| {
|
||||
comRelease(s);
|
||||
@@ -402,7 +570,7 @@ fn releaseResources() void {
|
||||
}
|
||||
}
|
||||
inline for (.{
|
||||
&rt_silhouette_tex, &rt_jfa_a_tex, &rt_jfa_b_tex,
|
||||
&rt_material_tex, &rt_silhouette_tex, &rt_jfa_a_tex, &rt_jfa_b_tex,
|
||||
}) |tex_ptr| {
|
||||
if (tex_ptr.*) |t| {
|
||||
comRelease(t);
|
||||
@@ -420,6 +588,46 @@ fn releaseResources() void {
|
||||
/// Flat colour pixel shader - outputs PS constant c0.
|
||||
const ps_flat_src = "ps_3_0\nmov oC0, c0\n";
|
||||
|
||||
/// Texture-alpha-aware silhouette shader.
|
||||
/// c0 = outline colour/encoded width, c1.x = -coverage threshold.
|
||||
const ps_alpha_src =
|
||||
"ps_3_0\n" ++
|
||||
"dcl_2d s0\n" ++
|
||||
"dcl_texcoord0 v0\n" ++
|
||||
"texld r0, v0, s0\n" ++
|
||||
"add r1, r0.aaaa, c1.xxxx\n" ++
|
||||
"texkill r1\n" ++
|
||||
"mov oC0, c0\n";
|
||||
|
||||
/// Coverage shader for additive/modulated M2 layers where transparency can be
|
||||
/// encoded as black RGB rather than useful texture alpha.
|
||||
const ps_rgb_src =
|
||||
"ps_3_0\n" ++
|
||||
"dcl_2d s0\n" ++
|
||||
"dcl_texcoord0 v0\n" ++
|
||||
"texld r0, v0, s0\n" ++
|
||||
"max r1.x, r0.r, r0.g\n" ++
|
||||
"max r1.x, r1.x, r0.b\n" ++
|
||||
"add r1, r1.xxxx, c1.xxxx\n" ++
|
||||
"texkill r1\n" ++
|
||||
"mov oC0, c0\n";
|
||||
|
||||
/// Normalize the exact-material scratch into the uniform outline mask.
|
||||
/// c0 = outline colour (alpha forced to 1), c1.x = -coverage threshold.
|
||||
/// Coverage accepts either alpha or RGB, so opaque dark materials and additive
|
||||
/// effects both survive while untouched transparent pixels remain discarded.
|
||||
const material_mask_src =
|
||||
"ps_3_0\n" ++
|
||||
"dcl_2d s0\n" ++
|
||||
"dcl_texcoord0 v0\n" ++
|
||||
"texld r0, v0, s0\n" ++
|
||||
"max r1.x, r0.r, r0.g\n" ++
|
||||
"max r1.x, r1.x, r0.b\n" ++
|
||||
"max r1.x, r1.x, r0.a\n" ++
|
||||
"add r1, r1.xxxx, c1.xxxx\n" ++
|
||||
"texkill r1\n" ++
|
||||
"mov oC0, c0\n";
|
||||
|
||||
/// JFA init: sample silhouette, output own UV as seed or sentinel (-1,-1).
|
||||
/// Sentinel must be outside [0,1] UV space so it never wins distance comparisons.
|
||||
const jfa_init_src =
|
||||
@@ -447,13 +655,17 @@ const jfa_prop_src =
|
||||
"def c9, 1.0, 1.0, 0.0, 0.0\n" ++
|
||||
"dcl_2d s0\n" ++
|
||||
"dcl_texcoord0 v0\n" ++
|
||||
// D3DX on this client only allows one c# register per arithmetic
|
||||
// instruction. Copy c0.xy (step size) to a temp once, then combine
|
||||
// that temp with c2..c9 in the neighbor MADs.
|
||||
"mov r7.xy, c0.xy\n" ++
|
||||
// Self sample - initialize best seed and distance
|
||||
"texld r0, v0, s0\n" ++
|
||||
"sub r2.xy, v0.xy, r0.xy\n" ++
|
||||
"dp2add r9.x, r2, r2, c1.x\n" ++ // best dist²
|
||||
"mov r8.xy, r0.xy\n" ++ // best seed UV
|
||||
// Neighbor (-1,-1) via c2
|
||||
"mad r4.xy, c2.xy, c0.xy, v0.xy\n" ++
|
||||
"mad r4.xy, c2.xy, r7.xy, v0.xy\n" ++
|
||||
"texld r5, r4, s0\n" ++
|
||||
"sub r2.xy, v0.xy, r5.xy\n" ++
|
||||
"dp2add r2.z, r2, r2, c1.x\n" ++
|
||||
@@ -461,7 +673,7 @@ const jfa_prop_src =
|
||||
"cmp r8.xy, r3.x, r8.xy, r5.xy\n" ++
|
||||
"cmp r9.x, r3.x, r9.x, r2.z\n" ++
|
||||
// Neighbor (-1, 0) via c3
|
||||
"mad r4.xy, c3.xy, c0.xy, v0.xy\n" ++
|
||||
"mad r4.xy, c3.xy, r7.xy, v0.xy\n" ++
|
||||
"texld r5, r4, s0\n" ++
|
||||
"sub r2.xy, v0.xy, r5.xy\n" ++
|
||||
"dp2add r2.z, r2, r2, c1.x\n" ++
|
||||
@@ -469,7 +681,7 @@ const jfa_prop_src =
|
||||
"cmp r8.xy, r3.x, r8.xy, r5.xy\n" ++
|
||||
"cmp r9.x, r3.x, r9.x, r2.z\n" ++
|
||||
// Neighbor (-1, 1) via c4
|
||||
"mad r4.xy, c4.xy, c0.xy, v0.xy\n" ++
|
||||
"mad r4.xy, c4.xy, r7.xy, v0.xy\n" ++
|
||||
"texld r5, r4, s0\n" ++
|
||||
"sub r2.xy, v0.xy, r5.xy\n" ++
|
||||
"dp2add r2.z, r2, r2, c1.x\n" ++
|
||||
@@ -477,7 +689,7 @@ const jfa_prop_src =
|
||||
"cmp r8.xy, r3.x, r8.xy, r5.xy\n" ++
|
||||
"cmp r9.x, r3.x, r9.x, r2.z\n" ++
|
||||
// Neighbor (0, -1) via c5
|
||||
"mad r4.xy, c5.xy, c0.xy, v0.xy\n" ++
|
||||
"mad r4.xy, c5.xy, r7.xy, v0.xy\n" ++
|
||||
"texld r5, r4, s0\n" ++
|
||||
"sub r2.xy, v0.xy, r5.xy\n" ++
|
||||
"dp2add r2.z, r2, r2, c1.x\n" ++
|
||||
@@ -485,7 +697,7 @@ const jfa_prop_src =
|
||||
"cmp r8.xy, r3.x, r8.xy, r5.xy\n" ++
|
||||
"cmp r9.x, r3.x, r9.x, r2.z\n" ++
|
||||
// Neighbor (0, 1) via c6
|
||||
"mad r4.xy, c6.xy, c0.xy, v0.xy\n" ++
|
||||
"mad r4.xy, c6.xy, r7.xy, v0.xy\n" ++
|
||||
"texld r5, r4, s0\n" ++
|
||||
"sub r2.xy, v0.xy, r5.xy\n" ++
|
||||
"dp2add r2.z, r2, r2, c1.x\n" ++
|
||||
@@ -493,7 +705,7 @@ const jfa_prop_src =
|
||||
"cmp r8.xy, r3.x, r8.xy, r5.xy\n" ++
|
||||
"cmp r9.x, r3.x, r9.x, r2.z\n" ++
|
||||
// Neighbor (1, -1) via c7
|
||||
"mad r4.xy, c7.xy, c0.xy, v0.xy\n" ++
|
||||
"mad r4.xy, c7.xy, r7.xy, v0.xy\n" ++
|
||||
"texld r5, r4, s0\n" ++
|
||||
"sub r2.xy, v0.xy, r5.xy\n" ++
|
||||
"dp2add r2.z, r2, r2, c1.x\n" ++
|
||||
@@ -501,7 +713,7 @@ const jfa_prop_src =
|
||||
"cmp r8.xy, r3.x, r8.xy, r5.xy\n" ++
|
||||
"cmp r9.x, r3.x, r9.x, r2.z\n" ++
|
||||
// Neighbor (1, 0) via c8
|
||||
"mad r4.xy, c8.xy, c0.xy, v0.xy\n" ++
|
||||
"mad r4.xy, c8.xy, r7.xy, v0.xy\n" ++
|
||||
"texld r5, r4, s0\n" ++
|
||||
"sub r2.xy, v0.xy, r5.xy\n" ++
|
||||
"dp2add r2.z, r2, r2, c1.x\n" ++
|
||||
@@ -509,7 +721,7 @@ const jfa_prop_src =
|
||||
"cmp r8.xy, r3.x, r8.xy, r5.xy\n" ++
|
||||
"cmp r9.x, r3.x, r9.x, r2.z\n" ++
|
||||
// Neighbor (1, 1) via c9
|
||||
"mad r4.xy, c9.xy, c0.xy, v0.xy\n" ++
|
||||
"mad r4.xy, c9.xy, r7.xy, v0.xy\n" ++
|
||||
"texld r5, r4, s0\n" ++
|
||||
"sub r2.xy, v0.xy, r5.xy\n" ++
|
||||
"dp2add r2.z, r2, r2, c1.x\n" ++
|
||||
@@ -520,33 +732,33 @@ const jfa_prop_src =
|
||||
"mov oC0.xy, r8.xy\n" ++
|
||||
"mov oC0.zw, c1.xx\n";
|
||||
|
||||
/// JFA decode + composite: compute distance to nearest seed, threshold, output outline.
|
||||
/// c0 = (screen_width, screen_height, 4.0, 0.0) set by CPU.
|
||||
/// V32 POLISH: hard 3px outline, no feather.
|
||||
/// c0 = (screen_width, screen_height, radius_px=3, 0).
|
||||
/// The shader squares radius_px and emits a binary 0/1 edge alpha.
|
||||
const jfa_decode_src =
|
||||
"ps_3_0\n" ++
|
||||
"def c1, 0.0, 1.0, -0.002, 0.0\n" ++
|
||||
"dcl_2d s0\n" ++ // JFA result (nearest seed UV)
|
||||
"dcl_2d s1\n" ++ // silhouette (colour + width-encoded alpha)
|
||||
"dcl_2d s0\n" ++
|
||||
"dcl_2d s1\n" ++
|
||||
"dcl_texcoord0 v0\n" ++
|
||||
// Read nearest seed UV
|
||||
// Nearest seed and pixel-space squared distance.
|
||||
"texld r0, v0, s0\n" ++
|
||||
// Pixel-space squared distance
|
||||
"sub r1.xy, v0.xy, r0.xy\n" ++
|
||||
"mul r1.xy, r1.xy, c0.xy\n" ++ // (du*W, dv*H)
|
||||
"dp2add r1.z, r1, r1, c0.w\n" ++ // dist² in pixels
|
||||
// Read seed's silhouette colour + width
|
||||
"mul r1.xy, r1.xy, c0.xy\n" ++
|
||||
"dp2add r1.z, r1, r1, c0.w\n" ++
|
||||
// Seed colour.
|
||||
"texld r2, r0, s1\n" ++
|
||||
"mul r3.x, r2.a, c0.z\n" ++ // outline_width = alpha * 4.0
|
||||
"mul r3.x, r3.x, r3.x\n" ++ // width²
|
||||
// Threshold: inside outline if dist² < width²
|
||||
"sub r3.y, r1.z, r3.x\n" ++ // dist² - width²
|
||||
"cmp r4.w, r3.y, c0.w, c1.y\n" ++ // >= 0 → 0 (outside), < 0 → 1 (inside)
|
||||
// Exclude silhouette interior (don't draw outline ON the model)
|
||||
"texld r5, v0, s1\n" ++ // silhouette at current pixel
|
||||
"add r5.x, r5.a, c1.z\n" ++ // alpha - 0.002
|
||||
"cmp r4.w, r5.x, c0.w, r4.w\n" ++ // if inside silhouette → 0
|
||||
// Output
|
||||
"mov r4.xyz, r2.xyz\n" ++ // outline colour from seed
|
||||
// Hard radius test: dist² < radius² => alpha 1, otherwise 0.
|
||||
"mov r3.x, c0.z\n" ++
|
||||
"mul r3.x, r3.x, r3.x\n" ++
|
||||
"sub r3.y, r1.z, r3.x\n" ++
|
||||
"mov r6.w, c1.y\n" ++
|
||||
"cmp r4.w, r3.y, c0.w, r6.w\n" ++
|
||||
// Do not paint over the model interior.
|
||||
"texld r5, v0, s1\n" ++
|
||||
"add r5.x, r5.a, c1.z\n" ++
|
||||
"cmp r4.w, r5.x, c0.w, r4.w\n" ++
|
||||
"mov r4.xyz, r2.xyz\n" ++
|
||||
"mov oC0, r4\n";
|
||||
|
||||
/// Debug: composite silhouette RT directly. Forces alpha to 1.0 where silhouette
|
||||
@@ -567,33 +779,55 @@ const debug_sil_src =
|
||||
|
||||
fn ensureShaders(device: *anyopaque) void {
|
||||
shaders_attempted = true;
|
||||
debug_shader_stage = 1; // entered
|
||||
|
||||
const d3dx = LoadLibraryA("d3dx9_43.dll") orelse
|
||||
LoadLibraryA("d3dx9_42.dll") orelse
|
||||
LoadLibraryA("d3dx9_41.dll") orelse return;
|
||||
debug_shader_stage = 2; // D3DX loaded
|
||||
|
||||
const assemble_ptr = GetProcAddress(d3dx, "D3DXAssembleShader") orelse return;
|
||||
debug_shader_stage = 3; // assembler found
|
||||
const assemble: D3DXAssembleShaderFn = @ptrCast(assemble_ptr);
|
||||
|
||||
// --- Flat-colour PS (for silhouettes) ---
|
||||
// --- Flat-colour PS (for solid silhouettes) ---
|
||||
outline_ps = assemblePS(device, assemble, ps_flat_src, ps_flat_src.len) orelse return;
|
||||
|
||||
// --- Alpha-aware silhouette PS (for textured cutout/translucent planes) ---
|
||||
outline_alpha_ps = assemblePS(device, assemble, ps_alpha_src, ps_alpha_src.len) orelse {
|
||||
releaseShaders();
|
||||
return;
|
||||
};
|
||||
outline_rgb_ps = assemblePS(device, assemble, ps_rgb_src, ps_rgb_src.len) orelse {
|
||||
releaseShaders();
|
||||
return;
|
||||
};
|
||||
material_mask_ps = assemblePS(device, assemble, material_mask_src, material_mask_src.len) orelse {
|
||||
releaseShaders();
|
||||
return;
|
||||
};
|
||||
debug_shader_stage = 4; // silhouette/material-mask PS variants ready
|
||||
|
||||
// --- JFA Init PS ---
|
||||
jfa_init_ps = assemblePS(device, assemble, jfa_init_src, jfa_init_src.len) orelse {
|
||||
releaseShaders();
|
||||
return;
|
||||
};
|
||||
debug_shader_stage = 5; // JFA init ready
|
||||
|
||||
// --- JFA Propagation PS ---
|
||||
jfa_prop_ps = assemblePS(device, assemble, jfa_prop_src, jfa_prop_src.len) orelse {
|
||||
releaseShaders();
|
||||
return;
|
||||
};
|
||||
debug_shader_stage = 6; // JFA propagation ready
|
||||
|
||||
// --- JFA Decode + Composite PS ---
|
||||
jfa_decode_ps = assemblePS(device, assemble, jfa_decode_src, jfa_decode_src.len) orelse {
|
||||
releaseShaders();
|
||||
return;
|
||||
};
|
||||
debug_shader_stage = 7; // JFA decode ready
|
||||
|
||||
// --- Debug silhouette composite PS (only when diagnostic enabled) ---
|
||||
if (DEBUG_SHOW_SILHOUETTE) {
|
||||
@@ -602,14 +836,42 @@ fn ensureShaders(device: *anyopaque) void {
|
||||
return;
|
||||
};
|
||||
}
|
||||
|
||||
debug_shader_stage = 8; // complete
|
||||
debug_shaders_ready_seen = true;
|
||||
}
|
||||
|
||||
/// Assemble a pixel shader from source text, create device PS object.
|
||||
fn captureD3DXError(buf: *anyopaque) void {
|
||||
@memset(&debug_shader_error_text, 0);
|
||||
|
||||
const get_ptr: *const fn (*anyopaque) callconv(hook.cc.stdcall) usize =
|
||||
@ptrFromInt(vt(buf)[3]);
|
||||
const get_size: *const fn (*anyopaque) callconv(hook.cc.stdcall) usize =
|
||||
@ptrFromInt(vt(buf)[4]);
|
||||
|
||||
const ptr_val = get_ptr(buf);
|
||||
const size = get_size(buf);
|
||||
if (ptr_val == 0 or size == 0) return;
|
||||
|
||||
const src_ptr: [*]const u8 = @ptrFromInt(ptr_val);
|
||||
const n = @min(size, debug_shader_error_text.len - 1);
|
||||
@memcpy(debug_shader_error_text[0..n], src_ptr[0..n]);
|
||||
|
||||
// Make sure the chat string ends cleanly even if the D3DX buffer does not.
|
||||
debug_shader_error_text[n] = 0;
|
||||
}
|
||||
|
||||
fn assemblePS(device: *anyopaque, assemble: D3DXAssembleShaderFn, src: [*]const u8, len: usize) ?*anyopaque {
|
||||
var code: ?*anyopaque = null;
|
||||
var err_buf: ?*anyopaque = null;
|
||||
if (assemble(src, @intCast(len), null, null, 0, &code, &err_buf) < 0 or code == null) {
|
||||
if (err_buf) |e| comRelease(e);
|
||||
const assemble_hr = assemble(src, @intCast(len), null, null, 0, &code, &err_buf);
|
||||
if (assemble_hr < 0 or code == null) {
|
||||
debug_shader_assemble_hr = assemble_hr;
|
||||
if (err_buf) |e| {
|
||||
captureD3DXError(e);
|
||||
comRelease(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
defer comRelease(code.?);
|
||||
@@ -622,12 +884,16 @@ fn assemblePS(device: *anyopaque, assemble: D3DXAssembleShaderFn, src: [*]const
|
||||
var ps_out: ?*anyopaque = null;
|
||||
const create: *const fn (*anyopaque, *anyopaque, *?*anyopaque) callconv(hook.cc.stdcall) i32 =
|
||||
@ptrFromInt(vt(device)[types.VT.CreatePixelShader]);
|
||||
if (create(device, buf_ptr, &ps_out) < 0) return null;
|
||||
const create_hr = create(device, buf_ptr, &ps_out);
|
||||
if (create_hr < 0) {
|
||||
debug_shader_create_hr = create_hr;
|
||||
return null;
|
||||
}
|
||||
return ps_out;
|
||||
}
|
||||
|
||||
fn releaseShaders() void {
|
||||
inline for (.{ &outline_ps, &jfa_init_ps, &jfa_prop_ps, &jfa_decode_ps, &debug_sil_ps }) |ps| {
|
||||
inline for (.{ &outline_ps, &outline_alpha_ps, &outline_rgb_ps, &material_mask_ps, &jfa_init_ps, &jfa_prop_ps, &jfa_decode_ps, &debug_sil_ps }) |ps| {
|
||||
if (ps.*) |p| {
|
||||
comRelease(p);
|
||||
ps.* = null;
|
||||
@@ -657,11 +923,10 @@ fn buildFullscreenQuad(w: u32, h: u32) [4]QuadVertex {
|
||||
// =============================================================================
|
||||
|
||||
fn hkEndScene(device: *anyopaque) callconv(hook.cc.stdcall) i32 {
|
||||
// One-time: check if depth/stencil surface has stencil bits.
|
||||
if (need_force_reset) {
|
||||
need_force_reset = false;
|
||||
forceD24S8IfNeeded(device);
|
||||
}
|
||||
debug_endscene_seen = true;
|
||||
|
||||
// DEBUG23: no forced D3D9 Reset. The client can hang the render thread
|
||||
// during Reset, so Outline runs without a stencil dependency.
|
||||
|
||||
// Per-frame: scan objects for outline tracking
|
||||
tracker.scanObjects();
|
||||
@@ -732,11 +997,14 @@ fn hkDIP(
|
||||
start_idx: u32,
|
||||
prim_count: u32,
|
||||
) callconv(hook.cc.stdcall) i32 {
|
||||
debug_dip_seen = true;
|
||||
|
||||
const OrigDIP = *const fn (*anyopaque, u32, i32, u32, u32, u32, u32) callconv(hook.cc.stdcall) i32;
|
||||
const origFn: OrigDIP = @ptrFromInt(orig_dip);
|
||||
|
||||
// ---- Cache outline draws for EndScene replay ----
|
||||
if (model_hook.rendering_outline) {
|
||||
debug_outline_dip_seen = true;
|
||||
const model_ptr = model_hook.current_model;
|
||||
const color = tracker.getModelColor(model_ptr) orelse
|
||||
return origFn(device, prim_type, base_vtx, min_vtx, num_verts, start_idx, prim_count);
|
||||
@@ -774,52 +1042,33 @@ fn hkDIP(
|
||||
// GetVertexDeclaration AddRef's
|
||||
draw.vertex_shader = deviceGetPtr(device, types.VT.GetVertexShader);
|
||||
// GetVertexShader AddRef's
|
||||
for (0..4) |stage| {
|
||||
draw.tex[stage] = deviceGetTexture(device, @intCast(stage));
|
||||
draw.alpha_op[stage] = deviceGetTSS(device, @intCast(stage), types.D3DTSS.ALPHAOP);
|
||||
draw.alpha_arg1[stage] = deviceGetTSS(device, @intCast(stage), types.D3DTSS.ALPHAARG1);
|
||||
draw.alpha_arg2[stage] = deviceGetTSS(device, @intCast(stage), types.D3DTSS.ALPHAARG2);
|
||||
}
|
||||
draw.pixel_shader = deviceGetPtr(device, types.VT.GetPixelShader);
|
||||
draw.state_block = deviceCreateStateBlock(device);
|
||||
if (draw.state_block != null) debug_state_block_seen = true;
|
||||
draw.alpha_test_enable = deviceGetRS(device, types.D3DRS.ALPHATESTENABLE);
|
||||
draw.alpha_ref = deviceGetRS(device, types.D3DRS.ALPHAREF);
|
||||
draw.alpha_func = deviceGetRS(device, types.D3DRS.ALPHAFUNC);
|
||||
draw.alpha_blend_enable = deviceGetRS(device, types.D3DRS.ALPHABLENDENABLE);
|
||||
draw.src_blend = deviceGetRS(device, types.D3DRS.SRCBLEND);
|
||||
draw.dst_blend = deviceGetRS(device, types.D3DRS.DESTBLEND);
|
||||
|
||||
// Capture VS constants (bone matrices, world/view/proj transforms)
|
||||
deviceGetVSConstF(device, 0, &draw.vs_consts, MAX_VS_CONST_REGS);
|
||||
|
||||
cached_draw_count = idx + 1;
|
||||
frame_has_outlines = true;
|
||||
debug_cached_draw_seen = true;
|
||||
}
|
||||
|
||||
// Mark visible pixels in stencil for this outline target.
|
||||
// At this point (outline targets draw last due to batch reordering),
|
||||
// the game's DS has terrain+WMO+all non-outline M2 model depth.
|
||||
// Pixels that pass the depth test get stencil=1; pixels behind any
|
||||
// scene geometry fail and keep stencil=0.
|
||||
// EndScene uses these marks to gate silhouette rendering.
|
||||
const s_enable = deviceGetRS(device, types.D3DRS.STENCILENABLE);
|
||||
const s_func = deviceGetRS(device, types.D3DRS.STENCILFUNC);
|
||||
const s_ref = deviceGetRS(device, types.D3DRS.STENCILREF);
|
||||
// (STENCILWRITEMASK not saved - intentionally set to 0 on restore)
|
||||
const s_pass = deviceGetRS(device, types.D3DRS.STENCILPASS);
|
||||
const s_fail = deviceGetRS(device, types.D3DRS.STENCILFAIL);
|
||||
const s_zfail = deviceGetRS(device, types.D3DRS.STENCILZFAIL);
|
||||
|
||||
deviceSetRS(device, types.D3DRS.STENCILENABLE, 1);
|
||||
deviceSetRS(device, types.D3DRS.STENCILFUNC, types.D3DCMP_ALWAYS);
|
||||
deviceSetRS(device, types.D3DRS.STENCILREF, 1);
|
||||
deviceSetRS(device, types.D3DRS.STENCILWRITEMASK, 0xFF);
|
||||
deviceSetRS(device, types.D3DRS.STENCILPASS, types.D3DSTENCILOP_REPLACE);
|
||||
deviceSetRS(device, types.D3DRS.STENCILFAIL, types.D3DSTENCILOP_KEEP);
|
||||
deviceSetRS(device, types.D3DRS.STENCILZFAIL, types.D3DSTENCILOP_KEEP);
|
||||
|
||||
const result = origFn(device, prim_type, base_vtx, min_vtx, num_verts, start_idx, prim_count);
|
||||
|
||||
// Restore stencil state to match WoW's GxDevice cache, but lock
|
||||
// stencil writes to protect our marks from subsequent draws (other
|
||||
// players' gear, NPCs, etc. that render after outline targets).
|
||||
deviceSetRS(device, types.D3DRS.STENCILENABLE, s_enable);
|
||||
deviceSetRS(device, types.D3DRS.STENCILFUNC, s_func);
|
||||
deviceSetRS(device, types.D3DRS.STENCILREF, s_ref);
|
||||
deviceSetRS(device, types.D3DRS.STENCILPASS, s_pass);
|
||||
deviceSetRS(device, types.D3DRS.STENCILFAIL, s_fail);
|
||||
deviceSetRS(device, types.D3DRS.STENCILZFAIL, s_zfail);
|
||||
// Write mask 0 instead of restoring original - prevents any
|
||||
// subsequent DIP from overwriting our stencil=1 marks.
|
||||
// Restored properly in EndScene before the JFA pipeline.
|
||||
deviceSetRS(device, types.D3DRS.STENCILWRITEMASK, 0);
|
||||
return result;
|
||||
// DEBUG23: no stencil writes. Preserve WoW's D3D state and draw once.
|
||||
// The cached geometry is replayed later into the silhouette RT.
|
||||
return origFn(device, prim_type, base_vtx, min_vtx, num_verts, start_idx, prim_count);
|
||||
}
|
||||
|
||||
// ---- Normal path ----
|
||||
@@ -846,6 +1095,20 @@ fn clearCachedDraws() void {
|
||||
comRelease(obj);
|
||||
draw.vertex_shader = null;
|
||||
}
|
||||
if (draw.pixel_shader) |obj| {
|
||||
comRelease(obj);
|
||||
draw.pixel_shader = null;
|
||||
}
|
||||
if (draw.state_block) |obj| {
|
||||
comRelease(obj);
|
||||
draw.state_block = null;
|
||||
}
|
||||
for (0..4) |stage| {
|
||||
if (draw.tex[stage]) |obj| {
|
||||
comRelease(obj);
|
||||
draw.tex[stage] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
cached_draw_count = 0;
|
||||
}
|
||||
@@ -855,11 +1118,25 @@ fn clearCachedDraws() void {
|
||||
// =============================================================================
|
||||
|
||||
fn runJfaPipeline(device: *anyopaque) void {
|
||||
debug_pipeline_entered_seen = true;
|
||||
|
||||
// DEBUG29: capture the complete WoW D3D state before any replay/JFA work.
|
||||
// The manual save/restore below remains as fallback, but this state block
|
||||
// restores states that are easy to miss (extra samplers/TSS, scissor,
|
||||
// shaders/constants, streams, declarations, etc.).
|
||||
const outer_state = deviceCreateStateBlock(device);
|
||||
defer if (outer_state) |sb| {
|
||||
if (stateBlockApply(sb)) debug_outer_state_restore_seen = true;
|
||||
comRelease(sb);
|
||||
};
|
||||
|
||||
// Verify all resources and shaders
|
||||
if (rt_silhouette_tex == null or rt_jfa_a_surf == null or rt_jfa_b_surf == null) return;
|
||||
if (rt_material_tex == null or rt_material_surf == null or rt_silhouette_tex == null or rt_jfa_a_surf == null or rt_jfa_b_surf == null) return;
|
||||
if (!shaders_attempted) ensureShaders(device);
|
||||
if (jfa_init_ps == null or jfa_prop_ps == null or jfa_decode_ps == null) return;
|
||||
if (outline_ps == null or rt_silhouette_surf == null) return;
|
||||
if (outline_ps == null or material_mask_ps == null or rt_silhouette_surf == null) return;
|
||||
|
||||
debug_pipeline_ready_seen = true;
|
||||
|
||||
var vp: types.D3DVIEWPORT9 = .{};
|
||||
deviceGetViewport(device, &vp);
|
||||
@@ -897,8 +1174,29 @@ fn runJfaPipeline(device: *anyopaque) void {
|
||||
const saved_dstblend = deviceGetRS(device, types.D3DRS.DESTBLEND);
|
||||
const saved_cull = deviceGetRS(device, types.D3DRS.CULLMODE);
|
||||
const saved_atest = deviceGetRS(device, types.D3DRS.ALPHATESTENABLE);
|
||||
const saved_aref = deviceGetRS(device, types.D3DRS.ALPHAREF);
|
||||
const saved_afunc = deviceGetRS(device, types.D3DRS.ALPHAFUNC);
|
||||
const saved_tfactor = deviceGetRS(device, types.D3DRS.TEXTUREFACTOR);
|
||||
const saved_cwrite = deviceGetRS(device, types.D3DRS.COLORWRITEENABLE);
|
||||
|
||||
const SavedTSS = struct {
|
||||
colorop: u32,
|
||||
colorarg1: u32,
|
||||
alphaop: u32,
|
||||
alphaarg1: u32,
|
||||
alphaarg2: u32,
|
||||
};
|
||||
var saved_tss: [4]SavedTSS = undefined;
|
||||
for (0..4) |stage| {
|
||||
saved_tss[stage] = .{
|
||||
.colorop = deviceGetTSS(device, @intCast(stage), types.D3DTSS.COLOROP),
|
||||
.colorarg1 = deviceGetTSS(device, @intCast(stage), types.D3DTSS.COLORARG1),
|
||||
.alphaop = deviceGetTSS(device, @intCast(stage), types.D3DTSS.ALPHAOP),
|
||||
.alphaarg1 = deviceGetTSS(device, @intCast(stage), types.D3DTSS.ALPHAARG1),
|
||||
.alphaarg2 = deviceGetTSS(device, @intCast(stage), types.D3DTSS.ALPHAARG2),
|
||||
};
|
||||
}
|
||||
|
||||
// Stencil states (Phase 1 reads stencil marks written by DIP hook)
|
||||
const saved_stencil_enable = deviceGetRS(device, types.D3DRS.STENCILENABLE);
|
||||
const saved_stencil_func = deviceGetRS(device, types.D3DRS.STENCILFUNC);
|
||||
@@ -936,57 +1234,83 @@ fn runJfaPipeline(device: *anyopaque) void {
|
||||
if (cached_draw_count > 0) {
|
||||
const origFn: *const fn (*anyopaque, u32, i32, u32, u32, u32, u32) callconv(hook.cc.stdcall) i32 =
|
||||
@ptrFromInt(orig_dip);
|
||||
const quad = buildFullscreenQuad(vp.Width, vp.Height);
|
||||
|
||||
// Accumulate normalized per-draw coverage into the silhouette mask.
|
||||
deviceSetRenderTarget(device, 0, rt_silhouette_surf.?);
|
||||
clearRenderTarget(device, 0x00000000);
|
||||
|
||||
// Keep game's DS bound - it has stencil marks from DIP hook where
|
||||
// outline targets passed the terrain depth test (stencil=1 = visible).
|
||||
// Don't write depth or stencil during replay.
|
||||
deviceSetRS(device, types.D3DRS.ZWRITEENABLE, 0);
|
||||
deviceSetRS(device, types.D3DRS.ZENABLE, types.D3DZB_FALSE);
|
||||
deviceSetRS(device, types.D3DRS.STENCILWRITEMASK, 0);
|
||||
|
||||
deviceSetPtr(device, types.VT.SetPixelShader, outline_ps.?);
|
||||
deviceSetRS(device, types.D3DRS.ALPHABLENDENABLE, 0);
|
||||
deviceSetRS(device, types.D3DRS.COLORWRITEENABLE, 0x0F);
|
||||
|
||||
for (0..cached_draw_count) |i| {
|
||||
const draw = &cached_draws[i];
|
||||
|
||||
// V31 POLISH: don't let additive/emissive passes (spell glows,
|
||||
// bloom-like model layers) expand the selection silhouette.
|
||||
// Normal alpha-blended materials remain included.
|
||||
if (draw.alpha_blend_enable != 0 and draw.dst_blend == types.D3DBLEND_ONE) {
|
||||
debug_additive_skipped_seen = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 1) Replay this draw with WoW's exact captured D3D state into a
|
||||
// transparent scratch RT. A D3DSBT_ALL state block restores pixel
|
||||
// shader, PS constants, textures, samplers, texture stages, blend,
|
||||
// alpha-test, vertex state and stream bindings.
|
||||
if (draw.state_block) |sb| {
|
||||
_ = stateBlockApply(sb);
|
||||
}
|
||||
|
||||
// Never rely on D3DSBT_ALL to restore geometry bindings correctly
|
||||
// across drivers/wrappers. Rebind the cached draw explicitly.
|
||||
deviceSetStreamSource(device, 0, draw.vb, draw.vb_offset, draw.vb_stride);
|
||||
deviceSetIndices(device, draw.ib);
|
||||
deviceSetPtrOrNull(device, types.VT.SetVertexDeclaration, draw.vertex_decl);
|
||||
deviceSetPtrOrNull(device, types.VT.SetVertexShader, draw.vertex_shader);
|
||||
deviceSetVSConstF(device, 0, &draw.vs_consts, MAX_VS_CONST_REGS);
|
||||
|
||||
var color_f4 = argbToFloat4(draw.color);
|
||||
color_f4[3] = tracker.getOutlinePixels(draw.category) / 4.0;
|
||||
deviceSetPSConstF(device, 0, &color_f4);
|
||||
|
||||
// Per-category stencil logic:
|
||||
// - dead_player: no stencil test (visible through walls for corpse finding)
|
||||
// - target/raid_marked: stencil test gates on terrain visibility
|
||||
if (draw.category == .dead_player) {
|
||||
deviceSetRS(device, types.D3DRS.STENCILENABLE, 0);
|
||||
} else {
|
||||
deviceSetRS(device, types.D3DRS.STENCILENABLE, 1);
|
||||
deviceSetRS(device, types.D3DRS.STENCILFUNC, types.D3DCMP_EQUAL);
|
||||
deviceSetRS(device, types.D3DRS.STENCILREF, 1);
|
||||
deviceSetRS(device, types.D3DRS.STENCILMASK, 0xFF);
|
||||
deviceSetRS(device, types.D3DRS.STENCILPASS, types.D3DSTENCILOP_KEEP);
|
||||
}
|
||||
deviceSetRenderTarget(device, 0, rt_material_surf.?);
|
||||
deviceSetViewport(device, &vp);
|
||||
clearRenderTarget(device, 0x00000000);
|
||||
deviceSetRS(device, types.D3DRS.ZENABLE, types.D3DZB_FALSE);
|
||||
deviceSetRS(device, types.D3DRS.ZWRITEENABLE, 0);
|
||||
deviceSetRS(device, types.D3DRS.STENCILENABLE, 0);
|
||||
deviceSetRS(device, types.D3DRS.COLORWRITEENABLE, 0x0F);
|
||||
|
||||
_ = origFn(device, draw.prim_type, draw.base_vtx, draw.min_vtx, draw.num_verts, draw.start_idx, draw.prim_count);
|
||||
|
||||
// 2) Convert only pixels actually produced by that exact material
|
||||
// into the uniform outline mask, preserving the draw/category colour.
|
||||
deviceSetRenderTarget(device, 0, rt_silhouette_surf.?);
|
||||
deviceSetViewport(device, &vp);
|
||||
deviceSetPtrOrNull(device, types.VT.SetDepthStencilSurface, null);
|
||||
deviceSetPtrOrNull(device, types.VT.SetVertexShader, null);
|
||||
deviceSetFVF(device, types.D3DFVF_XYZRHW | types.D3DFVF_TEX1);
|
||||
deviceSetRS(device, types.D3DRS.ZENABLE, types.D3DZB_FALSE);
|
||||
deviceSetRS(device, types.D3DRS.ZWRITEENABLE, 0);
|
||||
deviceSetRS(device, types.D3DRS.STENCILENABLE, 0);
|
||||
deviceSetRS(device, types.D3DRS.ALPHATESTENABLE, 0);
|
||||
deviceSetRS(device, types.D3DRS.CULLMODE, types.D3DCULL_NONE);
|
||||
deviceSetRS(device, types.D3DRS.COLORWRITEENABLE, 0x0F);
|
||||
deviceSetRS(device, types.D3DRS.ALPHABLENDENABLE, 1);
|
||||
deviceSetRS(device, types.D3DRS.SRCBLEND, types.D3DBLEND_SRCALPHA);
|
||||
deviceSetRS(device, types.D3DRS.DESTBLEND, types.D3DBLEND_INVSRCALPHA);
|
||||
deviceSetSamplerState(device, 0, types.D3DSAMP.ADDRESSU, types.D3DTADDRESS_CLAMP);
|
||||
deviceSetSamplerState(device, 0, types.D3DSAMP.ADDRESSV, types.D3DTADDRESS_CLAMP);
|
||||
deviceSetSamplerState(device, 0, types.D3DSAMP.MAGFILTER, types.D3DTEXF_POINT);
|
||||
deviceSetSamplerState(device, 0, types.D3DSAMP.MINFILTER, types.D3DTEXF_POINT);
|
||||
deviceSetSamplerState(device, 0, types.D3DSAMP.MIPFILTER, types.D3DTEXF_NONE);
|
||||
deviceSetTexture(device, 0, rt_material_tex);
|
||||
deviceSetPtr(device, types.VT.SetPixelShader, material_mask_ps.?);
|
||||
|
||||
var mask_color = argbToFloat4(draw.color);
|
||||
mask_color[3] = 1.0;
|
||||
deviceSetPSConstF(device, 0, &mask_color);
|
||||
// Trim very faint scratch pixels that otherwise become tiny hooks/noise.
|
||||
const threshold: [4]f32 = .{ -0.03, 0.0, 0.0, 0.0 };
|
||||
deviceSetPSConstF(device, 1, &threshold);
|
||||
deviceDrawPrimitiveUP(device, types.D3DPT_TRIANGLESTRIP, 2, @ptrCast(&quad), @sizeOf(QuadVertex));
|
||||
}
|
||||
|
||||
clearCachedDraws();
|
||||
|
||||
// Clear stencil marks to avoid affecting next frame's rendering
|
||||
deviceSetRS(device, types.D3DRS.STENCILENABLE, 0);
|
||||
const clearFn: *const fn (*anyopaque, u32, ?*anyopaque, u32, u32, f32, u32) callconv(hook.cc.stdcall) i32 =
|
||||
@ptrFromInt(vt(device)[types.VT.Clear]);
|
||||
_ = clearFn(device, 0, null, types.D3DCLEAR_STENCIL, 0, 1.0, 0);
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
@@ -1093,7 +1417,8 @@ fn runJfaPipeline(device: *anyopaque) void {
|
||||
if (saved_rt0) |rt| deviceSetRenderTarget(device, 0, rt);
|
||||
deviceSetTexture(device, 0, rt_jfa_a_tex);
|
||||
deviceSetTexture(device, 1, rt_silhouette_tex);
|
||||
c0 = [4]f32{ fw, fh, 4.0, 0.0 };
|
||||
// V32 POLISH: hard 3px edge, no feather.
|
||||
c0 = [4]f32{ fw, fh, 3.0, 0.0 };
|
||||
deviceSetPSConstF(device, 0, &c0);
|
||||
deviceSetPtr(device, types.VT.SetPixelShader, jfa_decode_ps.?);
|
||||
deviceSetRS(device, types.D3DRS.ALPHABLENDENABLE, 1);
|
||||
@@ -1115,7 +1440,17 @@ fn runJfaPipeline(device: *anyopaque) void {
|
||||
deviceSetRS(device, types.D3DRS.DESTBLEND, saved_dstblend);
|
||||
deviceSetRS(device, types.D3DRS.CULLMODE, saved_cull);
|
||||
deviceSetRS(device, types.D3DRS.ALPHATESTENABLE, saved_atest);
|
||||
deviceSetRS(device, types.D3DRS.ALPHAREF, saved_aref);
|
||||
deviceSetRS(device, types.D3DRS.ALPHAFUNC, saved_afunc);
|
||||
deviceSetRS(device, types.D3DRS.TEXTUREFACTOR, saved_tfactor);
|
||||
deviceSetRS(device, types.D3DRS.COLORWRITEENABLE, saved_cwrite);
|
||||
for (0..4) |stage| {
|
||||
deviceSetTSS(device, @intCast(stage), types.D3DTSS.COLOROP, saved_tss[stage].colorop);
|
||||
deviceSetTSS(device, @intCast(stage), types.D3DTSS.COLORARG1, saved_tss[stage].colorarg1);
|
||||
deviceSetTSS(device, @intCast(stage), types.D3DTSS.ALPHAOP, saved_tss[stage].alphaop);
|
||||
deviceSetTSS(device, @intCast(stage), types.D3DTSS.ALPHAARG1, saved_tss[stage].alphaarg1);
|
||||
deviceSetTSS(device, @intCast(stage), types.D3DTSS.ALPHAARG2, saved_tss[stage].alphaarg2);
|
||||
}
|
||||
|
||||
// Stencil states
|
||||
deviceSetRS(device, types.D3DRS.STENCILENABLE, saved_stencil_enable);
|
||||
@@ -1174,42 +1509,65 @@ fn hasStencilBits(fmt: u32) bool {
|
||||
fmt == types.D3DFMT_D24X4S4 or fmt == types.D3DFMT_D15S1;
|
||||
}
|
||||
|
||||
fn forceD24S8IfNeeded(device: *anyopaque) void {
|
||||
fn queryStencilFormat(device: *anyopaque) u32 {
|
||||
var pDS: ?*anyopaque = null;
|
||||
const getDS: *const fn (*anyopaque, *?*anyopaque) callconv(hook.cc.stdcall) i32 =
|
||||
@ptrFromInt(vt(device)[types.VT.GetDepthStencilSurface]);
|
||||
if (getDS(device, &pDS) < 0) return;
|
||||
const ds = pDS orelse return;
|
||||
defer comRelease(ds);
|
||||
if (getDS(device, &pDS) < 0) return 0;
|
||||
const ds = pDS orelse return 0;
|
||||
|
||||
var desc: types.D3DSURFACE_DESC = .{};
|
||||
const getDesc: *const fn (*anyopaque, *types.D3DSURFACE_DESC) callconv(hook.cc.stdcall) i32 =
|
||||
@ptrFromInt(vt(ds)[12]);
|
||||
if (getDesc(ds, &desc) < 0) return;
|
||||
const hr = getDesc(ds, &desc);
|
||||
comRelease(ds);
|
||||
if (hr < 0) return 0;
|
||||
return desc.Format;
|
||||
}
|
||||
|
||||
if (hasStencilBits(desc.Format)) return;
|
||||
fn forceD24S8IfNeeded(device: *anyopaque) bool {
|
||||
const current_fmt = queryStencilFormat(device);
|
||||
debug_stencil_format = current_fmt;
|
||||
if (hasStencilBits(current_fmt)) {
|
||||
debug_stencil_ready = true;
|
||||
debug_stencil_reset_hr = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
var pSwap: ?*anyopaque = null;
|
||||
const getSC: *const fn (*anyopaque, u32, *?*anyopaque) callconv(hook.cc.stdcall) i32 =
|
||||
@ptrFromInt(vt(device)[types.VT.GetSwapChain]);
|
||||
if (getSC(device, 0, &pSwap) < 0) return;
|
||||
const swap = pSwap orelse return;
|
||||
defer comRelease(swap);
|
||||
if (getSC(device, 0, &pSwap) < 0) return false;
|
||||
const swap = pSwap orelse return false;
|
||||
|
||||
var pp: types.D3DPRESENT_PARAMETERS = .{};
|
||||
const getPP: *const fn (*anyopaque, *types.D3DPRESENT_PARAMETERS) callconv(hook.cc.stdcall) i32 =
|
||||
@ptrFromInt(vt(swap)[9]);
|
||||
if (getPP(swap, &pp) < 0) return;
|
||||
const pp_hr = getPP(swap, &pp);
|
||||
// IMPORTANT: do not hold a swap-chain COM reference across Reset.
|
||||
comRelease(swap);
|
||||
if (pp_hr < 0) return false;
|
||||
|
||||
pp.AutoDepthStencilFormat = types.D3DFMT_D24S8;
|
||||
pp.EnableAutoDepthStencil = 1;
|
||||
|
||||
clearCachedDraws();
|
||||
releaseShaders();
|
||||
releaseResources();
|
||||
|
||||
const resetFn: *const fn (*anyopaque, *types.D3DPRESENT_PARAMETERS) callconv(hook.cc.stdcall) i32 =
|
||||
@ptrFromInt(vt(device)[types.VT.Reset]);
|
||||
_ = resetFn(device, &pp);
|
||||
const reset_hr = resetFn(device, &pp);
|
||||
debug_stencil_reset_hr = reset_hr;
|
||||
if (reset_hr < 0) {
|
||||
debug_stencil_ready = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
const new_fmt = queryStencilFormat(device);
|
||||
debug_stencil_format = new_fmt;
|
||||
debug_stencil_ready = hasStencilBits(new_fmt);
|
||||
return true;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
+117
-18
@@ -4,11 +4,12 @@
|
||||
//! and a Lua C callback for `/wu outline` commands.
|
||||
|
||||
const std = @import("std");
|
||||
const hook = @import("zhook");
|
||||
const lua = @import("../lua.zig");
|
||||
const logging = @import("../logging.zig");
|
||||
const tracker = @import("tracker.zig");
|
||||
const model_hook = @import("model_hook.zig");
|
||||
const d3d9_hook = @import("d3d9_hook.zig");
|
||||
const wow = @import("../wow.zig");
|
||||
|
||||
const WINAPI = std.builtin.CallingConvention.winapi;
|
||||
const mod_mutex = @import("../mutex.zig");
|
||||
@@ -18,6 +19,63 @@ var log: logging.Logger = .{};
|
||||
|
||||
var g_mutex: ?*anyopaque = null;
|
||||
var g_is_hook_owner: bool = false;
|
||||
var g_model_hooks_installed: bool = false;
|
||||
|
||||
|
||||
noinline fn luaGetTopNative(L: lua.State) i32 {
|
||||
var result: i32 = undefined;
|
||||
asm volatile (
|
||||
\\mov $0x6F3070, %%eax
|
||||
\\call *%%eax
|
||||
: [ret] "={eax}" (result),
|
||||
: [state] "{ecx}" (@intFromPtr(L)),
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
noinline fn luaIsStringNative(L: lua.State, index: i32) bool {
|
||||
var result: u32 = undefined;
|
||||
asm volatile (
|
||||
\\mov $0x6F3510, %%eax
|
||||
\\call *%%eax
|
||||
: [ret] "={eax}" (result),
|
||||
: [state] "{ecx}" (@intFromPtr(L)),
|
||||
[index] "{edx}" (index),
|
||||
);
|
||||
return result != 0;
|
||||
}
|
||||
|
||||
noinline fn luaToStringNative(L: lua.State, index: i32) ?[*:0]const u8 {
|
||||
var result: usize = undefined;
|
||||
asm volatile (
|
||||
\\mov $0x6F3690, %%eax
|
||||
\\call *%%eax
|
||||
: [ret] "={eax}" (result),
|
||||
: [state] "{ecx}" (@intFromPtr(L)),
|
||||
[index] "{edx}" (index),
|
||||
);
|
||||
return if (result == 0) null else @ptrFromInt(result);
|
||||
}
|
||||
|
||||
noinline fn luaPushBooleanNative(L: lua.State, value: i32) void {
|
||||
asm volatile (
|
||||
\\mov $0x6F39F0, %%eax
|
||||
\\call *%%eax
|
||||
:
|
||||
: [state] "{ecx}" (@intFromPtr(L)),
|
||||
[value] "{edx}" (value),
|
||||
);
|
||||
}
|
||||
|
||||
noinline fn luaPushStringNative(L: lua.State, value: [*:0]const u8) void {
|
||||
asm volatile (
|
||||
\\mov $0x6F3890, %%eax
|
||||
\\call *%%eax
|
||||
:
|
||||
: [state] "{ecx}" (@intFromPtr(L)),
|
||||
[value] "{edx}" (@intFromPtr(value)),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn isActive() bool {
|
||||
return g_is_hook_owner;
|
||||
@@ -37,6 +95,7 @@ pub fn init() bool {
|
||||
log = logging.Logger.open(module_name, .console);
|
||||
tracker.initLogger();
|
||||
if (!model_hook.installHooks()) return false;
|
||||
g_model_hooks_installed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -55,6 +114,7 @@ pub fn cleanup() void {
|
||||
log.close();
|
||||
mod_mutex.release(&g_mutex);
|
||||
}
|
||||
g_model_hooks_installed = false;
|
||||
g_is_hook_owner = false;
|
||||
}
|
||||
|
||||
@@ -74,28 +134,67 @@ pub fn isEnabled() bool {
|
||||
/// OutlineCommand() → returns (enabled: bool)
|
||||
/// OutlineCommand("on") → enable outlines
|
||||
/// OutlineCommand("off") → disable outlines
|
||||
pub fn outlineCommand(L: *anyopaque) callconv(.c) u32 {
|
||||
const nargs = hook.call(fn (usize) callconv(hook.cc.fastcall) i32, 0x6F3070, .{@intFromPtr(L)}); // lua_gettop
|
||||
pub fn outlineCommand(L: lua.State) callconv(.{ .x86_thiscall = .{} }) u32 {
|
||||
const nargs = luaGetTopNative(L);
|
||||
|
||||
if (nargs >= 1) {
|
||||
// Check if first arg is a string
|
||||
const is_str = hook.call(fn (usize, u32) callconv(hook.cc.fastcall) u32, 0x6F3510, .{ @intFromPtr(L), 1 }); // lua_isstring
|
||||
if (is_str != 0) {
|
||||
const str_ptr = hook.call(fn (usize, u32) callconv(hook.cc.fastcall) u32, 0x6F3690, .{ @intFromPtr(L), 1 }); // lua_tostring
|
||||
if (str_ptr != 0) {
|
||||
const s: [*:0]const u8 = @ptrFromInt(str_ptr);
|
||||
const span = @import("std").mem.span(s);
|
||||
if (eql(span, "on") or eql(span, "enable")) {
|
||||
setEnabled(true);
|
||||
} else if (eql(span, "off") or eql(span, "disable")) {
|
||||
setEnabled(false);
|
||||
}
|
||||
if (nargs >= 1 and luaIsStringNative(L, 1)) {
|
||||
if (luaToStringNative(L, 1)) |s| {
|
||||
const span = std.mem.span(s);
|
||||
if (eql(span, "on") or eql(span, "enable")) {
|
||||
setEnabled(true);
|
||||
} else if (eql(span, "off") or eql(span, "disable")) {
|
||||
setEnabled(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Push current state as boolean
|
||||
hook.call(fn (usize, u32) callconv(hook.cc.fastcall) void, 0x6F39F0, .{ @intFromPtr(L), @as(u32, if (isEnabled()) 1 else 0) }); // lua_pushboolean
|
||||
luaPushBooleanNative(L, if (isEnabled()) 1 else 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/// Return a compact sticky diagnostic line for in-game testing.
|
||||
/// Usage:
|
||||
/// /run DEFAULT_CHAT_FRAME:AddMessage(OutlineDebug())
|
||||
pub fn outlineDebug(L: lua.State) callconv(.{ .x86_thiscall = .{} }) u32 {
|
||||
// Capture player/target from the context where GetGUIDFromName is known to
|
||||
// work on this client, then pin the resolved objects for the render thread.
|
||||
const player_guid = wow.unitGUID("player");
|
||||
const target_guid = wow.unitGUID("target");
|
||||
const player_obj = if (player_guid != 0) wow.getObjectByGUID(player_guid) else 0;
|
||||
const target_obj = if (target_guid != 0) wow.getObjectByGUID(target_guid) else 0;
|
||||
tracker.setDebugPinnedObjects(player_obj, target_obj);
|
||||
|
||||
if (player_guid != 0) tracker.debug_unit_player_guid_seen = true;
|
||||
if (target_guid != 0) tracker.debug_unit_target_guid_seen = true;
|
||||
|
||||
_ = d3d9_hook.lateRehookIfLost();
|
||||
const live = d3d9_hook.getLiveHookState();
|
||||
|
||||
var buf: [336]u8 = undefined;
|
||||
const msg = std.fmt.bufPrintZ(
|
||||
&buf,
|
||||
"OutlineDBG shst={d} hooks={d}{d}{d} tgt={d} mdl={d} sb={d} rs={d} fx={d} odip={d} cache={d} pipe={d}/{d}",
|
||||
.{
|
||||
d3d9_hook.debug_shader_stage,
|
||||
@intFromBool(live.endscene_ours),
|
||||
@intFromBool(live.dip_ours),
|
||||
@intFromBool(live.reset_ours),
|
||||
@intFromBool(tracker.debug_target_seen),
|
||||
@intFromBool(tracker.debug_target_model_seen),
|
||||
@intFromBool(d3d9_hook.debug_state_block_seen),
|
||||
@intFromBool(d3d9_hook.debug_outer_state_restore_seen),
|
||||
@intFromBool(d3d9_hook.debug_additive_skipped_seen),
|
||||
@intFromBool(d3d9_hook.debug_outline_dip_seen),
|
||||
@intFromBool(d3d9_hook.debug_cached_draw_seen),
|
||||
@intFromBool(d3d9_hook.debug_pipeline_entered_seen),
|
||||
@intFromBool(d3d9_hook.debug_pipeline_ready_seen),
|
||||
},
|
||||
) catch {
|
||||
luaPushStringNative(L, "OutlineDBG format error");
|
||||
return 1;
|
||||
};
|
||||
|
||||
luaPushStringNative(L, msg.ptr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
+75
-52
@@ -182,7 +182,10 @@ fn addOutlineEntry(model_ptr: u32, cat: types.ModelCategory, mark: u8) void {
|
||||
|
||||
// Diagnostic: count classified models by category
|
||||
switch (cat) {
|
||||
.target => diag.classify_target += 1,
|
||||
.target => {
|
||||
diag.classify_target += 1;
|
||||
debug_target_model_seen = true;
|
||||
},
|
||||
.raid_marked => diag.classify_raid_mark += 1,
|
||||
.dead_player => diag.classify_dead_player += 1,
|
||||
.none => {},
|
||||
@@ -197,6 +200,13 @@ fn addOutlineEntry(model_ptr: u32, cat: types.ModelCategory, mark: u8) void {
|
||||
/// Stores object pointers directly so classifyModel can match model
|
||||
/// back-pointers without any pointer dereferencing.
|
||||
pub fn scanObjects() void {
|
||||
debug_scan_called_seen = true;
|
||||
const gen = @atomicRmw(u32, &debug_scan_generation, .Add, 1, .seq_cst) + 1;
|
||||
const published_at = @atomicLoad(u32, &debug_publish_scan_generation, .seq_cst);
|
||||
if (published_at != 0 and gen > published_at) {
|
||||
debug_scan_after_publish_seen = true;
|
||||
}
|
||||
|
||||
// Clear per-frame sets
|
||||
frame_outline_count = 0;
|
||||
tracked_obj_count = 0;
|
||||
@@ -204,62 +214,24 @@ pub fn scanObjects() void {
|
||||
game_obj_model_count = 0;
|
||||
resetDiag();
|
||||
|
||||
if (!wow.isInGame()) return;
|
||||
const local_player = wow.getLocalPlayer();
|
||||
if (local_player == 0) return;
|
||||
|
||||
// Local player renders before outline targets (occludes outlines) unless
|
||||
// the local player IS an outline target (partition logic checks outline first).
|
||||
if (game_obj_ptr_count < MAX_TRACKED_OBJS) {
|
||||
// DEBUG10: EndScene must not call WoW object/game functions. On this client
|
||||
// those functions only behave correctly from the Lua/main-thread context.
|
||||
// Consume atomically published object pointers captured by OutlineDebug().
|
||||
const local_player = @atomicLoad(u32, &debug_pinned_player_obj, .seq_cst);
|
||||
if (local_player != 0) debug_pin_player_consumed_seen = true;
|
||||
if (local_player != 0 and game_obj_ptr_count < MAX_TRACKED_OBJS) {
|
||||
game_obj_ptrs[game_obj_ptr_count] = local_player;
|
||||
game_obj_ptr_count += 1;
|
||||
}
|
||||
|
||||
// Cache raid target GUIDs
|
||||
wow.cacheRaidTargets();
|
||||
|
||||
// Resolve target to object pointer (highest priority - added first)
|
||||
const target_guid = wow.getTargetGUID();
|
||||
if (target_guid != 0) {
|
||||
const target_obj = wow.getObjectByGUID(target_guid);
|
||||
if (target_obj != 0) {
|
||||
addTrackedObj(target_obj, .target, 0);
|
||||
}
|
||||
const target_obj = @atomicLoad(u32, &debug_pinned_target_obj, .seq_cst);
|
||||
if (target_obj != 0) {
|
||||
debug_pin_target_consumed_seen = true;
|
||||
addTrackedObj(target_obj, .target, 0);
|
||||
}
|
||||
|
||||
// Iterate all visible objects
|
||||
var obj = wow.objectFirst();
|
||||
while (obj != 0) : (obj = wow.objectNext(obj)) {
|
||||
const obj_type = wow.getObjectType(obj);
|
||||
const guid = wow.getObjectGUID(obj);
|
||||
if (guid == 0) continue;
|
||||
|
||||
switch (obj_type) {
|
||||
.player => {
|
||||
if (wow.isUnitDead(obj) and wow.isUnitFriendly(obj, local_player)) {
|
||||
addTrackedObj(obj, .dead_player, 0);
|
||||
}
|
||||
const mark = wow.getRaidMarkForGUID(guid);
|
||||
if (mark != 0) addTrackedObj(obj, .raid_marked, mark);
|
||||
},
|
||||
.unit => {
|
||||
const mark = wow.getRaidMarkForGUID(guid);
|
||||
if (mark != 0) addTrackedObj(obj, .raid_marked, mark);
|
||||
},
|
||||
.corpse => {
|
||||
if (!wow.isSkeletonCorpse(obj)) {
|
||||
addTrackedObj(obj, .dead_player, 0);
|
||||
}
|
||||
},
|
||||
.game_object => {
|
||||
if (game_obj_ptr_count < MAX_TRACKED_OBJS) {
|
||||
game_obj_ptrs[game_obj_ptr_count] = obj;
|
||||
game_obj_ptr_count += 1;
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
// Secondary categories (raid marks/dead players/game objects) are disabled
|
||||
// in this diagnostic build until the target-only path is proven stable.
|
||||
}
|
||||
|
||||
fn addTrackedObj(obj_ptr: u32, cat: types.ModelCategory, mark: u8) void {
|
||||
@@ -283,7 +255,10 @@ fn addTrackedObj(obj_ptr: u32, cat: types.ModelCategory, mark: u8) void {
|
||||
|
||||
// Diagnostic: count tracked objects by category
|
||||
switch (cat) {
|
||||
.target => diag.scan_targets += 1,
|
||||
.target => {
|
||||
diag.scan_targets += 1;
|
||||
debug_target_seen = true;
|
||||
},
|
||||
.raid_marked => diag.scan_raid_marks += 1,
|
||||
.dead_player => diag.scan_dead_players += 1,
|
||||
.none => {},
|
||||
@@ -308,6 +283,54 @@ pub const Diag = struct {
|
||||
};
|
||||
|
||||
pub var diag: Diag = .{};
|
||||
|
||||
// Sticky diagnostics for the debug build. They turn true once the corresponding
|
||||
// stage has been observed and stay true for the whole process lifetime.
|
||||
pub var debug_in_world_seen: bool = false;
|
||||
pub var debug_object_manager_seen: bool = false;
|
||||
pub var debug_player_guid_seen: bool = false;
|
||||
pub var debug_local_player_seen: bool = false;
|
||||
pub var debug_unit_player_guid_seen: bool = false;
|
||||
pub var debug_unit_player_object_seen: bool = false;
|
||||
pub var debug_target_guid_seen: bool = false;
|
||||
pub var debug_target_object_seen: bool = false;
|
||||
pub var debug_unit_target_guid_seen: bool = false;
|
||||
pub var debug_unit_target_object_seen: bool = false;
|
||||
pub var debug_object_scan_seen: bool = false;
|
||||
pub var debug_scan_called_seen: bool = false;
|
||||
pub var debug_pin_player_published_seen: bool = false;
|
||||
pub var debug_pin_target_published_seen: bool = false;
|
||||
pub var debug_pin_player_consumed_seen: bool = false;
|
||||
pub var debug_pin_target_consumed_seen: bool = false;
|
||||
var debug_scan_generation: u32 = 0;
|
||||
var debug_publish_scan_generation: u32 = 0;
|
||||
pub var debug_scan_after_publish_seen: bool = false;
|
||||
|
||||
// DEBUG9: objects captured explicitly from OutlineDebug() while executing in
|
||||
// WoW's Lua/main-thread context. This lets us test the render/classification
|
||||
// path without calling game object lookup functions from D3D9 EndScene.
|
||||
var debug_pinned_player_obj: u32 = 0;
|
||||
var debug_pinned_target_obj: u32 = 0;
|
||||
|
||||
pub fn setDebugPinnedObjects(player_obj: u32, target_obj: u32) void {
|
||||
const gen = @atomicLoad(u32, &debug_scan_generation, .seq_cst);
|
||||
@atomicStore(u32, &debug_publish_scan_generation, gen, .seq_cst);
|
||||
@atomicStore(u32, &debug_pinned_player_obj, player_obj, .seq_cst);
|
||||
@atomicStore(u32, &debug_pinned_target_obj, target_obj, .seq_cst);
|
||||
if (player_obj != 0) debug_pin_player_published_seen = true;
|
||||
if (target_obj != 0) debug_pin_target_published_seen = true;
|
||||
if (player_obj != 0) {
|
||||
debug_unit_player_object_seen = true;
|
||||
debug_local_player_seen = true;
|
||||
}
|
||||
if (target_obj != 0) {
|
||||
debug_unit_target_object_seen = true;
|
||||
debug_target_object_seen = true;
|
||||
}
|
||||
}
|
||||
pub var debug_target_seen: bool = false;
|
||||
pub var debug_target_model_seen: bool = false;
|
||||
|
||||
var log: logging.Logger = .{};
|
||||
|
||||
pub fn initLogger() void {
|
||||
|
||||
@@ -84,6 +84,8 @@ pub const D3DRS = struct {
|
||||
pub const DESTBLEND: u32 = 20;
|
||||
pub const CULLMODE: u32 = 22;
|
||||
pub const ZFUNC: u32 = 23;
|
||||
pub const ALPHAREF: u32 = 24;
|
||||
pub const ALPHAFUNC: u32 = 25;
|
||||
pub const ALPHABLENDENABLE: u32 = 27;
|
||||
pub const STENCILENABLE: u32 = 52;
|
||||
pub const STENCILFAIL: u32 = 53;
|
||||
@@ -105,6 +107,8 @@ pub const D3DRS = struct {
|
||||
pub const D3DCMP_ALWAYS: u32 = 8;
|
||||
pub const D3DCMP_EQUAL: u32 = 3;
|
||||
pub const D3DCMP_LESSEQUAL: u32 = 4;
|
||||
pub const D3DCMP_GREATER: u32 = 5;
|
||||
pub const D3DCMP_GREATEREQUAL: u32 = 7;
|
||||
|
||||
pub const D3DSTENCILOP_KEEP: u32 = 1;
|
||||
pub const D3DSTENCILOP_REPLACE: u32 = 3;
|
||||
@@ -123,8 +127,12 @@ pub const D3DCLEAR_STENCIL: u32 = 4;
|
||||
// D3D9 blend modes
|
||||
// =============================================================================
|
||||
|
||||
pub const D3DBLEND_ZERO: u32 = 1;
|
||||
pub const D3DBLEND_ONE: u32 = 2;
|
||||
pub const D3DBLEND_SRCALPHA: u32 = 5;
|
||||
pub const D3DBLEND_INVSRCALPHA: u32 = 6;
|
||||
pub const D3DBLEND_DESTCOLOR: u32 = 9;
|
||||
pub const D3DBLEND_INVDESTCOLOR: u32 = 10;
|
||||
|
||||
// =============================================================================
|
||||
// D3D9 texture stage state IDs
|
||||
@@ -133,11 +141,18 @@ pub const D3DBLEND_INVSRCALPHA: u32 = 6;
|
||||
pub const D3DTSS = struct {
|
||||
pub const COLOROP: u32 = 1;
|
||||
pub const COLORARG1: u32 = 2;
|
||||
pub const COLORARG2: u32 = 3;
|
||||
pub const ALPHAOP: u32 = 4;
|
||||
pub const ALPHAARG1: u32 = 5;
|
||||
pub const ALPHAARG2: u32 = 6;
|
||||
};
|
||||
|
||||
pub const D3DTOP_DISABLE: u32 = 1;
|
||||
pub const D3DTOP_SELECTARG1: u32 = 2;
|
||||
|
||||
pub const D3DTA_DIFFUSE: u32 = 0;
|
||||
pub const D3DTA_CURRENT: u32 = 1;
|
||||
pub const D3DTA_TEXTURE: u32 = 2;
|
||||
pub const D3DTA_TFACTOR: u32 = 3;
|
||||
|
||||
// =============================================================================
|
||||
@@ -216,6 +231,7 @@ pub const VT = struct {
|
||||
pub const GetRenderState: usize = 58;
|
||||
pub const GetTexture: usize = 64;
|
||||
pub const SetTexture: usize = 65;
|
||||
pub const GetTextureStageState: usize = 66;
|
||||
pub const SetTextureStageState: usize = 67;
|
||||
pub const GetSamplerState: usize = 68;
|
||||
pub const SetSamplerState: usize = 69;
|
||||
|
||||
+29
-6
@@ -57,6 +57,12 @@ pub fn isInGame() bool {
|
||||
return hook.readMem(u32, o.IS_IN_WORLD) != 0;
|
||||
}
|
||||
|
||||
/// The object manager is a more useful runtime readiness signal for the
|
||||
/// standalone Outline path than IS_IN_WORLD on modified 1.12.1 clients.
|
||||
pub fn hasObjectManager() bool {
|
||||
return hook.readMem(u32, o.OBJECT_MANAGER_PTR) != 0;
|
||||
}
|
||||
|
||||
pub fn objectFirst() u32 {
|
||||
const obj_mgr = hook.readMem(u32, o.OBJECT_MANAGER_PTR);
|
||||
if (obj_mgr == 0) return 0;
|
||||
@@ -203,17 +209,32 @@ pub fn getMapId() u32 {
|
||||
|
||||
/// UnitGUID("player") / UnitGUID("target") → 64-bit GUID.
|
||||
pub fn unitGUID(unit_id: [*:0]const u8) u64 {
|
||||
return hook.call(fn ([*:0]const u8) callconv(hook.cc.fastcall) u64, o.FN_UNIT_GUID, .{unit_id});
|
||||
// UnitGUID is __fastcall with its single argument in ECX.
|
||||
// hook.call + hook.cc.fastcall is currently miscompiled by Zig 0.16 on x86
|
||||
// as a stack argument. Because the callee consumes no stack argument, that
|
||||
// leaked 4 bytes per call; Outline calls UnitGUID("player"/"target") every
|
||||
// frame, so ESP drifted until execution returned into stack data.
|
||||
//
|
||||
// With a single register argument, x86_thiscall is ABI-compatible here:
|
||||
// ECX=unit_id, no stack args, u64 returned in EDX:EAX.
|
||||
const f_native: *const fn ([*:0]const u8) callconv(.{ .x86_thiscall = .{} }) u64 =
|
||||
@ptrFromInt(o.FN_UNIT_GUID);
|
||||
return @call(.never_tail, f_native, .{unit_id});
|
||||
}
|
||||
|
||||
/// Resolve a GUID → object pointer via the object manager hash table.
|
||||
pub fn getObjectByGUID(guid: u64) u32 {
|
||||
if (guid == 0) return 0;
|
||||
if (hook.readMem(u32, o.OBJECT_MANAGER_PTR) == 0) return 0;
|
||||
const lo: u32 = @truncate(guid);
|
||||
const hi: u32 = @truncate(guid >> 32);
|
||||
|
||||
// Do not gate this call on OBJECT_MANAGER_PTR. Nampower uses WoW's
|
||||
// GetObjectPtr at 0x464870 directly, and on this modified client the
|
||||
// legacy OBJECT_MANAGER_PTR global remains 0 even while the game is live.
|
||||
// The stdcall(u64) ABI is equivalent on x86 to pushing lo + hi as two u32s.
|
||||
const result = hook.call(fn (u32, u32) callconv(hook.cc.stdcall) u32, o.FN_GET_OBJECT_BY_GUID, .{ lo, hi });
|
||||
// Guard: hash table can return stale/invalid pointers for destroyed objects
|
||||
|
||||
// Guard: hash table can return stale/invalid pointers for destroyed objects.
|
||||
if (result != 0 and !isValidPtr(result)) return 0;
|
||||
return result;
|
||||
}
|
||||
@@ -221,7 +242,6 @@ pub fn getObjectByGUID(guid: u64) u32 {
|
||||
/// Split-GUID variant for callers that already have lo/hi parts.
|
||||
pub fn getObjectByGUIDSplit(guid_lo: u32, guid_hi: u32) u32 {
|
||||
if (guid_lo == 0 and guid_hi == 0) return 0;
|
||||
if (hook.readMem(u32, o.OBJECT_MANAGER_PTR) == 0) return 0;
|
||||
return hook.call(fn (u32, u32) callconv(hook.cc.stdcall) u32, o.FN_GET_OBJECT_BY_GUID, .{ guid_lo, guid_hi });
|
||||
}
|
||||
|
||||
@@ -231,8 +251,10 @@ pub fn getPlayerGUID() u64 {
|
||||
}
|
||||
|
||||
/// Get the local player's object pointer.
|
||||
/// Use ClntObjMgrGetActivePlayer (0x468550) instead of UnitGUID("player").
|
||||
/// This matches the path used by Nampower and does not depend on legacy globals.
|
||||
pub fn getLocalPlayer() u32 {
|
||||
const guid = unitGUID("player");
|
||||
const guid = getPlayerGUID();
|
||||
if (guid == 0) return 0;
|
||||
return getObjectByGUID(guid);
|
||||
}
|
||||
@@ -317,8 +339,9 @@ pub fn getNameByObject(obj: u32) [*:0]const u8 {
|
||||
}
|
||||
|
||||
/// Get the current target's GUID.
|
||||
/// Read the locked target GUID directly (0xB4E2D8), as Nampower does.
|
||||
pub fn getTargetGUID() u64 {
|
||||
return unitGUID("target");
|
||||
return readGUID(o.LOCKED_TARGET_GUID);
|
||||
}
|
||||
|
||||
/// Check if a unit is friendly to the local player.
|
||||
|
||||
Reference in New Issue
Block a user