docs: list knulli, amberelec and emuelec

The download page drops a row whose platform has no pack in the
recorded release instead of linking a release that lacks it.
This commit is contained in:
Abdessamad Derraz committed 2026-10-22 03:42:25 +02:00
1 parent d488b14a7f
commit 44a9c8d9bb
11 files changed
+103 -14

No files matched your search

+18 -2
View File
@@ -5025,6 +5025,9 @@ def generate_wiki_data_model(db: dict, profiles: dict) -> str:
"`base_destination` is the prefix the pack applies to every entry. It is",
"empty when the upstream destinations already carry their own root, which",
"is why the RetroDECK pack ships `bios/` and `roms/` at its top level.",
"Core files then go under `core_destination` when the platform states one",
"(`bios` for AmberELEC and EmuELEC, whose lists also name `neogeo/` and",
"`ports/`), else under the root nearly every destination shares.",
"",
"## Emulator YAML",
"",
@@ -5248,6 +5251,9 @@ ZIP: extract them all into the same folder.
| Android (Retroid Pocket, Odin, etc.) | RetroArch for Android | [RetroArch]({rel}) | `RetroArch/system/` on internal storage or SD card |
| [ROCKNIX](https://rocknix.org/) | Linux OS for ARM and x86 handhelds, among them the RG35XX (H700) and the RG552 (RK3399) | [ROCKNIX]({rel}) | `/storage/roms/bios/`, over SSH or the network share |
| [Batocera](https://batocera.org/) | Also has images for handheld chips, among them H700 and RK3326 | [Batocera]({rel}) | `/userdata/bios/` |
| [KNULLI](https://knulli.org/) | Batocera-based OS for handhelds, among them the RG35XX family (H700) and the TrimUI Smart Pro | [KNULLI]({rel}) | `/userdata/bios/` |
| [AmberELEC](https://amberelec.org/) | Linux OS for Anbernic handhelds: RG351P, RG351V, RG351MP, RG353, RG503 and RG552 | [AmberELEC]({rel}) | `/storage/roms/`, which the pack fills with `bios/` and `neogeo/` |
| [EmuELEC](https://emuelec.org/) | Linux OS for Amlogic TV boxes, the Odroid Go Advance, GameForce and RK356x devices | [EmuELEC]({rel}) | `/storage/roms/`, which the pack fills with `bios/`, `neogeo/` and `ports/` |
### FPGA
@@ -5315,6 +5321,16 @@ def _link_pack_cells(page: str) -> str:
raise KeyError(f"no registered platform is displayed as {label!r}")
return f"| {download_links(keys[label], record, REPO_URL, label)} |"
def released(line: str) -> bool:
# A platform registered after the recorded release has no pack in
# it yet: its row would send the reader to a release without one.
found = cell.search(line)
if not found or not record.get("packs"):
return True
label = found.group(1)
return label not in keys or release_record.pack_for(keys[label], record) is not None
page = "\n".join(line for line in page.split("\n") if released(line))
return cell.sub(link, page)
@@ -5702,8 +5718,8 @@ def _generate(args: argparse.Namespace) -> None:
mkdocs_static = """\
site_name: RetroBIOS
site_description: Source-verified BIOS and firmware packs for RetroArch, Batocera,
Recalbox, Lakka, RetroPie, EmuDeck, RetroBat, RetroDECK, RomM, BizHawk, ROCKNIX
and MiSTer FPGA.
Recalbox, Lakka, RetroPie, EmuDeck, RetroBat, RetroDECK, RomM, BizHawk, ROCKNIX,
KNULLI, AmberELEC, EmuELEC and MiSTer FPGA.
site_url: https://abdess.github.io/retrobios/
repo_url: https://github.com/Abdess/retrobios
repo_name: Abdess/retrobios
+16
View File
@@ -152,6 +152,22 @@ class DownloadLinksNameTheFile(unittest.TestCase):
for asset in pack["assets"]:
self.assertIn(f"/releases/download/{RECORD['tag']}/{asset})", page)
def test_a_platform_the_release_lacks_has_no_row(self):
"""KNULLI, AmberELEC and EmuELEC were registered after the recorded
release: a row for them linked a release page holding no such pack."""
if not RECORD.get("packs"):
self.skipTest("release.json is not written")
from common import list_registered_platforms, load_platform_config
from release_record import pack_for
page = _which_pack()
for key in list_registered_platforms("platforms", include_archived=True):
if pack_for(key, RECORD) is not None:
continue
label = load_platform_config(key, "platforms").get("platform", key)
with self.subTest(platform=key):
self.assertNotIn(f"| [{label}](", page)
class SlicesLeaveWithTheirRelease(unittest.TestCase):
"""Parts named `.zip.001` were cut by v2026.09.04, deleted since. A page
+5 -2
View File
@@ -250,7 +250,10 @@ systems:
`base_destination` is what the pack prefixes every entry with. Leave it empty
when the upstream destinations already carry their own root, as RetroDECK does
with `bios/` and `roms/`.
with `bios/` and `roms/`. Core files then go under the root most destinations
share, or under `core_destination` when the list names other folders too:
AmberELEC and EmuELEC declare `neogeo/` and `ports/` beside `bios/`, and set
`core_destination: bios`.
### Launchers
@@ -411,7 +414,7 @@ to use:
| Mode | Behavior | Example platforms |
|------|----------|-------------------|
| `existence` | File must exist, no hash check | RetroArch, Lakka, RetroPie |
| `md5` | MD5 must match the declared hash | Batocera, Recalbox, RetroBat, EmuDeck, RetroDECK, RomM, ROCKNIX, MiSTer FPGA |
| `md5` | MD5 must match the declared hash | Batocera, Recalbox, RetroBat, EmuDeck, RetroDECK, RomM, ROCKNIX, KNULLI, AmberELEC, EmuELEC, MiSTer FPGA |
| `sha1` | SHA1 must match | BizHawk |
Inheriting platforms take the parent's mode: Lakka and RetroPie do not declare
+2 -1
View File
@@ -212,7 +212,8 @@ checks its DSP ROMs (`dsp_rom.bin` and `dsp_coef.bin`, hashed byte-swapped).
`IPL.bin` is not one of them: Dolphin loads it without a hash check.
Verification uses whichever one the platform itself uses: MD5 for Batocera,
RetroBat, Recalbox, EmuDeck, RetroDECK, RomM, ROCKNIX and MiSTer FPGA, SHA1 for
RetroBat, Recalbox, EmuDeck, RetroDECK, RomM, ROCKNIX, KNULLI, AmberELEC, EmuELEC
and MiSTer FPGA, SHA1 for
BizHawk, and nothing at all for RetroArch, Lakka and RetroPie, which only check
that the file exists.
+1 -1
View File
@@ -26,7 +26,7 @@ touched.
## Step 1. Run the command
On Linux, macOS, a Steam Deck, or a handheld running Batocera, Recalbox,
KNULLI, ROCKNIX or Lakka:
KNULLI, ROCKNIX, AmberELEC, EmuELEC or Lakka:
```bash
curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh | sh
+18 -1
View File
@@ -198,6 +198,22 @@ others) whose files only the RetroPie pack carries, laid out for
Accessible via SSH or Samba, like Lakka.
### KNULLI
```
/userdata/bios/
```
### AmberELEC and EmuELEC
```
/storage/roms/
```
Their BIOS lists name files under `/storage/roms`: `bios/` for most, and
`neogeo/` (and `ports/` on EmuELEC) for the rest. The pack holds those
folders, so it is extracted into `/storage/roms/` itself.
### MiSTer FPGA
```
@@ -247,7 +263,8 @@ dump shows up.
The installer's `--check` hashes every file against the list on every
platform, so it catches a wrong revision even where the frontend would not:
RetroArch, Lakka and RetroPie only check that a file exists, while Batocera,
RetroBat, Recalbox, EmuDeck, RetroDECK, RomM, ROCKNIX and MiSTer FPGA compare
RetroBat, Recalbox, EmuDeck, RetroDECK, RomM, ROCKNIX, KNULLI, AmberELEC,
EmuELEC and MiSTer FPGA compare
MD5 and BizHawk compares SHA1.
For a single system, or the files a single emulator core reads (`--list-systems`
+8 -5
View File
@@ -290,7 +290,9 @@ wins, since a machine is only one of them.
| Evidence | Platform | BIOS directory |
|----------|----------|----------------|
| `/etc/os-release` `OS_NAME=ROCKNIX` (or `ID=rocknix` on images before 2025) | ROCKNIX | `/storage/roms/bios` |
| `/etc/os-release` `OS_NAME=knulli` or `/usr/share/knulli/knulli.version` | Batocera (Knulli) | `/userdata/bios` |
| `/etc/os-release` `OS_NAME=knulli` or `/usr/share/knulli/knulli.version` | KNULLI | `/userdata/bios` |
| `/etc/os-release` `NAME=AmberELEC` | AmberELEC | `/storage/roms` (its list names `bios/` and `neogeo/`) |
| `/etc/os-release` `ID=emuelec` | EmuELEC | `/storage/roms` (its list names `bios/`, `neogeo/` and `ports/`) |
| `/etc/os-release` `ID=lakka` | Lakka | `/storage/system` |
| `/usr/share/batocera/batocera.version` | Batocera | `/userdata/bios` |
| `/recalbox/recalbox.version` or `/usr/bin/recalbox-settings` | Recalbox | `/recalbox/share/bios` |
@@ -302,9 +304,9 @@ wins, since a machine is only one of them.
Batocera, Recalbox, ROCKNIX and Lakka also name the board they were built for, and
that board narrows the install unless `--target` or `--every-board` says
otherwise (see the options above). A board with no core list of its own is
named and gets the files of every board. Knulli names its board in
`/usr/share/knulli/knulli.arch` but builds its own set of cores, so Batocera's
per-board lists are not applied to it and the run says so.
named and gets the files of every board. KNULLI names its board in
`/usr/share/knulli/knulli.arch` but publishes no core list per board, so the
run says so and installs the files of every board.
Some platforms keep their BIOS in a fixed directory, and that directory is the
evidence. They are looked for after the configurations below, for a platform
@@ -388,7 +390,8 @@ When a platform named with `--platform` is not found on the machine, the
destination falls back to the directory its detection looks for:
`/userdata/bios` for Batocera, `/recalbox/share/bios` for Recalbox,
`/storage/system` for Lakka, `~/retrodeck` for RetroDECK, `~/Emulation/bios`
for EmuDeck (`C:\Emulation\bios` on Windows), `/storage/roms/bios` for ROCKNIX, `/media/fat/games` for MiSTer,
for EmuDeck (`C:\Emulation\bios` on Windows), `/storage/roms/bios` for ROCKNIX,
`/userdata/bios` for KNULLI, `/storage/roms` for AmberELEC and EmuELEC, `/media/fat/games` for MiSTer,
`~/RetroPie/BIOS` for RetroPie, `/romm/library/bios` for RomM,
`C:\RetroBat\bios` for RetroBat, and RetroArch's own default system
directory for RetroArch. BizHawk has no such folder: it reads `Firmware`
+1 -1
View File
@@ -45,7 +45,7 @@ Every module in `tests/`, by the area it guards.
| Hardware targets and systems | synthetic + target files | `test_extract_targets`, `test_lakka_targets`, `test_libretro_slugs`, `test_system_ids`, `test_system_selection`, `test_target_names`, `test_target_scrapers`, `test_target_systems`, `test_target_verify` |
| Installer and downloads | loopback server | `test_check_release`, `test_download`, `test_install`, `test_install_cores`, `test_install_experience`, `test_install_selection` |
| Truth and native export | synthetic + cached originals | `test_export_counts`, `test_export_native_layer`, `test_export_report`, `test_exporters`, `test_truth_data_dir_readers`, `test_truth_diff`, `test_truth_layout` |
| Scrapers, upstream and provenance | inline sources, synthetic | `test_check_freshness`, `test_dedup`, `test_fbneo_parser`, `test_find_pinned_revision`, `test_hash_merge`, `test_launchers`, `test_launchers_emudeck`, `test_launchers_recalbox`, `test_launchers_retrobat`, `test_launchers_retrodeck`, `test_launchers_retropie`, `test_launchers_rocknix`, `test_mame_devices`, `test_mame_parser`, `test_mame_ref_audit`, `test_profile_sync`, `test_provenance`, `test_refresh_data_dirs`, `test_refresh_failures`, `test_refresh_stale`, `test_retrodeck_scraper_errors`, `test_romset_recipes`, `test_scraper_contract`, `test_upstream`, `test_upstream_index` |
| Scrapers, upstream and provenance | inline sources, synthetic | `test_check_freshness`, `test_dedup`, `test_elec_scrapers`, `test_fbneo_parser`, `test_find_pinned_revision`, `test_hash_merge`, `test_launchers`, `test_launchers_emudeck`, `test_launchers_recalbox`, `test_launchers_retrobat`, `test_launchers_retrodeck`, `test_launchers_retropie`, `test_launchers_rocknix`, `test_mame_devices`, `test_mame_parser`, `test_mame_ref_audit`, `test_profile_sync`, `test_provenance`, `test_refresh_data_dirs`, `test_refresh_failures`, `test_refresh_stale`, `test_retrodeck_scraper_errors`, `test_romset_recipes`, `test_scraper_contract`, `test_upstream`, `test_upstream_index` |
| Site, docs and repository contracts | rendered pages, sources | `test_contributing_text`, `test_data_dir_refs`, `test_dataclass_defaults`, `test_doc_pages`, `test_doc_surfaces`, `test_optional_dependencies`, `test_prose_style`, `test_site_availability`, `test_site_cores`, `test_site_emulator_page`, `test_site_exports`, `test_site_platform_hashes`, `test_site_render`, `test_site_system_links`, `test_site_validation`, `test_site_write`, `test_validate_pr`, `test_validate_pr_flow`, `test_validate_pr_hashes`, `test_workflow_paths` |
## Test architecture
+9
View File
@@ -143,6 +143,9 @@ Verification modes per platform:
| RetroDECK | md5 | MD5 per file via component manifests |
| RomM | md5 | size + any hash (MD5/SHA1/CRC32), no ZIP inspection |
| ROCKNIX | md5 | MD5 hash match, same shape as Batocera |
| KNULLI | md5 | Batocera's `checkBios` in `knulli-systems`, `checkInsideZip` and emulator/core gates included |
| AmberELEC | md5 | MD5 of each file under `/storage/roms`, `checkInsideZip` for `zippedFile` entries |
| EmuELEC | md5 | MD5 of each file under `/storage/roms`; `zippedFile` is never read |
| MiSTer FPGA | md5 | MD5 of the file at its destination, no ZIP inspection |
| BizHawk | sha1 | SHA1 per firmware from `FirmwareDatabase.cs` |
@@ -846,6 +849,9 @@ stand alone.
| `retrodeck_scraper` | component manifests | JSON per component |
| `romm_scraper` | known_bios_files.json | JSON |
| `rocknix_scraper` | rocknix-systems script | Python dict |
| `knulli_scraper` | knulli-systems script, es_systems.yml and configgen-defaults.yml (cores) | Python dict, YAML |
| `amberelec_scraper` | batocera-systems script at the newest prerelease's commit, es_systems.cfg (cores) | Python dict, XML |
| `emuelec_scraper` | batocera-systems script, es_systems.json (cores) | Python dict, JSON |
| `retropie_scraper` | RetroPie-Setup scriptmodules (`rp_module_id`, `rp_module_help`) and `retropie_packages.sh` (version) | Bash |
| `misterfpga_scraper` | BiosDB_MiSTer bios_db.json.zip | JSON in ZIP |
| `coreinfo_scraper` | .info files from libretro-core-info | INI-like |
@@ -991,6 +997,9 @@ Located in `scripts/exporter/`. Each inherits `BaseExporter`, defined in
| `lakka_exporter` | the same `System.dat`, which Lakka reads unchanged |
| `batocera_exporter` | the systems block of the `batocera-systems` script, patched entry by entry |
| `rocknix_exporter` | the systems mapping of the `rocknix-systems` script, patched the same way |
| `knulli_exporter` | the systems mapping of the `knulli-systems` script, patched the same way |
| `amberelec_exporter` | AmberELEC's `batocera-systems`, paths from `/storage/roms`, additions without a runner gate |
| `emuelec_exporter` | EmuELEC's `batocera-systems`, as AmberELEC's |
| `retrobat_exporter` | `batocera-systems.json` |
| `recalbox_exporter` | `es_bios.xml`, valid against `es_bios.xsd` |
| `emudeck_exporter` | the MD5 lists of the `checkBIOS.sh` functions, patched in place |
+2
View File
@@ -62,6 +62,8 @@ Each platform expects BIOS files in a specific base directory:
- Recalbox: `/recalbox/share/bios/`
- RetroPie: `~/RetroPie/BIOS/`
- ROCKNIX: `/storage/roms/bios/`
- KNULLI: `/userdata/bios/`
- AmberELEC and EmuELEC: `/storage/roms/` (the pack holds `bios/` and the other folders their lists name)
- MiSTer FPGA: `/media/fat/games/`
- BizHawk: `Firmware/` inside the BizHawk directory
- EmuDeck: `~/Emulation/bios/`
+23 -1
View File
@@ -28,7 +28,7 @@ Lakka and RetroPie inherit this behavior through platform config inheritance
## MD5 mode
**Platforms**: Batocera, RetroBat, Recalbox, EmuDeck, RetroDECK, RomM, ROCKNIX, MiSTer FPGA
**Platforms**: Batocera, RetroBat, Recalbox, EmuDeck, RetroDECK, RomM, ROCKNIX, KNULLI, AmberELEC, EmuELEC, MiSTer FPGA
All MD5-mode platforms compute a hash of the file and compare it against an expected value.
The details vary by platform.
@@ -137,6 +137,28 @@ shape: `md5sum()` on the file, `checkInsideZip()` for archive entries, and an
platform declares is mandatory in its own data, so the platform YAML marks all
38 files `required: true` and none of them use `zippedFile`.
### KNULLI, AmberELEC and EmuELEC verification
**Source**: `knulli-systems` (KNULLI) and `batocera-systems` (AmberELEC,
EmuELEC), function `checkBios`
All three keep Batocera's `systems` dict and its checker, run by their
EmulationStation's BIOS screen.
- **KNULLI** runs Batocera's `checkBios` on files under `/userdata`: `md5sum()`,
`checkInsideZip()` for `zippedFile` entries, and the `emulator`/`core` gates
read from `es_systems.cfg`.
- **AmberELEC** runs the same checker on files under `/storage/roms`. Its list
sets no gate, and names `neogeo/neogeo.zip` and `neogeo/aes.zip` beside
`bios/`, so the pack extracts into `/storage/roms`.
- **EmuELEC** hashes the whole file under `/storage/roms` and never reads
`zippedFile`: an entry that names a member inside an archive compares the
archive's MD5 with the member's and reports `UNTESTED` whatever the archive
holds. Its list also names game data under `ports/`.
A key written twice in the dict (`naomi` and `Dinothawr` in EmuELEC, `msx1`
and `msx2+` in KNULLI) keeps its later value, as when Python runs the script.
### MiSTer FPGA verification
**Source**: `Downloader_MiSTer`, `jobs/process_db_index_worker.py`