docs: input was never the blocker; the third permutation came from the main menu
The game receives the (A) press - measured, vk=5800 flags=0001, out of ~6500 polls. Input delivery was never broken; four harness bugs were, and with those fixed the boot goes title -> one (A) -> main menu. GP_DIALOG DIFFICULTY is still unreached (NEW GAME hits the standing black-screen hang), but the main menu carries two primitives, which is what the question needed. Records the slip in the instrumentation too: logging on XSUCCEEDED counts X_ERROR_EMPTY as success and buried the two real keystrokes under 6499 empty polls. Next step is now STATIC: a primitive's position is stable by role across three measured screens but nothing in the file predicts it, so dump the full 60-byte declaration entries - four words unread - and look for a word that sorts like the measured orders.
This commit is contained in:
@@ -107,40 +107,21 @@ See [`structures/ui-composable-bundles.md`](structures/ui-composable-bundles.md)
|
||||
title — the one screen with ground truth — it takes mean luminance from **+18 %
|
||||
to −1.3 %** of the capture (76.30 → 63.72 vs 64.58) and mean abs diff 16.07 →
|
||||
13.08. Off by default because of the item below.
|
||||
* ❔ **Where a primitive paints. ← next, and it needs the emulator.** A primitive
|
||||
has no `T8aD` header, so no layer key, and the derived order forces the keyless
|
||||
last — which wipes **36 of the 125** builds that draw one (`GP_DIALOG`'s
|
||||
`pzeff00.prm` is a single opaque-black full-screen keyframe). No constant
|
||||
default works: the splash paints its primitive **first** (a black backdrop),
|
||||
the title paints one at slot **4** and another **last**, and declaration order
|
||||
fails the title too. Cheapest first step: a third measured paint order from a
|
||||
screen carrying exactly one primitive — the `GP_DIALOG` DIFFICULTY box, which
|
||||
is reachable from the main menu, would say whether its dim is first or last.
|
||||
This is a runtime child-list read, so it is an emulator item, not a static one.
|
||||
🔴 **ATTEMPTED 2026-08-19 and BLOCKED.** The reader
|
||||
(`tools/re-capture/screen_children.py`) is written and validated — it
|
||||
reproduces both known permutations character-for-character off the live title.
|
||||
The screen could not be reached: **Ⓐ does not advance the title screen** at any
|
||||
hold from 0.10 s to 0.40 s, nor does START, with the pad verified end to end.
|
||||
Two harness bugs were found and fixed on the way (`--audio` is not a cvar and
|
||||
broke eight boot scripts; `vgamepad` no longer exists but `skip_intro.sh`
|
||||
still called it, pressing nothing while exiting 0). Neither unblocks the title.
|
||||
🔬 **2026-08-19, second attempt: a root cause found and then refuted.** With
|
||||
the emulator's stdout kept, the log said it at once — `dialogs=1`, and
|
||||
**8 388 601** `XamInputGetKeystrokeEx` calls discarded by `IsUIActive()`. The
|
||||
dialog is a **sign-in UI opened because the requested profile does not exist**:
|
||||
the script asked for `E0300000EFBEA3D4` and only `B13EBABEBABEBABE` is on disc.
|
||||
Signing in the real profile takes the swallow count to **0** — and the title
|
||||
*still* does not advance on a single Ⓐ. Real bug, not the explanation.
|
||||
Four harness bugs fixed on the way (stdout to `/dev/null`; a title probe using
|
||||
a 1280×720 pixel against the 1279×675 game surface, which caused a 600 s
|
||||
timeout with the title on screen; tapping through the movies, which reaches a
|
||||
title that accepts nothing; a hard-coded XUID). Boots now reach the title
|
||||
reliably at ~240 s.
|
||||
**Next probe:** `--log_mask=13` records no kernel *calls*, so it is not known
|
||||
whether the game polls at the title at all. Add a log on the **successful**
|
||||
`XamInputGetKeystrokeEx` return, mirroring the one already on the swallow path;
|
||||
one boot answers it. See
|
||||
* 🟡 **Where a primitive paints — three measured screens, still no derivation**
|
||||
(2026-08-19). A primitive has no `T8aD` header and so no layer key. Measured
|
||||
positions are stable **by role**: `palogo_eff0.prm` (opaque backdrop) **first**
|
||||
on the splash; `pteff02.prm` (25 % dim) at slot **4** on both the title and the
|
||||
main menu; `pteff00.prm` (transition fade) **last** on both. Nothing in the
|
||||
file predicts this — any key must live in the 60-byte declaration entry, of
|
||||
which four words are still unread. **That is the next first step**, and it is
|
||||
a *static* task now: dump the full declaration entries for these three screens
|
||||
and look for a word that sorts like the measured orders.
|
||||
✅ Input was never the blocker — the game receives the Ⓐ press
|
||||
(`vk=5800 flags=0001`, measured); four harness bugs were. Boots now reach the
|
||||
main menu, whose composite scores **0.9591** against a live capture.
|
||||
❔ `GP_DIALOG` DIFFICULTY remains unreached: Ⓐ on NEW GAME hits the standing
|
||||
black-screen hang.
|
||||
See [`structures/ui-paint-order-key.md`](structures/ui-paint-order-key.md) and
|
||||
[`ui-paint-order-third-permutation.md`](ui-paint-order-third-permutation.md).
|
||||
* ❔ **Blend mode.** Everything is straight alpha-over. The near-white flash
|
||||
quads (`0xf0ffffff`) and coloured ones (`0x60ff0000`) may be additive. The
|
||||
|
||||
@@ -73,46 +73,51 @@ skipped, which is most of why a boot takes seven minutes here.
|
||||
That second bug looked like it would explain the standing "Ⓐ at the title works
|
||||
about half the time" note. **It does not.** See below.
|
||||
|
||||
## 🔴 The blocker: Ⓐ does not advance the title
|
||||
## ✅ Resolved: Ⓐ was never the problem — the harness was
|
||||
|
||||
With the pad verified end to end — `pad.py` writes `/tmp/xenia_pad.txt`, the
|
||||
emulator was launched with `--hid=file --pad_file=/tmp/xenia_pad.txt`, and the
|
||||
log shows `[file-pad] keystroke vk=5800 down/up` for every press — the title
|
||||
screen does not advance. Holds of 0.10 s, 0.25 s and 0.40 s, Ⓐ and START, on a
|
||||
screen that unambiguously renders PRESS Ⓐ BUTTON
|
||||
([capture](captures/title-with-press-a-live.png)).
|
||||
|
||||
### ✅ A root cause found, measured, and fixed — a sign-in dialog
|
||||
|
||||
Keeping the emulator's stdout (see below) produced the line immediately:
|
||||
Instrumenting the *other* side of the keystroke path settled it. The existing
|
||||
`[RE-INPUT]` line only fires when `IsUIActive()` swallows a keystroke, so its
|
||||
silence was ambiguous between "the game polls and gets nothing" and "the game
|
||||
does not poll". Two logs were added on the reaching path (canary
|
||||
`f9170fd3f`, corrected in `8d3299975`), and the boot said:
|
||||
|
||||
```
|
||||
w> [RE-INPUT] XamInputGetKeystrokeEx swallowed by IsUIActive (dialogs=1 nui=0, 8388601 so far)
|
||||
[RE-INPUT] XamInputGetKeystrokeEx -> user=0 vk=5800 flags=0001 (call flags 00000003)
|
||||
[RE-INPUT] XamInputGetKeystrokeEx -> user=0 vk=5800 flags=0002 (call flags 00000003)
|
||||
```
|
||||
|
||||
`xam_dialogs_shown_` is **1**, so `XamState::IsUIActive()` returns true and
|
||||
`xam_input.cc` discards every keystroke — **8 388 601 of them in one boot**,
|
||||
while the pad log shows presses arriving normally the whole time.
|
||||
Exactly two events — a KEYDOWN and a KEYUP for `0x5800` (`VK_PAD_A`) — out of
|
||||
~6 500 polls. **The game polls constantly and receives precisely the press that
|
||||
was sent.** Input delivery was never broken.
|
||||
|
||||
The dialog is a **sign-in UI opened because the requested profile does not
|
||||
exist**. `boot_menu.sh` passed `--logged_profile_slot_0_xuid=E0300000EFBEA3D4`;
|
||||
the only profile on disc is `B13EBABEBABEBABE`, and the boot log says so at line
|
||||
309: `FindProfiles: Adding profile B13EBABEBABEBABE to profile list`,
|
||||
`ProfileManager: Found 1 Profiles`. Naming a XUID with nothing behind it is not
|
||||
a harmless no-op.
|
||||
With all four harness bugs fixed the boot then went straight through: title at
|
||||
289 s, one Ⓐ, **main menu** ([capture](captures/main-menu-oracle.png)).
|
||||
|
||||
Fixed — `boot_menu.sh` now signs in whichever profile exists. **Swallowed
|
||||
keystrokes: 8 388 601 → 0.**
|
||||
### A slip in the instrumentation, kept because it nearly misled
|
||||
|
||||
### 🔴 Refuted: that was not sufficient
|
||||
The first version logged on `XSUCCEEDED(result)`. `X_ERROR_EMPTY` is `0x10D2`
|
||||
and `XSUCCEEDED` is `((s & 0xC0000000) == 0)`, so an **empty poll passes it** —
|
||||
6 499 lines of `vk=0000` buried the two that mattered, and for a few minutes the
|
||||
reading was "the driver is flooding the guest with junk keystrokes". Testing
|
||||
`result == X_ERROR_SUCCESS` exactly fixes it. (The pre-existing assignment above
|
||||
shares the quirk and writes `user_index_ptr` on an empty poll; left alone, since
|
||||
the struct is zeroed first.)
|
||||
|
||||
Fixing the swallow does **not** make the title accept Ⓐ. With a valid profile
|
||||
signed in and **zero** swallowed calls, the title still does not advance. So the
|
||||
dialog bug was real and worth fixing, and it is not the explanation.
|
||||
## ✅ And the third permutation was obtained — from the main menu
|
||||
|
||||
That also means `menu_draw_capture.sh`'s note — "the title that ends the boot
|
||||
sequence accepts a single (A) (2 of 2 runs)" — no longer reproduces. The run
|
||||
here reached the title at 239 s, pressed once, and stayed.
|
||||
Not from `GP_DIALOG`: pressing Ⓐ on NEW GAME leads to the standing black-screen
|
||||
hang, and no dialog object was ever constructed. But the **main menu itself**
|
||||
carries two primitives, which is what the question needed. Recorded in
|
||||
[`structures/ui-paint-order-key.md`](structures/ui-paint-order-key.md):
|
||||
|
||||
```
|
||||
GP_TITLE.pak ratc-index 8, 16 elements
|
||||
paint order: 1 3 4 2 5 8 9 6 7 15 10 11 12 13 14 0
|
||||
```
|
||||
|
||||
`pteff02.prm` (the 25 % dim) paints 4th, beneath the UI; `pteff00.prm` (the
|
||||
transition fade) paints **last** — both exactly where they sit on the title
|
||||
screen. The composite scores **0.9591 at shift (0,0)** against the live capture.
|
||||
|
||||
## Three more harness bugs, all of which read as the game misbehaving
|
||||
|
||||
@@ -156,14 +161,13 @@ of itself can only ever report symptoms. It now keeps a log and prints its path.
|
||||
|
||||
## What is not settled
|
||||
|
||||
* 🔴 The third permutation. The reader is ready and validated; the screen is not
|
||||
reachable.
|
||||
* 🔴 **Why the title does not advance.** The sign-in dialog is excluded, the
|
||||
`vgamepad` no-op is excluded, and the pad is confirmed delivering. The next
|
||||
probe needs a log level that records kernel *calls* — at `--log_mask=13` the
|
||||
only mentions of `XamInputGetKeystrokeEx` in an 84 KB log are import-table
|
||||
entries, so it is not even known whether the game polls at the title. A
|
||||
targeted log on the **successful** return path (mirroring the existing one on
|
||||
the swallow path) would answer it in one boot.
|
||||
* ❔ Only **one** screen object was resident when the harness believed the main
|
||||
menu was up, where the title has seven.
|
||||
* ❔ **`GP_DIALOG` DIFFICULTY is still unreached.** Ⓐ on NEW GAME leads to a
|
||||
black screen the run never comes back from — the standing post-load hang, now
|
||||
the only thing between here and a fourth permutation.
|
||||
* ❔ **The derivation for primitives.** Three measured permutations now, and the
|
||||
positions are stable *by role* (backdrop first, dim at slot 4, fade last), but
|
||||
nothing in the file predicts them. A primitive has no `T8aD` header, so any
|
||||
key must be in the 60-byte declaration entry; four of its words are still
|
||||
unread.
|
||||
* ❔ `ptframe1`/`ptframe2` rest at alpha 0 and so are not composited, but the
|
||||
capture shows the menu frame plainly.
|
||||
|
||||
Reference in New Issue
Block a user