#!/usr/bin/env bash # Probe the challenge-mission gate on the running game. # # The gate (docs/re/challenge-mission-gate.md): GamePart_ChallengeMission tests a # CLEARED-STAGE bitmask on a static singleton at guest 0x828F4070 — # word A 0x828F40C0 bit = stage id, for ids < 24 (story 1-16, tutorial 18-23) # word B 0x828F4814 bit = stage id - 24 (challenge 24-29) # so setting every bit should make all six challenge missions available without # playing the campaign. This boots, reaches the title, pokes both words, and # screenshots the menus so the result can be seen. # # Runs as ONE blocking foreground call on purpose: setsid'd processes are reaped # at turn boundaries, so a session split across calls loses its emulator. # # Usage: challenge_probe.sh [boot_timeout_s] set -u export HOME=/sylph-home/re export DISPLAY=:99 export SDL_AUDIODRIVER=dummy export XENIA_PAD_FILE=/tmp/xenia_pad.txt HERE="$(cd "$(dirname "$0")" && pwd)" pad() { python3 "$HERE/pad.py" "$@"; } poke() { python3 "$HERE/gpoke.py" "$@"; } SHOTS="$HOME/shots" BOOT_TIMEOUT="${1:-420}" mkdir -p "$SHOTS" say() { echo "[$(date +%H:%M:%S)] $*"; } # --- clean slate ------------------------------------------------------------- pkill -9 -x xenia_canary 2>/dev/null sleep 1 rm -f /dev/shm/xenia_* 2>/dev/null : > "$XENIA_PAD_FILE" # --- launch ------------------------------------------------------------------ say "launching canary (lavapipe, file pad)" run-canary --audio --apu=sdl --log_mask=13 \ --logged_profile_slot_0_xuid=E0300000EFBEA3D4 \ --hid=file --pad_file="$XENIA_PAD_FILE" & CANARY_PID=$! trap 'pkill -9 -x xenia_canary 2>/dev/null' EXIT # --- wait for the title ------------------------------------------------------ # Oracle: the green "PRESS (A) BUTTON" glyph at (625,618). say "waiting for the title (up to ${BOOT_TIMEOUT}s)" TITLE=0 for _ in $(seq 1 "$BOOT_TIMEOUT"); do if screenshot /tmp/title-probe.png >/dev/null 2>&1; then read -r r g b < <(convert /tmp/title-probe.png -format \ "%[fx:int(255*p{625,618}.r)] %[fx:int(255*p{625,618}.g)] %[fx:int(255*p{625,618}.b)]" info: 2>/dev/null) if [ -n "${g:-}" ] && [ "$g" -gt 130 ] && [ $((g - r)) -gt 45 ] && [ $((g - b)) -gt 45 ]; then say "TITLE detected (rgb $r,$g,$b)" TITLE=1 break fi fi sleep 1 done [ "$TITLE" = 1 ] || { say "TIMEOUT: no title"; screenshot "$SHOTS/chal-00-timeout.png"; exit 1; } # --- prove the file pad works before trusting anything else ------------------- say "pad check: tapping A at the title" pad tap A 0.25 sleep 3 screenshot "$SHOTS/chal-01-after-A.png" >/dev/null say "file-pad log lines so far:" grep -c 'file-pad' "$HOME/canary.stdout" 2>/dev/null || true grep 'file-pad' "$HOME/canary.stdout" 2>/dev/null | tail -3 # --- read the gate words BEFORE poking --------------------------------------- say "gate words before poke:" poke r32 0x828F40C0 1 poke r32 0x828F4814 1 # --- poke -------------------------------------------------------------------- say "poking word A = 0xFFFFFFFF, word B = 0x3F" poke w32 0x828F40C0 0xFFFFFFFF poke w32 0x828F4814 0x0000003F # --- look at the menu -------------------------------------------------------- sleep 2 screenshot "$SHOTS/chal-02-mainmenu.png" >/dev/null say "screenshots in $SHOTS: chal-01-after-A.png chal-02-mainmenu.png" say "done — leaving the emulator running for follow-up" trap - EXIT