9ca1eb50fdcb46a2c1f61ca89088ce2a3f52c00e
13 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
9ca1eb50fd |
re(ui): answer four of the port's five asks -- splash, fade-out, focus, gamma
1 SPLASH ADDRESSING (was blocking P3). No content predicate exists: design size fails (every extra composable bundle sampled is 1280x720, like every screen) and element count fails (fragments run 2..15, the splash halves are 3 and 7). But GP_TITLE needs none -- `--all` adds exactly four bundles there and all four are real screens, with the --all index equal to the pak entry index 1:1. And there are TWO splash screens: 11/14 are the developer logos, 10/13 are the SQUARE ENIX publisher wordmark, which the port did not have and which the boot shows first. 2 FADE-OUT (was blocking P3). It is (a), and it is bigger than the fade quad. Every element ends on exactly ONE untimed keyframe, which rules out (b); that block is where the screen plays out -- quad to a=255, buttons/labels/glows to a=0, frames hold. (c) is refuted by a null test that discriminates: a black quad alone holds the button/background brightness ratio constant, and through the fade it falls 6.50 -> 1.94, 3.4x monotonic. 3 FOCUS (saves P5 rework). Over-vs-instead is unobservable -- the focused sprite covers the base at 100.0% of base-visible pixels on three pairs once aligned (true offset (7,7); the centre alignment reads a misleading 78-84%), and compositing both ways differs by RMSE 1.1 inside the button rect. The real defect is the focus record's SECOND element: ptbtn0Nf.rat declares ptbtneff01.t32 (a 42x46 glowing ring, focus only) plus the bright label, where the base declares one sprite. That ring is the marker the port draws nowhere. 5 GAMMA. The capture is not neutral: capture ~ 255*(render/255)^g, g ~ 1.34-1.49, and the chain says it is a ramp the GAME installed, not a capture artefact. So RMSE against captures has a floor. Reach stated: the flat patches are all dark (render ~0-60), so midtones and highlights are unconstrained. 4 ROTATION is a human's call and is recorded in MISSION, not acted on -- the port rotating while the reference renderer does not would make verify-screen report a large diff meaning "the port is right". The RE half is answered: rotation is about the declared pivot, measured against a GPU capture. The focus record's +20 element-count word is marked 🟡 not ✅ -- read on GP_TITLE's ten button records only; the disc-wide check is written and still running. |
||
|
|
c3cf3c2e81 |
re: a title negative that survives its own cross-check
Three earlier "the title never appears" claims came from instruments later found broken -- a stale pixel oracle, a 41 s sampling interval, a freezing stream. This one carries its own evidence. title_probe_xchecked.py restarts its capture stream every 30 s AND prints its reading beside an independent `import` grab every 60 s: 1851 frames in 560.2 s = 3.30 fps cross-checks 9, disagreements 1 max glyph 0 t= 62s stream 6.05 | import 0.07 disagree (a fade, logos mid-transition) t=123s stream 7.40 | import 7.49 agree t=183s stream 8.18 | import 8.29 agree t=243s stream 0.23 | import 0.10 agree t=311s stream 89.68 | import 89.51 agree t=371s stream 80.97 | import 81.58 agree t=426s stream 117.43 | import 117.72 agree t=487s stream 77.71 | import 76.25 agree t=546s stream 70.43 | import 70.55 agree Eight of nine agree within 2%, fps held at 3.30 with no collapse to 1.60, and the surface moved through dark and bright phases. So the frames were live: over 560 continuous seconds from launch, sampled 3.3 times a second, the interactive title's green (A) plate never appears while the game renders throughout. The final frame correlates 0.0145 / -0.0047 / 0.0102 with our title / main menu / EXTRAS renders -- attract-movie content, not a UI screen. Why remains unknown. live-title-press-a.png with its 753 glyph pixels proves the title was reachable from this container on 2026-08-28, and clearing the shader cache fixed the black surface but not this. The two emulator-side questions (gamma control, 8AX vs ptbase) are therefore blocked on a characterised failure rather than a suspicion. Neither blocks the five menu screens, so I am returning to static work; the probe is committed for whoever picks it up. METHOD: a probe that cross-checks itself turns "no result" into a result. |
||
|
|
e9924ff9e8 |
re: build the fast probe -- and it refutes the diagnosis that motivated it
Last iteration I blamed four failed runs on the probe sampling every ~41 s, slower than the title screen lasts, and withdrew three earlier conclusions on that basis. Building the fix tested the claim and killed it. The speedup is real and control-verified. One long-lived ffmpeg x11grab stream, raw RGB, glyph counted in numpy -- no per-sample process startup, no PNG encode, no convert -crop: wrapper `screenshot` 3.98 s per sample (emulator running) import -window root -> PPM 1.20 s long-lived x11grab stream 0.29 s 13.7x The counter is byte-identical to is_title.py: 753 on the committed title capture, 327 on the main menu. Pointed at a running game it says the opposite of what I expected: 332 frames in 85.3 s = 3.89 fps; max glyph 0 1674 frames in 420.0 s = 3.99 fps; max glyph 0 1674 consecutive samples over seven unbroken minutes, four per second, zero green-(A) pixels. Sampling rate was a real defect that happened not to be the cause. So "neither locale reaches the interactive title without a pad press" -- withdrawn last iteration for want of evidence -- is reinstated, now as a dense measurement, with its reach stated: a MID-RUN window only, silent about the boot title. Leading hypothesis, unconfirmed: the PRESS (A) plate appears only in the boot title window and the attract loop's title carries none, which is exactly what title_states_capture.sh was written to test. The experiment is to start the fast probe from t=0 rather than attach to a run already in progress. METHOD: fixing the instrument is how you test the explanation that blamed it -- a plausible mechanism is a hypothesis, and the fix is its experiment, not its proof. |
||
|
|
cc4e5e04a7 |
re: the boot harness was blinking slower than the title -- diagnosed
Four consecutive runs failed to reach the interactive title, across two locales, two launch paths and two display-gamma settings. I attributed it in turn to a stale oracle, to the locale, and to the attract loop. It was none of those. one `screenshot` call, emulator running: 10.8 s one `screenshot` call, emulator killed: 0.117 s 92x, measured at a 1-minute load average of 1.80 -- so it is contention with the emulator through the X server, not background load. skip_intro.sh takes two grabs per iteration plus a numpy import, giving a median sampling interval of 41 s in the last run (38/82/41/20/30/30/35/ 47/46/45/44/43/21/19). The title lasts "a few seconds" before the attract loop reclaims it -- wait_title.sh's own header says so. The harness was sampling slower than the event it was waiting for. That also explains why runs at 16:43-18:05 the same day succeeded. Withdrawn as CAUSES, though the observations stand: "the JP run never reaches the interactive title", "neither locale reaches it without a pad press", and "the game sat in the attract loop for 604 s". The English control did control for locale -- it just shared the same defect. Also recorded: I set kernel_display_gamma_type = 0 for the gamma control run, which brightens the frame (mid-attract mean 122.8 vs 52.5/82.8 at type 2) -- and skip_intro classifies movie-vs-static on an ABSOLUTE rmse threshold, so the gamma change biased the very classifier the run depended on. Changing a display setting and a capture behaviour in one run confounds both. Config restored to type 2. The fix is not applied: make the probe cheap enough to outpace the title window (small region, no convert round trip, one long-lived process). Every remaining emulator-side question is waiting on that. |
||
|
|
7d07eb945f |
re: the title capture is not reachable here either; stopping this line
Run 3 used the proven path rather than my own probe: launched exactly as
boot_menu.sh does (DISPLAY=:98, --apu=sdl, /dev/shm/xenia_* cleared, the
existing profile signed in) and drove skip_intro.sh, the detector that is
documented to work and that is stricter than mine (glyph >= 800 AND a
static frame).
It classified every one of 20 samples over 604 s as "movie", waited them
all out, and timed out. A direct check at 604 s says it was right: zero
green-glyph pixels, screen_id = other, warm mean (69,53,40), correlation
0.09 with build 7. The game really was playing attract movies for ten
minutes.
Three runs, two locales, two launch paths, ~35 minutes of emulator time,
no interactive title. Either the attract loop is far longer than the
600-780 s windows tried, or something regressed since
live-title-press-a.png was captured -- that one came from a pad-driven
boot (
|
||
|
|
8783adda14 |
re: the English control removes the locale from the title-capture problem
Two iterations framed the JP title capture as possibly locale-specific. It is not. Same flags, same oracle, English locale: 75 samples over 734 s, every one glyph = 0. The English boot does not present the interactive title either. Canary was alive throughout, polling XamInputGetKeystrokeEx (1801 calls); the frame at 734 s has content but correlates 0.15 with build 4's render and 0.05 with the main menu -- an attract-movie frame, not title art. So neither locale reaches the interactive title in ~12 minutes without a pad press. What is actually untested is the pad: pad.py and nav_to_flight.sh exist, and title_states_capture.sh reaches title states with a flag set this probe did not replicate (--log_ui_draws --ui_draw_capture_frames, plus xdotool window focus). It is a capture problem, not a locale one. MISSION.md updated. Also resolved a caveat I had given the port agent without checking it: "rotation is decoded but not rendered" does NOT affect their five screens at rest. Title, main menu, EXTRAS and both splash halves have ZERO top-level elements with a non-zero rotation. The only rotations on any of them are the title's two nested ptloop records (r = 30 and -45), and at rest those sit at x = 1521 and x = -839 -- a 399-wide sprite entirely off both edges of a 1280 screen. The caveat now applies only to animating the title build-in, where the sweeps cross the screen rotated. Two METHOD lines: run the control before theorising about the difference, and log every sample so a failure is a measurement rather than a silence. Emulator stopped, lock cleared, locale English. |
||
|
|
f10edf1e79 |
re: fix wait_title.sh's stale oracle; the JP title still is not reached
Last iteration's "never reached the title in 787 s" was a broken tool reporting on the world. wait_title.sh was still sampling the single pixel (625,618) that is_title.py had already been written to replace -- its docstring says why: a 1280x720 coordinate sampled against the 1279x675 game surface, so it always reads the copyright line. The replacement sat in the same directory. wait_title.sh now delegates to it. is_title.py passes its own controls before being trusted here: 753 green-glyph pixels on the committed English title capture, 327 on the main menu, threshold 400. Re-ran with the working oracle and the profile flag the English captures use. The game STILL did not present the interactive title -- but that is now a measurement rather than an artefact: not one frame showed a single green-(A) glyph pixel, and content correlation against either build-7 render never exceeded 0.22. Canary was alive and polling XamInputGetKeystrokeEx (601 calls), sitting in the attract movie. So the open question narrowed again, and is written into MISSION.md: whether the attract loop returns to the INTERACTIVE title without a pad press. title_states_capture.sh claims it does on the English boot with no pad input; if that holds, the difference is the locale. Nothing decided about the keyframe-time association or the rest() rule. Emulator stopped, lock cleared, locale restored to English. |
||
|
|
0f1c0f4e14 |
re: the JP-locale capture is not blocked -- I stopped one grep too early
Last iteration I wrote into MISSION.md that a Japanese-locale capture is impossible here, because user_language is DECLARE_int32 at four call sites with no DEFINE and no entry in xenia-canary.config.toml. That is true, and it was not the question. The language is PERSISTED: kernel_state.cc builds XConfig over <storage_root>/xconfig.settings, SetDefaults() only supplies a value when the file has none, and the file is writable. Checking where a setting is stored rather than where it is configured turned "blocked, needs a human decision" into a two-line edit. Withdrawn from MISSION.md; METHOD and REFUTED lines added. The field is located from struct landmarks rather than a hard-coded offset, and the check re-runs on every invocation so it fails loudly if the layout moves: music_volume 0.7f at User+449 -> BE float at 2727 -> User base 0x8e6 language at User+44 -> reads 1 (kEnglish) at 0x912 country at User+64 -> reads 103 (US) at 0x926 XLanguage::kJapanese = 2 (xbox.h:307). set_console_language.py wraps it with a backup and a --restore. The capture itself is still NOT taken, for a smaller reason than I claimed. A run with the locale set to Japanese booted fine but never reached the title in 787 s: wait_title.sh's green-(A) oracle never fired and burst-sampling found no frame correlating above 0.18 with either build-7 render -- the run sat in the attract loop. So it needs a longer or pad-driven run, not a rebuilt emulator. Emulator stopped, lock cleared, locale restored to English. Nothing is decided about the keyframe-time association or the rest() rule; this only changes what standing between us and deciding them. |
||
|
|
6d246c7a97 |
re(ui): finish the top-level rotation census; record the JP-capture blocker
Two threads had converged on needing one capture this container cannot take, so this iteration records that and finishes something reachable. BLOCKED, written into MISSION.md rather than worked around: Q1's keyframe time association and the rest() rule for plateau-less elements both now hinge on a running capture of GP_TITLE build 7, the Japanese title. The console language is not settable here -- user_language appears only as DECLARE_int32 at four call sites with no DEFINE anywhere in the tree, and it is absent from the registered cvars in xenia-canary.config.toml. There is no flag to pass, and guessing one is specifically unsafe: run-canary's own header records that xenia calls ShowSimpleMessageBox from ParseLaunchArguments before logging starts, so a bad flag blocks forever with an empty log. Rebuilding canary to add the cvar would be improvising around the blocker; it needs a human decision. Neither question blocks the five menu screens. FINISHED: the disc-wide top-level rotation count, left running four iterations ago as a shell loop over `screen info --geometry` that never completed (it decodes every texture per build). Walking the placement region directly takes seconds. top-level elements with a keyframe group 15 493 carrying a non-zero rotation 2 152 (13.89 %) Both controls pass: GP_TITLE build 4 reports 0 (its rotations are the nested ptloop records) and GP_DIALOG build 0 reports the expected two. The control earned its place -- the first version indexed the pak with a `screen list` BUILD number and got 0 for a screen that has two, because GP_DIALOG build 0 is entry 2. GP_TITLE maps 1:1, which is how the assumption survived. METHOD line added. Two free corroborations of the rotation decode. The rotated population is dominated by tactical-map ship icons -- pbb_destroyer 444, pbr_destroyer 402, pbr_fighter 276 -- i.e. markers rotated to heading, the single largest use of the field on the disc. And GP_TITLE entry 7's Japanese wordmark pieces settle from ALTERNATING tilts: ptlogo3a r = 0, -14, -4, -1, 0, ... ptlogo3b r = 0, +14, +4, +1, 0, ... ptlogo3c r = 0, -14, -4, -1, 0, ... Same magnitudes, opposite signs, all decaying to upright. A misread field does not produce that. |
||
|
|
e64965b3f9 |
re: S1 -- the Ready Room probe is a no-go, and not for the reason the
question expected One iteration, as the mission gates it, and no Ready Room work started. The two criteria it named both pass. It is NOT 3D: the corpus already holds a capture of the running screen and it is a flat six-item menu in the same visual language as the title. And unrecoverable paths do not mean unrenderable screens -- content enumeration finds 60 builds in the 1106 entries. What kills it is something the question did not ask. GP_READY_ROOM.pak does not appear to hold the Ready Room's own menu at all. Two signals agree: the four largest builds composite to briefing panels -- a planet readout, unit markers, direction arrows -- and, much harder, the 902 bundles declare 317 distinct element names of which NONE matches any of the six labels on the screen (takeoff, brief, hangar, pilot, option, mission, ready all return nothing). The pak is the briefing / tactical-map content behind the BRIEFINGS item. If this is ever picked up, the first job is finding where the menu lives -- GP_BUNK.pak is the obvious place -- not naming this pak's keys. The probe also reaches back into something already delivered: kind == 0x3002 finds ZERO buttons across all 902 bundles here, while GP_TITLE has 16. 0x3002 is one member of a 0x3000 family with sub-bits, and this pak uses 0x3000/0x3004/0x300c/0x3008. Nothing in the milestone changes -- every screen in scope is GP_TITLE -- but the handoff now says not to ship that equality as a general button test. |
||
|
|
69291a315f |
port: add Q10 -- what a music bank's sub-waves actually are
Some checks failed
BGM_001.slb is three sub-waves (10 KB, 4.47 MB, 4.67 MB) and the decoder concatenates them into one 347 s track. That is a default nobody chose, not a decision: two near-equal halves could be intro + loop, two variations, or two halves of one piece, and a menu that loops its music needs to know which. Found while wiring the Audio Library up to the shared banks. Recorded in HANDOFF.md as a trap too, so the port does not build looping on top of the concatenated track before the question is answered. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
|
|
ecbd70f69a |
port: the container agent answers the questions, it does not build the port
Some checks failed
Corrects the split I got wrong in
|
||
|
|
b5a193839c |
port: make the Godot menu shell the agent's primary objective
Sets a new mission: boot the real disc through developer splash -> intro video -> title -> main menu -> submenus in Godot 4, interactively, with no gameplay, no 3D and no emulator. docs/port/MISSION.md defines it -- eight gated milestones, each finished by an ARTIFACT rather than by compiling, plus the Ready Room as an explicitly gated stretch goal with a one-iteration probe that decides go/no-go. GP_READY_ROOM is 1106 entries with 6 recoverable names and is ISL-scripted, so it is either a week or a quarter, and the agent must not start it on its own authority. Architecture, per the user's decision: the Godot project is INDEPENDENT of the Rust viewer and never reads a disc format. An offline Rust exporter converts the disc into open formats; Godot reads only those. No GDExtension, no Rust in the Godot project, and sylpheed-viewer is off limits -- it stays the human's verification tool with its static-data rule intact. docs/port/FORMAT.md specifies the open format, versioned, because modding is the port's second goal and that makes the layout a deliverable rather than a temp directory: JSON over XML (Godot parses JSON natively; its XMLParser is SAX), names never hashes, provenance in every generated file, and unknowns listed rather than guessed. The discipline the whole thing rests on is the derived/authored split. `export/` is regenerated wholesale and never hand-edited; `authored/` is hand-written and survives a re-export. Three things this milestone needs are NOT on the disc in any decoded form -- which button does what, paint order, and menu sound cues -- so they live in `authored/` with a stated `why`. Deleting an authored entry because the exporter can now emit it IS the measure of progress. `export/` is gitignored: it is generated from the user's own disc and this stays a clean-room repo. Container: adds a pinned Godot 4 (windowed under Xvfb for screenshots, plus a headless wrapper). ffmpeg already carries libtheora, which is the video target -- Godot 4 plays only Ogg Theora natively and the disc's ADV.wmv is WMV3/WMA Pro. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |