menu_blend_capture.sh counted two DOWNs to reach EXTRAS, which is wrong twice over -- EXTRAS is the fifth item, and on 2026-08-31 four DOWNs landed on OPTIONS because one press was dropped. It now presses until the cursor stops moving, which needs no item count and no row calibration. Its title deadline follows the same change as title_blend_capture.sh, 1200 s not 420. ui_blend_map.py takes a comma-separated build list, because the live title is TWO builds composited -- 4 draws the art, 2 draws the PRESS (A) plate -- and a one-build size table cannot name the elements of a title capture. frame_alpha_census takes its builds from argv for the same reason. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wuu56cE8vJGTBtn1ppsk8v
107 lines
4.6 KiB
Bash
Executable File
107 lines
4.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Boot -> title -> MAIN MENU (or EXTRAS) -> arm the UI draw capture and read the
|
|
# BLEND STATE the guest set for each draw.
|
|
#
|
|
# Why this exists rather than `menu_draw_capture.sh`: that script launches with
|
|
# `--log_ui_draws=true`, and Canary's own source records why that is now a bad
|
|
# idea — `RequestUiDrawCapture()` in command_processor.cc says arming is
|
|
# unconditional since the flag "correlates, across 12 runs, with the title screen
|
|
# refusing (A) (0 of 7 with the flag, 4 of 5 without)". The flag is OBSOLETE and
|
|
# only kept so old command lines parse. So this one does not pass it.
|
|
#
|
|
# The measurement: `CaptureUiDrawForRE` now also logs RB_BLENDCONTROL0,
|
|
# RB_COLORCONTROL and RB_COLOR_MASK per draw, raw and decoded. That is what the
|
|
# GPU was actually told, which is the only thing that can settle whether the
|
|
# menu's `ptframe1`/`ptframe2` are composited with something other than
|
|
# alpha-over — nothing on the disc selects a per-element mode
|
|
# (docs/re/structures/t32-blend-mode-not-on-disc.md).
|
|
#
|
|
# Two traps this inherits from menu_draw_capture.sh, both measured:
|
|
# * (A) at the title is accepted only intermittently, but the title that ENDS
|
|
# the boot accepts a single tap; repeating is pointless.
|
|
# * F10 arms the capture AND opens the emulator's menu bar. Any Xenia UI makes
|
|
# IsUIActive() true, and then XamInputGetKeystrokeEx returns SUCCESS with a
|
|
# zeroed keystroke forever, which is the guest-side crash in
|
|
# docs/re/structures/title-a-press-fault.md. So click the game surface
|
|
# immediately after F10 to dismiss it.
|
|
#
|
|
# Usage: menu_blend_capture.sh [out_dir]
|
|
# SCREEN=extras walk one step right and (A) into EXTRAS before arming
|
|
set -u
|
|
export HOME=/sylph-home/re SDL_AUDIODRIVER=dummy DISPLAY=:98
|
|
SD="$(cd "$(dirname "$0")" && pwd)"
|
|
OUT="${1:-/sylph-home/re/blendcap}"
|
|
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/mbc.png; python3 "$SD/screen_id.py" /tmp/mbc.png | awk '{print $1}'; }
|
|
|
|
( 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"
|
|
|
|
# 1. WAIT for the title. Do not tap through the intro: it is ~3.5 minutes and it
|
|
# gets there on its own; a run that tapped every 4 s delivered 88 presses and
|
|
# ended on a black screen.
|
|
s=""
|
|
deadline=$(( SECONDS + ${DEADLINE:-1200} ))
|
|
while [ $SECONDS -lt $deadline ]; do
|
|
s="$(screen)"; echo "t=${SECONDS}s $s"
|
|
[ "$s" = "title" ] && break
|
|
sleep 4
|
|
done
|
|
[ "$s" = "title" ] || { echo "NEVER REACHED THE TITLE"; exit 1; }
|
|
|
|
# 2. ONE tap.
|
|
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"
|
|
[ "$s" = "menu" ] || { echo "NO MENU (screen=$s)"; exit 2; }
|
|
echo "MENU at ${SECONDS}s"
|
|
|
|
if [ "${SCREEN:-menu}" = "extras" ]; then
|
|
# EXTRAS is the LAST of the five items and the menu opens on NEW GAME. Do not
|
|
# count presses: on 2026-08-31 four DOWNs landed on OPTIONS because one was
|
|
# dropped. Press until the cursor STOPS MOVING instead, which needs no item
|
|
# count and no row calibration -- `ring_row.py`'s ROW0/SPACING are x11grab
|
|
# constants and are 45 px out on a `screenshot` grab (see METHOD.md), but the
|
|
# raw row it returns is still a monotone function of the item.
|
|
row(){ shot /tmp/mbc_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/mbc_row.png")))
|
|
except Exception: print("nan")
|
|
PY
|
|
}
|
|
prev="$(row)"; echo " cursor row $prev"
|
|
for _ in 1 2 3 4 5 6 7 8; do
|
|
python3 "$SD/pad.py" tap DOWN 0.2; sleep 2
|
|
cur="$(row)"; echo " cursor row $cur"
|
|
[ "$cur" = "$prev" ] && break
|
|
prev="$cur"
|
|
done
|
|
python3 "$SD/pad.py" tap A 0.3; sleep 5
|
|
s="$(screen)"; echo " after EXTRAS attempt: $s"; shot "$OUT/extras.png"
|
|
fi
|
|
|
|
# 3. arm, then dismiss the menu bar F10 opened
|
|
xdotool windowactivate --sync "$win"; sleep 1
|
|
xdotool key F10; sleep 3
|
|
xdotool mousemove 900 400 click 1; sleep 2
|
|
shot "$OUT/after-f10.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
|
|
pkill -x xenia_canary 2>/dev/null; sleep 2
|
|
echo "DONE at ${SECONDS}s (emulator killed)"
|