#!/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 ─────────────────────────────────────────────────────────────────── pick_bin() { [ -n "${XENIA_BIN:-}" ] && { echo "$XENIA_BIN"; return; } for c in \ "${XENIA_BUILD_DIR:-/sylph-home/re/canary-build}/bin/Linux/Release/xenia_canary" \ "${XENIA_BUILD_DIR:-/sylph-home/re/canary-build}/bin/Linux/Debug/xenia_canary" \ "$PROJECT_DIR/xenia-canary/build/bin/Linux/Release/xenia_canary" \ "$PROJECT_DIR/xenia-canary/build/bin/Linux/Debug/xenia_canary"; do [ -x "$c" ] && { echo "$c"; return; } 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 # ── 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 \ "$@"