Some checks failed
The trickiest reading on the disc lived in the Bevy viewer: resolving a cutscene's voice to a continuous byte REGION of the sound stream, because the movie voices are one XMA stream chunked into VOICE_*.slb entries whose boundaries do not match the cues -- a cue routinely spans two chunks, so a .slb need not hold the track its name claims. That put the logic most likely to be re-derived incorrectly in the crate least likely to be reused. The Godot port's exporter needs the same answers, and there must be one implementation of them. New `sylpheed_formats::media` owns every case where the bytes of one playable thing are not one archive entry: segment-spanning reads, multi-sub-wave banks, and the voice-region resolution. Callers supply bytes through a `DiscSource` trait, so the viewer keeps its ISO/directory abstraction and a headless consumer gets `DirectorySource` for free. The seam is deliberate: this module returns XMA RIFFs, not PCM. Decoding means shelling out to FFmpeg, which is native-only and a policy decision for the consumer -- everything up to "here are the bytes that belong together" is disc knowledge, everything after it is a codec choice. The four moved functions were previously untested; `tests/media_disc.rs` now pins them, including the negative the corpus paid for -- an unbound movie must stay unvoiced rather than borrow a neighbour's clip, which was tried and played the WRONG recording. The algorithm is unchanged, moved verbatim (same window sizes, same fallbacks). The new disc tests pass; the broader audio suite was not re-run in this pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
88 lines
3.4 KiB
Bash
Executable File
88 lines
3.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Catch a screen being BUILT, not sitting still.
|
|
#
|
|
# `log_ui_draws` armed at the title only ever sees the steady state, and the
|
|
# steady state is the one thing an animation question cannot be asked of. The
|
|
# build — the wordmarks zooming in, the effect sprites wiping across, the black
|
|
# fade lifting — happens in the seconds BEFORE the screen classifier can call it
|
|
# a title.
|
|
#
|
|
# So arm repeatedly and keep every log: each F10 opens a new numbered file and
|
|
# CLOSES the previous one (which stays on disk, complete). Re-arming every few
|
|
# seconds through the boot therefore tiles the whole approach to the title, and
|
|
# whichever file happens to straddle the build contains it. Stop re-arming the
|
|
# instant the title is classified, so a press cannot land mid-build and abandon
|
|
# the one file that matters.
|
|
#
|
|
# NO pad input at all. Ⓐ during the boot has ended a run on a permanent black
|
|
# screen (docs/re/canary-scripted-input-traps.md), and nothing here needs it.
|
|
#
|
|
# Usage: screen_build_capture.sh [out_dir] [timeout_s]
|
|
# FRAMES= frames per capture window (default 1200 ~ 20 s at 60 Hz)
|
|
# MAXDRAWS= hard stop per capture (default 80000)
|
|
# REARM= seconds between F10 presses (default 10)
|
|
set -u
|
|
export HOME=/sylph-home/re SDL_AUDIODRIVER=dummy DISPLAY=:98
|
|
SD="$(cd "$(dirname "$0")" && pwd)"
|
|
OUT="${1:-/sylph-home/re/buildcap}"
|
|
TIMEOUT="${2:-600}"
|
|
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; }
|
|
wide(){ [ "$(identify -format '%w' "$1" 2>/dev/null || echo 0)" -gt 1000 ]; }
|
|
|
|
( cd "$OUT" && nohup run-canary \
|
|
--ui_draw_capture_frames="${FRAMES:-1200}" \
|
|
--ui_draw_capture_max="${MAXDRAWS:-80000}" \
|
|
--create_profile_if_none="${SYLPH_TAG:-SylphRE}" \
|
|
--logged_profile_slot_0_xuid="${SYLPH_XUID:-B13EBABEBABEBABE}" \
|
|
>"$OUT/canary.stdout" 2>"$OUT/canary.stderr" & )
|
|
sleep "${LAUNCH_WAIT:-8}"
|
|
until xdotool search --name "Xenia-canary" >/dev/null 2>&1; do
|
|
[ -n "$(alive)" ] || { echo "EMULATOR GONE before the window appeared"; exit 4; }
|
|
sleep 1
|
|
done
|
|
win="$(xdotool search --name "Xenia-canary" | tail -1)"
|
|
echo "WINDOW=$win"
|
|
|
|
arm(){
|
|
xdotool windowactivate "$win" 2>/dev/null
|
|
xdotool key --window "$win" F10 2>/dev/null
|
|
xdotool key F10 2>/dev/null
|
|
}
|
|
|
|
last_arm=-999
|
|
deadline=$(( SECONDS + TIMEOUT ))
|
|
s=none
|
|
while [ $SECONDS -lt $deadline ]; do
|
|
[ -n "$(alive)" ] || { echo "EMULATOR GONE at ${SECONDS}s"; exit 4; }
|
|
shot /tmp/sbc.png
|
|
if [ -s /tmp/sbc.png ]; then
|
|
wide /tmp/sbc.png || { echo "GRAB IS NOT THE GAME SURFACE at ${SECONDS}s"; exit 6; }
|
|
s="$(python3 "$SD/screen_id.py" /tmp/sbc.png | awk '{print $1}')"
|
|
fi
|
|
if [ "$s" = "${WANT:-title}" ]; then
|
|
echo "t=${SECONDS}s $s <- STOP re-arming"
|
|
cp /tmp/sbc.png "$OUT/reached.png"
|
|
break
|
|
fi
|
|
if [ $(( SECONDS - last_arm )) -ge "${REARM:-10}" ]; then
|
|
arm; last_arm=$SECONDS
|
|
echo "t=${SECONDS}s $s (armed)"
|
|
else
|
|
echo "t=${SECONDS}s $s"
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
# Let the straddling window run itself out rather than cutting it short.
|
|
echo "waiting for the in-flight capture to close..."
|
|
for _ in $(seq 1 40); do
|
|
grep -q "UI-CAP. done" "$OUT/canary.stdout" 2>/dev/null && break
|
|
sleep 1
|
|
done
|
|
grep -i "UI-CAP" "$OUT/canary.stdout" | tail -6
|
|
ls -l "$OUT"/xenia_re_ui_draws_*.log 2>/dev/null || echo "NO CAPTURE LOG"
|
|
echo "SCREEN BUILD CAPTURE DONE (screen=$s, emulator left running)"
|