diff --git a/README.md b/README.md index d8368b16..e69a4ce0 100644 --- a/README.md +++ b/README.md @@ -47,17 +47,17 @@ The Files column counts what a pack holds once extracted, the figure a file mana | Platform | Files | Extracted size | Extract to | Download | |----------|------:|---------------:|-----------|----------| -| AmberELEC | - | - | `/storage/roms/` | [Download](../../releases/latest) | +| AmberELEC | - | - | `/storage/roms/` | Not in v2026.10.10: [use the installer](https://abdess.github.io/retrobios/wiki/installer/) | | Batocera | 12,810 | 7.5 GB | `/userdata/bios/` | Download: [part 1 of 3](../../releases/download/v2026.10.10/Batocera_43.1_BIOS_Pack.part1of3.zip), [part 2 of 3](../../releases/download/v2026.10.10/Batocera_43.1_BIOS_Pack.part2of3.zip), [part 3 of 3](../../releases/download/v2026.10.10/Batocera_43.1_BIOS_Pack.part3of3.zip) | | BizHawk | 950 | 2.4 GB | `Firmware/` | [Download](../../releases/download/v2026.10.10/BizHawk_2.11.1_BIOS_Pack.zip) | | EmuDeck | 620 | 3.2 GB | `~/Emulation/bios/` | [Download](../../releases/download/v2026.10.10/EmuDeck_2.7EA_BIOS_Pack.zip) | -| EmuELEC | - | - | `/storage/roms/` | [Download](../../releases/latest) | -| KNULLI | - | - | `/userdata/bios/` | [Download](../../releases/latest) | +| EmuELEC | - | - | `/storage/roms/` | Not in v2026.10.10: [use the installer](https://abdess.github.io/retrobios/wiki/installer/) | +| KNULLI | - | - | `/userdata/bios/` | Not in v2026.10.10: [use the installer](https://abdess.github.io/retrobios/wiki/installer/) | | Lakka | 8,504 | 6.0 GB | `/storage/system/` | Download: [part 1 of 2](../../releases/download/v2026.10.10/RetroArch_Lakka_v1.22.2_BIOS_Pack.part1of2.zip), [part 2 of 2](../../releases/download/v2026.10.10/RetroArch_Lakka_v1.22.2_BIOS_Pack.part2of2.zip) | | MiSTer FPGA | 83 | 29 MB | `/media/fat/games/` | [Download](../../releases/download/v2026.10.10/MiSTer_FPGA_2026-09-29_BIOS_Pack.zip) | -| MinUI | - | - | `/` | [Download](../../releases/latest) | -| MyMinUI | - | - | `/` | [Download](../../releases/latest) | -| NextUI | - | - | `/` | [Download](../../releases/latest) | +| MinUI | - | - | `/` | Not in v2026.10.10: [use the installer](https://abdess.github.io/retrobios/wiki/installer/) | +| MyMinUI | - | - | `/` | Not in v2026.10.10: [use the installer](https://abdess.github.io/retrobios/wiki/installer/) | +| NextUI | - | - | `/` | Not in v2026.10.10: [use the installer](https://abdess.github.io/retrobios/wiki/installer/) | | ROCKNIX | 5,804 | 6.1 GB | `/storage/roms/bios/` | Download: [part 1 of 2](../../releases/download/v2026.10.10/ROCKNIX_20261001_BIOS_Pack.part1of2.zip), [part 2 of 2](../../releases/download/v2026.10.10/ROCKNIX_20261001_BIOS_Pack.part2of2.zip) | | Recalbox | 3,222 | 3.9 GB | `/recalbox/share/bios/` | Download: [part 1 of 2](../../releases/download/v2026.10.10/Recalbox_10.1.1_BIOS_Pack.part1of2.zip), [part 2 of 2](../../releases/download/v2026.10.10/Recalbox_10.1.1_BIOS_Pack.part2of2.zip) | | RetroArch | 8,504 | 6.0 GB | `system/` | Download: [part 1 of 2](../../releases/download/v2026.10.10/RetroArch_Lakka_v1.22.2_BIOS_Pack.part1of2.zip), [part 2 of 2](../../releases/download/v2026.10.10/RetroArch_Lakka_v1.22.2_BIOS_Pack.part2of2.zip) | @@ -208,4 +208,4 @@ The scripts and tooling are released under the [MIT License](LICENSE). The BIOS and firmware files are not covered by that license: they are third-party system software, preserved and provided for personal backup, archival, and interoperability with emulation software. [NOTICE](NOTICE) sets out their status and how to ask for a file to be removed. The reasoning, and where it is weakest, is in the [FAQ](https://abdess.github.io/retrobios/wiki/faq/#is-this-legal). -*Auto-generated on 2026-10-22T08:44:18Z* +*Auto-generated on 2026-10-22T08:57:23Z* diff --git a/scripts/generate_readme.py b/scripts/generate_readme.py index b0cd1e7a..57a5cb4b 100644 --- a/scripts/generate_readme.py +++ b/scripts/generate_readme.py @@ -134,14 +134,18 @@ def download_links( A pack published whole gets one link labelled *label*. A pack in parts gets one link per part, since every part is needed. *repo* is the repository URL, absolute or relative, and *attrs* a MkDocs attribute list - put after each link. Without a pack in the record the link falls back to - the latest release page. + put after each link. Without a recorded release the link is the latest + release page. A release recorded without this platform's pack says so and + names the installer, which reads the file lists on main: a button to the + release served six platforms a page with no pack for them. """ pack = release_record.pack_for(platform_name, record) tag = record.get("tag") assets = pack.get("assets") if pack else None - if not tag or not assets: + if not tag: return f"[{label}]({repo}/releases/latest){attrs}" + if not assets: + return f"Not in {tag}: [use the installer]({SITE_URL}wiki/installer/)" urls = [f"{repo}/releases/download/{tag}/{asset}" for asset in assets] if len(urls) == 1: return f"[{label}]({urls[0]}){attrs}" diff --git a/scripts/generate_site.py b/scripts/generate_site.py index f051a779..55573f1e 100644 --- a/scripts/generate_site.py +++ b/scripts/generate_site.py @@ -15,6 +15,7 @@ from siterender import WIKI_SRC_DIR import argparse import csv import hashlib +import functools import html import io import json @@ -33,6 +34,7 @@ from pathlib import Path sys.path.insert(0, os.path.dirname(__file__)) from common import ( + upstream_profile_index, build_zip_contents_index, compute_composition, count_catalog_matched, @@ -2088,6 +2090,33 @@ def _category_evidence_md(evidence: dict) -> str: return f"No reader found ({cores})" +@functools.lru_cache(maxsize=1) +def _profiles_by_core_name() -> dict[str, list[str]]: + """Core name as a platform writes it -> the profiles with a page for it. + + MinUI writes `mednafen_vb`, the profile and its page are `beetle_vb`. + """ + profiles = load_emulator_profiles(str(_REPO_ROOT / "emulators")) + with_page = {k for k, v in profiles.items() if v.get("type") not in ("alias", "test")} + return { + core: sorted(k for k in keys if k in with_page) + for core, keys in upstream_profile_index(profiles).items() + } + + +def _core_name_links(cores: list[str]) -> list[str]: + """A link per core to its profile page, the bare name where none exists.""" + index = _profiles_by_core_name() + links = [] + for core in cores: + keys = [core] if core in index.get(core, []) else index.get(core, []) + if keys: + links.extend(_emulator_link(k, "../") for k in keys) + else: + links.append(f"`{core}`") + return links + + def _core_folders_section(name: str, config: dict) -> list[str]: """Each folder the frontend hands a core, and the files the pack puts in it. @@ -2111,7 +2140,7 @@ def _core_folders_section(name: str, config: dict) -> list[str]: f["dest"][len(folder) + 1:] for f in files if f.get("dest", "").startswith(folder + "/") ) - cores = ", ".join(_emulator_link(c, "../") for c in info.get("cores", [])) + cores = ", ".join(_core_name_links(info.get("cores", []))) shown = ", ".join(f"`{n}`" for n in held) if held else "none needed" lines.append(f"| `{folder}/` | {cores} | {shown} |") lines.append("") diff --git a/tests/test_doc_surfaces.py b/tests/test_doc_surfaces.py index 18703ec3..770c4690 100644 --- a/tests/test_doc_surfaces.py +++ b/tests/test_doc_surfaces.py @@ -112,9 +112,16 @@ class DownloadLinksNameTheFile(unittest.TestCase): cell = download_links("batocera", SPLIT_RECORD, REPO, "Pack", " { .x }") self.assertEqual(cell.count("{ .x }"), 2) - def test_without_a_pack_the_link_is_the_release_page(self): + def test_a_release_without_the_pack_names_the_installer(self): self.assertEqual( download_links("bizhawk", SPLIT_RECORD, "../..", "Download"), + "Not in v2000.01.01: " + "[use the installer](https://abdess.github.io/retrobios/wiki/installer/)", + ) + + def test_without_a_recorded_release_the_link_is_the_release_page(self): + self.assertEqual( + download_links("bizhawk", {}, "../..", "Download"), "[Download](../../releases/latest)", )