`purge` is what `remove` does for one game, for all of them at once, plus the
directories the store made: the shell script that wraps it has no way of knowing
which ROMs, box art, Ports payloads and gamelist entries were ours, and
state.json does. uninstall.sh runs it first and only then removes the Ports
entry, the launcher and the store home; if the engine cannot finish, nothing
else is touched.
The gamelists are merged rather than deleted — they belong to the box, and the
user's own games, play counts and favourites stay in them. Two things the tests
caught:
- The backup was being made on the *uninstall's* first touch of a gamelist we
had created ourselves, so a purge produced a backup of our own file. It is now
skipped when purging: by then any copy worth having was made on the first sync.
- With that fixed, "no backup next to it" means we created the file, so a
gamelist that is left empty is deleted too. The box ends up as it was.
- The Ports entry cannot be derived from the config: a store repository sets
BATOCERA_PORT_NAME to whatever it likes (ours is "Teletype Games Store" while
store.name is "Teletype Games"), so the uninstaller looked for a file that was
never there. It now finds the entry by looking inside the Ports scripts for
this store's home — which also works for boxes installed before this existed,
and picks up entries left over from an earlier name.
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>
The engine was 1073 lines, of which the catalog, release selection,
architecture matching, state and HTTP had nothing to do with Batocera. A
second engine now needs exactly that code, so it moved to engines/warpstore
and this repository keeps only the Batocera adapter: ROM folders, Ports
launchers, gamelist.xml, EmulationStation. 426 lines went, 115 came back.
Nothing about the CLI or the on-disk layout changes. The installer now
places two files side by side, store.py and warpstore.py, and the engine
imports the core from its own directory.
Behaviour was checked against the live catalog with a fake ROMs root: the
cartridge path, the Ports path, box art, gamelist merging, idempotent
re-sync, prune and uninstall all produce what they did before. A state.json
written by the previous version — records with `system` but no `scope` —
keys the same way, so an installed box neither re-downloads nor prunes
anything on the first sync after the upgrade.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The store could only offer what an emulator boots, which meant cartridges
and nothing else — a little under half the catalog. Batocera runs Linux
and its Ports system takes .sh launchers, so a native build can be
installed too; it just is not a ROM.
`install: "port"` unpacks the zip, finds the executable and writes a
launcher that cd's into its directory first, because a game loads assets
relative to the working directory. Python's extraction drops the
executable bit, so it is restored from the mode the archive records.
The payload lives under ports/.data/<subfolder>/, whose leading dot is
what hides it from EmulationStation, while the launchers sit in a normal
subfolder that gets scanned like any other system's. Both carry the store
subfolder, so an uninstall or prune cannot reach another store's files —
verified by pointing a state entry at a foreign namespace and watching it
be refused.
Verified end to end on aarch64 against the live catalog: bevydemo
installs as a port with an executable launcher and binary, re-syncs
without re-downloading 29 MB, and uninstalls taking its 106 MB payload
with it. `install` defaults to rom, so existing configs are unaffected.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Batocera runs on x86_64 mini-PCs, on the Raspberry Pi and on the ARM
handhelds alike. A cartridge is data for an emulator and runs anywhere,
which is why this store has been portable so far — but a native build
only starts on the architecture it was built for, and an x86_64 binary
installed on a Pi is worse than one never offered.
`kind` and `ext` may now be a map from architecture to value, with `*`
as a fallback; a plain string still means "the same everywhere", so
existing configs are untouched. The detected architecture is reported at
the top of `list` and as `detected_arch` in `config`, and a title with no
asset for the running machine is skipped with a reason that names it.
Verified on aarch64 against the live catalog: the cartridge config
behaves exactly as before, a bevy entry mapped per architecture pulls
bevydemo-1.0.0-linux-arm64.zip, and the binary inside is an AArch64 ELF.
A map without an entry for the machine skips and says so.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>