ttg-batocera-storeputs our catalog on Batocera. It pulls games from the WarpEngine catalog on https://teletypegames.org onto the box, writes EmulationStation metadata and box art, and gives the store its own menu entry — Teletype Games — from which it can be re-run for new releases.
This repository is only the store definition: a config.json and two wrapper scripts. The logic is in the shared engine, warp-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 TTG RetroArch Store.
list, sync, remove, config, with dry-run, per-title sync and overrides./userdata/roms/teletypegames/, 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.gamelist.xml, so the games look scraped./api/download so they count in the statistics.Teletype Games ▸ "Update Teletype Games"
│
├─ GET /api/software the whole catalog
├─ keep platforms this box can run c64 → c64, tic80 → tic80
├─ pick the newest non-dev release that carries the right asset
├─ GET /api/download?path=<asset> .prg / .tic into the store's folder
├─ GET <software.imageUrl> box art
├─ write gamelist.xml title, desc, author, image
└─ write es_systems_ttg.cfg the menu entry
Repository: https://git.teletypegames.org/stores/ttg-batocera-store
Engine: warp-engine-batocera-store
RetroArch equivalent: TTG RetroArch Store
Batocera: https://batocera.org
Catalog side: WarpEngine (libs/ruby/warp_engine in the teletypegames repo)
Over SSH on the Batocera box:
curl -fsSL https://git.teletypegames.org/stores/ttg-batocera-store/raw/branch/master/install.sh | sh
Or from a checkout:
scp -r ttg-batocera-store root@batocera:/tmp/
ssh root@batocera /tmp/ttg-batocera-store/install.sh
The installer fetches the engine, puts it and this config in /userdata/system/batocera-store/ttg/, writes the ttg-store launcher, and runs the first sync — which downloads the games and writes the menu entry. Restart EmulationStation to see it:
batocera-es-swissknife --restart
Re-running the installer is the upgrade path: it refreshes the engine and the config (keeping the old one as config.json.bak) and leaves state.json alone.
Coming from a pre-4.0 install, that first sync also changes the layout: the games move out of c64/teletypegames/, tic80/teletypegames/ and ports/teletypegames/ into /userdata/roms/teletypegames/, and the old Ports ▸ Teletype Games Store 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 gamelist.xml.ttg-backup copies are listed and left alone. -n sync shows all of it first; emulationstation.menu.mode: "merge" in config.json keeps the old layout.
On the device. Teletype Games ▸ "Update Teletype Games". It downloads anything new, then restarts EmulationStation. A script launched by ES gets no console, so the output goes to /userdata/system/batocera-store/ttg/store.log.
main carousel: … ▸ Teletype Games ▸ ┌ Commodore 64
├ TIC-80
├ Ports the native builds
└ Update Teletype Games the sync
The entry carries our own TTG logo, shipped in the store repository (logo.png, plus a vector logo.svg 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 TELETYPE GAMES 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 goes after a reboot — the next update puts it back, batocera-save-overlay makes it permanent.
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 c64, tic80 and ports systems, so whatever you configured for the C64 applies to our C64 games too. How that is put together is on the engine page.
Over SSH, through the launcher:
S=/userdata/system/batocera-store/ttg-store
$S list # compatible catalog entries; * installed, ^ update available
$S sync # download everything new, refresh gamelists and the menu
$S sync blessingofra # just one title (never prunes)
$S -n sync # dry run
$S remove c64demo # uninstall (c64:c64demo works too)
$S config # effective configuration, and where the menu entry lives
Global flags go before the subcommand: $S --roms-root /tmp/roms sync.
| Flag | Meaning |
|---|---|
--config <path> |
store config file (default /userdata/system/batocera-store/ttg/config.json) |
--base-url <url> |
override the catalog base URL |
--roms-root <path> |
override the ROMs root |
-n, --dry-run |
report actions, change nothing |
-v, --verbose |
verbose logging |
--version |
print version |
Two subcommands are for the machine, not the user: sync-from-es is what the menu entry calls, and apply-gamelists --wait-pid <pid> is the detached child it spawns.
/userdata/system/batocera-store/
├── ttg-store launcher
└── ttg/
├── store.py, warpstore.py the engine and its shared core
├── config.json the store that selected it
└── state.json, catalog.json, store.log
/userdata/system/configs/emulationstation/es_systems_ttg.cfg the menu entry
/userdata/roms/teletypegames/ the store's own ROM folder
├── c64/
│ ├── blessingofra-2.0.0.prg, rabbit-1.0.0.prg, …
│ ├── images/blessingofra.png, …
│ └── gamelist.xml
├── tic80/ …
├── ports/
│ ├── rabbitroller.sh, images/rabbitroller.png
│ ├── .data/rabbitroller/ the unpacked program
│ └── gamelist.xml
└── store/ the menu entry itself
├── update.sh the sync, as the menu starts it
└── 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 state.json records is ever deleted — a ROM you drop in there yourself keeps its file and its gamelist entry.
/userdata/system/batocera-store/ttg/config.json, shipped by this repository:
{
"store": {
"id": "ttg",
"name": "Teletype Games",
"base_url": "https://teletypegames.org",
"api": { "catalog": "/api/software", "download": "/api/download" }
},
"paths": { "roms_root": "/userdata/roms", "subfolder": "teletypegames" },
"emulationstation": { "menu": { "mode": "system", "theme": "ports", "updater": true }, "restart": true },
"catalog": { "statuses": ["released", "archived"], "owner_id": null, "only": [], "exclude": [] },
"platforms": {
"c64": { "system": "c64", "kind": "cartridge", "ext": ".prg", "enabled": true },
"tic80": { "system": "tic80", "kind": "cartridge", "ext": ".tic", "enabled": true }
},
"behavior": { "prune": true, "timeout": 30, "insecure": false }
}
The full key reference is on the engine page. Edits on the box survive until the next install, which keeps the old file as config.json.bak. Anything permanent belongs in the repository.
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 es_systems.cfg, which is an emulator that got built into this image:
| Catalog platform | Asset kind | Batocera system | Extension |
|---|---|---|---|
c64 |
cartridge |
c64 (VICE) |
.prg |
tic80 |
cartridge |
tic80 |
.tic |
ebitengine, bevy, godot and love ship native Linux builds, and are installed the way Batocera's Ports system does it — a .sh launcher next to the unpacked program, under Teletype Games ▸ Ports. phaser ships only a web build, so it is not mapped. Adding a mapping is a config edit:
"platforms": { "godot": { "system": "godot", "kind": "linux_x64", "ext": ".zip", "enabled": true } }
A platform is skipped when the box does not have its system; list reports skipped <name>: this box has no '<system>' system.
curl -fsSL https://git.teletypegames.org/stores/ttg-batocera-store/raw/branch/master/uninstall.sh | sh
It removes the games (including any unpacked port payload), our gamelists, the menu entry, the launcher and the store home, then restarts EmulationStation. DRY_RUN=1 shows what would go; KEEP_HOME=1 keeps state.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 gamelist.xml.ttg-backup copies are listed and left alone.
| Symptom | What to check |
|---|---|
| Nothing appears after an update | /userdata/system/batocera-store/ttg/store.log, then batocera-es-swissknife --restart |
skipped <name>: this box has no '<system>' system |
that emulator is not in this Batocera image |
| The Teletype Games entry is missing | the sync writes /userdata/system/configs/emulationstation/es_systems_ttg.cfg; check the log, then restart ES |
| TLS errors against a test instance | set behavior.insecure to true in config.json |
| A title should never be installed | add its catalog name to catalog.exclude in config.json |
| Gamelist looks wrong | ours is at /userdata/roms/teletypegames/<system>/gamelist.xml; a pre-4.0 original is at <system>/gamelist.xml.ttg-backup |
| Games vanished after a sync | a missing ROMs mount aborts the sync by design; check /userdata/roms is mounted |