Commit Graph

29 Commits

Author SHA1 Message Date
gamer147
8bee45f483 Refresh retained presentation references 2026-07-10 21:56:57 -04:00
gamer147
82afcf5506 Fix native retained presentation batching 2026-07-10 21:37:42 -04:00
gamer147
992215cc48 Implement native 0x202 color interpolation 2026-07-10 20:49:28 -04:00
gamer147
7f900c8fc6 Implement ADV foreground transition lifecycle 2026-07-10 18:49:53 -04:00
gamer147
bb16a5496a feat: add affine rotation rendering and timeline diagnostics 2026-07-10 17:54:33 -04:00
gamer147
014d128ccd fix: align animation pacing and transforms with native 2026-07-10 15:41:17 -04:00
gamer147
c14c7fdced feat: split native scale and translation channels 2026-07-10 10:28:11 -04:00
gamer147
54bd9a7006 docs: scene-entry state snapshot (capture_global_writes + --state)
engine-re.md: the write-log method, plaintext-value insight (store is obfuscated),
vm_operand_write ABI, the packer poll-until-unpacked fix, and the validation
(34k-global capture seeds the VM to match the engine's whole opening). Plus the
resync-tolerant align() / operand-hook blind-spot notes. tools-reference: the new
tool + trace --state.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 13:32:52 -04:00
gamer147
d2e9cc5a99 re: record differential oracle outcome + first divergence
engine-re.md: the offset-path diff method, the working capture (operand hook
0x41b940; tick 0x410fb0 sees ecx!=ctx), the two capture caveats (hook-before-load
gating + operand-mode argc>=1 filter), SC0000 codebase id, and the first
divergence found (op 0xa0 jcc @ 0x8d on uninitialized G[0x6c1], the ADV-chrome
enable — a two-boot-gap state hole, not the predicted coroutine yield).
tools-reference.md: trace_engine_ops.py, diff_optrace.py, --trace-json.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 12:00:12 -04:00
gamer147
1b151f6e17 re: apply EngineCtx struct to /v2 handlers; doc + canonical-map wiring
Created the EngineCtx Ghidra struct (35 fields, 0xa1000) and retyped all
419 dispatch handlers' this -> EngineCtx* (CUSTOM_STORAGE, ctx in ECX).
Handlers now decompile ctx->cur_ctx_index / ctx->run_state_flags etc.
Validated: sleep_op_0xc8, gfx_op_0x215. tools-reference + engine-re.md
wired; CLAUDE.md canonical-map updated (root, outside repo).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 10:08:06 -04:00
gamer147
e166cab2d5 re(frida): import-map applied to /v2 — 248 Win32 APIs named; validated
Labeled the module-resident IAT at RVA 0x16f000 (imp_<dll>_<func> x248,
0 clobbers). Validated in decomp: FUN_0044f390 -> imp_kernel32_CreateFileA
/ SetFilePointer; sleep_timer_arm -> imp_winmm_timeGetTime (pins
DAT_0056f3d4). d3d9/heap-resolved DLLs unlabeled by design.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 09:26:19 -04:00
gamer147
ace8ddf8eb re: auto-label all opcode dispatch handlers from FUN_00413860
Extract op->real-handler map (handler(op)=ctx[0x26c93+op]) from the
registration routine's override stores; ghidra_handler_map.py +
build/op-handler-map.json (420 overrides). Cross-check vs opcodes.toml
found 0 real drift. One-shot Ghidra pass then labeled the /v2 image:
281 raw FUN_/LAB_ handlers -> op_0xNN_handler, 107 bare VAs -> functions,
31 hand-named preserved, opcode plate comment on every handler.

Includes the Task A spec + plan and the two-program (/v2 vs SMM) gotcha.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 08:32:01 -04:00
gamer147
9dd96bbc84 docs: grey-BG root cause (color-op alpha=tint-strength; slot-0 collision) + --gfx-log tool
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 23:42:20 -04:00
gamer147
1f9bde3739 docs+godot: finish frame-cadence RE notes + headless shot null-guard
Prior-session WIP: RE findings on the engine frame cadence (engine-re.md,
phase-a-slice-plan.md, tools-reference.md) and a null-guard so headless
--shot-sequence advances without a rendered viewport texture.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 17:26:27 -04:00
gamer147
791ea2ca0f fix(vm): faithful headless — halt at wait-for-input instead of plowing
The headless divergence that sent us chasing a phantom 'sleep' spin: op 0x72
wait-for-input was a no-op headless, so a run plowed past all 166 of a scene's
prompts into code no real playthrough reaches (SC0000 -> the name-entry poll
loop, spinning sleep 1 493k x to STEP-LIMIT). That path is a fiction.

Fix: VmOptions.HaltAtWaitForInput -> the VM halts (reason 'wait-for-input') at
0x72. run/play default to faithful (SC0000 now halts at ~402 steps, 0 sleeps,
matching the real run's path to the first prompt); --plow opts into the old
walk-every-page coverage. sweep stays plow by default (dialogue oracle, 284/13
unchanged); --halt-at-wait makes all 297 scenes halt cleanly at their first
prompt (0 STEP-LIMIT). Godot unaffected (really blocks on input; flag false).

Engine 58/58 (2 new); sweep default 284/13 unchanged; Godot selftest OK.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 10:45:32 -04:00
gamer147
9ccfc43959 feat(diag): aggregating + filtered trace sinks (histogram, op-filter, per-script)
The existing trace framework only had a flat text formatter, so every question
became 'dump millions of lines, then grep'. This session that cost a long wrong
detour. Add, all observe-only (parity preserved):

- HistogramTraceSink: execution counts per opcode AND per call-site (script:pc)
  with a sample operand. Dumped sorted after the run. This is what instantly
  showed the 493k headless 'sleep's are INPUTNAME.BIN:0x1c3 (a name-entry poll
  loop), not the opening.
- TraceSinkBase: tracks the frame stack -> attributes each step to its REAL
  script (nested call-script frames included) = the 'which script is this pc in?'
  answer a bare step trace can't give.
- TextTraceSink: op-filter (--trace-ops sleep,draw-texture,...) + script:pc tags.
- CompositeTraceSink: fan-out (text + histogram + Godot's call-script queue).
- OpcodeTable.ByLabel: mnemonic -> opcode for --trace-ops.
- CLI: --trace-histogram, --trace-ops, robust --trace-file (mkdir -p).
- Godot: --trace-histogram <file> profiles the REAL run (headless flow diverges:
  real run to page 1 is 562 steps / 0 sleeps vs headless 2M steps / 493k sleeps).
- Also: --sleep-scale <f> debug knob to slow the paced opening for inspection.

Engine 56/56 (4 new); sweep parity 284/13; Godot builds + dogfooded end-to-end.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 09:50:07 -04:00
gamer147
2ed5eed261 feat(godot): --shot-sequence per-frame capture for verifying paced animation
Dumps one PNG per _Process frame (auto-advancing past input waits), so a
time-based sleep-paced effect can be verified as distinct frames. Confirmed
the SC0000 opening now steps through paced AE*/character/CG frames instead of
jumping straight to the final state.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 09:07:22 -04:00
gamer147
a8f742cf57 feat(tools): per-scene opcode completeness tracker + 0x259 marker fix
Add tools/scene_opcode_coverage.py: histograms a scene's static opcodes and
classifies each vs the C# VM as impl / safe-noop / GAP (effectful op the VM
silently stubs). Implemented set is parsed live from VirtualMachine.cs case arms
(no drift); metadata from build/opcodes.json. Makes a half-rendered scene legible
("N ops still stubbed") instead of implying everything runs.

SC0000 baseline: 129 distinct ops, ~94.8% instruction-weighted handled, 68 GAP.
The tracker cross-checks opcodes.toml vs VM behavior and surfaced 0x259
(script-entry marker) missing its noop_headless flag -> reconciled in opcodes.toml
and rebuilt (regen: age_opcodes_himegari.py, opcode-reference.md).

Docs: tools-reference.md (tool row), phase-a-slice-plan.md (completeness gauge).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 22:57:25 -04:00
gamer147
9859fd88b9 docs(gfx): render drift RESOLVED (native ops + system boot); align living docs
engine-re.md: 0x1f7 erase correction + 'The render drift's SECOND half' (missing
INIT2 boot state via SYSTEM4) + gfx-command-buffer backlog marked DONE + worker
annotations noted. phase-a-slice-plan A2b: drift RESOLVED (both halves; CGs render
screenshot-confirmed; residual = AE* alpha/blend + cold anchors). tools-reference:
gfx --boot + godot --boot. (Status memory + MEMORY.md + CLAUDE.md updated on disk.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 19:09:34 -04:00
gamer147
ea4bf99fab docs(gfx): settle render drift as native cmd-buffer op 0x215 (Ghidra), align docs
Read op 0x215's real handler FUN_0042a0b0 (resolved via the dispatch table
ctx[0x26c93+op]; Kelebek's 0x421160 is VA-drift). It writes cmd-type 5 into the
current gfx-object record and returns a std::map::find over an engine-internal
registry populated by sibling gfx ops (0x1a2 hash insert). The return is native
command-buffer state, not the VM global bank -> seeding story-state cannot fix
the drift. Verdict: (b) a genuine native op, NOT (a) state-divergence.

Reconcile the previously contradictory drift accounts onto one canonical home
(engine-re.md op 0x215), with opcodes.toml carrying the opcode-level semantics
and phase-a-slice-plan / tools-reference / frida README corrected to point at it
instead of repeating the disproven state-divergence conclusion.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 16:40:59 -04:00
gamer147
527fba02e8 feat(cli): --trace flag streams engine diagnostics (run/play/sweep)
Also gate Stub events under TracingSteps: stubbed ops are per-instruction
frequency (0x258/0x259 stmt markers en masse), so --trace stays a clean
high-level flow view; --trace-steps shows step+stub detail.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 15:25:59 -04:00
gamer147
7c751200bb docs: sync living references + roadmap to call-script solved/executing
- tools-reference: run/play/sweep execute call-script (provider); trace/audio/gfx
  stay provider-less; corrected stale sweep numbers (284 exit/13 STEP-LIMIT) and the
  Godot --selftest description (synthetic vs headless, not vm0/186); added --scene.
- roadmap: call-script marked SOLVED (resolution + execution) throughout; no longer a
  'long pole'; per-game registry now automatic from SYS4INI.
- phase-a-slice-plan: SC0240 live-demonstration note.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-07 14:21:09 -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
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
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
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
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
10f6656c2d feat(a2b): first-pass texture render — full-screen event-CG layer
Wire asset resolution into a live Godot render. VM executes set-texture ->
ResourceMap resolves (scene,resId) -> files[section_base+resId] across all
archives -> pre-converted BMP -> composite behind the dialogue. The full-screen
event-CG layer (EV052*) renders end-to-end from the executed bytecode.

- Age.Engine/Sys4/ResourceMap.cs: Resolve + BMP TexturePath; Paths: asset JSONs
- GodotAdvHost: create/set/draw-texture -> TextureRect in a _stage layer
- IHost.DrawTexture + VM dispatch extended with dst x/y (draw-texture args 7/8)
- project.godot 800x600; convert_agf.py all-archive + --scene batch
- engine 8/8, C# --selftest still byte-matches vm0 trace (VM behaviour unchanged)

Known limitations (next chunk = graphics geometry/blend subsystem):
- sprites + BG* via the CG-load subroutine get garbage dst/size — native ops
  stubbed (0x208 get-texture-size + sprite position/anim chain)
- AE* fades draw opaque/instant (no alpha); no chromakey
- slot model approximates the game's immediate-mode blit-onto-slot-0 canvas
- AGF pre-converted to BMP offline (runtime decoder deferred)
See docs/phase-a-slice-plan.md (A2b section).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 21:22:42 -04:00
gamer147
a61c0c9abd feat(assets): solve asset resolution (SYS4INI per-scene section manifest)
resId -> files[section_base(scene) + resId]. SYS4INI's file list is
sectioned, one per scene (SCxxxx.BIN + its cross-archive asset manifest);
file_number is the index within the section. Unified for set-texture,
play-bgm, play-voice. Fully static/general -> no per-scene capture.

- tools/parse_sys4ini.py: SYS4INI (S4IC422, LZSS) -> build/asset-index.json
- tools/resolve_asset.py: sections + (scene,resId) resolver -> build/asset-sections.json
- validated: 97% structural, SC0000 17/17 vs Frida, 586/595 captured loads
- opcodes.toml: set-texture/create/draw-texture, play-bgm/voice enriched (frida-grounded)
- Frida tooling (capture_load_order all-archive, correlate_scope, ...) + vm0 --settex
- docs: asset-resolution-re (step2 SOLVED), global-memory-re (shelved), tools-reference

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 20:48:22 -04:00