Anchored the opcode dispatch table: handler(op)=ctx[0x26c93+op] (default FUN_004162b0, registered by FUN_00413860). Corrects the prior note: raw Kelebek VA 0x428010 is op 0x1ac (a save op, 0x427fb0); op 0x1a2's REAL handler is FUN_0042d360 = a graphics command-buffer op (cmd-type 3, '%c%8.8x' key). So u00428010 is NOT decision->scene and NOT save; the FIELD 0x5f0ed/0x62ccf snippet is gfx/UI. decision->scene premise discredited; real mechanism = call-script/script-load (still unidentified). Lesson: resolve handlers via the table, never the raw Kelebek VA. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
3.7 KiB
SCJUMP — progression / scene-sequencing logic
SCJUMP.BIN (29,790 instructions) is the game's progression state machine: it decides "what
comes next" in the story. tools/scjump_decode.py decodes its decision logic faithfully into a
readable, VM-verified table.
Mechanism
- Top switch on
0x3234(chapter_mode, 1..9) dispatches to a per-chapter block (1→0x81, 2→0x9f, 3→0x1ded, 4→0x1e6d, 5→0x54c1, 6→0x93c4, 7→0x1e868, 8→0x2aa42, 9→0x2b8a3). - Each chapter block is an acyclic tree (0 back-edges) of comparisons on progress counters
(
scjump_progress_a/b/c/d=0x4dfbc/0x2052e/0x152618/0xe6c5d), story flags, and a native computed value (see below). - At each of 1755 decision sites it writes
0x0 = 1(decision-valid) and0x62ccf = <decision value>(847 distinct values, 0..1693).
The decode
scjump_decode.py walks the CFG with a guarded DFS (a guard stack + a local→symbolic-value map, so
mov local, global ; cmp local, imm resolves back to a real global condition) and emits, per site,
(chapter, path-condition guards) → decision. Output (generated, disposable):
build/scjump-decisions.json (machine) and build/scjump-decisions.md (human, globals rendered by
their vm-map/globals.toml registry names). Regenerate: py -3.11 -X utf8 tools/scjump_decode.py.
Native-gated decisions (a key finding)
1732 of 1755 decisions have at least one guard on a native computed value — op 0x60
(u0041A270(local, 0x64), e.g. local = f(100), then local < 25). This is resolved at runtime by
the engine (it looks like a random/derived roll: ≈ rand()%100 < N), so it is not recoverable
statically and is honestly marked opaque in the guards. The decode still captures every global
condition (chapter, counters, story flags) on each path; only the native term is opaque. Takeaway:
SCJUMP mixes deterministic story-state gating with native/random gating — many "what comes next"
choices are not pure story flags.
Verification
--verify cross-checks the decode against the executing VM (vm0.py) two ways:
- static-witness — for the fully-static decisions (no native/opaque guard), synthesize a state from the guards and confirm the VM emits that decision (3/3 exact).
- execution-driven — seed thousands of random states, run SCJUMP (the VM executes the native op
too), and for every realized decision assert the table has that site with the right value and
no static guard is violated by the realizing state (279/279 consistent over 2000 seeds, 0
failures). This is the oracle that covers native-gated paths.
Run:
py -3.11 -X utf8 tools/scjump_decode.py --verify.
The native decision→scene boundary (deferred)
A FIELD snippet does lookup-array(ptr, 0x5f0ed, 0x62ccf) then u00428010(ptr), which the spec
guessed was the scene resolver. Correction (2026-07-07, via Ghidra): u00428010 (op 0x1a2) is a
graphics command-buffer op (its real handler FUN_0042d360 sets gfx cmd-type 3 and builds a
"%c%8.8x" key) — not save, not scene-load. So that snippet is a graphics/UI operation, not the
decision→scene dispatch. The real decision→scene mechanism is still unidentified and belongs with
the call-script / script-load dispatch (name-resolution.md §1). See docs/engine-re.md for the
verified handler analysis and the opcode-dispatch table that will crack call-script next.
See also
vm-map/globals.toml— the named globals SCJUMP switches on (chapter_mode, progress counters, flags).name-resolution.md §1— call-script / native dispatch (the decision→scene boundary lives here too).docs/superpowers/specs/2026-07-07-scjump-decision-decode-design.md— the design.