tools: capture the title's blend states, and replicate the menu in a second session
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
This commit is contained in:
87
tools/re-capture/title_blend_capture.sh
Executable file
87
tools/re-capture/title_blend_capture.sh
Executable file
@@ -0,0 +1,87 @@
|
||||
#!/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"
|
||||
Reference in New Issue
Block a user