TITLE=TTG Batocera Store ROUTE=/stores/ttg-batocera-store LOCALE=en MODIFIED=1792413440 URL=https://wiki.teletypegames.org/stores/ttg-batocera-store L=1 https://batocera.org L=2 #22 L=3 https://teletypegames.org L=4 #45 L=5 #44 L=6 https://git.teletypegames.org/stores/ttg-batocera-store L=7 #24 L=8 #57 L=9 #63 L=10 #64 q│ cttg-batocera-storeq puts our catalog on 1Batocera. It pulls games from the q│ 2WarpEngine catalog on 3https://teletypegames.org onto the box, writes q│ EmulationStation metadata and box art, and gives the store bits own menu q│ bentryq — iTeletype Gamesq — from which it can be re-run for new releases. This repository is only the store definition: a cconfig.json0 and two wrapper scripts. The logic is in the shared engine, 4warp-engine-batocera-store — this is the Teletype Games configuration of it. For RetroArch instead of a Batocera box — desktop, Android, Steam Deck, handheld — use the 5TTG RetroArch Store. 1What you get 1════════════ • bIts own menu entry0 — iTeletype Games0 in the main carousel, with a folder per platform inside it and an iUpdate Teletype Games0 entry that downloads everything new in one click. • bCLI over SSH0 — clist0, csync0, cremove0, cconfig0, with dry-run, per-title sync and overrides. • bYour library is left alone0 — the games live in c/userdata/roms/teletypegames/0, not in the box's own systems, so nothing of yours can be touched by a sync or a prune. Favourites and play counts on our games survive a rewrite. • bBox art and metadata0 — title, description, author and image are merged into cgamelist.xml0, so the games look scraped. • bZero dependencies0 — Python 3 standard library only. • bIdempotent syncs0 — existing files are left alone, and a release bump replaces the old asset. Downloads go through c/api/download0, but teletypegames.org deliberately leaves the store's cwarpstore/*0 user agent out of the download counts, so installs on a box do not show up in the statistics. 1Overview 1════════ p Teletype Games ▸ "Update Teletype Games" p │ p ├─ GET /api/software the whole catalog p ├─ keep platforms this box can run c64 → c64, plus4 → cplus» p 4, tic80 → tic80 p ├─ pick the newest non-dev release that carries the right a» p sset p ├─ GET /api/download?path= .prg / .tic into the sto» p re's folder p ├─ GET box art p ├─ write gamelist.xml title, desc, author, ima» p ge p └─ write es_systems_ttg.cfg the menu entry bRepository:0 6https://git.teletypegames.org/stores/ttg-batocera-store bEngine: 4warp-engine-batocera-store bRetroArch equivalent:0 5TTG RetroArch Store bBatocera: 1https://batocera.org bCatalog side:0 2WarpEngine (clibs/ruby/warp_engine0 in the cteletypegames0 repo) r────────────────────────────────────────────────────────────────────────────── 1Install 1═══════ Over SSH on the Batocera box: p curl -fsSL https://git.teletypegames.org/stores/ttg-batocera-store/raw/branc» p h/master/install.sh | sh Or from a checkout: p scp -r ttg-batocera-store root@batocera:/tmp/ p ssh root@batocera /tmp/ttg-batocera-store/install.sh The installer fetches the engine — pinned to the commits of 4warp-engine-batocera-store and 7warpstore this config was tested with, set at the top of cinstall.sh0 (cENGINE_REF0 and cWARPSTORE_REF0 override them) — puts it and this config in c/userdata/system/batocera-store/ttg/0, writes the cttg-store launcher, and runs the first sync — which downloads the games and writes the menu entry. Restart EmulationStation to see it: p batocera-es-swissknife --restart Re-running the installer is the upgrade path: it refreshes the engine, the config (keeping the old one as cconfig.json.bak0) and the cached logo, and leaves cstate.json0 alone. Run from a checkout, it takes the logo from the checkout too. bComing from a pre-4.0 install0, that first sync also changes the layout: the games move out of cc64/teletypegames/0, ctic80/teletypegames/0 and cports/teletypegames/0 into c/userdata/roms/teletypegames/0, and the old iPorts ▸ iTeletype Games Store0 entry goes away — the updater is inside the menu entry now. The move is a removal plus a fresh download rather than a file shuffle, which for today's catalog is minutes. Your gamelists are rewritten without our nodes, never deleted, and the cgamelist.xml.ttg-backup0 copies are listed and left alone. c-n sync0 shows all of it first; cemulationstation.menu.mode: "merge" in cconfig.json0 keeps the old layout. r────────────────────────────────────────────────────────────────────────────── 1Use 1═══ bOn the device.0 iTeletype Games ▸ "Update Teletype Games"0. It downloads anything new, then restarts EmulationStation. A script launched by ES gets no console, so the output goes to c/userdata/system/batocera-store/ttg/store.log0. p main carousel: … ▸ Teletype Games ▸ ┌ Commodore 64 p ├ Commodore Plus/4 p ├ TIC-80 p ├ Ports the nati» p ve builds p └ Update Teletype Games the sync The entry carries our own TTG logo, shipped in the store repository (clogo.png0, plus a vector clogo.svg0 for themes that only look for an SVG) and installed by the engine into the logo folder of every theme on the box. A theme it cannot reach shows iTELETYPE GAMES0 as text instead. Themes that came with the Batocera image live on the read-only system, so their copy is in the RAM overlay and would go after a reboot; a few lines the engine adds to c/boot/postshare.sh0 put it back before EmulationStation starts, and the uninstall takes them out again. Only the platforms that have something installed show up, and the games are launched exactly as the box would launch them: the engine copies the launch command and the emulator list off the box's own cc640, ccplus40, ctic800 and cports systems, so whatever you configured for the C64 applies to our C64 games too. How that is put together is on the 4engine page. bOver SSH0, through the launcher: p S=/userdata/system/batocera-store/ttg-store p p $S list # compatible catalog entries; * installed, ^ update » p available p $S sync # download everything new, refresh gamelists and the» p menu p $S sync blessingofra # just one title (never prunes) p $S -n sync # dry run p $S remove c64-demo # uninstall (c64:c64-demo works too) p $S config # effective configuration, and where the menu entry » p lives Global flags go bbefore0 the subcommand: c$S --roms-root /tmp/roms sync0. t┌────────────────────┬───────────────────────────────────────────────────────┐ t│h Flag t│h Meaning t│ t├────────────────────┼───────────────────────────────────────────────────────┤ t│0 c--config 0 t│0 store config file (default t│ t│0 t│0 c/userdata/system/batocera-store/ttg/config.json0) t│ t├────────────────────┼───────────────────────────────────────────────────────┤ t│0 c--base-url 0 t│0 override the catalog base URL t│ t├────────────────────┼───────────────────────────────────────────────────────┤ t│0 c--roms-root 0 t│0 override the ROMs root t│ t├────────────────────┼───────────────────────────────────────────────────────┤ t│0 c-n0, c--dry-run0 t│0 report actions, change nothing t│ t├────────────────────┼───────────────────────────────────────────────────────┤ t│0 c-v0, c--verbose0 t│0 verbose logging t│ t├────────────────────┼───────────────────────────────────────────────────────┤ t│0 c--version0 t│0 print version t│ t└────────────────────┴───────────────────────────────────────────────────────┘ Two subcommands are for the machine, not the user: csync-from-es0 is what the menu entry calls, and capply-gamelists --wait-pid 0 is the detached child it spawns. r────────────────────────────────────────────────────────────────────────────── 1Where things land 1═════════════════ p /userdata/system/batocera-store/ p ├── ttg-store launcher p └── ttg/ p ├── store.py, warpstore.py the engine and its shared core p ├── config.json the store that selected it p └── state.json, catalog.json, store.log p p /userdata/system/configs/emulationstation/es_systems_ttg.cfg the menu entr» p y p //ttg.png, ttg.svg the entry's » p logo p /boot/postshare.sh a marked block that restores that l» p ogo at boot p p /userdata/roms/teletypegames/ the store's own ROM folder p ├── c64/ p │ ├── blessingofra-2.2.0.prg, rabbit-1.0.0.prg, … p │ ├── images/blessingofra.png, … p │ └── gamelist.xml p ├── cplus4/ … p ├── tic80/ … p ├── ports/ p │ ├── rabbitroller.sh, images/rabbitroller.png p │ ├── .data/rabbitroller/ the unpacked program p │ └── gamelist.xml p └── store/ the menu entry itself p ├── update.sh the sync, as the menu starts it p └── gamelist.xml The box's own system folders are not touched at all: a prune cannot reach your ROMs because they are not in the same tree, and inside our folder only what cstate.json0 records is ever deleted — a ROM you drop in there yourself keeps its file and its gamelist entry. r────────────────────────────────────────────────────────────────────────────── 1Configuration 1═════════════ c/userdata/system/batocera-store/ttg/config.json0, shipped by this repository: p { p "store": { p "id": "ttg", p "name": "Teletype Games", p "base_url": "https://teletypegames.org", p "api": { p "catalog": "/api/software", p "download": "/api/download" p } p }, p p "paths": { p "roms_root": "/userdata/roms", p "subfolder": "teletypegames" p }, p p "emulationstation": { p "menu": { p "mode": "system", p "updater": true, p "logo": [ p "https://git.teletypegames.org/stores/ttg-batocera-store/raw/branch/» p master/logo.png", p "https://git.teletypegames.org/stores/ttg-batocera-store/raw/branch/» p master/logo.svg" p ] p }, p "folder_name": "Teletype Games", p "restart": true p }, p p "catalog": { p "statuses": ["released", "archived"], p "owner_id": null, p "only": [], p "exclude": [] p }, p p "platforms": { p "c64": { "system": "c64", "kind": "cartridge", "ext": ".prg", "enabled":» p true }, p "plus4": { "system": "cplus4", "kind": "cartridge", "ext": ".prg", "enab» p led": true }, p "tic80": { "system": "tic80", "kind": "cartridge", "ext": ".tic", "enabl» p ed": true }, p p "ebitengine": { "system": "ports", "install": "port", p "kind": { "x86_64": "linux_x64", "aarch64": "linux_arm64» p " }, p "ext": ".zip", "enabled": true }, p "bevy": { "system": "ports", "install": "port", p "kind": { "x86_64": "linux_x64", "aarch64": "linux_arm64" }, p "ext": ".zip", "enabled": true }, p "godot": { "system": "ports", "install": "port", p "kind": { "x86_64": "linux_x64" }, p "ext": ".zip", "enabled": true }, p "love": { "system": "ports", "install": "port", p "kind": { "x86_64": "linux_x64" }, p "ext": ".zip", "enabled": true } p }, p p "behavior": { p "prune": true, p "timeout": 30, p "insecure": false p } p } The full key reference is on the 4engine page. Edits on the box survive until the next install, which keeps the old file as cconfig.json.bak0. Anything permanent belongs in the repository, whose CI (cscripts/ci-check.sh0) has the pinned engine load the config and fails on any key the engine does not know — a misspelt key would otherwise be silently ignored. 2Platform mapping 2──────────────── A platform is installable when its release asset is a file a system this box has can boot directly — "has" meaning declared in the box's own ces_systems.cfg0, which is an emulator that got built into this image: t┌──────────────────┬────────────┬──────────────────────┬───────────┐ t│h Catalog platform t│h Asset kind t│h Batocera system t│h Extension t│ t├──────────────────┼────────────┼──────────────────────┼───────────┤ t│0 8cc640 t│0 ccartridge0 t│0 cc640 (VICE) t│0 c.prg0 t│ t│0 9cplus40 t│0 ccartridge0 t│0 ccplus40 (VICE cxplus40) t│0 c.prg0 t│ t│0 10ctic800 t│0 ccartridge0 t│0 ctic800 t│0 c.tic0 t│ t└──────────────────┴────────────┴──────────────────────┴───────────┘ cebitengine0, cbevy0, cgodot0 and clove0 ship native Linux builds, and are installed the way Batocera's Ports system does it — a c.sh0 launcher next to the unpacked program, under iTeletype Games ▸ Ports0, with the asset picked for the box's architecture (clinux_x640 or clinux_arm640; cgodot0 and clove0 are x86_64-only). cphaser0 ships only a web build, so it is not mapped. Most native titles are cdemo0, which ccatalog.statuses0 leaves out; adding it is a config edit: p "catalog": { "statuses": ["released", "archived", "demo"] } A platform is skipped when the box does not have its system; clist0 reports cskipped : this box has no '' system0. r────────────────────────────────────────────────────────────────────────────── 1Uninstall 1═════════ p curl -fsSL https://git.teletypegames.org/stores/ttg-batocera-store/raw/branc» p h/master/uninstall.sh | sh It removes the games (including any unpacked port payload), our gamelists, the menu entry, the logos it put in your themes and its block in c/boot/postshare.sh0, the launcher and the store home, then restarts EmulationStation. cDRY_RUN=10 shows what would go; cKEEP_HOME=10 keeps cstate.json and the log for a reinstall. Nothing of yours goes with it: everything the store installed is in one folder of its own, so an uninstall leaves the box exactly as it was. On a box that still uses the old layout, your gamelists are rewritten without our nodes rather than deleted, so play counts and favourites survive, and the cgamelist.xml.ttg-backup0 copies are listed and left alone. r────────────────────────────────────────────────────────────────────────────── 1Troubleshooting 1═══════════════ t┌─────────────────────────────────────┬──────────────────────────────────────┐ t│h Symptom t│h What to check t│ t├─────────────────────────────────────┼──────────────────────────────────────┤ t│0 Nothing appears after an update t│0 c/userdata/system/batocera-store/ttg/0 t│ t│0 t│0 cstore.log0, then t│ t│0 t│0 cbatocera-es-swissknife --restart0 t│ t├─────────────────────────────────────┼──────────────────────────────────────┤ t│0 cskipped : this box has no0 t│0 that emulator is not in this t│ t│0 c'' system0 t│0 Batocera image t│ t├─────────────────────────────────────┼──────────────────────────────────────┤ t│0 The iTeletype Games0 entry is missing t│0 the sync writes t│ t│0 t│0 c/userdata/system/configs/0 t│ t│0 t│0 cemulationstation/es_systems_ttg.cfg0; t│ t│0 t│0 check the log, then restart ES t│ t├─────────────────────────────────────┼──────────────────────────────────────┤ t│0 TLS errors against a test instance t│0 set cbehavior.insecure0 to ctrue0 in t│ t│0 t│0 cconfig.json0 t│ t├─────────────────────────────────────┼──────────────────────────────────────┤ t│0 A title should never be installed t│0 add its catalog name to t│ t│0 t│0 ccatalog.exclude0 in cconfig.json0 t│ t├─────────────────────────────────────┼──────────────────────────────────────┤ t│0 Gamelist looks wrong t│0 ours is at t│ t│0 t│0 c/userdata/roms/teletypegames/0 t│ t│0 t│0 c/gamelist.xml0; a pre-4.0 t│ t│0 t│0 original is at t│ t│0 t│0 c/gamelist.xml.ttg-backup0 t│ t├─────────────────────────────────────┼──────────────────────────────────────┤ t│0 The entry shows iTELETYPE GAMES0 as t│0 the cttg-store: theme logos0 block in t│ t│0 text after a reboot t│0 c/boot/postshare.sh0; if it is t│ t│0 t│0 missing, the log says why (c/boot0 t│ t│0 t│0 could not be remounted read-write) — t│ t│0 t│0 re-run the installer t│ t├─────────────────────────────────────┼──────────────────────────────────────┤ t│0 cskipped : sold on the store's0 t│0 a paid title; the store downloads t│ t│0 csite0 t│0 anonymously, so buy it on the site t│ t├─────────────────────────────────────┼──────────────────────────────────────┤ t│0 Games vanished after a sync t│0 a missing ROMs mount aborts the sync t│ t│0 t│0 by design; check c/userdata/roms0 is t│ t│0 t│0 mounted t│ t└─────────────────────────────────────┴──────────────────────────────────────┘