docs: native-engine RE — Ghidra+MCP loop + opcode dispatch table + u00428010 correction

Ghidra+MCP workflow validated. Found the opcode->handler dispatch table
(*(ctx+0x9b8f4+op*4), registered by FUN_00413860) — the general fix for Kelebek VA
drift. Corrected: u00428010 (op 0x1a2) is a save/resource op, NOT decision->scene;
the SCJUMP consumer persists the visited-decision flag. New doc docs/engine-re.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-07-07 11:29:02 -04:00
parent dea3ef8347
commit dde015f7e3
3 changed files with 111 additions and 5 deletions

View File

@@ -40,10 +40,13 @@ choices are not pure story flags.
Run: `py -3.11 -X utf8 tools/scjump_decode.py --verify`.
## The native decision→scene boundary (deferred)
The decision value → actual `SCxxxx.BIN` is resolved by the **native op `u00428010`** (consumers do
`lookup-array(ptr, 0x5f0ed, 0x62ccf)` then `u00428010(ptr)`). That mapping is compiled into `AGE.EXE`,
not present in any script — so it is engine-level, the same bucket as the `call-script` dispatch (see
`name-resolution.md §1`). Cracking it needs the engine dump / Frida and is a separate slice.
Consumers do `lookup-array(ptr, 0x5f0ed, 0x62ccf)` then `u00428010(ptr)`. **Correction (2026-07-07,
via Ghidra):** `u00428010` (op `0x1a2`) is **not** the scene resolver — it's a **save/resource-file
op** (its handler formats `%s\SAVE%2.2d.DAT`), so that pattern **persists the "visited-decision" flag
into the save**, not loads a scene. See `docs/engine-re.md`. The actual decision→scene resolution is a
different, still-native mechanism — most likely call-script/script-load-adjacent (`name-resolution.md
§1`). Cracking it uses the engine-dump + Ghidra loop (the opcode-dispatch table found in
`engine-re.md` is the key); still a separate slice.
## See also
- `vm-map/globals.toml` — the named globals SCJUMP switches on (chapter_mode, progress counters, flags).