From 50f13ccf7892ee121f2bd925d6a01a9c69ba4e51 Mon Sep 17 00:00:00 2001 From: gamer147 Date: Tue, 7 Jul 2026 09:33:41 -0400 Subject: [PATCH] =?UTF-8?q?docs:=20SCJUMP=20progression=20decode=20?= =?UTF-8?q?=E2=80=94=20canonical=20doc=20+=20references=20(Task=206)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/PROJECT-STRUCTURE.md | 5 +++- docs/name-resolution.md | 6 +++++ docs/scjump-progression.md | 51 ++++++++++++++++++++++++++++++++++++++ docs/tools-reference.md | 2 ++ 4 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 docs/scjump-progression.md diff --git a/docs/PROJECT-STRUCTURE.md b/docs/PROJECT-STRUCTURE.md index eb9972c..5c2bc1f 100644 --- a/docs/PROJECT-STRUCTURE.md +++ b/docs/PROJECT-STRUCTURE.md @@ -39,6 +39,7 @@ S:\Game Hacking\Eushully\Himegari\ ← workspace root (three siblings) │ ├── age_opcodes_himegari.py GENERATED from opcodes.toml (do not hand-edit) │ ├── globals_build.py generator/linter: vm-map/globals.toml (+auto map) -> build/globals.json, docs/global-reference.md │ ├── story_flags.py static story-flag miner (branch-condition) -> build/story-flags-candidates.json; --bootstrap + │ ├── scjump_decode.py decode SCJUMP progression logic -> build/scjump-decisions.* ; --verify (VM cross-check) │ ├── test_globals.py, test_opcodes.py unit tests for the globals / opcode tooling │ ├── vm0.py headless Python VM (Phase A0); `--test` = RECOVER unit test │ ├── extract_phase2.py batch: disasm + text + data extraction @@ -71,7 +72,8 @@ S:\Game Hacking\Eushully\Himegari\ ← workspace root (three siblings) │ ├── sys4-format-notes.md byte-level container format │ ├── script-inventory.md what the 481 scripts are │ ├── opcode-reference.md GENERATED from opcodes.toml (human-readable opcode reference) - │ └── global-reference.md GENERATED from globals.toml (human-readable global registry) + │ ├── global-reference.md GENERATED from globals.toml (human-readable global registry) + │ └── scjump-progression.md SCJUMP progression decode + native decision→scene boundary │ ├── build/ DERIVED (our-work-side) — generated by tools/; disposable │ ├── disasm/ .asm — human-readable disassembly, one per script @@ -87,6 +89,7 @@ S:\Game Hacking\Eushully\Himegari\ ← workspace root (three siblings) │ ├── global-var-map.{json,md} partial global-variable name map (auto shape inference; feeds globals.toml merge) │ ├── globals.json GENERATED from globals.toml merged over global-var-map (sys4load labels + C# VM) │ ├── story-flags-candidates.json GENERATED by story_flags.py (ranked story-flag review surface) + │ ├── scjump-decisions.{json,md} GENERATED by scjump_decode.py (progression decision table) │ ├── opcodes.json GENERATED from opcodes.toml (machine view for the C# VM) │ └── manifest.json, opcode-coverage.md (opcode-coverage.md GENERATED from opcodes.toml) │ diff --git a/docs/name-resolution.md b/docs/name-resolution.md index 9fe039a..161ce27 100644 --- a/docs/name-resolution.md +++ b/docs/name-resolution.md @@ -43,6 +43,12 @@ on disk to read. Resolving it needs one of: **Status: deferred.** Not the quick win first assumed. Belongs with the engine/dispatch work (Phase 3), or a dedicated `SCJUMP.BIN` reverse. Until then `call-script` stays numeric. +**Update (2026-07-07):** SCJUMP's *decision logic* is now decoded — `(chapter_mode, guards) → +decision value` — see `docs/scjump-progression.md` and `tools/scjump_decode.py`. That confirmed +SCJUMP is not the `call-script` registry (it produces a decision value, not a script id). The +decision→scene hop itself is a **native op (`u00428010`)**, engine-level, in the same bucket as +`call-script`; both await the engine dump / Frida. + --- ## #2 — The global-variable map (naming the data) diff --git a/docs/scjump-progression.md b/docs/scjump-progression.md new file mode 100644 index 0000000..07a0e3c --- /dev/null +++ b/docs/scjump-progression.md @@ -0,0 +1,51 @@ +# 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) and + `0x62ccf = ` (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: +1. **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). +2. **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) +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. + +## 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. diff --git a/docs/tools-reference.md b/docs/tools-reference.md index 014c920..b0ee476 100644 --- a/docs/tools-reference.md +++ b/docs/tools-reference.md @@ -46,6 +46,8 @@ All opcode knowledge (ABI, semantics, provenance, `depends_on`) is hand-edited * | `globals_build.py` | Merge curated `globals.toml` over the auto shape map; generate the global registry + linter. | `--build` · `--lint` | `vm-map/globals.toml`, `build/global-var-map.json` → ⚙ `build/globals.json`, ⚙ `docs/global-reference.md` | | `story_flags.py` | Static story-flag miner (branch-condition mining) + `--bootstrap` skeleton seeding. | `story_flags.py` · `--bootstrap` | corpus, `build/global-var-map.json` → ⚙ `build/story-flags-candidates.json`, appends `vm-map/globals.toml` | | `test_globals.py` | Unit tests for the globals registry + story-flag miner. | `test_globals.py` | — | +| `scjump_decode.py` | Decode SCJUMP's progression logic → decision table; `--verify` VM cross-check. | `scjump_decode.py` · `--verify` | SCJUMP.BIN, `build/globals.json` → ⚙ `build/scjump-decisions.{json,md}` | +| `test_scjump.py` | Unit tests for the SCJUMP decoder. | `test_scjump.py` | — | ## Extraction / data corpora