Completes the reach of ui-blend-mode-measured.md, which was explicitly two screens and one session and explicitly not the title. Three things it does differently from menu_blend_capture.sh, each one paid for yesterday: a 1200 s title deadline instead of 420 (the 420 fired while the game was minutes from the settled title); the title arm is gated on the plate pulse because build 2's PRESS (A) plate is part of what is being measured; and on timeout it leaves the emulator up and says to attach rather than reboot. Committed before it is run, per METHOD.md. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
88 lines
3.9 KiB
Bash
Executable File
88 lines
3.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Boot -> TITLE -> arm the UI draw/blend capture there -> (A) -> MAIN MENU -> arm again.
|
|
#
|
|
# Completes the blend table started in docs/re/structures/ui-blend-mode-measured.md,
|
|
# whose stated reach was "two screens, one session" and explicitly NOT the title.
|
|
# The second arm at the menu is deliberate: it replicates the main-menu result in a
|
|
# SECOND session, which is the part of that page's reach a rerun can actually widen.
|
|
#
|
|
# Differences from menu_blend_capture.sh, each one paid for:
|
|
#
|
|
# * DEADLINE defaults to 1200 s, not 420. On 2026-08-31 a 420 s wait reported
|
|
# "NEVER REACHED THE TITLE" and the emulator, left running, was at the settled
|
|
# title minutes later and took (A) first try. A timeout measures the timeout.
|
|
# * The title arm is gated on the PLATE PULSE (wait_plate_pulse.py), not on
|
|
# screen_id alone: build 2's `PRESS (A)` plate is part of what is being
|
|
# measured, and it pulses.
|
|
# * On timeout it LEAVES THE EMULATOR UP and says so, so the run can be rescued
|
|
# by attaching instead of rebooting.
|
|
# * No --log_ui_draws. Canary's own source records that arming is unconditional
|
|
# now and that launching with the flag correlates with the title refusing (A),
|
|
# 0 of 7 runs against 4 of 5 without.
|
|
#
|
|
# F10 opens Xenia's menu bar, and any Xenia UI makes IsUIActive() true, after
|
|
# which XamInputGetKeystrokeEx returns SUCCESS with a zeroed keystroke and the
|
|
# guest's unbounded pump queues one entry per poll -- the crash in
|
|
# docs/re/structures/title-a-press-fault.md. So the surface is clicked
|
|
# immediately after every F10, and stdout is size-checked afterwards.
|
|
#
|
|
# Usage: title_blend_capture.sh [out_dir]
|
|
set -u
|
|
export HOME=/sylph-home/re SDL_AUDIODRIVER=dummy DISPLAY=:98
|
|
SD="$(cd "$(dirname "$0")" && pwd)"
|
|
OUT="${1:-/sylph-home/re/titleblend}"
|
|
DEADLINE="${DEADLINE:-1200}"
|
|
mkdir -p "$OUT"; rm -f "$OUT"/xenia_re_ui_draws_*.log
|
|
alive(){ ps -o pid=,stat= -C xenia_canary 2>/dev/null | awk '$2 !~ /^Z/ {print $1}'; }
|
|
shot(){ screenshot "$1" >/dev/null 2>&1; }
|
|
screen(){ shot /tmp/tbc.png; python3 "$SD/screen_id.py" /tmp/tbc.png | awk '{print $1}'; }
|
|
arm(){ # $1 = label
|
|
xdotool windowactivate --sync "$win"; sleep 1
|
|
xdotool key F10; sleep 2
|
|
xdotool mousemove 900 400 click 1; sleep 2
|
|
echo "ARMED ($1) at ${SECONDS}s -- stdout $(stat -c%s "$OUT/canary.stdout") bytes"
|
|
}
|
|
|
|
( cd "$OUT" && nohup run-canary --mem_watch=false \
|
|
--ui_draw_capture_frames="${FRAMES:-4}" --ui_draw_capture_max="${MAXDRAWS:-4000}" \
|
|
--logged_profile_slot_0_xuid=B13EBABEBABEBABE \
|
|
>"$OUT/canary.stdout" 2>"$OUT/canary.stderr" & )
|
|
sleep 8
|
|
until xdotool search --name "Xenia-canary" >/dev/null 2>&1; do
|
|
[ -n "$(alive)" ] || { echo "EMULATOR GONE"; exit 4; }; sleep 1
|
|
done
|
|
win="$(xdotool search --name "Xenia-canary" | tail -1)"
|
|
echo "WINDOW=$win"
|
|
|
|
s=""
|
|
until [ "$s" = "title" ]; do
|
|
[ $SECONDS -lt $DEADLINE ] || { echo "TIMEOUT at ${SECONDS}s, screen=$s -- EMULATOR LEFT RUNNING, attach rather than reboot"; exit 1; }
|
|
s="$(screen)"; echo "t=${SECONDS}s $s"
|
|
[ "$s" = "title" ] || sleep 4
|
|
done
|
|
echo "TITLE at ${SECONDS}s"
|
|
|
|
# The plate is part of the title. Gate on its pulse before arming.
|
|
timeout 180 python3 "$SD/wait_plate_pulse.py" || echo " (plate gate did not fire; arming anyway)"
|
|
shot "$OUT/title.png"
|
|
arm title
|
|
ls -l "$OUT"/xenia_re_ui_draws_*.log 2>/dev/null || echo "NO TITLE LOG -- F10 was lost"
|
|
|
|
# One tap. The title that ENDS the boot accepts a single (A); repeating is pointless.
|
|
python3 "$SD/pad.py" tap A 0.3
|
|
for _ in 1 2 3 4 5 6; do
|
|
sleep 4; s="$(screen)"; echo " after A: $s"
|
|
[ "$s" = "menu" ] && break
|
|
done
|
|
shot "$OUT/menu.png"
|
|
if [ "$s" = "menu" ]; then
|
|
arm menu
|
|
else
|
|
echo "NO MENU (screen=$s) -- title capture kept"
|
|
fi
|
|
ls -l "$OUT"/xenia_re_ui_draws_*.log 2>/dev/null
|
|
grep -i "UI-CAP" "$OUT/canary.stdout" | tail -6
|
|
echo "stdout $(stat -c%s "$OUT/canary.stdout") bytes (a guest fault dumps ~100 MB/30 s)"
|
|
pkill -x xenia_canary 2>/dev/null; sleep 2
|
|
echo "DONE at ${SECONDS}s"
|