The database mount below was one of three ways the decoder could not reach its own oracle. The other two are here. `run-canary` never looked in `Checked/`. It tried `Release/` then `Debug/`, and both of those exist on this box -- an Aug 28 binary and a Jul 19 one. They boot the game perfectly well and carry NO `audit_61` branch probe, so a probe run against either returns zero hits that read as a finding about the game rather than as a stale binary. Configuration is now the outer loop and location the inner one, so a `Checked` build anywhere beats a `Release` build anywhere; `$XENIA_BIN` still overrides everything. Measured here: `Checked` has `audit_61_branch_probe_pcs`, `Release` and `Debug` do not. The launcher also now says which instrumentation is missing BEFORE the run, because the alternative is reading an empty log afterwards and guessing. `build-canary` built `$PROJECT_DIR/xenia-canary`, which does not exist in this container -- the source is bind-mounted at `/canary` and the launcher already exports `XENIA_SRC=/canary`. CONTAINER-NOTES has carried that defect since 2026-08-29 with a symlink workaround and a warning to remember to delete the symlink afterwards. It now reads `$XENIA_SRC` first, so there is nothing to remember. Its default configuration moves Release -> Checked to match what `run-canary` picks; the old default spent a full build on a binary nothing ran. Two documented blockers are refuted rather than deleted, since the sequence of wrong readings is what makes the right one checkable: the CONTAINER-NOTES symlink dance (the warm build volume it was configured against is gone too, removed in the 2026-09-18 cleanup, so the next build configures cleanly against `/canary`), and `upstream-baseline.md`'s "`version.h` is never generated" -- `CMakeLists.txt` generates it at configure time now, with a stub fallback. `decoder-loop.md` claimed the oracle was at `Linux/Release/` and that the probe was on two side branches; both were true when written and neither is now. Verified: five pick_bin cases against the extracted function body, and `strings` on all three real binaries. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
118 lines
5.5 KiB
Bash
Executable File
118 lines
5.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Launch Xenia Canary with the settings this title actually needs.
|
|
#
|
|
# Four of these are not preferences — they are measured requirements, and every
|
|
# one of them cost a debugging session before it was pinned down:
|
|
#
|
|
# --apu=sdl + SDL_AUDIODRIVER=dummy
|
|
# There is no PulseAudio here, so `--apu=nop` looks like the safe muted
|
|
# choice. It is not: the log then fills with "CreateDriver failed for
|
|
# index=0", the guest never gets past the intro movie, and the window
|
|
# stays black for 8+ minutes. The SDL driver against a dummy device is
|
|
# both silent AND lets the title advance.
|
|
#
|
|
# NO --audio flag
|
|
# The RE notes say "--audio --apu=sdl". `--audio` is NOT a cvar in this
|
|
# tree, and an unknown argument is not a friendly error: xenia calls
|
|
# ShowSimpleMessageBox from ParseLaunchArguments, BEFORE logging is
|
|
# initialised, and that SDL dialog blocks on XIfEvent forever. Headless,
|
|
# the symptom is a 10x10 window, an empty log, and no guest memory —
|
|
# which reads like a hang deep in the emulator rather than a typo.
|
|
# If this ever appears to hang at startup, suspect a bad flag first.
|
|
#
|
|
# --hid=file --pad_file=...
|
|
# The old vgamepad path made its device through /dev/uinput, which is NOT
|
|
# namespaced — a pad created inside a container registers with the HOST's
|
|
# input stack and every scripted press leaks to the user's desktop. This
|
|
# driver reads a text file instead. Drive it with tools/re-capture/pad.py.
|
|
# Trap worth remembering: 360 menus poll XamInputGetKeystrokeEx, not
|
|
# GetState, so a stubbed GetKeystroke looks like a completely dead pad.
|
|
#
|
|
# one instance at a time
|
|
# Two emulators (or ours + canary) at once perturbs both and the box.
|
|
# Enforced with a lockfile rather than left to discipline.
|
|
#
|
|
# Usage: run-canary [extra xenia flags...]
|
|
# ISO from $SYLPH_ISO, else the first *.iso under $PROJECT_DIR.
|
|
# Binary from $XENIA_BIN, else the container build, else the repo build.
|
|
set -u
|
|
|
|
LOCK=/tmp/xenia-canary.lock
|
|
exec 9>"$LOCK"
|
|
if ! flock -n 9; then
|
|
echo "run-canary: an emulator is already running (lock $LOCK)." >&2
|
|
echo " Only one at a time — kill it first: pkill -x xenia_canary" >&2
|
|
exit 1
|
|
fi
|
|
|
|
PROJECT_DIR="${PROJECT_DIR:-/work}"
|
|
|
|
# ── Binary ───────────────────────────────────────────────────────────────────
|
|
# `Checked` FIRST. All three configurations can be built, but `Checked` is the
|
|
# one this project actually builds, so it is the one carrying our
|
|
# instrumentation; the `Release/` and `Debug/` binaries beside it are months-old
|
|
# leftovers that still run and still boot the game, which is exactly what makes
|
|
# them dangerous -- a probe run against one reports zero hits and reads as a
|
|
# finding about the game. Measured on this box 2026-09-21: `Checked` has
|
|
# `audit_61_branch_probe_pcs`, `Release` (Aug 28) and `Debug` (Jul 19) do not.
|
|
pick_bin() {
|
|
[ -n "${XENIA_BIN:-}" ] && { echo "$XENIA_BIN"; return; }
|
|
# Configuration is the OUTER loop, location the inner one: a `Checked` build
|
|
# anywhere beats a `Release` build anywhere. The other order picks a stale
|
|
# container `Release` over a fresh instrumented repo `Checked`, which is the
|
|
# exact mistake this is here to stop.
|
|
for cfg in Checked Release Debug; do
|
|
for c in \
|
|
"${XENIA_BUILD_DIR:-/sylph-home/re/canary-build}/bin/Linux/$cfg/xenia_canary" \
|
|
"$PROJECT_DIR/xenia-canary/build/bin/Linux/$cfg/xenia_canary"; do
|
|
[ -x "$c" ] && { echo "$c"; return; }
|
|
done
|
|
done
|
|
}
|
|
BIN="$(pick_bin)"
|
|
if [ -z "${BIN:-}" ]; then
|
|
echo "run-canary: no xenia_canary binary found. Build one with: build-canary" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Say what is in the binary before the run, not after reading an empty log.
|
|
# A missing probe is a build that predates it, never a quiet game.
|
|
for sym in audit_61_branch_probe_pcs RE-DRAW; do
|
|
grep -aqm1 -- "$sym" "$BIN" \
|
|
|| echo "run-canary: ⚠ $BIN has NO '$sym' -- it predates that instrumentation. Rebuild with: build-canary" >&2
|
|
done
|
|
|
|
# ── ISO ──────────────────────────────────────────────────────────────────────
|
|
ISO="${SYLPH_ISO:-}"
|
|
if [ -z "$ISO" ]; then
|
|
# Prefer a REAL file over a symlink and take the largest: the tree carries
|
|
# `xenia-rs/sylpheed.iso` as a symlink to the retail image, and a symlink has
|
|
# already cost a session once (Wine could not resolve it -> "path invalid").
|
|
ISO="$(find "$PROJECT_DIR" -maxdepth 2 -type f -iname '*.iso' -printf '%s\t%p\n' 2>/dev/null \
|
|
| sort -rn | head -1 | cut -f2-)"
|
|
fi
|
|
if [ -z "$ISO" ] || [ ! -f "$ISO" ]; then
|
|
echo "run-canary: no ISO. Set SYLPH_ISO=/path/to/game.iso" >&2
|
|
exit 1
|
|
fi
|
|
ISO="$(readlink -f "$ISO")"
|
|
|
|
export SDL_AUDIODRIVER="${SDL_AUDIODRIVER:-dummy}"
|
|
export DISPLAY="${DISPLAY:-:98}"
|
|
PAD="${XENIA_PAD_FILE:-/tmp/xenia_pad.txt}"
|
|
: > "$PAD"
|
|
|
|
# Guest memory is backed by /dev/shm; a stale file from a killed run confuses
|
|
# the memory readers (gmem.py finds two candidates and picks the dead one).
|
|
rm -f /dev/shm/xenia_memory_* /dev/shm/xenia_code_cache_* 2>/dev/null || true
|
|
|
|
echo "run-canary: $BIN" >&2
|
|
echo " iso: $ISO" >&2
|
|
echo " pad: $PAD display: $DISPLAY shm: $(df -h /dev/shm | awk 'NR==2{print $2}')" >&2
|
|
|
|
exec "$BIN" "$ISO" \
|
|
--apu=sdl \
|
|
--hid=file --pad_file="$PAD" \
|
|
--mute=true \
|
|
"$@"
|