The previous entry called the title's Ⓐ a hard blocker. It is not: the main menu is reached and screenshotted (NEW GAME / LOAD GAME / TUTORIAL / OPTIONS / EXTRAS). What is true is narrower — Ⓐ advances the title only intermittently, about one attempt in four, with the press verifiably delivered every time and no Xenia UI active. Three candidate causes were eliminated with measurements rather than argument: * IsUIActive is now observable (Canary logs when it swallows a keystroke) and it never fires on the failing runs; * the driver filter is fine — the game polls with flags=3 and the file pad reports Controller=1, so FilterDrivers keeps it; * the game makes no content/user/signin call on the press at all — tracing every Xam call around it shows only input polling. And two traps in my own measuring rig, which cost more than the bug and are written down so nobody repeats them: a FIFO trace consumer that exits STALLS the emulator (the guest stops polling — indistinguishable from a dead pad, and it produced two runs of false evidence), and phase-A's kernel.return events carry a placeholder return_value of literally 0, so "every keystroke call returns SUCCESS" was an artifact of the logger, not a finding.
119 lines
6.9 KiB
Markdown
119 lines
6.9 KiB
Markdown
# Getting past the title screen in the container — three traps and one blocker
|
|
|
|
**Status:** ✅ `CONFIRMED` for the three traps (each reproduced, and two of them
|
|
fixed in the toolkit). 🔴 **blocked** on reaching the main menu: the guest reads
|
|
Ⓐ at the title, and the step *behind* Ⓐ either crashes it or stalls it.
|
|
|
|
Written down because each of these presents as "the pad is dead" or "the game
|
|
ignores input", and each has a completely different cause.
|
|
|
|
## 1. F10 opens the emulator's menu bar, and that swallows every guest keystroke
|
|
|
|
F10 is the RE hotkey (ship capture, and now `log_ui_draws`). It is *also* the
|
|
toolkit key that opens the application menu bar, and it does both. With that menu
|
|
open, `XamState::IsUIActive()` is true, so `XamInputGetKeystrokeEx` returns
|
|
`X_ERROR_SUCCESS` with a **zeroed** keystroke before the driver is ever asked
|
|
(`xam_input.cc`). The guest polls, gets an empty event, and does nothing —
|
|
forever, and with no error anywhere.
|
|
|
|
This cost most of an iteration. Symptom: a run presses F10 to arm a capture,
|
|
then every later Ⓐ does nothing; the title sits there while the pad log happily
|
|
prints `[file-pad] keystroke vk=5800 down`. Screenshot
|
|
[`captures/title-with-emulator-menu-open.png`](captures/title-with-emulator-menu-open.png)
|
|
shows the giveaway — `Open… / Open Recent / Install Content… / Exit` over the
|
|
title art. It is easy to miss because the menu is at the far left of the window
|
|
and the game is still animating behind it.
|
|
|
|
**Fix:** after any F10, click once on the game surface to dismiss the menu.
|
|
`Escape` does *not* close it. `ui_draw_capture.sh` now does this.
|
|
|
|
## 2. The pad is fine — the driver was never the problem
|
|
|
|
Worth stating because the file-pad's own history (`--hid=file`, `GetKeystroke`
|
|
implemented specifically so "PRESS Ⓐ BUTTON" would work) makes it the natural
|
|
suspect. It delivers: the log shows `vk=5800 down`/`up` per tap, and Ⓐ during a
|
|
movie skips the movie, every time. When the title ignores Ⓐ, the pad is not why.
|
|
|
|
## 3. Ⓐ at the title needs a signed-in profile — and this container cannot type one in
|
|
|
|
With no profile, Ⓐ **is** handled: the guest calls `XamShowSigninUI` and Xenia
|
|
pops its Sign In dialog ([`captures/title-signin-dialog.png`](captures/title-signin-dialog.png)),
|
|
`ProfileManager: Found 0 Profiles`. That dialog is the proof the guest reads the
|
|
button.
|
|
|
|
The dialog cannot be completed from here: "Create Profile" opens an **ImGui text
|
|
field** for a gamertag, and synthetic X key events do not reach it. Tried, all
|
|
with the window focused (`xdotool getwindowfocus` = the game window):
|
|
`xdotool type --window`, `xdotool type` via XTEST, single `xdotool key` presses,
|
|
clicking the field first. The field stays empty; `Create` with an empty gamertag
|
|
is refused. Mouse clicks *do* work (they opened the dialog and the sub-panel), so
|
|
it is text entry specifically.
|
|
|
|
Xenia's own **Profile ▸ Show Profile Menu** panel does have a working `Create
|
|
Profile` button, and mouse clicks reach it — but the gamertag it then asks for is
|
|
the same ImGui field, so the UI route dead-ends in the same place.
|
|
|
|
**Fix, landed in Canary** (`auto/re-ui-draw-order`): a `--create_profile_if_none=<gamertag>`
|
|
cvar that calls `ProfileManager::CreateProfile(tag, autologin, default_xuid)` at
|
|
startup when no profile exists. `default_xuid` is fixed at `B13EBABEBABEBABE`, so
|
|
a script can then pass `--logged_profile_slot_0_xuid=B13EBABEBABEBABE` and be
|
|
deterministic. Verified: `ProfileManager: RE bootstrap profile 'SylphRE' ->
|
|
created`, then on the next boot `Found 1 Profiles` / `Loaded SylphRE (GUID:
|
|
B13EBABEBABEBABE) to slot 0`, and the Profiles Menu panel shows the user
|
|
([`captures/xenia-profile-panel-signed-in.png`](captures/xenia-profile-panel-signed-in.png)).
|
|
The profile persists on disk, so later runs need only
|
|
`--logged_profile_slot_0_xuid=B13EBABEBABEBABE`.
|
|
|
|
## Withdrawn: Ⓐ is not blocked — it works, intermittently
|
|
|
|
**The main menu has been reached**
|
|
([`captures/main-menu-reached.png`](captures/main-menu-reached.png) — NEW GAME /
|
|
LOAD GAME / TUTORIAL / OPTIONS / EXTRAS). So the previous version of this entry,
|
|
which called the title's Ⓐ a hard blocker, is **withdrawn**. What is true is
|
|
narrower and more annoying: **Ⓐ advances the title only sometimes** — one success
|
|
in roughly four attempts, with the press verifiably delivered every time
|
|
(`[file-pad] keystroke vk=5800 down/up` in the log) and no Xenia UI active.
|
|
|
|
The successful press landed on the **first** title after boot, before any attract
|
|
cycle; the failures were on a title that had come round again. That is a
|
|
hypothesis, not a rule — a later attempt on a first title also failed (the screen
|
|
flickered to something else for one sample and came back), so timing is involved
|
|
and the window has not been pinned down.
|
|
|
|
## What was eliminated on the way, so nobody re-walks it
|
|
|
|
* **`IsUIActive()` is now observable.** `XamInputGetKeystrokeEx` returns
|
|
`X_ERROR_SUCCESS` with a zeroed keystroke *before consulting any driver* when a
|
|
Xenia dialog is up — an invisible input blackout. Canary now logs
|
|
`[RE-INPUT] XamInputGetKeystrokeEx swallowed by IsUIActive (dialogs=N …)` when
|
|
that happens. On the runs where Ⓐ failed, **it never fires**, so the blackout
|
|
is not the cause there. (It *is* the cause after F10 — trap 1 above.)
|
|
* **The driver filter is fine.** The game polls with `flags = 3`, and
|
|
`InputSystem::FilterDrivers` keeps a driver when `flags & InputType` is
|
|
non-zero; the file pad reports `Controller = 1`, so it is selected.
|
|
* **The game does not query sign-in on the press.** Tracing every `Xam*` call
|
|
around a press shows only `XamInputGetCapabilities`, `XamInputGetKeystrokeEx`
|
|
and `XamInputGetState` — no content, user or signin call at all.
|
|
|
|
### Two traps in the measuring rig itself, which cost more than the bug
|
|
|
|
* **A FIFO trace consumer that exits stalls the emulator.** `--phase_a_event_log_path`
|
|
writes 1.5 GB a minute, so it was pointed at a named pipe with a `grep` on the
|
|
other end. When that reader goes away, the emulator blocks on the write — and
|
|
the symptom is *exactly* "the pad is dead": the guest stops polling, the driver
|
|
never refreshes, nothing is logged. Two runs' worth of "the driver never
|
|
reacts" was this, not the game.
|
|
* **The trace's `return_value` is a placeholder.** `phase_a_bridge::EmitReturn`
|
|
is called as `EmitReturn(name, 0)` — literally zero — so every `kernel.return`
|
|
event reads `"status": "0x00000000"`. "Every keystroke call returns SUCCESS"
|
|
looked like hard evidence for the `IsUIActive` theory and means nothing. The
|
|
theory had to be tested with a real log line instead.
|
|
|
|
## Still open
|
|
|
|
Why the acceptance is intermittent. The next step is not another guess: capture
|
|
the *first* title's Ⓐ under `--cache_throw_diag`-style logging of the guest's own
|
|
state machine, or simply script "tap Ⓐ once per second for 30 s on the first
|
|
title" and see whether the success rate goes to 1 — a cheap, honest experiment
|
|
that also gives a reliable route to the main menu for the UI paint-order work.
|