Merge pull request #123 from outis1one/claude/happy-euler-co0mgo

wolf: make ES-DE usable on first run (RetroArch cores, BIOS mount)
This commit is contained in:
Outis
2026-06-23 10:53:01 -04:00
committed by GitHub
+152 -9
View File
@@ -231,6 +231,7 @@ EOF
echo " - Write $WOLF_DIR/docker-compose.yml and $WOLF_DIR/manage.sh" echo " - Write $WOLF_DIR/docker-compose.yml and $WOLF_DIR/manage.sh"
echo " - Open Moonlight UFW ports: TCP ${WOLF_PORTS_TCP[*]} / UDP ${WOLF_PORTS_UDP[*]}" echo " - Open Moonlight UFW ports: TCP ${WOLF_PORTS_TCP[*]} / UDP ${WOLF_PORTS_UDP[*]}"
echo " - Start Wolf and inject Steam + EmulationStation app profiles" echo " - Start Wolf and inject Steam + EmulationStation app profiles"
echo " - Create bios/ + retroarch/cores/ on the game drive and pre-download RetroArch cores (~1.5 GB)"
return 0 return 0
fi fi
@@ -700,7 +701,8 @@ UDEV
"$GAME_STORAGE_DIR/media" "$GAME_STORAGE_DIR/lutris" \ "$GAME_STORAGE_DIR/media" "$GAME_STORAGE_DIR/lutris" \
"$GAME_STORAGE_DIR/firefox" "$GAME_STORAGE_DIR/minecraft" \ "$GAME_STORAGE_DIR/firefox" "$GAME_STORAGE_DIR/minecraft" \
"$GAME_STORAGE_DIR/kodi" "$GAME_STORAGE_DIR/emulators" \ "$GAME_STORAGE_DIR/kodi" "$GAME_STORAGE_DIR/emulators" \
"$GAME_STORAGE_DIR/steam-cache" "$GAME_STORAGE_DIR/steam-cache" \
"$GAME_STORAGE_DIR/bios" "$GAME_STORAGE_DIR/retroarch/cores"
# ── Wolf state folder on the game drive ─────────────────────────────────── # ── Wolf state folder on the game drive ───────────────────────────────────
# This is the clean way to keep Steam (and everything else) off the OS # This is the clean way to keep Steam (and everything else) off the OS
@@ -1299,7 +1301,10 @@ CATALOG = {
mounts=[f'{games}/roms:/ROMs:rw', mounts=[f'{games}/roms:/ROMs:rw',
f'{games}/saves:/mnt/games/saves:rw', f'{games}/saves:/mnt/games/saves:rw',
f'{games}/media:/media:rw', f'{games}/media:/media:rw',
f'{games}/emulators:/mnt/games/emulators:rw'], f'{games}/bios:/home/retro/bioses:rw',
f'{games}/retroarch/cores:/home/retro/.config/retroarch/cores:rw',
f'{games}/emulators:/mnt/games/emulators:rw',
f'{games}/emulators:/home/retro/Applications:rw'],
env=STD_ENV, cap_add=STD_CAP, security_opt=[], ipc_mode='host', env=STD_ENV, cap_add=STD_CAP, security_opt=[], ipc_mode='host',
ulimits=[], privileged=False, ulimits=[], privileged=False,
), ),
@@ -1319,7 +1324,9 @@ CATALOG = {
icon='https://games-on-whales.github.io/wildlife/apps/retroarch/assets/icon.png', icon='https://games-on-whales.github.io/wildlife/apps/retroarch/assets/icon.png',
image='ghcr.io/games-on-whales/retroarch:edge', image='ghcr.io/games-on-whales/retroarch:edge',
mounts=[f'{games}/roms:/ROMs:rw', mounts=[f'{games}/roms:/ROMs:rw',
f'{games}/saves:/mnt/games/saves:rw'], f'{games}/saves:/mnt/games/saves:rw',
f'{games}/bios:/home/retro/bioses:rw',
f'{games}/retroarch/cores:/home/retro/.config/retroarch/cores:rw'],
env=STD_ENV, cap_add=STD_CAP, security_opt=[], ipc_mode='host', env=STD_ENV, cap_add=STD_CAP, security_opt=[], ipc_mode='host',
ulimits=[], privileged=False, ulimits=[], privileged=False,
), ),
@@ -1469,6 +1476,80 @@ PYEOF
docker compose restart wolf docker compose restart wolf
echo "Wolf restarted. Apps will appear in Moonlight on next connection." echo "Wolf restarted. Apps will appear in Moonlight on next connection."
;; ;;
cores)
# Download libretro (RetroArch) cores so retro games launch on first run.
# ES-DE / RetroArch ship with NO cores; both ES-DE's bundled config and
# GoW's rom_launcher.sh call ~/.config/retroarch/cores/*.so. Cores live on
# the game drive (retroarch/cores/) and are bind-mounted into the ES-DE and
# RetroArch containers at ~/.config/retroarch/cores.
#
# Usage: ./manage.sh cores [all|common] [force]
# all - every core on the libretro buildbot (~1.5 GB) [default]
# common - the ~30 cores GoW wires up + popular ES-DE defaults
# force - re-download cores already present (otherwise they're skipped)
GAME_DIR=$(grep '^GAME_STORAGE_DIR=' "$SCRIPT_DIR/.env" 2>/dev/null | cut -d= -f2-)
if [ -z "$GAME_DIR" ]; then read -r -p " Game storage path: " GAME_DIR; fi
if [ -z "$GAME_DIR" ]; then echo "No game storage path."; exit 1; fi
CORES_DIR="$GAME_DIR/retroarch/cores"
SCOPE="${2:-all}"
FORCE="${3:-}"
BASE="https://buildbot.libretro.com/nightly/linux/x86_64/latest"
mkdir -p "$CORES_DIR"
# 'common' = the cores GoW's rom_launcher.sh wires up, plus a few very
# common ES-DE Linux defaults (NES/PCE/NDS/C64/ZX Spectrum).
COMMON="opera mame puae stella a5200 prosystem virtualjaguar mednafen_lynx \
flycast gambatte mgba picodrive genesis_plus_gx mupen64plus_next fbneo fceumm \
nestopia mednafen_ngp ppsspp pcsx_rearmed mednafen_psx_hw mednafen_saturn snes9x \
bsnes_hd_beta mednafen_vb mednafen_wswan mednafen_pce melonds desmume vice_x64 \
fuse scummvm"
if [ "$SCOPE" = "common" ]; then
CORE_FILES=""
for c in $COMMON; do CORE_FILES="$CORE_FILES ${c}_libretro.so"; done
else
echo "Fetching full core list from libretro buildbot..."
# The directory autoindex and the .index text file both contain the
# literal <core>_libretro.so.zip filenames, so this grep works on
# either. Fall back to the common set if the index can't be read.
CORE_FILES=$(curl -fsSL "$BASE/" \
| grep -oE '[A-Za-z0-9_]+_libretro\.so\.zip' \
| sed 's/\.zip$//' | sort -u)
if [ -z "$CORE_FILES" ]; then
echo "Could not read the full core index — falling back to the common set."
CORE_FILES=""
for c in $COMMON; do CORE_FILES="$CORE_FILES ${c}_libretro.so"; done
fi
fi
_unzip_to() { # $1=zip $2=destdir (unzip if present, else python3)
if command -v unzip >/dev/null 2>&1; then
unzip -o -q "$1" -d "$2"
else
python3 -c "import zipfile,sys; zipfile.ZipFile(sys.argv[1]).extractall(sys.argv[2])" "$1" "$2"
fi
}
total=$(echo $CORE_FILES | wc -w); n=0; ok=0; skip=0; fail=0
for so in $CORE_FILES; do
n=$((n+1))
if [ -f "$CORES_DIR/$so" ] && [ "$FORCE" != "force" ]; then
skip=$((skip+1)); continue
fi
printf "\r [%d/%d] %-34s" "$n" "$total" "$so"
tmp=$(mktemp)
if curl -fsSL -o "$tmp" "$BASE/$so.zip" && _unzip_to "$tmp" "$CORES_DIR" 2>/dev/null; then
ok=$((ok+1))
else
fail=$((fail+1))
fi
rm -f "$tmp"
done
echo ""
echo "RetroArch cores: $ok downloaded, $skip already present, $fail failed"
echo " → $CORES_DIR"
[ "$fail" -gt 0 ] && echo " Retry failed cores: ./manage.sh cores $SCOPE force"
;;
reorder) reorder)
# Interactively reorder the Moonlight tiles by reordering the # Interactively reorder the Moonlight tiles by reordering the
# [[profiles.apps]] blocks in Wolf's config.toml (Moonlight shows them # [[profiles.apps]] blocks in Wolf's config.toml (Moonlight shows them
@@ -2379,7 +2460,7 @@ PYEOF
cat > "$COMP_FILE" << 'COMPEOF' cat > "$COMP_FILE" << 'COMPEOF'
_manage_wolf_complete() { _manage_wolf_complete() {
local cur="${COMP_WORDS[COMP_CWORD]}" local cur="${COMP_WORDS[COMP_CWORD]}"
local commands="start stop restart logs status pin update apps reorder local commands="start stop restart logs status pin update apps cores reorder
add-web ge-proton games setup-swbf2 fix-ea-game wait-ea-app add-web ge-proton games setup-swbf2 fix-ea-game wait-ea-app
install-ea-app diagnose-ea fix-perms install-completion backup" install-ea-app diagnose-ea fix-perms install-completion backup"
COMPREPLY=( $(compgen -W "$commands" -- "$cur") ) COMPREPLY=( $(compgen -W "$commands" -- "$cur") )
@@ -2433,6 +2514,7 @@ COMPEOF
echo " ./manage.sh pin - Show recent Moonlight pairing PIN link" echo " ./manage.sh pin - Show recent Moonlight pairing PIN link"
echo " ./manage.sh update - Pull latest Wolf image and restart" echo " ./manage.sh update - Pull latest Wolf image and restart"
echo " ./manage.sh apps - Add / update game launchers in Wolf" echo " ./manage.sh apps - Add / update game launchers in Wolf"
echo " ./manage.sh cores [all|common] - Download/refresh RetroArch cores (retro ROMs)"
echo " ./manage.sh reorder - Reorder the Moonlight tile list" echo " ./manage.sh reorder - Reorder the Moonlight tile list"
echo " ./manage.sh add-web [name] [url] - Add a URL shortcut tile (Firefox kiosk)" echo " ./manage.sh add-web [name] [url] - Add a URL shortcut tile (Firefox kiosk)"
echo " ./manage.sh ge-proton [version] - Install GE-Proton (latest, or pin a version)" echo " ./manage.sh ge-proton [version] - Install GE-Proton (latest, or pin a version)"
@@ -2503,7 +2585,10 @@ CATALOG = {
mounts=[f'{games}/roms:/ROMs:rw', mounts=[f'{games}/roms:/ROMs:rw',
f'{games}/saves:/mnt/games/saves:rw', f'{games}/saves:/mnt/games/saves:rw',
f'{games}/media:/media:rw', f'{games}/media:/media:rw',
f'{games}/emulators:/mnt/games/emulators:rw'], f'{games}/bios:/home/retro/bioses:rw',
f'{games}/retroarch/cores:/home/retro/.config/retroarch/cores:rw',
f'{games}/emulators:/mnt/games/emulators:rw',
f'{games}/emulators:/home/retro/Applications:rw'],
env=STD_ENV, cap_add=STD_CAP, security_opt=[], ipc_mode='host', env=STD_ENV, cap_add=STD_CAP, security_opt=[], ipc_mode='host',
ulimits=[], privileged=False, ulimits=[], privileged=False,
), ),
@@ -2523,7 +2608,9 @@ CATALOG = {
icon='https://games-on-whales.github.io/wildlife/apps/retroarch/assets/icon.png', icon='https://games-on-whales.github.io/wildlife/apps/retroarch/assets/icon.png',
image='ghcr.io/games-on-whales/retroarch:edge', image='ghcr.io/games-on-whales/retroarch:edge',
mounts=[f'{games}/roms:/ROMs:rw', mounts=[f'{games}/roms:/ROMs:rw',
f'{games}/saves:/mnt/games/saves:rw'], f'{games}/saves:/mnt/games/saves:rw',
f'{games}/bios:/home/retro/bioses:rw',
f'{games}/retroarch/cores:/home/retro/.config/retroarch/cores:rw'],
env=STD_ENV, cap_add=STD_CAP, security_opt=[], ipc_mode='host', env=STD_ENV, cap_add=STD_CAP, security_opt=[], ipc_mode='host',
ulimits=[], privileged=False, ulimits=[], privileged=False,
), ),
@@ -2749,11 +2836,24 @@ PYEOF
echo "" echo ""
echo " ${GAME_STORAGE_DIR}/" echo " ${GAME_STORAGE_DIR}/"
echo " roms/ → /ROMs (EmulationStation)" echo " roms/ → /ROMs (EmulationStation)"
echo " bios/ → /home/retro/bioses (emulator BIOS/firmware)"
echo " retroarch/cores/ → ~/.config/retroarch/cores (libretro cores)"
echo " saves/ → /home/retro/.config/retroarch/saves (RetroArch saves)" echo " saves/ → /home/retro/.config/retroarch/saves (RetroArch saves)"
echo " media/ → /media (ES-DE scraped artwork)" echo " media/ → /media (ES-DE scraped artwork)"
echo " wolf-state/ → Wolf state: Steam install, games, Proton prefixes," echo " wolf-state/ → Wolf state: Steam install, games, Proton prefixes,"
echo " ES-DE settings, controller maps, config.toml" echo " ES-DE settings, controller maps, config.toml"
echo "" echo ""
echo "── EMULATION (ES-DE) FIRST RUN ───────────────────────"
echo ""
echo " • Drop ROMs in roms/<system>/ (e.g. roms/snes/, roms/genesis/)"
echo " • Drop BIOS files in bios/ (PSX/Saturn/Dreamcast/Neo-Geo need them)"
echo " • RetroArch cores are pre-downloaded — retro games launch immediately"
echo " • Controllers auto-configure: ES-DE & RetroArch map Wolf's virtual pad"
echo " via SDL, no manual input setup needed"
echo " • Exit a game → ES-DE: Start+Select opens RetroArch menu → Quit,"
echo " or the Wolf hotkey START+UP+RB / Ctrl+Alt+Shift+W"
echo " • Add / refresh cores later: ./manage.sh cores"
echo ""
echo " Wolf's entire state folder lives on the game drive, so Steam games" echo " Wolf's entire state folder lives on the game drive, so Steam games"
echo " and everything else stay off the OS drive automatically." echo " and everything else stay off the OS drive automatically."
echo "" echo ""
@@ -2835,6 +2935,7 @@ cd $WOLF_DIR
./manage.sh status # container status ./manage.sh status # container status
./manage.sh update # pull latest image and restart ./manage.sh update # pull latest image and restart
./manage.sh apps # add / update game launchers ./manage.sh apps # add / update game launchers
./manage.sh cores # download/refresh RetroArch cores (retro ROMs)
./manage.sh reorder # reorder the Moonlight tile list ./manage.sh reorder # reorder the Moonlight tile list
./manage.sh add-web # add a URL shortcut tile (Firefox kiosk) ./manage.sh add-web # add a URL shortcut tile (Firefox kiosk)
./manage.sh ge-proton # install GE-Proton (needed for EA games) ./manage.sh ge-proton # install GE-Proton (needed for EA games)
@@ -2892,8 +2993,10 @@ Two things live on the game drive (\`$GAME_STORAGE_DIR\`):
**1. Shared media folders** — bind-mounted into app containers: **1. Shared media folders** — bind-mounted into app containers:
- \`roms/<system>/\` → /ROMs (EmulationStation — drop ROMs here) - \`roms/<system>/\` → /ROMs (EmulationStation — drop ROMs here)
- \`bios/\` → /home/retro/bioses (emulator BIOS/firmware — drop BIOS here)
- \`retroarch/cores/\` → ~/.config/retroarch/cores (libretro cores, pre-downloaded)
- \`saves/\` → /mnt/games/saves (RetroArch saves & states) - \`saves/\` → /mnt/games/saves (RetroArch saves & states)
- \`emulators/\` → /mnt/games/emulators (Azahar 3DS AppImage, etc.) - \`emulators/\` → /mnt/games/emulators + ~/Applications (Azahar 3DS AppImage, etc.)
**2. \`wolf-state/\`** — Wolf's entire state folder (\`WOLF_STATE_DIR\` in **2. \`wolf-state/\`** — Wolf's entire state folder (\`WOLF_STATE_DIR\` in
\`.env\`). This is the key to keeping games off the OS drive: it holds \`.env\`). This is the key to keeping games off the OS drive: it holds
@@ -2909,10 +3012,29 @@ Steam's home is born on the game drive, its installs, games, and Proton prefixes
go there with nothing to configure — no \`libraryfolders.vdf\` seeding, no go there with nothing to configure — no \`libraryfolders.vdf\` seeding, no
symlinks, no \`fix-perms\` dance. symlinks, no \`fix-perms\` dance.
## Emulation (ES-DE) — first run
ES-DE is usable the moment you launch it; only ROMs and BIOS are yours to add.
- **ROMs**: drop files into \`roms/<system>/\` (e.g. \`roms/snes/\`, \`roms/genesis/\`).
Every standard system folder is pre-created.
- **BIOS**: drop firmware into \`bios/\` (mounted at \`~/bioses\`). Required for
PSX, Saturn, Dreamcast, Neo-Geo, PC Engine CD, etc.
- **RetroArch cores**: pre-downloaded into \`retroarch/cores/\` and mounted at
\`~/.config/retroarch/cores\`, so libretro games launch right away. Refresh or
expand the set with \`./manage.sh cores\` (\`all\` = full buildbot set,
\`common\` = mainstream systems, add \`force\` to re-download).
- **Controllers**: auto-configured. ES-DE and RetroArch map Wolf's virtual pad
through SDL with no manual input setup. RPCS3 ships Wolf-specific bindings.
To remap inside ES-DE: Main menu → Input device settings → Configure
keyboard and controllers.
- **Exit a game** back to ES-DE: Start+Select opens the RetroArch menu → Quit,
or use the Wolf hotkey START+UP+RB (Ctrl+Alt+Shift+W on a keyboard).
## 3DS emulation ## 3DS emulation
Azahar (open-source Citra fork) AppImage lives in \`emulators/\` → mounted at Azahar (open-source Citra fork) AppImage lives in \`emulators/\` → mounted at
\`/mnt/games/emulators\`. Point ES-DE's 3DS system at it via Alternative \`/mnt/games/emulators\` and \`~/Applications\` (where ES-DE's app finder looks).
Emulators if it isn't auto-detected. Re-download with: Point ES-DE's 3DS system at it via Alternative Emulators if it isn't
auto-detected. Re-download with:
\`\`\`bash \`\`\`bash
cd $WOLF_DIR && ./manage.sh apps cd $WOLF_DIR && ./manage.sh apps
\`\`\` \`\`\`
@@ -2931,6 +3053,27 @@ sudo ./setup.sh backup # covers wolf-state/ saves and ES-DE settings
\`\`\` \`\`\`
MD MD
# ── Pre-download RetroArch cores (so ES-DE retro games launch first run) ──
# The GoW es-de/retroarch images ship with NO libretro cores — only the
# RetroArch frontend. Without cores, every libretro-based game (SNES, NES,
# Genesis, N64, PSX, GBA, ...) fails to launch. Cores download onto the game
# drive at retroarch/cores/ and are bind-mounted into the apps at
# ~/.config/retroarch/cores. Idempotent + resumable via ./manage.sh cores.
if echo "$_APP_KEYS" | grep -qwE 'esde|retroarch'; then
echo ""
local _get_cores=""
prompt_yn "Pre-download all RetroArch cores now (~1.5 GB) so retro games work on first launch? (y/n):" "y" _get_cores
if [[ "$_get_cores" =~ ^[Yy]$ ]]; then
log_info "Downloading RetroArch cores into $GAME_STORAGE_DIR/retroarch/cores (this can take a while)..."
bash "$WOLF_DIR/manage.sh" cores all \
|| log_warning "Some cores failed — retry later with: cd $WOLF_DIR && ./manage.sh cores"
chown -R "$ACTUAL_USER:$ACTUAL_USER" "$GAME_STORAGE_DIR/retroarch" 2>/dev/null || true
log_success "RetroArch cores ready — retro games launch on first run"
else
log_info "Skipped — fetch later with: cd $WOLF_DIR && ./manage.sh cores"
fi
fi
# ── Pre-download GE-Proton ──────────────────────────────────────────────── # ── Pre-download GE-Proton ────────────────────────────────────────────────
# GE-Proton is required for EA games (Battlefront II etc.) and needs to be # GE-Proton is required for EA games (Battlefront II etc.) and needs to be
# installed into Steam's compatibilitytools.d after first launch. Download # installed into Steam's compatibilitytools.d after first launch. Download