This repository has been archived on 2026-09-16. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Syplheed-Reborn/docs/re/canary-scripted-input-traps.md
Sylpheed RE agent ab8a0d9e6d docs/re: the title-screen crash is an STL map/set erase, 40 s from a cold boot
The crash PC from the previous iteration resolves, and it names itself:
sub_823070B0 references the string 'invalid map/set<T> iterator', builds it with
the string helpers and throws it — which is the guest C++ exception (E06D7363)
Xenia reports one line earlier. Its node offsets are MSVC's red-black tree node
exactly (_Left 0, _Parent 4, _Right 8, _Color 24, _Isnil 25), so this is a
std::map/set erase, and the fault is the first dereference after the iterator
check.

That matters beyond this blocker. The canary handoff's Ready-Room crash is the
same shape — a guest STL exception in a cache/save path — and its bisection plan
is priced at "one build plus one Ready-Room run" per suspect. If it is the same
defect, each suspect now costs 40 seconds, and suspect #1 is already measured:
--mem_watch=false removes it.

Stated as unresolved rather than guessed: the fault address 0x1_0000000C is a
32-bit value with bit 32 set, which fits BOTH a corrupt guest tree and a stale
high word on the emulator side. The measurement that separates them (read the
node from guest memory at the throw) is written down rather than assumed.

The second, unreproduced crash PC is identified too: an unrolled 4x16-bit copy
loop faulting on the STORE, i.e. a bad destination — a different failure.
2026-08-18 21:05:39 +00:00

128 lines
7.3 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`.
## The blocker: what happens *after* Ⓐ
With the profile created and signed in — Xenia's own **Profile ▸ Show Profile
Menu** confirms it, `User: SylphRE / XUID: B13EBABEBABEBABE / Assigned to slot: 1`
(the panel counts from 1; the log says slot 0) — Ⓐ at the title **still does not
advance the screen**. Measured across four boots, including one that was allowed
to play the whole intro untouched, with holds of 10 s and bursts of five taps.
What is *not* the explanation, each checked:
* the emulator menu bar being open (trap 1) — the grabs are clean;
* the pad (trap 2) — Ⓐ skips movies in the same runs;
* the profile being absent (trap 3) — it is present, signed in, and the Sign In
dialog no longer appears;
* an ImGui window being open — but note this generalises trap 1: **any** Xenia UI
window makes `IsUIActive()` true, the Profiles Menu included, so a panel left
open silently disables the guest's input for as long as it is up.
**One observation that did not reproduce, recorded rather than dropped.** In the
run where the Profiles Menu had just been closed, a single Ⓐ **crashed the
guest** — PC `0x824578A0`, guest thread 6, distinct from the boot-path crash
below. Two later attempts to reproduce it (open the panel, close it, press Ⓐ)
produced neither a crash nor a transition. So it is one data point that Ⓐ *can*
reach code the title does not otherwise run, and nothing more.
**The reproducible crash is the boot-time content path.** With the default
`--mem_watch=true`, in two independent runs, the guest dies with an access
violation reading `0x00000001_0000000C` at PC `0x82307128` on guest thread 9,
right after `HostPathDevice::ResolvePath(\aab216c3\5\c10eae6)` and a
`RtlRaiseException(E06D7363)` (a C++ throw). Xenia pauses itself and stacks up
crash dialogs — 991 in one run. With `--mem_watch=false` that crash does not
happen at all.
That pairing — crash with the memory-probe thread on, none with it off, both in a
save/content path — matches the suspect ranked #1 in
`xenia-canary/HANDOFF-crash-oracle-2026-07-16.md`, whose crash was also a guest
C++ exception in a cache/save path. This is a second, far cheaper reproduction of
it: at the **title screen**, ~40 s from a cold boot, instead of in the Ready Room
after a mission.
**The first of those next steps is done**, and it moves the crash out of this
entry into its own: `0x82307128` is inside an MSVC `std::map`/`set` **erase**
the function carries the string `'invalid map/set<T> iterator'` and the node
offsets to prove it — which makes this a 40-second reproduction of the crash the
Canary handoff blames on the `mem_watch` probe. See
[`title-crash-stl-tree.md`](title-crash-stl-tree.md).
**Next step, for whoever picks this up.** What is left:
1. dump the tree nodes from guest memory at the throw, to separate "the guest's
tree is corrupt" from "the emulator left a stale high word in a register" —
the fault address `0x1_0000000C` is consistent with either;
2. the content exports this title imports — `XamContentCreateEnumerator`,
`XamContentGetDeviceState`, `XamUserReadProfileSettings` — logged around the
press, to see whether the guest is handed a bad enumerator or a device state
it does not accept. `\aab216c3\5\…` is the content directory in question.
**What this blocks:** every dynamic experiment past the title — a second UI
screen's paint order, the main menu, mission captures. Everything up to and
including the title screen is unaffected, which is why the paint-order result
above could still be measured.