From ec40fa1d374c70cf4ac8222f6d5063040cde91e5 Mon Sep 17 00:00:00 2001 From: gamer147 Date: Tue, 7 Jul 2026 00:23:22 -0400 Subject: [PATCH] =?UTF-8?q?feat(phase-b):=20boot=20data=20tables=20?= =?UTF-8?q?=E2=80=94=20play=20--boot=20runs=20*INIT=20into=20session=20sta?= =?UTF-8?q?te?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All 9 *INIT scripts run clean and populate the game's data tables into the global bank. play --boot prepends them before scenes so scenes see real skill/item/unit/map/stage state (23646 globals for SC0000). Test cross-checks the VM's SKINIT population against the static extraction (build/data/SKINIT.json: skill 0 '飛行'@0x23a3, G[0xa6e5b]=30) — validates both the VM and extract_init. Engine 16/16. Co-Authored-By: Claude Opus 4.8 (1M context) --- engine/Age.Cli/Program.cs | 14 ++++++++++---- engine/Age.Engine.Tests/GameSessionTests.cs | 14 ++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/engine/Age.Cli/Program.cs b/engine/Age.Cli/Program.cs index 31453c0..403034b 100644 --- a/engine/Age.Cli/Program.cs +++ b/engine/Age.Cli/Program.cs @@ -66,11 +66,17 @@ if (args[0] == "gfx") if (args[0] == "play") { - // play [0xADDR=VAL ...] — run a sequence of scenes carrying persistent global state - // across them (optional up-front seeds). The state substrate for cross-scene flow; headless. + // play [--boot] [0xADDR=VAL ...] — run a sequence of scenes carrying persistent global + // state across them (optional up-front seeds). --boot first runs the data-table *INIT scripts so scenes + // see the real skill/item/unit/etc. state. The state substrate for cross-scene flow; headless. + // The *INIT boot set — all run clean (halt: exit) and populate the game's data tables into globals. + string[] bootScripts = { "SKINIT.BIN", "ITINIT.BIN", "EBINIT.BIN", "CGINIT.BIN", "MPINIT.BIN", + "AFINIT.BIN", "CCINIT.BIN", "STINIT.BIN", "STINIT2.BIN" }; var scripts = Paths.Scripts(); - var scenes = args.Skip(1).Where(a => a.ToUpperInvariant().EndsWith(".BIN")).ToList(); - if (scenes.Count == 0) { Console.WriteLine("usage: play [0xADDR=VAL ...]"); return 1; } + bool boot = args.Contains("--boot"); + var userScenes = args.Skip(1).Where(a => a.ToUpperInvariant().EndsWith(".BIN")).ToList(); + if (userScenes.Count == 0) { Console.WriteLine("usage: play [--boot] [0xADDR=VAL ...]"); return 1; } + var scenes = (boot ? bootScripts.Concat(userScenes) : userScenes).ToList(); var session = new GameSession(); foreach (var s in args.Skip(1).Where(a => a.Contains('='))) { diff --git a/engine/Age.Engine.Tests/GameSessionTests.cs b/engine/Age.Engine.Tests/GameSessionTests.cs index 46ffed3..418a1e2 100644 --- a/engine/Age.Engine.Tests/GameSessionTests.cs +++ b/engine/Age.Engine.Tests/GameSessionTests.cs @@ -76,6 +76,20 @@ public class GameSessionTests Assert.Equal("exit", r.Halt); } + [Fact] + public void BootingSkinitPopulatesDataTableGlobals() + { + // Running the SKINIT data script through the session populates the real skill table into the + // global bank (the game's boot behavior). Cross-check vs the static extraction (build/data/SKINIT.json): + // skill 0 = "飛行" at global-string 0x23a3, field G[0xa6e5b] = 30. + var session = new GameSession(); + var r = session.RunScene(Sys4Loader.Load(Paths.Scripts()["SKINIT.BIN"], Table), Table, new CaptureHost()); + Assert.Equal("exit", r.Halt); + Assert.Equal("飛行", session.GlobalStrings[0x23a3]); + Assert.Equal(30, session.Globals[0xa6e5b]); + Assert.True(session.Globals.Count > 1000, $"SKINIT should populate the skill table (got {session.Globals.Count})"); + } + [Fact] public void SeedingFormFlagChangesBehavior() {