Both changes are what the third adapter asked for, the way the second one asked for resolve_for_host. `pick_release` now takes several kinds in order of preference. A desktop store on Apple Silicon wants `mac_universal`, will settle for `mac_arm64`, and takes `mac_x64` only as a last resort because that one runs under Rosetta. Release order still wins over kind order: the newest release with any acceptable asset beats an older release with a better one. Records also carry `asset_path`, the catalog-side path, because not every asset is a download — an `html` build is a hosted directory, and the desktop store needs its URL rather than a file name. The machine-wide uninstaller learned the third engine. It also had to stop assuming one store per directory name: the RetroArch and desktop engines share a store root, so a home is now identified by which engine file is in it, and the launcher name is derived from the home rather than guessed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
135 lines
5.1 KiB
Bash
Executable File
135 lines
5.1 KiB
Bash
Executable File
#!/bin/sh
|
|
# Remove every WarpEngine store from this machine — the games, the store
|
|
# engines, this shared core and the launchers.
|
|
#
|
|
# curl -fsSL https://git.teletypegames.org/engines/warpstore/raw/branch/master/uninstall.sh | sh
|
|
#
|
|
# A single store is better removed by its own engine's uninstaller, which knows
|
|
# that host's extras (a Batocera Ports entry, say). This one is for taking the
|
|
# whole framework off a machine without having to remember what is installed:
|
|
# it finds every store home under the known roots, has each store's own engine
|
|
# purge what it installed, and then deletes the store.
|
|
#
|
|
# Three engines exist today — Batocera, RetroArch and desktop — and the last two
|
|
# share a store root, so a home is identified by which engine file is in it.
|
|
#
|
|
# DRY_RUN=1 print what would go, remove nothing
|
|
# KEEP_GAMES=1 leave the installed games alone, remove only the scripts
|
|
# FORCE=1 purge even while RetroArch is running
|
|
#
|
|
# POSIX sh on purpose: `| sh` has to work on a machine whose /bin/sh is dash.
|
|
set -eu
|
|
|
|
BATOCERA_STORE_ROOT="${BATOCERA_STORE_ROOT:-/userdata/system/batocera-store}"
|
|
BATOCERA_PORTS_DIR="${BATOCERA_PORTS_DIR:-/userdata/roms/ports}"
|
|
STORE_ROOT="${STORE_ROOT:-${XDG_DATA_HOME:-$HOME/.local/share}/warp-engine-store}"
|
|
BIN_DIR="${BIN_DIR:-$HOME/.local/bin}"
|
|
DRY_RUN="${DRY_RUN:-0}"
|
|
KEEP_GAMES="${KEEP_GAMES:-0}"
|
|
FORCE="${FORCE:-0}"
|
|
|
|
say() { echo "[uninstall-all] $*"; }
|
|
run() {
|
|
if [ "$DRY_RUN" = "1" ]; then echo "[uninstall-all] would: $*"; else "$@"; fi
|
|
}
|
|
|
|
command -v python3 >/dev/null 2>&1 || {
|
|
echo "[uninstall-all] error: python3 is required to ask the engines what they installed" >&2
|
|
exit 1
|
|
}
|
|
|
|
FOUND=0
|
|
|
|
# `purge` is the engine's own "remove everything I installed": the shell has no
|
|
# way of knowing which ROMs, playlists, thumbnails or gamelist entries were a
|
|
# store's, and state.json does.
|
|
purge_store() { # purge_store <store home> <engine file> <home env var> <extra flag>
|
|
home="$1"; engine="$2"; homevar="$3"; extra="$4"
|
|
if [ "$KEEP_GAMES" = "1" ]; then
|
|
say "keeping the games of '$(basename "$home")' (KEEP_GAMES=1)"
|
|
return 0
|
|
fi
|
|
if [ ! -f "$home/$engine" ] || [ ! -f "$home/config.json" ]; then
|
|
say "warning: no engine in $home — its games have to go by hand"
|
|
return 0
|
|
fi
|
|
dry=""
|
|
[ "$DRY_RUN" = "1" ] && dry="--dry-run"
|
|
# shellcheck disable=SC2086
|
|
env "$homevar=$home" python3 "$home/$engine" --config "$home/config.json" $dry purge $extra || {
|
|
say "error: the engine in $home could not finish — stopping before anything else is removed."
|
|
say " Close RetroArch (or FORCE=1), fix what it reported, and run this again."
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
# --- Batocera stores -------------------------------------------------------
|
|
if [ -d "$BATOCERA_STORE_ROOT" ]; then
|
|
for home in "$BATOCERA_STORE_ROOT"/*/; do
|
|
[ -f "$home/config.json" ] || continue
|
|
id="$(basename "$home")"
|
|
FOUND=$((FOUND + 1))
|
|
say "batocera store '$id' in $home"
|
|
purge_store "${home%/}" store.py BATOCERA_STORE_HOME ""
|
|
|
|
# The Ports entry's name is whatever the store repository chose, not
|
|
# store.name — but it always runs this store's launcher, so it is found by
|
|
# looking inside the scripts rather than by guessing a file name.
|
|
if [ -d "$BATOCERA_PORTS_DIR" ]; then
|
|
for entry in "$BATOCERA_PORTS_DIR"/*.sh; do
|
|
[ -f "$entry" ] || continue
|
|
if grep -q -F "${home%/}" "$entry" 2>/dev/null; then
|
|
run rm -f "$entry"
|
|
fi
|
|
done
|
|
fi
|
|
run rm -f "$BATOCERA_STORE_ROOT/$id-store"
|
|
run rm -rf "${home%/}"
|
|
done
|
|
[ "$DRY_RUN" = "1" ] || rmdir "$BATOCERA_STORE_ROOT" 2>/dev/null || true
|
|
fi
|
|
|
|
# --- RetroArch and desktop stores -----------------------------------------
|
|
# Both live under the same root, one directory per store id, told apart by the
|
|
# engine file inside. A store id can even carry both.
|
|
if [ -d "$STORE_ROOT" ]; then
|
|
extra=""
|
|
[ "$FORCE" = "1" ] && extra="--force"
|
|
for home in "$STORE_ROOT"/*/; do
|
|
[ -f "$home/config.json" ] || continue
|
|
name="$(basename "$home")"
|
|
home="${home%/}"
|
|
if [ -f "$home/retroarch_store.py" ]; then
|
|
id="${name%-retroarch}"
|
|
FOUND=$((FOUND + 1))
|
|
say "retroarch store '$id' in $home"
|
|
purge_store "$home" retroarch_store.py RETROARCH_STORE_HOME "$extra"
|
|
run rm -f "$BIN_DIR/$id-retroarch-store"
|
|
fi
|
|
if [ -f "$home/desktop_store.py" ]; then
|
|
id="${name%-desktop}"
|
|
FOUND=$((FOUND + 1))
|
|
say "desktop store '$id' in $home"
|
|
purge_store "$home" desktop_store.py DESKTOP_STORE_HOME ""
|
|
run rm -f "$BIN_DIR/$id-desktop-store"
|
|
fi
|
|
run rm -rf "$home"
|
|
done
|
|
[ "$DRY_RUN" = "1" ] || rmdir "$STORE_ROOT" 2>/dev/null || true
|
|
fi
|
|
|
|
if [ "$FOUND" = "0" ]; then
|
|
say "no WarpEngine store found. Looked in:"
|
|
say " $BATOCERA_STORE_ROOT"
|
|
say " $STORE_ROOT"
|
|
say "Point at them with BATOCERA_STORE_ROOT / STORE_ROOT if they live elsewhere."
|
|
exit 0
|
|
fi
|
|
|
|
if [ "$DRY_RUN" = "1" ]; then
|
|
say "dry run — nothing was removed ($FOUND store(s) found)."
|
|
else
|
|
say "done: $FOUND store(s) removed, engines and the shared core with them."
|
|
say "Restart EmulationStation or RetroArch to see the games go."
|
|
fi
|