This repository has been archived on 2026-09-16. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Syplheed-Reborn/tools/re-capture/skip_intro.sh
Sylpheed RE agent 0d8d2ab758 tools: a reliability harness, and a title test that also requires a static frame
resume_reliability.sh boots N times and records, per boot, whether the title's
loader thread was created and whether it ever became the CALLER of a kernel call.
That is the thing the threading fix targets, and it is binary; "did the menu
appear" is a worse test because it needs the game to get further and the attract
loop confuses it.

Two defects fixed on the way, both found by the harness disagreeing with itself:

  * is_title.py's glyph count alone still fired at 113s and 173s, during the
    intro movie, and the single press was wasted there. Measured, not guessed:
    on those runs NO loader thread was created at all, and the title handler
    always creates one - even on the boots where it then fails to run. The title
    check now also requires the frame to be STATIC, reusing the RMSE between the
    two grabs 0.6s apart that the movie branch already computes, and raises the
    glyph threshold to 800 (a real title measures ~1450-1520, the SQUARE ENIX
    logo 0).

  * `grep -ac ... || echo 0` corrupted every CSV field it guarded: grep exits 1
    on zero matches, so the fallback APPENDED a second value and the field came
    out "0 0".

Retrospective before/after from the logs already on disc, using ResolvePath after
the resume (logged at every verbosity, so it is comparable across all runs):
before the fix 1 of 5 boots that created a loader thread went on to load assets;
after it, 2 of 2. n is small and a proper count is running.
2026-08-19 10:59:35 +00:00

79 lines
4.5 KiB
Bash
Executable File

#!/usr/bin/env bash
# Drive the boot sequence to the main menu without a human in the loop.
# - movie playing (two grabs 0.6s apart differ a lot) -> tap A to skip
# - static screen -> if the green "PRESS (A) BUTTON" glyph is there, tap A and stop
# Static logo screens are left alone, so a stray tap can never land on NEW GAME.
#
# LIVENESS IS CHECKED EVERY ITERATION, and that is not a nicety. When the
# display died 3 minutes into a run, `screenshot` started failing silently and
# left /tmp/f1.png and /tmp/f2.png at their last contents — two *stale* files,
# which compare to a constant non-zero RMSE, which reads exactly like "the
# screen is changing a lot". So the loop reported "movie -> skip A" every 5 s
# for the full 600 s timeout with no emulator and no X server running. A dead
# display and a playing movie must never be able to look the same.
set -u
export HOME=/sylph-home/re
DISP="${DISPLAY:-:98}"
alive(){ ps -o pid=,stat= -C xenia_canary 2>/dev/null | awk '$2 !~ /^Z/ {print $1}'; }
# Press through pad.py, NOT `vgamepad`. That command no longer exists — the
# uinput pad was replaced by the `--hid=file` driver — and because this script
# runs without `set -e`, calling it failed silently: the title branch pressed
# nothing and still `exit 0`, so a caller was told "TITLE -> A" while the game
# sat on the title screen forever. Fail loudly instead.
SD_SI="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
tapA(){ python3 "$SD_SI/pad.py" tap A 0.25 || { echo "PAD PRESS FAILED"; exit 6; }; }
# The X root keeps the DEAD session's last frame, so a fresh launch would be
# detected as "already at the title". Blank it, and wait for the new window.
xsetroot -solid black 2>/dev/null || true
until xdotool search --name "Xenia-canary" >/dev/null 2>&1; do
xdpyinfo -display "$DISP" >/dev/null 2>&1 || { echo "DISPLAY LOST at ${SECONDS}s (before the window appeared)"; exit 3; }
[ -n "$(alive)" ] || { echo "EMULATOR GONE at ${SECONDS}s (before the window appeared)"; exit 4; }
sleep 1
done
deadline=$(( SECONDS + ${1:-900} ))
while [ $SECONDS -lt $deadline ]; do
rm -f /tmp/f1.png /tmp/f2.png
screenshot /tmp/f1.png >/dev/null 2>&1; sleep 0.6
screenshot /tmp/f2.png >/dev/null 2>&1
if [ ! -s /tmp/f1.png ] || [ ! -s /tmp/f2.png ]; then
xdpyinfo -display "$DISP" >/dev/null 2>&1 \
|| { echo "DISPLAY LOST at ${SECONDS}s"; exit 3; }
echo "SCREENSHOT FAILED at ${SECONDS}s with the display up"; exit 5
fi
[ -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}
# COUNT the green (A) glyph over the whole frame; do not probe one absolute
# pixel and do not trust screen_id.py here. The pixel probe used a 1280x720
# coordinate against the 1279x675 game surface and always read the copyright
# line (600s TIMEOUT with the title on screen). screen_id.py fixed that and
# then over-matched the other way: it called the SQUARE ENIX publisher logo
# "title" 151s into a boot, and this script spent its one press there.
# is_title.py counts glyph pixels, which only the interactive title has.
# AND the frame must be STATIC. A glyph count alone still fired at 113-173s,
# during the intro movie, and the press was wasted — measured by the loader
# thread that the title handler ALWAYS creates being absent entirely on those
# runs. The resting title barely changes between two grabs 0.6s apart; a movie
# does, and `$d` is already computed above for exactly that distinction.
if [ "$d" -le 1500 ] && python3 "$SD_SI/is_title.py" /tmp/f2.png 800 >/dev/null 2>&1; 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 -> waiting it out (tapping breaks the title)"
sleep 3
fi
sleep 1
done
echo "TIMEOUT"; exit 1