3 Commits
Author SHA1 Message Date
mr.zeroandClaude Opus 5 72ff17fff6 Give the store home its own name per engine
The desktop engine shares this store root, and both keyed their home by the store
id alone — so installing both from the same publisher meant one config.json and
one state.json between them, and the second install silently adopted the first
one's state. Caught by installing the two side by side.

The home is now `<id>-retroarch`. The installer moves an existing `<id>` home on
its way past, so an install from yesterday keeps its state and its games; the
uninstaller accepts either name. The launcher was already `<id>-retroarch-store`,
so nothing the user types changes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 10:00:09 +02:00
mr.zeroandClaude Opus 5 51493c13f0 Add purge and an uninstaller, and move to the stores org
`purge` removes every title this store installed, then the playlists, the
thumbnail folders and the store's own directories — what a shell script cannot
do, because only state.json knows which files were ours. uninstall.sh runs it
before deleting the launcher and the store home, and stops if the engine cannot
finish, so you are never left with the files but not the engine that knew about
them.

What survives on purpose: content you put in the store's folder (directories go
only when empty), and a playlist that still holds an entry of yours — only items
pointing inside the store's content folder are dropped, and a playlist that ends
up empty is deleted while one that does not is rewritten without us.

A playlist backup is no longer made while purging. It was being created on the
uninstall's first touch of a playlist we had written ourselves, which meant the
run reported a backup it had just made — and described it as possibly holding
entries that were never ours, which was the opposite of true.

install.sh and uninstall.sh are POSIX sh now, checked with dash, so `curl … | sh`
works where /bin/sh is not bash. The repository moved from the tools org to
stores; the old raw URLs still redirect, but every reference is updated.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 08:32:29 +02:00
mr.zeroandClaude Opus 5 e6a55f56a2 A RetroArch store engine for WarpEngine catalogs
The Batocera store reaches one kind of machine. RetroArch reads the same
cartridges on desktop Linux, Windows and macOS, on Android, on the Steam
Deck and on the console ports — the same seven titles, a far larger
audience — so this writes RetroArch's own library format instead:
one .lpl playlist per platform, with box art in the thumbnail folders that
playlist's name points at.

Three things it does differently, each because RetroArch is not one
machine:

- It reads retroarch.cfg for its directories. On the macOS install this
  was developed against, playlist_directory is ~/Documents/RetroArch/
  playlists while cores and thumbnails are under ~/Library/Application
  Support/RetroArch — an engine assuming <base>/playlists writes into the
  void. The leading ':' of a portable install is expanded too.
- It can write for a machine it is not running on (`export`). Android runs
  RetroArch but cannot run Python, so the only way to serve it is to
  render the tree here and copy it over; the same mechanism handles an SD
  card that will be mounted elsewhere.
- It refuses to write while RetroArch is running, because RetroArch holds
  its playlists in memory and writes them back on exit. The Batocera
  engine defers the write instead; here there is no Ports menu launching
  us, so refusing is both simpler and safer.

Decisions worth recording. One playlist per platform, because
default_core_path is a header field — that is what makes every entry start
without a core prompt; the entries themselves stay on DETECT so the file
survives being moved to a machine whose cores live elsewhere. A missing
core is a note, not an error, or the store would never run for anyone the
first time. The playlist name carries the store's name, so we never write
into RetroArch's own Commodore - 64.lpl and never collide with the
official thumbnail packs — and an entry the user added to our playlist
survives a rewrite. Thumbnails must be PNG and one of our covers is a
GIF, so the engine shells out to sips/magick/convert/ffmpeg, and simply
goes without the image if the machine has none of them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 18:00:19 +02:00