tools: fix four boot-harness bugs; find the swallowed-keystroke cause and refute it
Chasing why (A) does not advance the title turned up four harness bugs, one real
root cause, and a refutation of that root cause as the explanation.
FIXED, each verified:
* boot_menu.sh sent the emulator stdout to /dev/null, which is why none of the
rest was visible. It now keeps a log and prints its path.
* skip_intro.sh probed pixel (625,618) for the green (A) glyph - a 1280x720
coordinate. screenshot returns the 1279x675 game surface, where that point
is on the copyright line and reads (8,17,31). Symptom: a 600s TIMEOUT with
the title on screen. Now classified with screen_id.py; next boot printed
"TITLE at 239s -> A".
* skip_intro.sh no longer taps through the movies. Making that press real (the
vgamepad fix) was a REGRESSION: the boot then reached the title in 90s and
that title accepted nothing. menu_draw_capture.sh records the same finding
independently.
* boot_menu.sh signs in whichever profile exists instead of a hard-coded XUID.
ROOT CAUSE FOUND: naming a XUID with no profile behind it opens a sign-in
dialog, xam_dialogs_shown_ goes to 1, and IsUIActive() then discards every
XamInputGetKeystrokeEx. Measured 8,388,601 swallowed calls in one boot with the
pad log showing presses arriving normally throughout. Only B13EBABEBABEBABE
exists on disc; the script asked for E0300000EFBEA3D4. Signing in the real
profile takes the swallow count 8,388,601 -> 0.
AND REFUTED: that is not why the title is stuck. With a valid profile and zero
swallowed keystrokes, a single (A) on the title that ends the boot still does
nothing. The dialog bug was real and worth fixing; it is not the explanation.
Next probe is specific: --log_mask=13 records no kernel calls, so it is not even
known whether the game polls at the title. A log on the SUCCESSFUL
XamInputGetKeystrokeEx return, mirroring the one already on the swallow path,
answers it in one boot.
This commit is contained in:
@@ -37,10 +37,26 @@ ensure_display(){
|
||||
rm -f /dev/shm/xenia_memory_* /dev/shm/xenia_code_cache_* 2>/dev/null
|
||||
ensure_display
|
||||
cd /sylph-home/re
|
||||
# KEEP the emulator's stdout. It used to go to /dev/null, which cost a session:
|
||||
# the pad appeared dead at the title and the one log line that would have said
|
||||
# why — "[RE-INPUT] XamInputGetKeystrokeEx swallowed by IsUIActive" — was being
|
||||
# discarded. A boot harness that throws away the emulator's own account of
|
||||
# itself can only ever report symptoms.
|
||||
LOG="${BOOT_MENU_LOG:-$SHOTS/$TAG-canary.stdout}"
|
||||
# Sign in the profile that EXISTS. Naming a XUID with no profile behind it is
|
||||
# not a no-op: the title opens a sign-in dialog, xam_dialogs_shown_ goes to 1,
|
||||
# and IsUIActive() then swallows every XamInputGetKeystrokeEx for the rest of
|
||||
# the run — a pad that delivers keystrokes to an emulator that discards them.
|
||||
# Measured: 8.4 MILLION swallowed calls in one boot, with the pad log showing
|
||||
# `vk=5800 down/up` arriving normally the whole time.
|
||||
XUID="${SYLPH_XUID:-$(ls "${XENIA_CONTENT:-$HOME/.local/share/Xenia/content}" 2>/dev/null | head -1)}"
|
||||
[ -n "$XUID" ] || { echo "NO PROFILE — run once with --create_profile_if_none=Tag"; exit 2; }
|
||||
echo "signing in profile $XUID"
|
||||
nohup run-canary --apu=sdl --log_mask=13 \
|
||||
--logged_profile_slot_0_xuid=E0300000EFBEA3D4 </dev/null >/dev/null 2>&1 &
|
||||
--logged_profile_slot_0_xuid="$XUID" </dev/null >"$LOG" 2>&1 &
|
||||
sleep 5
|
||||
"$SD/skip_intro.sh" 600 || { echo "BOOT FAILED (skip_intro exit $?)"; exit 1; }
|
||||
sleep 14
|
||||
screenshot "$SHOTS/$TAG-menu.png" >/dev/null 2>&1
|
||||
echo "AT MAIN MENU (cursor on NEW GAME); LOAD GAME is one d-pad step down"
|
||||
echo "emulator log: $LOG"
|
||||
|
||||
@@ -43,12 +43,29 @@ while [ $SECONDS -lt $deadline ]; do
|
||||
[ -n "$(alive)" ] || { echo "EMULATOR GONE at ${SECONDS}s"; exit 4; }
|
||||
d=$(compare -metric RMSE /tmp/f1.png /tmp/f2.png null: 2>&1 | sed 's/ .*//' | cut -d. -f1)
|
||||
d=${d:-0}
|
||||
read -r r g b < <(convert /tmp/f2.png -format "%[fx:int(255*p{625,618}.r)] %[fx:int(255*p{625,618}.g)] %[fx:int(255*p{625,618}.b)]" info:)
|
||||
if [ "$g" -gt 130 ] && [ $((g - r)) -gt 45 ] && [ $((g - b)) -gt 45 ]; then
|
||||
# Classify the whole frame, do NOT probe one absolute pixel. The old test
|
||||
# sampled (625,618) for the green (A) glyph, which is a coordinate from a
|
||||
# 1280x720 grab; `screenshot` now returns the 1279x675 GAME SURFACE, where
|
||||
# that point lands on the copyright line and reads (8,17,31). The symptom was
|
||||
# a 600s TIMEOUT with the title sitting on screen the entire time — measured
|
||||
# 2026-08-19, with screen_id.py calling it "title" on the same frames.
|
||||
if [ "$(python3 "$SD_SI/screen_id.py" /tmp/f2.png | awk '{print $1}')" = "title" ]; then
|
||||
echo "TITLE at ${SECONDS}s -> A"; tapA; exit 0
|
||||
fi
|
||||
# DO NOT tap through the movies. This branch used to, and for a long time it
|
||||
# was harmless only because `vgamepad` did not exist and the call failed
|
||||
# silently. Once the press became real, the boot reached the title in 90s
|
||||
# instead of 434s — and that title accepts NOTHING. menu_draw_capture.sh
|
||||
# records the same thing from the other direction: "a run that tapped (A)
|
||||
# every 4s through the boot delivered 88 presses and ended on a black screen
|
||||
# that never came back", and "the title the attract loop returns to accepts
|
||||
# nothing at all, and 40 taps at 1/s do not change that".
|
||||
#
|
||||
# So: wait the intro out (~3.5 min) and spend the single press on the title
|
||||
# that ends the boot sequence, which is the one that accepts it.
|
||||
if [ "$d" -gt 1500 ]; then
|
||||
echo "movie (rmse $d) at ${SECONDS}s -> skip A"; tapA; sleep 3
|
||||
echo "movie (rmse $d) at ${SECONDS}s -> waiting it out (tapping breaks the title)"
|
||||
sleep 3
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
|
||||
Reference in New Issue
Block a user