options_draw_capture.sh counted three DOWNs. A dropped press lands the cursor on TUTORIAL, whose (A) starts the tutorial. My two attempts to avoid counting were both worse: 'press until the cursor stops moving' is unreachable on a wrapping menu, and an earlier version of it read the same row twice after a lost press and pressed (A) on NEW GAME. The wrap is the landmark. Press down until the row DECREASES -- the cursor is then on item 1 whatever it started on -- and take three steps, retrying any press that does not move the cursor, aborting rather than pressing (A) if one never lands. Needs the row to be monotone in the item, not calibrated. Also: 1200 s title deadline, and the emulator is killed at the end. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
104 lines
4.2 KiB
Bash
Executable File
104 lines
4.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Boot -> first title -> main menu -> OPTIONS, and capture the UI draw order and
|
|
# the BLEND STATE there. OPTIONS is the fourth menu item.
|
|
#
|
|
# 🔴 It used to say "so: three d-pad steps down, then (A)" and do exactly that.
|
|
# Counting presses does not work here -- presses get dropped, and a dropped one
|
|
# lands the cursor on TUTORIAL, whose (A) starts the tutorial. Two later attempts
|
|
# to avoid counting were worse: "press until the cursor stops moving" is
|
|
# unreachable on a menu that WRAPS, and an earlier version of it read the same
|
|
# row twice because the first press was lost and pressed (A) on NEW GAME.
|
|
#
|
|
# What works is to use the wrap as the landmark. Press down until the row
|
|
# DECREASES -- that is the wrap, and the cursor is now on item 1 whatever it
|
|
# started on -- then take exactly three verified steps, retrying any press that
|
|
# does not move the cursor. Geometry-free: it needs the row to be monotone in the
|
|
# item, not calibrated, and ring_row.py's ROW0/SPACING are x11grab constants that
|
|
# do not fit a `screenshot` grab (METHOD.md).
|
|
#
|
|
# The recipe for the first two hops is measured, not guessed (see
|
|
# docs/re/canary-scripted-input-traps.md): the title that ENDS THE BOOT accepts a
|
|
# single (A); the one the attract loop returns to accepts nothing. So never tap
|
|
# during the boot, and take the first title.
|
|
set -u
|
|
export HOME=/sylph-home/re SDL_AUDIODRIVER=dummy DISPLAY=:98
|
|
SD="$(cd "$(dirname "$0")" && pwd)"
|
|
OUT="${1:-/sylph-home/re/optcap}"
|
|
mkdir -p "$OUT"; rm -f "$OUT"/xenia_re_ui_draws_*.log
|
|
shot(){ screenshot "$1" >/dev/null 2>&1; }
|
|
screen(){ shot /tmp/odc.png; python3 "$SD/screen_id.py" /tmp/odc.png | awk '{print $1}'; }
|
|
alive(){ ps -o pid=,stat= -C xenia_canary 2>/dev/null | awk '$2 !~ /^Z/ {print $1}'; }
|
|
|
|
# No capture flags at launch: F10 arms the capture unconditionally now, and
|
|
# launching with --log_ui_draws correlated (0 of 7) with the title refusing (A).
|
|
( cd "$OUT" && nohup run-canary --mem_watch=false \
|
|
--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)"
|
|
|
|
deadline=$(( SECONDS + ${DEADLINE:-1200} ))
|
|
while [ $SECONDS -lt $deadline ]; do
|
|
s="$(screen)"; echo "t=${SECONDS}s $s"
|
|
[ "$s" = "title" ] && break
|
|
sleep 1
|
|
done
|
|
[ "$s" = "title" ] || { echo "NO BOOT TITLE"; exit 1; }
|
|
|
|
echo "-> A on the boot title"
|
|
python3 "$SD/pad.py" tap A 0.3
|
|
for _ in 1 2 3 4 5 6 7 8; do sleep 3; s="$(screen)"; echo " $s"; [ "$s" = "menu" ] && break; done
|
|
shot "$OUT/menu.png"
|
|
[ "$s" = "menu" ] || { echo "NO MENU (screen=$s)"; exit 2; }
|
|
|
|
row(){ shot /tmp/odc_row.png; python3 - <<'PY'
|
|
from PIL import Image
|
|
import sys; sys.path.insert(0,"/work/tools/re-capture")
|
|
import ring_row
|
|
try: print("%.1f" % ring_row.ring_row(Image.open("/tmp/odc_row.png")))
|
|
except Exception: print("nan")
|
|
PY
|
|
}
|
|
down(){ python3 "$SD/pad.py" tap DOWN 0.2; sleep 3; }
|
|
|
|
echo "-> find the wrap, then take three verified steps to OPTIONS"
|
|
prev="$(row)"; echo " row $prev"
|
|
wrapped=0
|
|
for _ in $(seq 1 12); do
|
|
down; cur="$(row)"; echo " row $cur"
|
|
if [ "$(python3 -c "print(1 if float('$cur') < float('$prev') - 5 else 0)")" = "1" ]; then
|
|
wrapped=1; prev="$cur"; break
|
|
fi
|
|
prev="$cur"
|
|
done
|
|
[ "$wrapped" = 1 ] || { echo "NEVER SAW THE WRAP -- not pressing (A)"; exit 3; }
|
|
echo " wrapped to item 1"
|
|
for step in 1 2 3; do
|
|
ok=0
|
|
for _ in 1 2 3; do
|
|
down; cur="$(row)"; echo " step $step row $cur"
|
|
if [ "$(python3 -c "print(1 if float('$cur') > float('$prev') + 5 else 0)")" = "1" ]; then
|
|
ok=1; prev="$cur"; break
|
|
fi
|
|
echo " (press dropped, retrying)"
|
|
done
|
|
[ "$ok" = 1 ] || { echo "STEP $step NEVER LANDED -- not pressing (A)"; exit 3; }
|
|
done
|
|
shot "$OUT/menu-on-options.png"
|
|
python3 "$SD/pad.py" tap A 0.3
|
|
sleep 8
|
|
shot "$OUT/options.png"
|
|
echo "screen after A: $(screen)"
|
|
|
|
xdotool windowactivate --sync "$win"; sleep 1
|
|
xdotool key F10; sleep 3
|
|
xdotool mousemove 900 400 click 1; sleep 2
|
|
shot "$OUT/options-armed.png"
|
|
ls -l "$OUT"/xenia_re_ui_draws_*.log 2>/dev/null || echo "NO CAPTURE LOG"
|
|
grep -i "UI-CAP" "$OUT/canary.stdout" | tail -3
|
|
echo "OPTIONS CAPTURE DONE"
|
|
pkill -x xenia_canary 2>/dev/null; sleep 2; echo "emulator killed"
|