wolf: add optional ES-DE/RetroArch AppImage downloads for Steam Input
Both projects ship official standalone Linux AppImages separate from the esde/retroarch Wolf catalog containers this repo already runs. Adding either one to Steam as a non-Steam game (steam-add-nonsteam-game, now reaching the same roms/saves/bios/retro-home/retroarch mounts as the esde/retroarch containers) lets Steam Input assign a 4th controller its own identity by device path, past Wolf's 3-concrete-pad-type ceiling. ES-DE is hosted on GitLab rather than GitHub, so this adds a GitLab Releases API counterpart to the existing GitHub-based download helper. RetroArch's own buildbot doesn't publish through either API, so this uses hizzlekizzle/RetroArch-AppImage, the community nightly-build project the AppImage catalogs themselves point to (flagged as third-party, same treatment this file already gives the Dolphin community build). Both downloads are opt-in (default no) and symlink to a fixed filename so steam-add-nonsteam-game's case-sensitive substring match finds them regardless of the vendor's own asset name. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015Z4nqULUEipWNgBsPoSuAb
This commit is contained in:
@@ -285,6 +285,84 @@ print(pick[0]["browser_download_url"] if pick else "")
|
||||
esac
|
||||
}
|
||||
|
||||
# Same job as _wolf_download_emulator_appimage above, but against GitLab's
|
||||
# Releases API instead of GitHub's — needed for any project (ES-DE included)
|
||||
# that's hosted on GitLab rather than GitHub, since GitHub's API obviously
|
||||
# can't answer for a repo it doesn't host. Mirrors the same arch-matching /
|
||||
# post-download ELF-header verification logic so both call sites behave
|
||||
# identically from the caller's point of view.
|
||||
_wolf_download_emulator_appimage_gitlab() {
|
||||
local _display_name="$1" _project_path="$2" _existing_glob="$3" _dir="$4"
|
||||
if ls "$_dir"/$_existing_glob 2>/dev/null | grep -q .; then
|
||||
log_info "$_display_name already present in $_dir/"
|
||||
return 0
|
||||
fi
|
||||
local _get=""
|
||||
echo ""
|
||||
log_info "$_display_name can be auto-downloaded."
|
||||
prompt_yn "Download $_display_name AppImage now? (y/n):" "y" _get
|
||||
[[ "$_get" =~ ^[Yy]$ ]] || return 0
|
||||
|
||||
log_info "Fetching latest $_display_name release from GitLab..."
|
||||
local _encoded_path
|
||||
_encoded_path=$(python3 -c "import urllib.parse,sys; print(urllib.parse.quote(sys.argv[1], safe=''))" "$_project_path")
|
||||
local _url
|
||||
_url=$(curl -fsSL "https://gitlab.com/api/v4/projects/${_encoded_path}/releases" \
|
||||
| HOST_ARCH="$(uname -m)" python3 -c '
|
||||
import sys, json, os
|
||||
host = os.environ.get("HOST_ARCH", "")
|
||||
arch_tags = {
|
||||
"x86_64": ["x86_64", "amd64", "x64"],
|
||||
"aarch64": ["aarch64", "arm64"],
|
||||
"arm64": ["aarch64", "arm64"],
|
||||
}.get(host, [host] if host else [])
|
||||
all_arch_tags = ["x86_64", "amd64", "x64", "aarch64", "arm64", "armv7", "armhf", "i386", "i686"]
|
||||
def has(name, tags):
|
||||
n = name.lower()
|
||||
return any(t in n for t in tags)
|
||||
releases = json.load(sys.stdin)
|
||||
# GitLab does not guarantee list order — sort explicitly instead of
|
||||
# assuming index 0 is the newest (the mistake that would silently pick a
|
||||
# stale/older release on some future API response ordering change).
|
||||
releases = sorted(releases, key=lambda r: r.get("released_at") or "", reverse=True)
|
||||
assets = []
|
||||
for r in releases:
|
||||
for link in r.get("assets", {}).get("links", []):
|
||||
url = link.get("direct_asset_url") or link.get("url") or ""
|
||||
if url.endswith(".AppImage"):
|
||||
assets.append({"name": link.get("name", url), "browser_download_url": url})
|
||||
if assets:
|
||||
break
|
||||
matching = [a for a in assets if arch_tags and has(a["name"], arch_tags)]
|
||||
untagged = [a for a in assets if not has(a["name"], all_arch_tags)]
|
||||
pick = matching or untagged or assets
|
||||
print(pick[0]["browser_download_url"] if pick else "")
|
||||
' 2>/dev/null)
|
||||
if [[ -z "$_url" ]]; then
|
||||
log_warning "Could not resolve download URL — get it manually from https://gitlab.com/${_project_path}/-/releases"
|
||||
return 1
|
||||
fi
|
||||
local _file="$_dir/$(basename "$_url")"
|
||||
curl -fL --progress-bar -o "$_file" "$_url" \
|
||||
&& chmod +x "$_file" \
|
||||
&& chown "$ACTUAL_USER:$ACTUAL_USER" "$_file" \
|
||||
&& log_success "$_display_name downloaded: $_file" \
|
||||
|| { log_warning "Download failed — get it manually from https://gitlab.com/${_project_path}/-/releases"; return 1; }
|
||||
|
||||
local _got_arch
|
||||
_got_arch=$(file -b "$_file" 2>/dev/null)
|
||||
case "$(uname -m)" in
|
||||
x86_64)
|
||||
echo "$_got_arch" | grep -qi 'x86-64\|x86_64' || \
|
||||
log_warning "$_file doesn't look like an x86_64 build ($_got_arch) — it will fail with 'exec format error'. Grab the x86_64 asset by hand from https://gitlab.com/${_project_path}/-/releases"
|
||||
;;
|
||||
aarch64|arm64)
|
||||
echo "$_got_arch" | grep -qi 'aarch64\|arm64' || \
|
||||
log_warning "$_file doesn't look like an aarch64 build ($_got_arch) — it may fail to run. Grab the aarch64 asset by hand from https://gitlab.com/${_project_path}/-/releases"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
install_wolf() {
|
||||
require_docker || return 1
|
||||
|
||||
@@ -939,6 +1017,85 @@ UDEV
|
||||
log_info "not something this installer can supply. Cemu's own First-Time Setup Wizard covers where"
|
||||
log_info "to put it once you have one."
|
||||
|
||||
# ── Optional: ES-DE and RetroArch as standalone AppImages (for Steam) ────
|
||||
# These are ADDITIONAL to the esde/retroarch Wolf catalog containers
|
||||
# above, not a replacement — nothing here removes or changes those. The
|
||||
# only reason to want this: once added as a Steam non-Steam game
|
||||
# (./manage.sh steam-add-nonsteam-game, which the wolf mount fix above
|
||||
# already extended to reach the same roms/saves/bios/retro-home/
|
||||
# retroarch paths the esde/retroarch containers use), Steam Input can
|
||||
# give each of up to 4 identical-model controllers its own distinct
|
||||
# identity by device path — the one thing Wolf's own 3-concrete-pad-type
|
||||
# ceiling can't do for a 4th controller (see manage.sh's own "4
|
||||
# controllers (Cemu / Wii U games)" help text). Skip both prompts below
|
||||
# if you're happy running Wii U/retro systems through the esde app
|
||||
# directly and don't need Steam's per-device controller assignment.
|
||||
#
|
||||
# Both download to a FIXED, predictable filename (ES-DE.AppImage /
|
||||
# RetroArch.AppImage) regardless of the real upstream release asset's
|
||||
# own name — steam-add-nonsteam-game matches by substring against the
|
||||
# actual filename on disk, and bash glob matching is case-sensitive, so
|
||||
# a fixed name (same symlink trick already used for Dolphin above) is
|
||||
# what makes './manage.sh steam-add-nonsteam-game es-de' reliably find
|
||||
# it regardless of how the vendor's own release happens to be named.
|
||||
if [ ! -f "$_EMU_DIR/ES-DE.AppImage" ]; then
|
||||
echo ""
|
||||
# ES-DE is hosted on GitLab, not GitHub (confirmed against its own
|
||||
# project page) — a different Releases API than every other
|
||||
# standalone emulator above, hence the separate _gitlab helper.
|
||||
log_info "ES-DE also ships an official standalone Linux AppImage — separate from the esde Wolf"
|
||||
log_info "app above. Only useful for adding to Steam (see this repo's wolf README); skip this if"
|
||||
log_info "you'll only ever use the esde app directly."
|
||||
local _GET_ESDE_APPIMAGE=""
|
||||
prompt_yn "Download the ES-DE AppImage for use via Steam? (y/n):" "n" _GET_ESDE_APPIMAGE
|
||||
if [[ "$_GET_ESDE_APPIMAGE" =~ ^[Yy]$ ]]; then
|
||||
_wolf_download_emulator_appimage_gitlab \
|
||||
"ES-DE" "es-de/emulationstation-de" "ES-DE.AppImage" "$_EMU_DIR"
|
||||
local _ESDE_REAL
|
||||
_ESDE_REAL=$(ls "$_EMU_DIR"/*.AppImage 2>/dev/null \
|
||||
| grep -iE '/(es-?de|emulationstation)[^/]*\.AppImage$' \
|
||||
| grep -v '/ES-DE\.AppImage$' | head -1)
|
||||
if [[ -n "$_ESDE_REAL" ]]; then
|
||||
ln -sf "$(basename "$_ESDE_REAL")" "$_EMU_DIR/ES-DE.AppImage"
|
||||
chown -h "$ACTUAL_USER:$ACTUAL_USER" "$_EMU_DIR/ES-DE.AppImage" 2>/dev/null || true
|
||||
log_success "Linked $_EMU_DIR/ES-DE.AppImage -> $(basename "$_ESDE_REAL")"
|
||||
log_info "Once you've signed into Steam once in Moonlight, run:"
|
||||
log_info " cd $WOLF_DIR && ./manage.sh steam-add-nonsteam-game es-de \"EmulationStation (ES-DE)\""
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -f "$_EMU_DIR/RetroArch.AppImage" ]; then
|
||||
echo ""
|
||||
# Libretro's own buildbot doesn't publish through a GitHub/GitLab
|
||||
# Releases API this installer can automate against — hizzlekizzle/
|
||||
# RetroArch-AppImage is the well-regarded THIRD-PARTY nightly-build
|
||||
# project the AppImage community catalogs (appimage.github.io etc.)
|
||||
# themselves point to, same "flagged, not silently offered as
|
||||
# official" treatment as the Dolphin community build above.
|
||||
log_info "RetroArch also has a standalone Linux AppImage, separate from the retroarch Wolf app"
|
||||
log_info "above — same rationale as ES-DE just above (Steam Input's per-device controller"
|
||||
log_info "assignment). This comes from hizzlekizzle/RetroArch-AppImage, a well-regarded but"
|
||||
log_info "THIRD-PARTY nightly-build project, not an official libretro.org release — grab"
|
||||
log_info "RetroArch's own build by hand instead if you'd rather not run that."
|
||||
local _GET_RA_APPIMAGE=""
|
||||
prompt_yn "Download the RetroArch AppImage for use via Steam? (y/n):" "n" _GET_RA_APPIMAGE
|
||||
if [[ "$_GET_RA_APPIMAGE" =~ ^[Yy]$ ]]; then
|
||||
_wolf_download_emulator_appimage \
|
||||
"RetroArch" "hizzlekizzle/RetroArch-AppImage" "RetroArch.AppImage" "$_EMU_DIR"
|
||||
local _RA_REAL
|
||||
_RA_REAL=$(ls "$_EMU_DIR"/*[Rr]etro[Aa]rch*.AppImage 2>/dev/null \
|
||||
| grep -v '/RetroArch\.AppImage$' | head -1)
|
||||
if [[ -n "$_RA_REAL" ]]; then
|
||||
ln -sf "$(basename "$_RA_REAL")" "$_EMU_DIR/RetroArch.AppImage"
|
||||
chown -h "$ACTUAL_USER:$ACTUAL_USER" "$_EMU_DIR/RetroArch.AppImage" 2>/dev/null || true
|
||||
log_success "Linked $_EMU_DIR/RetroArch.AppImage -> $(basename "$_RA_REAL")"
|
||||
log_info "Once you've signed into Steam once in Moonlight, run:"
|
||||
log_info " cd $WOLF_DIR && ./manage.sh steam-add-nonsteam-game retroarch \"RetroArch\""
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Optional: TI-99/4A as its own ES-DE system ────────────────────────────
|
||||
# TI-99/4A has no libretro core and isn't one of ES-DE's built-in systems,
|
||||
# so getting it real ES-DE treatment (artwork scraping, gameplay-time
|
||||
|
||||
Reference in New Issue
Block a user