diff --git a/docs/tools-reference.md b/docs/tools-reference.md index 5724b18..e7a6a0c 100644 --- a/docs/tools-reference.md +++ b/docs/tools-reference.md @@ -59,6 +59,28 @@ All opcode knowledge (ABI, semantics, provenance, `depends_on`) is hand-edited * | `vm0.py` | Headless Python bytecode VM (Phase A0 execution-model prototype; reuses `sys4load`). | `--test` (RECOVER unit test) · `--sweep [N]` (oracle coverage) · `--scene NAME` · `--settex NAME` (set-texture resId trace + exec trace) · `` | corpus → stdout; `build/vm0-trace.json`; `build/settex-.json` | | `correlate_scope.py` | Align the VM's `set-texture(resId)` trace with the game's Frida load order → tag each load's DATA2 package, flag package transitions, dump the significant ops in each transition span (the **scope selector** hunt). | `correlate_scope.py ` | `build/settex-.json` + `build/frida-load-order-result.json` + index → stdout | +## Engine (C#) — VM core, CLI, Godot frontend + +The `engine/` .NET solution (`AgeEngine.sln`) is the runtime VM; `godot/` is the ADV frontend. Not +Python, but listed here as the things you *run*. Build: `dotnet build engine/AgeEngine.sln`; test: +`dotnet test engine/AgeEngine.sln`. Run a CLI command: `dotnet run --project engine/Age.Cli -- `. + +| Command | Purpose | Notes | +|---|---|---| +| `run ` | Execute a scene, print steps + first 20 show-text lines. | `CaptureHost` (headless). | +| `trace ` | Trace every SC/SP scene → offsets + halt + steps (the A1 differential-test oracle). | writes JSON. | +| `audio [0xADDR=VAL…]` | Dump executed `play-bgm`/`play-voice` in order + resolved file. | optional global seeds. | +| `gfx [0xADDR=VAL…]` | Dump executed `set-texture`/`get-texture-size`/`draw-texture` with resolved file + computed geometry (the headless geometry oracle). | optional seeds. | +| `play [--boot] [--state ] [--save-state ] [0xADDR=VAL…]` | ★ Cross-scene **state runner**: run a scene sequence carrying persistent globals. `--boot` first runs the 9 `*INIT` data scripts (real skill/item/unit/map/stage state). `--state`/`--save-state` load/persist a JSON snapshot. | `GameSession`. | +| `sweep [--boot] [0xADDR=VAL…]` | Corpus-scale validation (unbooted matches vm0.py: 294 exit + 3 LOOP). **With seeds = a story-state explorer**: reports which scenes' dialogue changes ±seed (e.g. form flag `0xa57=1` → 34/297 scenes). | | + +**Godot frontend** (`S:/Godot/Godot_v4.7…`; project = `godot/`). Toolchain: `godot --headless --path godot +--import` → `dotnet build godot/Himegari.csproj` → `godot [--headless] --path godot [-- ]`. It +plays `SC0000` from the real bytecode. User args (after `--`): +- `--selftest` — headless; asserts the emitted 186 offsets == `build/vm0-trace.json` (parity guardrail). Exits. +- `--seed 0xADDR=VAL` (repeatable) — seed initial global state, e.g. `--seed 0xa57=1` unlocks Lily's form-A voiced dialogue (186→229 lines). +- `--shot [--shot-page N]` — capture page N to a PNG then quit (headless-verifiable render check / dev screenshots). + ## Asset resolution / graphics | Tool | Purpose | Run | Reads → Writes | diff --git a/godot/Main.cs b/godot/Main.cs index ac591e0..9d2829d 100644 --- a/godot/Main.cs +++ b/godot/Main.cs @@ -72,16 +72,28 @@ public partial class Main : Godot.Control var userArgs = OS.GetCmdlineUserArgs(); _selftest = System.Array.IndexOf(userArgs, "--selftest") >= 0; + var seeds = new List<(int Addr, long Val)>(); // --seed 0xADDR=VAL (repeatable) — initial global state for (int i = 0; i < userArgs.Length; i++) { if (userArgs[i] == "--shot" && i + 1 < userArgs.Length) _shotPath = userArgs[i + 1]; if (userArgs[i] == "--shot-page" && i + 1 < userArgs.Length) int.TryParse(userArgs[i + 1], out _shotPage); + if (userArgs[i] == "--seed" && i + 1 < userArgs.Length) + { + var kv = userArgs[i + 1].Split('='); + if (kv.Length == 2) + { + int k = kv[0].StartsWith("0x") ? System.Convert.ToInt32(kv[0], 16) : int.Parse(kv[0]); + long v = kv[1].StartsWith("0x") ? System.Convert.ToInt64(kv[1], 16) : long.Parse(kv[1]); + seeds.Add((k, v)); + } + } } var table = OpcodeTableJson.Load(Paths.OpcodesJson); var script = Sys4Loader.Load(Paths.Scripts()["SC0000.BIN"], table); _host = new GodotAdvHost(this, ResourceMap.Load(), "SC0000"); _vm = new VirtualMachine(script, table, _host); + foreach (var (addr, val) in seeds) _vm.Globals[addr] = val; // seed initial state before running _ = Task.Run(() => { _vm.Run(); _done = true; }); if (_selftest)