Commit Graph

231 Commits

Author SHA1 Message Date
gamer147
3d88c13c72 Add design spec: call-script execution in the C# VM
Design for making call-script actually execute (load target .BIN by id,
run nested sharing globals, return to caller). Scope: subroutine
execution only. Drops vm0.py from oracle duty; C# owns golden traces.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 12:44:51 -04:00
gamer147
14ad23b8c7 Resolve call-script dispatch: id = raw SYS4INI file index
Native-RE (Ghidra) cracked call-script <id> (opcode 0x03): its handler
FUN_0041bc90 -> loader FUN_0040e980 -> resolver FUN_0044f390 indexes an
80-byte record table at base + id*0x50 == the SYS4INI record layout. So
`call-script <id>` is a direct RAW index into the SYS4INI global file
table (the asset index we already parse) -- there is no separate on-disk
id->code registry. This resolves name-resolution.md #1, statically, no
Frida.

Confirmed: all 297 distinct corpus call-script ids resolve to a .BIN
script with a semantically-exact name (0x1ab->ADDITEM, 0x2ae7->MES,
0x143->BUNKI, 0x329d->CALCREVISE), 0 out-of-range, 0 alternate-pack.
Companion op 0x8f `call` is an intra-script JSR (FUN_0041fba0), not
cross-script.

- parse_sys4ini.py: preserve `raw_index` per entry (= the engine file id;
  index the RAW records incl. '@' placeholders) + emit
  build/callscript-names.json (id->name).
- sys4load.py: annotate `call-script 0x1ab =ADDITEM.BIN`.
- opcodes.toml 0x03/0x8f refined (source=investigation, confidence high,
  handler VAs) + rebuilt opcode-reference.md.
- docs: engine-re.md (op 0x03 section + backlog re-aimed),
  name-resolution.md #1 (SOLVED), script-inventory.md (call graph +
  living-reference decision), tools-reference.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 12:44:51 -04:00
gamer147
057a26a97a docs: propagate u00428010/op-0x1a2 correction into opcodes.toml + name-resolution
op 0x1a2 resolve-handle? -> gfx-cmd-register (verified handler FUN_0042d360, gfx
command-buffer op). name-resolution: decision->scene hop is native+unidentified, not
u00428010 (disproven). Regenerated opcode-reference + shim.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 11:47:35 -04:00
gamer147
1323ec8852 chore: pe-sieve32.exe -> bin/ (3rd-party binary convention) + doc refs
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 11:46:17 -04:00
gamer147
47dbdbd5fc docs: correct u00428010/op-0x1a2 finding via anchored dispatch table
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>
2026-07-07 11:35:42 -04:00
gamer147
0ae4f607c0 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>
2026-07-07 11:29:02 -04:00
gamer147
320070f56f docs: implementation plan for Ghidra+MCP native-RE workflow (u00428010)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 10:35:55 -04:00
gamer147
5667a17b64 docs: design spec for Ghidra+MCP native-RE workflow (target u00428010)
Stand up bethington/ghidra-mcp loop; prove it by reversing SCJUMP's native
decision->scene resolver. Raw-dump-first, pe-sieve fallback; findings -> docs/engine-re.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 10:28:11 -04:00
gamer147
20a2fd7f2b Merge feat/scjump-decode: SCJUMP progression decision-logic decode
Guarded-DFS decode of SCJUMP's acyclic DAG into (chapter_mode, guards)->decision
table (1755 sites/847 decisions), VM-verified (execution-driven, 0 failures).
Finding: 1732/1755 decisions gated by native op 0x60. Decision->scene (u00428010)
documented as the deferred native boundary.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 09:47:19 -04:00
gamer147
50f13ccf78 docs: SCJUMP progression decode — canonical doc + references (Task 6)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 09:33:41 -04:00
gamer147
d08157d689 feat: name SCJUMP progression counters in globals registry (Task 5)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 09:31:54 -04:00
gamer147
e76fb6b373 feat: --verify execution-driven VM cross-check for SCJUMP decode (Task 4)
Reworked from static-witness-only to execution-driven: ~99% of SCJUMP decisions
are gated by a native computed value (op 0x60), so witness-synthesis alone can't
cover them. Value-local tracking resolves load-then-compare guards to real globals;
native terms honestly marked opaque. Verify: static 3/3 exact + 279/279 execution-
driven consistent over 2000 seeds, 0 failures.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 09:30:10 -04:00
gamer147
a67ebfa263 feat: emit SCJUMP decision table (json+md, registry-named) (Task 3)
build/scjump-decisions.* are generated (build/ gitignored).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 09:20:33 -04:00
gamer147
ad27167c95 feat: SCJUMP guarded-DFS decode -> decision list (Task 2)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 09:18:29 -04:00
gamer147
0b348a87d5 feat: SCJUMP decoder scaffold — CFG acyclic check + chapter dispatch (Task 1)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 09:17:30 -04:00
gamer147
7318940b35 docs: implementation plan for SCJUMP decision-logic decode
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 09:08:28 -04:00
gamer147
82fad44bbe docs: design spec for SCJUMP static decision-logic decode
Guarded-DFS decode of SCJUMP's DAG into (chapter, guards)->decision table;
VM witness cross-check; native decision->scene boundary documented/deferred.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 09:00:45 -04:00
gamer147
64c4ffda9d curate: name game_started, recategorize render/gfx false-positives out of story-flag
Quick curation pass: 0xa99->game_started (GAMESTART writer); 0x6be/0x6c3 ->ui-toggle
(message-window render chrome), 0x6249e/0x204f4 ->unknown (gfx/field display state).
Finding: high-scene-reach tail is dominated by shared render chrome, not story flags.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 08:45:15 -04:00
gamer147
9210b2ef39 docs: add globals.toml registry + story-flag tooling to PROJECT-STRUCTURE
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 08:40:29 -04:00
gamer147
a1d2d02f9a Merge feat/globals-registry-story-flags: globals.toml registry + story-flag miner
Curated single-source global registry (opcodes.toml analogue) with static
story-flag discovery miner; sys4load labels from merged build/globals.json.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 08:36:45 -04:00
gamer147
169075fe37 feat: curate story-flags into registry + docs (Task 6)
Bootstrap 56 high-signal skeletons; name game_mode/route_branch/scjump_decision_out2,
recategorize CONFIG-written globals as non-story. Docs: name-resolution.md registry
section, CLAUDE.md canonical+SoT tables + trigger (root, untracked), tools-reference rows.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 08:33:51 -04:00
gamer147
f0d7775d15 feat: story_flags --bootstrap seeds skeletons into globals.toml (Task 5)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 08:27:31 -04:00
gamer147
681737604c feat: static story-flag miner -> candidates JSON (Task 4)
Mines 1261 branch-read globals (205 story-flag candidates); build/story-flags-
candidates.json is generated (build/ gitignored). Known anchors 0x3234/0xa57 verified.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 08:26:03 -04:00
gamer147
69c788b65a feat: sys4load labels globals from merged build/globals.json (Task 3)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 08:24:36 -04:00
gamer147
84a8da765f feat: globals_build --build merge + generated JSON/MD (Task 2)
build/globals.json is generated (build/ is gitignored, regenerable via
globals_build.py --build); docs/global-reference.md is the tracked human view.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 08:23:17 -04:00
gamer147
7306423505 feat: globals.toml registry source + loader/lint (Task 1)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 08:21:59 -04:00
gamer147
b4c9e1cc0e docs: implementation plan for globals.toml registry + story-flag miner
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 08:19:48 -04:00
gamer147
5bc99bd723 docs: design spec for globals.toml registry + story-flag miner
Curated single-source global registry modeled on opcodes.toml; static
story-flag discovery miner; sys4load labels from merged build/globals.json.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 08:10:40 -04:00
gamer147
34dfa2f747 feat(godot): --seed 0xADDR=VAL for the playable frontend + doc CLI/frontend commands
godot -- --seed 0xa57=1 seeds initial global state in the ADV frontend (e.g.
Lily's form-gated voiced dialogue plays live; forms A/B/C = 0xa57/0xa58/0xa59).
Additive: no seed = unchanged, selftest OK. Also documents the engine CLI
(run/trace/audio/gfx/play/sweep) + Godot frontend flags in tools-reference.md
(they lived only in memory/commits).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 07:54:22 -04:00
gamer147
8012b5d25c feat(phase-b): sweep seeds — story-state explorer (which scenes a flag affects)
sweep [--boot] [0xADDR=VAL...] runs each scene with and without the seeds from
the same baseline and reports which scenes' dialogue changes. Maps a story flag's
reach across the corpus. Finding: the Lily form-A flag 0xa57=1 changes dialogue
in 34/297 scenes (SC0000 186->229, SC0040 111->156, ...) — a pervasive ADV lever,
validating the state-divergence finding at scale. Engine 18/18.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 00:32:04 -04:00
gamer147
1bfa374a78 Merge: Phase B — cross-scene state substrate
The Frida-free foundation for cross-scene playthrough and state seeding:
- GameSession: persistent global bank carried across scenes (VM untouched, parity held).
- play [--boot]: run a scene sequence carrying state; --boot runs the 9 *INIT
  data scripts into the bank (23646 globals) so scenes see real skill/item/unit data.
- Snapshot save/load (--state/--save-state): capture an expensive booted state once,
  reuse it; foundation for save-files.
- sweep [--boot]: corpus-scale validation (matches vm0.py: 294 exit + 3 LOOP).

Findings operationalized: seeding the Lily form flag 0xa57=1 takes SC0000 186->229
lines (state divergence, headless). Data-boot is regression-free but doesn't change
ADV flow — story-state flags drive that. Engine 18/18; Godot selftest OK.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 00:28:56 -04:00
gamer147
d46940158c feat(phase-b): sweep [--boot] — corpus-scale VM+state validation harness
Runs every SC/SP scene through GameSession (fresh or booted-from-snapshot) and
reports halt distribution + line counts. Unbooted matches the vm0.py A0 baseline
(294 exit + the same 3 LOOP scenes) — cross-validates the C# VM at scale.
FINDING: booting the *INIT data tables gives byte-identical results (regression-
free) but does NOT change ADV dialogue flow — *INIT feeds gameplay; ADV branches
key on story-state flags (chapter/form/choices) from the progression layer, not
*INIT. So story-state seeding (e.g. the Lily form flag) is the ADV-unlock lever.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 00:28:36 -04:00
gamer147
f6d1384b00 feat(phase-b): GameSession snapshot save/load (JSON)
ToJson/FromJson round-trip the persistent global bank; play --save-state <file>
persists it, --state <file> restores it — so an expensive booted state (23646
globals) is captured once and reused without re-running *INIT, and it's the
foundation for real save-file work. Tests: unit round-trip + booted-state
survives snapshot (skill data intact). Engine 18/18.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 00:26:23 -04:00
gamer147
ec40fa1d37 feat(phase-b): boot data tables — play --boot runs *INIT into session state
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) <noreply@anthropic.com>
2026-07-07 00:23:22 -04:00
gamer147
b762b3d1c0 feat(phase-b): cross-scene state — GameSession + play runner
Persistent global store carried across scenes (the engine's flat global bank),
the substrate for cross-scene flow and state seeding. GameSession seeds a fresh
VM from session state, runs, merges back; VM untouched so trace/selftest parity
holds. Age.Cli play <SCENE...> [0xADDR=VAL] runs a sequence carrying state.

Tests (engine 15/15): state persists A->B; seed visible to scene; SC0000 via
session byte-identical to single run; seeding form flag 0xa57=1 changes behavior.
Demonstrated: play SC0000 0xa57=1 -> 186->229 lines (Lily's form-gated dialogue
executes); SC0000->SC0030 carries 76 globals. Operationalizes the state-
divergence finding. Godot selftest OK.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 00:19:11 -04:00
gamer147
dc78a92ecd docs(phase-b): design — cross-scene state (persistent globals + runner)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 00:17:25 -04:00
gamer147
9b3b922c30 Merge: gfx-object-manager RE — drift root cause + engine-dump unlock
Reverse-engineered the post-opening bg/sprite drift end-to-end:
- Root cause: stubbed 0x215 (native gfx-object query) collapses draws to slot 0.
- Unlocked static analysis of the unpacked engine (dump_engine.py + capstone;
  AGE.EXE unpacks in-place at 0x400000; SYS4AB = XOR-0xFF(AGE.EXE) dead end).
- Live capture verdict: the real opening uses zero CG object-records => the drift
  is a STATE-DIVERGENCE artifact of the unseeded headless VM, not a missing op.
  Fix = Phase B state/choices flow. Native gfx-op modeling deferred (dump in hand).

No engine-code changes (RE tooling + docs only); engine 11/11 green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 00:10:58 -04:00
gamer147
6233f58e6c docs: update living refs — engine-dump tooling, gfx drift = state-divergence
tools-reference: dump_engine/probe_handlers/capture_gfx_objects + SYS4AB note.
phase-a-slice-plan: post-opening drift RESOLVED as a state-divergence artifact
(fix = Phase B state flow, not a native-op subsystem). PROJECT-STRUCTURE:
build/{textures,engine-dump}, engine/, tools/frida/.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 00:10:45 -04:00
gamer147
16fe754422 feat(frida): capture_gfx_objects + finding — drift is state-divergence
Live capture (esi=engine ctx via operand-fetch, poll object-record array
[esi+0x53d64] stride 120). KEY FINDING: through the full real opening, the record
array holds only 3 persistent UI objects — NO CG objects. The real game does NOT
draw opening CGs via the 0x212-0x21a positioned-object path our headless VM uses;
with state it takes a different (direct) branch. So the bg/sprite drift is a
STATE-DIVERGENCE artifact of the unseeded headless VM, not a missing native op —
the fix is the Phase B state/choices flow (makes label_12649 take the if-branch).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 00:04:49 -04:00
gamer147
fbc78b0c00 feat(frida): probe_handlers — confirms interpreter runs from module 0x400000
operand-fetch (call 0x41b940) fires ~8500/sec => the VM interpreter executes
from the in-place unpacked module at 0x400000 (NOT the heap copy) => handlers are
hookable by dump address. gfx-family(0x212-0x215)=0 at the title (no CG commands
until a scene runs).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 23:51:18 -04:00
gamer147
70db77af46 fix(frida): dump_engine — use ptr.readByteArray (frida 17), skip system DLLs
Dump validated: AGE.EXE is unpacked in-place at 0x400000 in memory (AGF-decoder
landmark @0x474f1f reads real code with the 'BM' 0x4D42 check). Kelebek handler
VAs map directly (VA-0x400000 = file offset). Handler ABI: thiscall (esi=engine
context), operands fetched via call 0x41b940, per-object command-type table at
[esi+idx*120+0x53d88]. 0x215 is part of a native gfx command-buffer manager.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 23:43:54 -04:00
gamer147
f16e5bd7b8 feat(frida): dump_engine.py — dump unpacked engine code from live process
Both on-disk engine images are the same packed binary, so native handler code
exists only unpacked in memory. dump_engine attaches, enumerates ranges, and
dumps the AGE.EXE module + large r-x heap regions (chunked) to build/engine-dump/
for offline disassembly (locate 0x215 @ VA 0x421160 via the dispatch table).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 23:33:02 -04:00
gamer147
986f2080a0 docs: SYS4AB.BIN = XOR-0xFF(AGE.EXE), same packed binary (dead end for static RE)
Decrypted SYS4AB: 0x2c header (S4AB magic + 0x10E000 size + 8-byte key) then
XOR-0xFF payload == AGE.EXE byte-for-byte. Not a patched/unpacked VM. Real
handler code exists only unpacked in the runtime heap (0x62411000). Corrects the
'likely the patched VM' assumption.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 23:29:37 -04:00
gamer147
2951425108 docs(opcodes): 0x215 = native graphics-object query (root cause of gfx drift)
Investigation upgrade: 0x215 queries the native object manager by element
handle-id (0x62455[idx]) and returns the object's slot/status; sign-tested to
drive label_12649's slot-select. Stubbing it collapses all draws onto slot 0 →
the anchor-preserve geometry reads foreign textures → cumulative bg/sprite
drift. Keystone for the graphics object-manager; exact return via unicorn
(handler @0x421160).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 23:21:08 -04:00
gamer147
d177a31185 Merge: A2b graphics geometry (0x208 keystone + blit compositor)
Opening event-CG sequence now renders correctly. 0x208 get-texture-size
implemented as a real VM op; faithful 800x600 immediate-mode blit compositor;
gfx + --shot diagnostics. Post-opening background/sprite anchor drift is
characterized and deferred to the anchor-record subsystem (next chunk).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 23:06:39 -04:00
gamer147
5f5fb072cd docs(a2b): record graphics-geometry result + deferred anchor-record drift
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 23:05:56 -04:00
gamer147
78199e9b8c feat(a2b): screen-backbuffer blit compositor + real GetTextureSize
Godot host: source images kept per slot with dims read from the BMP header on
the VM thread (synchronous, so the bytecode's geometry math sees real sizes);
draw-texture blits src rect -> dst into one 800x600 canvas in execution order,
shown by a single TextureRect. Selftest byte-parity preserved.

Slot 0 = primary/screen surface: seed its dims to 800x600 (the boot-time
create-texture the single-scene harness skips) and record create-texture(w,h),
so the first CG's anchor-preserve math stays an identity instead of corrupting
the persistent base globals. Fixes the grey-first-CG opening. Verified: SC0000
pages 1/3 composite the opening event CG at (0,0) with dialogue over it.

Also adds a --shot <png> [--shot-page N] dev capture to Main (page-gated on a
VM-thread counter) for headless visual verification.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 23:02:58 -04:00
gamer147
de5eb6562e feat(a2b): BmpHeader.ReadDims + gfx diagnostic (headless geometry oracle)
BmpHeader.ReadDims reads texture dims from the pre-converted BMP header (VM-
thread-safe, no pixel decode) — the data source for GetTextureSize. `Age.Cli gfx
<SCENE>` runs a scene and dumps set-texture/get-texture-size/draw-texture in
order with resolved file + computed geometry, mirroring the `audio` diagnostic.

Confirms 0x208 now yields real dims (800x600/800x800/800x227) and sane UI
geometry; surfaces the first-load anchor edge (unseeded state, Phase B).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 22:50:25 -04:00
gamer147
ea0f1ddc7a feat(a2b): implement 0x208 get-texture-size (geometry keystone)
Promote 0x208 from stub to a real VM op that writes the loaded texture's
width/height into its two output globals, via new IHost.GetTextureSize. This is
the single native primitive the CG-load subroutine (SC0000 label_12649) needs;
all downstream centering/anchor geometry is already computed in bytecode.

Non-Godot hosts return (0,0) so trace/selftest parity holds (engine 9/9 incl.
TraceDiffTests). Godot host gets a temporary (0,0) stub; real impl in the
compositor task.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 22:48:10 -04:00
gamer147
e96b1a6fef docs(a2b): implementation plan — graphics geometry (0x208 + blit compositor)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 22:43:35 -04:00