The `tools` definition — "one person installs and runs it for themselves" — stopped
describing the store engines: they are products other people can build on. So the
four store repositories left `tools` for a `stores` org of their own, and the org
map, the clone list, the wiki table and the ecosystem plan follow. `check-repos.sh`
is green at 49.
Worth writing down for the next org change: `tea` has no transfer subcommand, but
`tea api` makes authenticated calls with tea's own credentials, which is enough for
all of it — POST /orgs, POST /orgs/{org}/teams, PUT /teams/{id}/members/{user},
POST /repos/{owner}/{repo}/transfer. And the transfer keeps Gitea's redirect: the
old `tools/...` raw URLs answer 301, so the installers on already-configured boxes
did not break. That is why this was a transfer and not a migrate-and-delete.
`warpstore` stayed in `engines`: it is a library other repos depend on, which is
what that org is for. The launcher gap moved out of the tools chapter of
ECOSYSTEM_PLAN into the new stores one, where it belongs.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
591 lines
25 KiB
Markdown
591 lines
25 KiB
Markdown
# RetroArch store — kidolgozott terv
|
|
|
|
Készült 2026-08-17-én, a `MORE_STORES.md` 4. pontjának kifejtéseként. Minden
|
|
alább szereplő formátum- és útvonal-állítás **egy valódi RetroArch-telepítésen
|
|
mérve** (macOS, `~/Library/Application Support/RetroArch`), a mag-elérhetőség
|
|
pedig a libretro buildbot indexéből.
|
|
|
|
> **Megvalósítva ugyanaznap.** Három repó:
|
|
> [`engines/warpstore`](https://git.teletypegames.org/engines/warpstore) (közös mag),
|
|
> [`stores/warp-engine-retroarch-store`](https://git.teletypegames.org/stores/warp-engine-retroarch-store) (motor),
|
|
> [`stores/ttg-retroarch-store`](https://git.teletypegames.org/stores/ttg-retroarch-store) (a mi store-unk).
|
|
> A 11. pont javaslatával szemben a közös rész **azonnal** kiemelve — lásd az
|
|
> „Amit a megvalósítás megváltoztatott" szakaszt a végén. Wiki:
|
|
> [warpstore](https://wiki.teletypegames.org/engines/warpstore),
|
|
> [motor](https://wiki.teletypegames.org/others/warp-engine-retroarch-store),
|
|
> [store](https://wiki.teletypegames.org/others/ttg-retroarch-store).
|
|
|
|
---
|
|
|
|
## 1. Mit ad, és mit nem
|
|
|
|
Új asset-fajtát **nem** fogyaszt: a RetroArch magok emulátorok, tehát pontosan
|
|
azt eszik, amit a Batocera-store is — a `cartridge`-et. A nyereség nem a
|
|
katalógus szélessége, hanem a **gazdagépek száma**: asztali Linux/Windows/macOS,
|
|
Android, Steam Deck, konzolportok.
|
|
|
|
A mai katalógusból (`released` + `archived`, 2026-08-17-i mérés) ez **6 cím,
|
|
összesen 233 KB**:
|
|
|
|
| Platform | Szoftver | Cím | Fájl | Méret | CRC32 |
|
|
|---|---|---|---|---|---|
|
|
| c64 | blessingofra | Blessing of Ra | `blessingofra-2.0.0.prg` | 10 561 | `4253CFC6` |
|
|
| c64 | rabbitc64 | Rabbit Scooter | `rabbit-1.0.0.prg` | 8 072 | `31BF558B` |
|
|
| c64 | c64demo | C64 Demo for Arok Party 2026 | `c64demo-1.0.prg` | 4 641 | `AF681FFC` |
|
|
| tic80 | impostor | Definitely not an Impostor | `impostor-1.0.tic` | 163 497 | `8D9A45DA` |
|
|
| tic80 | bombexpert | BombExpert | `bombexpert-0.2.tic` | 25 227 | `A67DDF4A` |
|
|
| tic80 | mranderson | Mr Anderson's Adventure | `mranderson-0.1.tic` | 21 657 | `E98C7712` |
|
|
|
|
Egy egész store negyed megabájt — ez az, ami miatt a szinkron gyakorlatilag
|
|
ingyen van, és amiért az Android-célpont zip-ként is elküldhető.
|
|
|
|
**A két mag mindenhol van.** A buildbot indexét végigkérdezve:
|
|
|
|
| Cél | `vice_x64` | `tic80` | Fájlnév-alak |
|
|
|---|---|---|---|
|
|
| `apple/osx/arm64`, `apple/osx/x86_64` | ✔ | ✔ | `*_libretro.dylib` |
|
|
| `linux/x86_64`, `linux/aarch64`, `linux/armhf`, `linux/armv7-neon-hf` | ✔ | ✔ | `*_libretro.so` |
|
|
| `windows/x86_64` | ✔ | ✔ | `*_libretro.dll` |
|
|
| `android/arm64-v8a`, `android/armeabi-v7a` | ✔ | ✔ | `*_libretro_android.so` |
|
|
|
|
Tehát nincs olyan RetroArch-gazdagép, amin a katalógusunk ne futna — az Androidon
|
|
viszont a mag fájlneve más (`_android.so`), és ez a tervben külön kezelendő.
|
|
|
|
**Amire nem jó.** A `html` és a natív zipek maradnak kiszolgálatlanul: nincs
|
|
böngésző-mag, és a LÖVE-hoz sincs valódi mag (a `lutro` nem LÖVE-kompatibilis).
|
|
Batocerán és a többi ES-frontendes gépen szintén nem ez a helyes út — ott a
|
|
RetroArch a háttérben fut, a könyvtár az ES-é, tehát a Batocera-store dolgozik.
|
|
|
|
---
|
|
|
|
## 2. A gazdaplatform, ahogy valóban kinéz
|
|
|
|
Négy dolgot érdemes tudni, mert mind a négy megcáfol egy kézenfekvő feltevést.
|
|
|
|
**(a) A `retroarch.cfg` az egyetlen igazságforrás, és a könyvtárak szét vannak
|
|
szórva.** A mért gépen:
|
|
|
|
```
|
|
libretro_directory = ~/Library/Application Support/RetroArch/cores
|
|
thumbnails_directory = ~/Library/Application Support/RetroArch/thumbnails
|
|
playlist_directory = ~/Documents/RetroArch/playlists ← más ág!
|
|
system_directory = ~/Documents/RetroArch/system
|
|
```
|
|
|
|
A playlist-könyvtár tehát nem a RetroArch alapkönyvtárában van. Aki a
|
|
`<base>/playlists` mintát égeti bele, az ezen a gépen csendben a semmibe ír.
|
|
**A motor kötelezően a `retroarch.cfg`-ből olvassa a négy útvonalat**, és csak
|
|
akkor tippel, ha nincs cfg. A `~` feloldandó, ahogy a Windows-os portable
|
|
telepítések `:` prefixe is (a RetroArch saját alapkönyvtárát jelenti).
|
|
|
|
**(b) A `.lpl` sémája.** Egy valódi playlist a gépről (rövidítve) — ez a
|
|
formátum, amit írunk, nem egy dokumentációból vett közelítés:
|
|
|
|
```json
|
|
{
|
|
"version": "1.5",
|
|
"default_core_path": "",
|
|
"default_core_name": "",
|
|
"label_display_mode": 0,
|
|
"right_thumbnail_mode": 0,
|
|
"left_thumbnail_mode": 0,
|
|
"thumbnail_match_mode": 0,
|
|
"sort_mode": 0,
|
|
"items": [
|
|
{
|
|
"path": "/…/Altered Beast (USA).md",
|
|
"entry_slot": -1,
|
|
"label": "Altered Beast (USA)",
|
|
"core_path": "/…/cores/genesis_plus_gx_libretro.dylib",
|
|
"core_name": "Sega - MS/GG/MD/CD (Genesis Plus GX)",
|
|
"crc32": "00000000|crc",
|
|
"db_name": "Sega - Mega Drive - Genesis.lpl"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
Két tanulság: a `path` **absztolút út a célgépen** (ez a 7. pont egész
|
|
problémája), a `crc32` pedig `"<8 hex>|crc"` alakú — a RetroArch maga gyakran
|
|
csak nullákat ír bele, mi a valódit tudjuk beírni (`zlib.crc32`, ingyen).
|
|
|
|
**(c) A borítókép útja a playlist nevéből jön.** A mért gépen:
|
|
|
|
```
|
|
thumbnails/Sega - Mega Drive - Genesis/Named_Boxarts/*.png
|
|
Named_Titles/*.png
|
|
Named_Snaps/*.png
|
|
```
|
|
|
|
Vagyis `thumbnails/<db_name a .lpl nélkül>/Named_*/<label>.png`, és **kizárólag
|
|
PNG** — a RetroArch a fájlnevet `.png`-re képezi. Ez a 6. pont.
|
|
|
|
**(d) A magok saját nevet és kiterjesztést hoznak.** A `.info` fájlokból:
|
|
|
|
| Mag | `corename` | `supported_extensions` | `database` |
|
|
|---|---|---|---|
|
|
| `tic80_libretro` | `TIC-80` | `tic` | `TIC-80` |
|
|
| `vice_x64_libretro` | `VICE x64` | `d64\|…\|prg\|p00\|crt\|…` | `Commodore - 64` |
|
|
|
|
A `prg` valóban ott van a VICE listájában, tehát a nyers `.prg`-ket a mag
|
|
közvetlenül indítja — nem kell `.d64`-be csomagolni.
|
|
|
|
**Amit a formátum nem tud: leírást.** A `.lpl` bejegyzésnek nincs `desc` vagy
|
|
`developer` mezője. A katalógus `desc`/`author` adata itt elveszik — a Batocera
|
|
`gamelist.xml`-hez képest ez visszalépés, és nem konfigurálható. (Elvileg egy
|
|
generált `.rdb` adná meg az Információ-panelt és az Explore nézetet, de az bináris
|
|
libretrodb-formátum, külső eszközlánccal — ez a terven kívül, külön feladat.)
|
|
|
|
---
|
|
|
|
## 3. A megoldás felépítése
|
|
|
|
A Batocera-store bevált kettéosztását követi, ugyanazokkal a nevekkel:
|
|
|
|
```
|
|
stores/warp-engine-retroarch-store a motor: retroarch_store.py, install.sh
|
|
▲
|
|
│ config.json
|
|
stores/ttg-retroarch-store a mi store-unk: config + wrapper installer
|
|
```
|
|
|
|
Amit telepít:
|
|
|
|
```
|
|
<content_root>/<subfolder>/<platform>/ blessingofra-2.0.0.prg, impostor-1.0.tic, …
|
|
<playlists_dir>/Teletype Games - Commodore 64.lpl
|
|
<playlists_dir>/Teletype Games - TIC-80.lpl
|
|
<thumbnails_dir>/Teletype Games - Commodore 64/Named_Boxarts/Blessing of Ra.png
|
|
/Named_Titles/… /Named_Snaps/…
|
|
<store_home>/config.json, state.json, catalog.json, store.log
|
|
```
|
|
|
|
`content_root` alapértéke `<retroarch_dir>/content`, a `subfolder` a store
|
|
azonosítója. A `retroarch.cfg`-hez **soha nem nyúlunk** — így a store nem tud
|
|
elrontani semmit, amit a felhasználó beállított.
|
|
|
|
**Playlist platformonként, nem egyben.** Nem esztétikai döntés: a
|
|
`default_core_path`/`default_core_name` a playlist **fejlécében** van, tehát csak
|
|
akkor tud minden bejegyzés magválasztás nélkül, egy kattintással indulni, ha egy
|
|
playlisten egy mag van. Egy összevont „Teletype Games" listánál minden elemnek
|
|
saját, gépspecifikus `core_path`-ot kellene adni — ami az exportnál (7. pont)
|
|
azonnal elromlik.
|
|
|
|
A név sablonból jön: `{store} - {label}` → `Teletype Games - Commodore 64`.
|
|
Ez egyben a `db_name` és a borítókép-könyvtár neve is, tehát **a store nevével
|
|
elszigetelt névtérben vagyunk**: nem írunk bele a hivatalos
|
|
`Commodore - 64.lpl`-be, és a hivatalos thumbnail-pakkokkal sem ütközünk.
|
|
|
|
Cserébe a saját playlist-nevünkhöz nincs ikon. A RetroArch a menü-ikont
|
|
`assets/xmb/<téma>/png/<Playlist neve>.png` alapján keresi (a mért gépen ott
|
|
vannak a `Commodore - 64.png` / `Commodore - 64-content.png` párok), tehát egy
|
|
TTG-ikont le tudnánk tenni — de az `assets/` a RetroArch online updaterének
|
|
tulajdona, ami a következő frissítéskor felülírja. **Opcionális, alapból ki**
|
|
(`branding.icons: false`), és a README mondja ki, hogy nem tartós.
|
|
|
|
CLI, a Batocera-store-ral azonos alakban:
|
|
|
|
```sh
|
|
retroarch-store list # kompatibilis címek; * telepítve, ^ frissítés van
|
|
retroarch-store sync # letöltés + playlist + borító
|
|
retroarch-store sync impostor
|
|
retroarch-store -n sync # dry run
|
|
retroarch-store remove c64:blessingofra
|
|
retroarch-store export <dir> --target-prefix <út> # 7. pont
|
|
retroarch-store config
|
|
```
|
|
|
|
---
|
|
|
|
## 4. A playlist, amit írunk
|
|
|
|
Konkrétan, valódi adatokkal (a fenti CRC-k mértek):
|
|
|
|
```json
|
|
{
|
|
"version": "1.5",
|
|
"default_core_path": "/Users/tasi/Library/Application Support/RetroArch/cores/vice_x64_libretro.dylib",
|
|
"default_core_name": "VICE x64",
|
|
"label_display_mode": 0,
|
|
"right_thumbnail_mode": 0,
|
|
"left_thumbnail_mode": 0,
|
|
"thumbnail_match_mode": 0,
|
|
"sort_mode": 0,
|
|
"items": [
|
|
{
|
|
"path": "/Users/tasi/Library/Application Support/RetroArch/content/teletypegames/c64/blessingofra-2.0.0.prg",
|
|
"entry_slot": -1,
|
|
"label": "Blessing of Ra",
|
|
"core_path": "DETECT",
|
|
"core_name": "DETECT",
|
|
"crc32": "4253CFC6|crc",
|
|
"db_name": "Teletype Games - Commodore 64.lpl"
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
- `label` = a katalógus `title`-je, mert ez a megjelenő név **és** a
|
|
borítókép-fájlnév egyszerre.
|
|
- `core_path`/`core_name` a tételekben `DETECT`: a fejléc alapmagja dönt. Így egy
|
|
playlist-fájl akkor is használható marad, ha a magok máshol vannak — a
|
|
legrosszabb eset egy magválasztó kérdés, nem egy halott bejegyzés.
|
|
- `db_name` a saját fájlnevünk, ezen keresztül találja meg a borítót.
|
|
- A `*_thumbnail_mode` és `label_display_mode` marad `0` = a felhasználó globális
|
|
beállítása érvényes. Nem írjuk felül a preferenciáit (lásd 6. pont).
|
|
|
|
---
|
|
|
|
## 5. Mag-feloldás
|
|
|
|
A configban a mag **fájlneve suffix nélkül** és a megjelenő neve áll:
|
|
|
|
```json
|
|
"core": { "file": "vice_x64_libretro", "name": "VICE x64" }
|
|
```
|
|
|
|
A motor a suffixet a célgépből teszi hozzá — `darwin` → `.dylib`, `linux` →
|
|
`.so`, `windows` → `.dll`, `android` → `_android.so` —, majd megnézi, hogy a
|
|
fájl létezik-e a `libretro_directory`-ban:
|
|
|
|
- **létezik** → a playlist fejlécébe absztolút útként bekerül, a `name` mellé;
|
|
- **nem létezik** → `default_core_path` üres marad, `default_core_name` a `name`,
|
|
a tételek `DETECT`-en. A playlist működik, a RetroArch az első indításnál kérdez.
|
|
A `list`/`sync` kiírja: `magot nem találtam: vice_x64_libretro (.dylib) — DETECT`,
|
|
és mellé az egy soros megoldást: *Online Updater ▸ Core Downloader ▸ VICE x64*.
|
|
|
|
Ez a legfontosabb ergonómiai döntés: **a hiányzó mag nem hiba, hanem figyelmeztetés**.
|
|
Egy store, ami magtelepítést követel meg, nem fog lefutni senkinek elsőre.
|
|
|
|
---
|
|
|
|
## 6. Borítókép
|
|
|
|
Egy letöltés, három hely. A `thumbnail_kinds` alapból mindhárom `Named_*`
|
|
könyvtár, mert a felhasználó globális beállítása dönti el, melyiket kéri
|
|
(bal/jobb panel), és mi nem akarjuk átírni a beállítását. Három 100 KB-os fájl
|
|
6 játéknál semmi; ahol a fájlrendszer engedi, hardlink, egyébként másolat.
|
|
|
|
**A PNG-kényszer valódi problémát is jelent.** A katalógus képei ma:
|
|
|
|
| Formátum | Címek |
|
|
|---|---|
|
|
| `image/png` | bombexpert, mranderson, rabbitc64, c64demo, blessingofra (+ nem-cartridge: rabbitroller, trickster-tiles) |
|
|
| `image/gif` | **impostor** |
|
|
|
|
A RetroArch PNG-t (és JPEG/TGA/BMP-t) tud, GIF-et nem, a fájlnevet pedig `.png`-re
|
|
képezi. Az `impostor` borítója tehát ma nem jelenne meg. A `/api/image/:id`
|
|
`send_file`-lal az eredetit adja vissza, átkódolás nélkül — a szerver nem segít.
|
|
|
|
A motor sorrendje:
|
|
1. magic bytes alapján PNG? → kiírjuk, kész.
|
|
2. nem PNG, és van külső átkódoló? → `sips` (macOS gyári), `magick`/`convert`
|
|
(ImageMagick), `ffmpeg` — az első elérhető nyer.
|
|
3. semmi? → figyelmeztetés, a bejegyzés borító nélkül is jó.
|
|
|
|
Stdlib-only megkötéssel Pythonból nem kódolunk át (GIF-dekóder kellene) — és nem
|
|
is kellene. **A helyes javítás felfelé van**: a `warp_engine` `imageUrl`-je
|
|
adhatna PNG-változatot (vagy a feltöltés normalizálhatna). Egy sor a
|
|
`MORE_STORES.md`-be: ez a store az első fogyasztó, aminek a kép **formátuma** is
|
|
számít, nem csak a léte.
|
|
|
|
---
|
|
|
|
## 7. Út-átírás: a motor egyetlen valódi új képessége
|
|
|
|
A `.lpl` absztolút utakat tartalmaz **a célgépen**. Amíg a store ott fut, ahol a
|
|
RetroArch, ez nem kérdés. Csak épp a legérdekesebb célpont, az **Android, nem tud
|
|
Pythont futtatni** — ott a store nem tud „ott" futni.
|
|
|
|
A megoldás egy kettéválasztás, ami minden esetet lefed:
|
|
|
|
| | hova írunk | mi kerül a playlistbe |
|
|
|---|---|---|
|
|
| `stage_root` | valódi fájlműveletek helye | — |
|
|
| `target_prefix` | — | ezzel az előtaggal az útvonalak |
|
|
|
|
Alapból a kettő ugyanaz (helyi telepítés). Ha eltér, egy „idegen gépre szánt"
|
|
RetroArch-alakú fát írunk:
|
|
|
|
```sh
|
|
# 1. Android, adb-vel
|
|
retroarch-store export /tmp/ra-ttg \
|
|
--target-prefix /storage/emulated/0/RetroArch \
|
|
--core-suffix _android.so
|
|
adb push /tmp/ra-ttg/. /storage/emulated/0/RetroArch/
|
|
|
|
# 2. SD-kártya: a Mac-en /Volumes/SDCARD, a kézikonzolon /mnt/sdcard
|
|
retroarch-store export /Volumes/SDCARD/RetroArch --target-prefix /mnt/sdcard/RetroArch
|
|
```
|
|
|
|
Ugyanez a mechanizmus adja a Steam Deck flatpak-elrendezését
|
|
(`~/.var/app/org.libretro.RetroArch/config/retroarch`) is, ott viszont
|
|
`target_prefix` nélkül, mert helyben fut.
|
|
|
|
Az Android útvonalait **nem égetjük be**: az `export --android` (2. fázis) az
|
|
`adb shell cat /storage/emulated/0/RetroArch/retroarch.cfg`-ből olvassa ki a
|
|
készülék saját könyvtárait, ugyanazzal a parserrel, amivel helyben. A fenti
|
|
`/storage/emulated/0/RetroArch` csak a tartalék tipp — és ez az egyik pont, amit
|
|
valódi készüléken meg kell mérni (12. pont).
|
|
|
|
---
|
|
|
|
## 8. Tulajdonlás, prune, állapot
|
|
|
|
Ez a store a három közül a legtisztább: minden, amit ír, a nevét viseli.
|
|
|
|
- **Tartalom**: csak `<content_root>/<subfolder>/` alatt törlünk, soha kívül.
|
|
- **Playlist**: a fájl a miénk, de a merge mégis megőrző — újraírás előtt
|
|
kiválogatjuk azokat a tételeket, amiknek a `path`-ja **nem** a mi tartalmi
|
|
könyvtárunkban van, és visszaírjuk őket (ha a felhasználó kézzel vett fel
|
|
valamit a listánkba, nem tűnik el). Ha a végén nulla tétel marad, a fájlt
|
|
töröljük, nem hagyunk üres listát a menüben.
|
|
- **Borító**: `<thumbnails_dir>/<playlist neve>/Named_*/<label>.png`, prune-nál
|
|
mind a három példány megy.
|
|
- **Állapot**: `state.json`, `<platform>:<name>` kulccsal (itt nincs „system",
|
|
a platform az). Külön store, külön fájl — a Batocera v2 állapotával nincs
|
|
migrációs viszony.
|
|
- A verzióváltás (`1.0` → `2.0.0`) a régi fájlt előbb törli, mint ahogy az újat
|
|
hozza, ahogy ma is.
|
|
|
|
A `label` fájlnévvé alakításánál a RetroArch a `&*/:` `` ` `` `<>?\|`
|
|
karaktereket `_`-ra cseréli; a sanitizert ugyanígy írjuk meg. A mai hat címben
|
|
egyik sem szerepel (az `Mr Anderson's Adventure` aposztrófja nincs a listán),
|
|
tehát ez ma elméleti — de egy jövőbeli `Foo: Bar` cím néma borítóhiba lenne.
|
|
|
|
---
|
|
|
|
## 9. Verseny a futó RetroArch-csal
|
|
|
|
Ugyanaz a csapda, mint az EmulationStation gamelistjeivel: a RetroArch a
|
|
playlisteket memóriában tartja, és **visszaírja őket**, ha módosultak (kedvencek,
|
|
utolsó játék, rendezés). Ha futás közben írunk, a kilépése eltörölheti a
|
|
munkánkat.
|
|
|
|
Batocerán ezt a `sync-from-es` + `apply-gamelists --wait-pid` páros oldja meg, de
|
|
ott van egy Ports-menü, ami elindít minket. Itt nincs: **a RetroArch nem tud
|
|
szkriptet futtatni**, tehát a store gazdagépen belüli indítója nem létezik. Ezért
|
|
a döntés egyszerűbb és szigorúbb:
|
|
|
|
- `behavior.refuse_while_running: true` (alapérték) — ha fut RetroArch process,
|
|
a `sync` nem ír, hanem kiírja, hogy zárjuk be. `--force` felülbírálja.
|
|
- A `sync` után az új playlistek **a RetroArch újraindítása után** látszanak; a
|
|
menü a listát indulásnál építi. Ezt a README első bekezdésében kell kimondani,
|
|
különben „nem működik" lesz a tapasztalat.
|
|
- Indítás: shell, illetve ütemezve (`cron`, `systemd --user` timer, launchd,
|
|
Task Scheduler). Ez a store-nak nincs in-app belépője, és ezt nem érdemes
|
|
szépíteni.
|
|
|
|
---
|
|
|
|
## 10. A store configja
|
|
|
|
`ttg-retroarch-store/config.json` teljes egészében:
|
|
|
|
```json
|
|
{
|
|
"store": {
|
|
"id": "ttg",
|
|
"name": "Teletype Games",
|
|
"base_url": "https://teletypegames.org",
|
|
"api": { "catalog": "/api/software", "download": "/api/download" }
|
|
},
|
|
|
|
"paths": {
|
|
"retroarch_dir": null,
|
|
"playlists_dir": null,
|
|
"thumbnails_dir": null,
|
|
"libretro_dir": null,
|
|
"content_root": null,
|
|
"subfolder": "teletypegames",
|
|
"target_prefix": null
|
|
},
|
|
|
|
"playlist": {
|
|
"name_template": "{store} - {label}",
|
|
"write_crc32": true,
|
|
"thumbnail_kinds": ["Named_Boxarts", "Named_Titles", "Named_Snaps"]
|
|
},
|
|
|
|
"catalog": {
|
|
"statuses": ["released", "archived"],
|
|
"owner_id": null,
|
|
"only": [],
|
|
"exclude": []
|
|
},
|
|
|
|
"platforms": {
|
|
"c64": {
|
|
"label": "Commodore 64",
|
|
"kind": "cartridge",
|
|
"ext": ".prg",
|
|
"core": { "file": "vice_x64_libretro", "name": "VICE x64" },
|
|
"enabled": true
|
|
},
|
|
"tic80": {
|
|
"label": "TIC-80",
|
|
"kind": "cartridge",
|
|
"ext": ".tic",
|
|
"core": { "file": "tic80_libretro", "name": "TIC-80" },
|
|
"enabled": true
|
|
}
|
|
},
|
|
|
|
"behavior": {
|
|
"prune": true,
|
|
"timeout": 30,
|
|
"insecure": false,
|
|
"refuse_while_running": true,
|
|
"convert_images": true
|
|
},
|
|
|
|
"branding": { "icons": false }
|
|
}
|
|
```
|
|
|
|
A `null` útvonalak mindenütt azt jelentik: *derítsd ki* — env (`RETROARCH_DIR`),
|
|
majd `retroarch.cfg`, majd OS-enkénti tipplista. A `config` alparancs kiírja,
|
|
mire jutott, ahogy a Batocera-store a `detected_arch`-ot:
|
|
|
|
```
|
|
detected: os=darwin arch=aarch64 core_suffix=.dylib
|
|
retroarch_dir ~/Library/Application Support/RetroArch (candidate)
|
|
playlists_dir ~/Documents/RetroArch/playlists (retroarch.cfg)
|
|
libretro_dir ~/Library/Application Support/RetroArch/cores (retroarch.cfg)
|
|
cores vice_x64_libretro ✗ tic80_libretro ✗ → DETECT
|
|
```
|
|
|
|
---
|
|
|
|
## 11. A közös rész: mit mikor emeljünk ki
|
|
|
|
A `MORE_STORES.md` helyesen mondja, hogy a katalógus-lekérés, kiadásválasztás,
|
|
`state.json`, borító és prune-védelem közös. A most kézenfekvő lépés mégis **nem**
|
|
az azonnali kiemelés.
|
|
|
|
**1. fázis — a RetroArch-motor önálló, egyfájlos, és igen, duplikál.**
|
|
A `retroarch_store.py` átveszi a HTTP/katalógus/állapot réteget (~250 sor). Ok:
|
|
egyetlen minta alapján kiemelt absztrakció rossz absztrakció, és a Batocera-store
|
|
**élesben fut** — nem az a pillanat, hogy alatta cseréljünk réteget, amikor még
|
|
azt sem tudjuk, mit tanul a második adapter. A telepítés is egyszerűbb marad:
|
|
egy `curl` egy fájlra.
|
|
|
|
**2. fázis — kiemelés, amikor két adapter már megmondja, mi közös.**
|
|
Amit ma is látok jelöltnek: `http_get`/`http_download`, `fetch_catalog` +
|
|
gyorsítótár, `pick_release`, a config-merge, a `state.json` séma és az
|
|
atomikus írás. Ezek egy `warpstore_core.py`-ba mennek, mindkét installer két
|
|
fájlt tölt le. Amit a RetroArch már most tanított, és a közös részbe tartozik:
|
|
|
|
- a `resolve_for_arch` **általánosítása**: a Batocerán csak az architektúra volt
|
|
a változó, itt az operációs rendszer is (mag-suffix). Egy `resolve_for_host`,
|
|
ami `x86_64` / `aarch64` / `linux` / `darwin` / `windows` / `android` / `*`
|
|
kulcsokat is fogad, mindkettőt kiszolgálja;
|
|
- a **`stage_root` / `target_prefix` kettősség** (7. pont). Ez nem RetroArch-specifikus:
|
|
az asztali store (`MORE_STORES.md` 2.) és a kézikonzolos ES-disztrók
|
|
ugyanígy „innen írok, ott fog futni" helyzetben vannak.
|
|
|
|
---
|
|
|
|
## 12. Munka és ellenőrzés
|
|
|
|
Becslés, a Batocera-motor mérete alapján (`store.py` 1073 sor):
|
|
|
|
| Rész | Új sor (kb.) |
|
|
|---|---|
|
|
| átvett réteg (HTTP, katalógus, állapot, config) | 250 |
|
|
| `retroarch.cfg` parser + útvonal-felderítés | 90 |
|
|
| playlist írás/merge (`.lpl`) | 120 |
|
|
| mag-feloldás | 60 |
|
|
| borító 3 könyvtárba + átkódolás | 80 |
|
|
| export / út-átírás | 70 |
|
|
| CLI, logolás | 90 |
|
|
| **összesen** | **~760** |
|
|
|
|
Plusz `install.sh` (a Batocera-inak asztali változata: `~/.local/share`-be, nem
|
|
`/userdata`-ba) és két README.
|
|
|
|
**Amit valódi gépen meg kell mérni** — ez a lista a terv gyenge pontjai, nem
|
|
formalitás:
|
|
|
|
1. **A két mag valóban elindítja-e a fájljainkat.** Ez a store létjogosultsága.
|
|
Nem drága ellenőrizni:
|
|
```sh
|
|
# magok a scratchpadbe, nem a felhasználó RetroArch-jába
|
|
curl -fsSLO https://buildbot.libretro.com/nightly/apple/osx/arm64/latest/vice_x64_libretro.dylib.zip
|
|
curl -fsSLO https://buildbot.libretro.com/nightly/apple/osx/arm64/latest/tic80_libretro.dylib.zip
|
|
unzip -o '*.zip'
|
|
retroarch -L ./vice_x64_libretro.dylib ./blessingofra-2.0.0.prg
|
|
retroarch -L ./tic80_libretro.dylib ./impostor-1.0.tic
|
|
```
|
|
Külön kérdés a `.prg` autostartja: a VICE-mag betölti, de futtatja-e magától,
|
|
vagy `RUN`-ra vár. Ha vár, kell egy magopció (`vice_autostart`) vagy `.d64`-be
|
|
csomagolás — ez a terv legvalószínűbb sérülési pontja.
|
|
2. **A generált playlist megjelenik-e**, a saját nevével, és a borító előjön-e a
|
|
`Named_*` könyvtárakból, a felhasználó globális thumbnail-beállításával.
|
|
3. **Az Android útvonalai** (`playlist_directory`, `libretro_directory` a
|
|
készülék cfg-jében) és hogy az `adb push`-olt tartalom olvasható-e a
|
|
RetroArch-nak scoped storage mellett.
|
|
4. **A visszaírás-verseny** valódi mérete: mennyit ír vissza a RetroArch a
|
|
playlistünkből kilépéskor, elég-e a `refuse_while_running`.
|
|
5. **A `label` → borító-fájlnév sanitizálás** pontos karakterkészlete (a fenti
|
|
lista következtetés, nem mérés) — egy `Foo: Bar` címmel kipróbálva kiderül.
|
|
|
|
## Javasolt sorrend
|
|
|
|
1. Az 1. ellenőrzési pont (magok + a két fájltípus indítása) — **mielőtt** kód
|
|
íródna. Egy órányi munka, és megválaszolja, hogy a store egyáltalán működhet-e.
|
|
2. `warp-engine-retroarch-store`: cfg-parser + `list` + `sync` a helyi telepítéssel,
|
|
macOS-en és Linuxon.
|
|
3. `ttg-retroarch-store` config, a fenti tartalommal.
|
|
4. Export/út-átírás + Android próba valódi készüléken.
|
|
5. Kiemelés `warpstore_core.py`-ba (11. pont, 2. fázis), és az `imageUrl`
|
|
PNG-normalizálása a `warp_engine`-ben (6. pont).
|
|
|
|
---
|
|
|
|
## Amit a megvalósítás megváltoztatott
|
|
|
|
*(2026-08-17, a fenti terv leírása után.)*
|
|
|
|
**A közös rész azonnal kiemelve.** A 11. pont fázisolását felülírtuk: a
|
|
`warpstore.py` a saját repójában (`engines/warpstore`) született meg, és a
|
|
Batocera-motor **ugyanabban a körben** átült rá — 1073 sorból 426 eltűnt, 115
|
|
jött vissza. A `tools/` alá nem került, mert az org-definíció szerint az
|
|
`engines/` a „saját motor és könyvtár, amitől más repók függnek", és ez pontosan
|
|
az. Mindkét telepítő két fájlt tölt le, a `warpstore.py`-t a saját repójából;
|
|
egyik sem pin-el verziót, tehát egy magbeli javítás a következő telepítéssel
|
|
mindkét store-hoz megérkezik.
|
|
|
|
Az élesben futó Batocera-store miatt az átültetés visszamérve: a cartridge-út, a
|
|
Ports-út, a borító, a gamelist-merge, az idempotens újraszinkron, a prune és az
|
|
eltávolítás ugyanazt adja, mint előtte. A régi `state.json` (rekordokban `system`,
|
|
`scope` nélkül) **ugyanúgy kulcsolódik**, tehát a frissítés után az első szinkron
|
|
nem tölt le és nem prune-ol semmit — ezt szándékosan a `record_scope()`
|
|
tartaléka adja, nem migráció.
|
|
|
|
**Két általánosítás, amit a második adapter kényszerített ki:**
|
|
|
|
- `resolve_for_arch` → `resolve_for_host`. A Batocerán csak az architektúra volt
|
|
a változó; a RetroArch-nál az OS is (`.dll` / `.dylib` / `_android.so`). A
|
|
kulcssorrend `os-arch`, `arch`, `os`, `*`.
|
|
- Az állapot `scope` szerint kulcsolódik, nem ES-`system` szerint. A RetroArch-nál
|
|
a scope a platform, nem a playlist neve — így a store átnevezése nem kulcsolja
|
|
újra a telepítést.
|
|
|
|
**Amit a terven felül kapott a motor:** `paths` alparancs. A négy útvonal
|
|
felderítése annyira nem magától értetődő (lásd az (a) pontot), hogy önmagában
|
|
megérdemel egy kimenetet, ami azt is megmutatja, **melyik válasz honnan jött** —
|
|
config, `retroarch.cfg`, vagy tartalék. Ezt írja ki a telepítő is.
|
|
|
|
**Ami az 5. pontból pontosításra került:** a hiányzó mag nem csak „figyelmeztetés",
|
|
hanem konkrét lépés is a kimenetben (*Online Updater ▸ Core Downloader ▸ VICE x64*).
|
|
|
|
**Amit végigmértünk:** letöltés, playlist, borító (a GIF `sips`-szel PNG-vé
|
|
konvertálva), idegen playlist-bejegyzés megőrzése, mag-felismerés, prune,
|
|
eltávolítás, üres playlist törlése, Android-export `_android.so`-val és POSIX
|
|
célútvonalakkal, végül teljes telepítés a forge-ról `curl | bash`-sal. A CRC32-k
|
|
egyeznek a doksi 1. pontjában független módszerrel mért értékekkel.
|
|
|
|
**Ami továbbra is nyitott, változatlanul:** a VICE `.prg`-autostart és az Android
|
|
`adb push` scoped storage kérdése. Ezt kód nem tudja megválaszolni.
|