wolf: auto-add downloaded emulators to Steam's library on rerun

Downloading an emulator AppImage (Cemu, Azahar, PCSX2, Dolphin, ES-DE,
RetroArch) only ever dropped the file into emulators/ — getting it into
Steam's own shortcuts.vdf as a non-Steam game still needed a separate,
manually-typed manage.sh command per emulator, and steam-setup-frontends
only ever covered ES-DE/RetroArch, leaving Cemu out entirely.

- steam-setup-frontends now scans emulators/ and adds every AppImage it
  finds (deduping a fixed-name symlink like ES-DE.AppImage against the
  real versioned file it points at), instead of only handling ES-DE and
  RetroArch by name.
- install_wolf() now calls it automatically at the end of a run whenever
  Steam already has a signed-in profile, so the flow is just: run
  setup.sh wolf, sign into Steam via Moonlight, run it again — no
  separate manage.sh command to remember.
- Docs (manage.sh help text, printed install summary, generated
  README.md) updated to match.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013BWYKEERLA1a7gv86Z4W23
This commit is contained in:
Claude
2026-09-10 02:47:18 +00:00
parent c63237a3db
commit cf879f0090
+122 -29
View File
@@ -4191,17 +4191,21 @@ VDFPY
echo "other native Linux AppImages don't run through Proton."
;;
steam-setup-frontends)
# One command covering what the ES-DE/RetroArch AppImage download
# step in setup.sh can't finish on its own: that step runs before
# Wolf/Steam containers even exist, so it can only download the
# AppImages and print instructions. This picks up from there —
# start Steam if needed, WAIT for it to be signed in (Steam Guard's
# QR-code sign-in itself can't be scripted: it needs a phone
# approving a prompt, so this only polls for the result, never
# performs the sign-in), then add whichever AppImage(s) already got
# downloaded as Steam non-Steam games via the existing
# One command covering what the emulator AppImage download step in
# setup.sh can't finish on its own: that step runs before Wolf/Steam
# containers even exist, so it can only download the AppImages and
# print instructions. This picks up from there — start Steam if
# needed, WAIT for it to be signed in (Steam Guard's QR-code sign-in
# itself can't be scripted: it needs a phone approving a prompt, so
# this only polls for the result, never performs the sign-in), then
# add EVERY AppImage already sitting in emulators/ (ES-DE, RetroArch,
# Cemu, Azahar, PCSX2, Dolphin — whatever setup.sh's download prompts
# were said yes to) as a Steam non-Steam game via the existing
# steam-add-nonsteam-game command above (re-invoked, not
# reimplemented, so the two never drift apart).
# reimplemented, so the two never drift apart). Safe to re-run any
# time — already-added shortcuts are updated in place, never
# duplicated, so running this again after downloading one more
# emulator only adds the new one.
if ! docker ps --format '{{.Names}}' | grep -qi WolfSteam; then
echo "Starting Wolf (docker compose up -d)..."
docker compose up -d
@@ -4241,15 +4245,51 @@ VDFPY
if [ -z "$GAME_DIR" ]; then read -r -p " Game storage path: " GAME_DIR; fi
_SSF_EMU_DIR="$GAME_DIR/emulators"
_SSF_ADDED_ANY=0
if [ -f "$_SSF_EMU_DIR/ES-DE.AppImage" ]; then
"$0" steam-add-nonsteam-game es-de "EmulationStation (ES-DE)" && _SSF_ADDED_ANY=1
# realpaths already registered — so a fixed-name symlink
# (ES-DE.AppImage, RetroArch.AppImage, Dolphin_Emulator.AppImage) and the
# real versioned file it points at don't both end up as separate Steam
# tiles for the same emulator.
_SSF_SEEN=""
_ssf_add_appimage() {
# $1 = filename inside emulators/, $2 = display name (optional —
# falls back to steam-add-nonsteam-game's own filename-derived default).
local _file="$1" _name="${2:-}" _real
[ -f "$_SSF_EMU_DIR/$_file" ] || return 1
_real=$(readlink -f "$_SSF_EMU_DIR/$_file")
case " $_SSF_SEEN " in *" $_real "*) return 1 ;; esac
_SSF_SEEN="$_SSF_SEEN $_real"
if [ -n "$_name" ]; then
"$0" steam-add-nonsteam-game "$_file" "$_name"
else
echo "ES-DE.AppImage not found in $_SSF_EMU_DIR — download it first: sudo ./setup.sh wolf"
"$0" steam-add-nonsteam-game "$_file"
fi
if [ -f "$_SSF_EMU_DIR/RetroArch.AppImage" ]; then
"$0" steam-add-nonsteam-game retroarch "RetroArch" && _SSF_ADDED_ANY=1
else
echo "RetroArch.AppImage not found in $_SSF_EMU_DIR — download it first: sudo ./setup.sh wolf"
}
# Fixed-name symlinks first, so their friendly display names win over
# the versioned real filename each one points at (see the emulator
# download step in setup.sh for why these symlinks exist).
_ssf_add_appimage "ES-DE.AppImage" "EmulationStation (ES-DE)" && _SSF_ADDED_ANY=1
_ssf_add_appimage "RetroArch.AppImage" "RetroArch" && _SSF_ADDED_ANY=1
_ssf_add_appimage "Dolphin_Emulator.AppImage" "Dolphin (GameCube/Wii)" && _SSF_ADDED_ANY=1
# Everything else already downloaded into emulators/ — Cemu, Azahar,
# PCSX2, and any future emulator this list doesn't yet special-case by
# name. The realpath dedupe above skips the Dolphin/ES-DE/RetroArch
# symlinks' own real targets when this glob reaches them.
shopt -s nullglob
for _appimg in "$_SSF_EMU_DIR"/*.AppImage; do
_base=$(basename "$_appimg")
case "${_base,,}" in
cemu*) _ssf_add_appimage "$_base" "Cemu (Wii U)" && _SSF_ADDED_ANY=1 ;;
azahar*) _ssf_add_appimage "$_base" "Azahar (3DS)" && _SSF_ADDED_ANY=1 ;;
pcsx2*) _ssf_add_appimage "$_base" "PCSX2 (PS2)" && _SSF_ADDED_ANY=1 ;;
*) _ssf_add_appimage "$_base" && _SSF_ADDED_ANY=1 ;;
esac
done
shopt -u nullglob
if [ "$_SSF_ADDED_ANY" = 0 ]; then
echo "No emulator AppImages found in $_SSF_EMU_DIR yet — download them first: sudo ./setup.sh wolf"
fi
if [ "$_SSF_ADDED_ANY" = 1 ]; then
@@ -4573,7 +4613,8 @@ SYNCPY
echo " ./manage.sh steam-add-nonsteam-game [name] [display name]"
echo " - Add an emulator (emulators/) as a non-Steam game, no GUI needed"
echo " ./manage.sh steam-setup-frontends - Wait for Steam sign-in (QR code via Moonlight), then add"
echo " downloaded ES-DE/RetroArch AppImages to Steam"
echo " EVERY downloaded emulator AppImage (ES-DE, RetroArch,"
echo " Cemu, Azahar, PCSX2, Dolphin, ...) to Steam"
echo " ./manage.sh cemu-clone-controller [slot 0-3] [uuid] [display name]"
echo " - Clone a working Cemu controller mapping onto a new device slot"
echo " ./manage.sh cemu-sync-controllers - Auto-detect connected controllers via SDL and clone mappings onto all of them"
@@ -4962,6 +5003,35 @@ PYEOF
# Hand the folder back to the real user
chown -R "$ACTUAL_USER:$ACTUAL_USER" "$WOLF_DIR"
# ── Optional: auto-wire already-downloaded emulators into Steam ──────────
# Steam's own shortcuts.vdf (and the userdata/<id>/ dir it lives under)
# only exists once Steam has actually been signed into at least once —
# meaningless on a brand-new install, since Steam hasn't even been opened
# in Moonlight yet at this point in a first run. But on a RERUN of this
# module (sudo ./setup.sh wolf again, after you've since signed in),
# every AppImage already sitting in emulators/ can go straight into
# Steam's library with no extra manual step. No blocking here: this is
# a quick, one-shot check for a Steam profile that already exists — the
# actual up-to-10-minutes sign-in wait lives in
# 'manage.sh steam-setup-frontends' itself (re-invoked below, not
# duplicated), and only ever triggers there if this check somehow raced
# against a sign-in that hadn't finished landing on disk yet.
if echo "$_APP_KEYS" | grep -qw steam; then
local _WOLF_ANY_STEAM_UID=""
_WOLF_ANY_STEAM_UID=$(find "$WOLF_STATE_DIR" -maxdepth 2 -type d -name Steam 2>/dev/null \
| while IFS= read -r _sd; do ls "$_sd/.steam/steam/userdata/" 2>/dev/null | head -1; done | head -1)
if [ -n "$_WOLF_ANY_STEAM_UID" ]; then
log_info "Steam is already signed in — adding downloaded emulators to Steam's library..."
(cd "$WOLF_DIR" && bash manage.sh steam-setup-frontends) \
|| log_warning "Couldn't auto-add emulators to Steam — run manually: cd $WOLF_DIR && ./manage.sh steam-setup-frontends"
else
log_info "Once you've signed into Steam via Moonlight, run this to add every downloaded"
log_info "emulator (Cemu, ES-DE, RetroArch, etc.) to Steam's library — or just re-run"
log_info "'sudo ./setup.sh wolf' after signing in and it'll be done automatically:"
log_info " cd $WOLF_DIR && ./manage.sh steam-setup-frontends"
fi
fi
local ALL_IPS
ALL_IPS=$(ip -4 addr show | grep -oP '(?<=inet )\d+\.\d+\.\d+\.\d+(?=/)' | grep -v '^127\.')
@@ -5069,8 +5139,7 @@ PYEOF
echo " 4 controllers, one game (e.g. Cemu/Wii U)? Steam Input tells identical"
echo " controllers apart by device path, not just SDL GUID — add the emulator"
echo " to Steam as a non-Steam game instead of launching it from ES-DE:"
echo " ./manage.sh steam-setup-frontends # ES-DE/RetroArch AppImages into Steam"
echo " ./manage.sh steam-add-nonsteam-game cemu"
echo " ./manage.sh steam-setup-frontends # adds every downloaded emulator AppImage to Steam"
echo " ./manage.sh cemu-clone-controller / cemu-sync-controllers"
echo " For the ES-DE/RetroArch Wolf apps directly, force distinct virtual pad"
echo " types per slot instead: ./manage.sh controllers (see README.md)"
@@ -5155,10 +5224,33 @@ cd $WOLF_DIR
Run \`./manage.sh install-completion\` once, then re-open your shell (or
\`source ~/.bashrc\`). After that, \`./manage.sh <TAB><TAB>\` lists all commands.
## Adding an emulator to Steam as a non-Steam game
Steam's own "Add a Non-Steam Game" is a Big Picture file-browser flow —
usable through Moonlight, but slow to click through for something you're
scripting or repeating. This does the same thing directly:
## Getting downloaded emulators into Steam — the easy way
Downloading an emulator AppImage (\`sudo ./setup.sh wolf\`'s Cemu/Azahar/
PCSX2/Dolphin/ES-DE/RetroArch prompts) only puts the file in \`emulators/\`
— it doesn't touch Steam's own library on its own, since that step runs
before Steam has even been signed into. Once you've signed into Steam via
Moonlight, one command adds everything already downloaded:
\`\`\`bash
cd $WOLF_DIR && ./manage.sh steam-setup-frontends
\`\`\`
This scans \`emulators/\` and adds every AppImage found (Cemu, Azahar,
PCSX2, Dolphin, ES-DE, RetroArch — whichever prompts you said yes to) as
a Steam non-Steam game in one pass; see the section below for exactly how
it waits for sign-in. **You don't even need to run this yourself on a
rerun** — \`sudo ./setup.sh wolf\` run again after you've signed into Steam
detects the signed-in profile automatically and adds everything for you
as the last step, no extra command needed. It's still worth knowing the
command directly for downloading one more emulator later without
re-running the whole installer.
## Adding a single emulator to Steam as a non-Steam game
\`steam-add-nonsteam-game\` is the one-off building block
\`steam-setup-frontends\` above calls for each emulator it finds — reach
for it directly when you only want to (re-)add one specific file, e.g.
after manually dropping in an AppImage the setup script doesn't know
about. Steam's own "Add a Non-Steam Game" is a Big Picture file-browser
flow — usable through Moonlight, but slow to click through for something
you're scripting or repeating. This does the same thing directly:
\`\`\`bash
cd $WOLF_DIR && ./manage.sh steam-add-nonsteam-game cemu
\`\`\`
@@ -5219,11 +5311,12 @@ cd $WOLF_DIR && ./manage.sh steam-setup-frontends
Starts Wolf if it isn't already up, checks whether Steam's already
signed in (proceeds immediately if so), otherwise prints the QR-code
steps and waits (up to 10 minutes) for sign-in to complete, then adds
whichever of ES-DE.AppImage/RetroArch.AppImage was downloaded as a Steam
non-Steam game — re-using \`steam-add-nonsteam-game\` above rather than
duplicating its shortcuts.vdf-writing logic. Safe to re-run any time
(e.g. if it timed out waiting, or you downloaded the second AppImage
later) — already-added shortcuts are updated in place, not duplicated.
every AppImage already sitting in \`emulators/\` — not just ES-DE and
RetroArch, but Cemu/Azahar/PCSX2/Dolphin too — as a Steam non-Steam game,
re-using \`steam-add-nonsteam-game\` above rather than duplicating its
shortcuts.vdf-writing logic. Safe to re-run any time (e.g. if it timed
out waiting, or you downloaded another emulator since) — already-added
shortcuts are updated in place, not duplicated.
**Returning to Steam from ES-DE, without a second instance:** if ES-DE
is running as a Steam non-Steam game (via the AppImage above, not the