From 4f42c52a7f2f00077019184fa1f9f0ca9eb3bcd6 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Sun, 18 Oct 2026 16:11:13 +0200 Subject: [PATCH] fix: count systems once per normalized id --- README.md | 6 +++--- scripts/common.py | 28 ++++++++++++++++------------ scripts/generate_readme.py | 5 ++--- scripts/generate_site.py | 5 ----- tests/test_audit_regressions.py | 21 +++++++++++++++++++++ 5 files changed, 42 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index c9d42c4c..ce6d8d52 100644 --- a/README.md +++ b/README.md @@ -71,14 +71,14 @@ Each file is checked the way your platform checks it. Most compare a checksum, t - **425 files** the platforms' emulators load are still to be found, and 55 more cannot be sourced at all (per-user keys, user-filled slots, dumps nobody has made); both are named in the [gap analysis](https://abdess.github.io/retrobios/gaps/) - **12 platforms** supported with platform-specific verification - **530 emulators** profiled from source (RetroArch cores + standalone) -- **550 systems** handled by those emulators (NES, SNES, PlayStation, Saturn, Dreamcast, ...) +- **513 systems** handled by those emulators (NES, SNES, PlayStation, Saturn, Dreamcast, ...) - **19,079 files in the collection**, each with its SHA1, MD5, SHA256, CRC32 and Adler-32 fingerprints: 5,337 system files, 2,813 arcade ROM sets, 10,929 game and engine data files. That is every platform and emulator together, so no pack holds them all: each carries what its own emulators load, counted in the download table - **562 of 5,337 system files** matched to dump-preservation catalogs (No-Intro, Redump, TOSEC); arcade sets and engine data fall outside what those catalogs index - **16755 MB** total collection size ## Supported systems -NES, SNES, Nintendo 64, GameCube, Wii, Game Boy, Game Boy Advance, Nintendo DS, Nintendo 3DS, Switch, PlayStation, PlayStation 2, PlayStation 3, PSP, PS Vita, Mega Drive, Saturn, Dreamcast, Game Gear, Master System, Neo Geo, Atari 2600, Atari 7800, Atari Lynx, Atari ST, MSX, PC Engine, TurboGrafx-16, ColecoVision, Intellivision, Commodore 64, Amiga, ZX Spectrum, Arcade (MAME), and 516+ more. +NES, SNES, Nintendo 64, GameCube, Wii, Game Boy, Game Boy Advance, Nintendo DS, Nintendo 3DS, Switch, PlayStation, PlayStation 2, PlayStation 3, PSP, PS Vita, Mega Drive, Saturn, Dreamcast, Game Gear, Master System, Neo Geo, Atari 2600, Atari 7800, Atari Lynx, Atari ST, MSX, PC Engine, TurboGrafx-16, ColecoVision, Intellivision, Commodore 64, Amiga, ZX Spectrum, Arcade (MAME), and 479+ more. Full list with per-file details: **[https://abdess.github.io/retrobios/](https://abdess.github.io/retrobios/)** @@ -180,4 +180,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-18T13:59:53Z* +*Auto-generated on 2026-10-18T14:10:55Z* diff --git a/scripts/common.py b/scripts/common.py index b7932804..f5b7ab66 100644 --- a/scripts/common.py +++ b/scripts/common.py @@ -2459,25 +2459,29 @@ def list_emulator_profiles(emulators_dir: str, skip_aliases: bool = True) -> Non print(f" {name:30s} {display:40s} [{ptype}] {systems}{more}") -def list_system_ids(emulators_dir: str) -> None: - """Print available system IDs with emulator count. +def system_index(profiles: dict) -> dict[str, tuple[str, set[str]]]: + """Normalized system id -> (the first spelling met, the emulators). - Counted on the normalized id, as --system selects: listed per spelling, - 3do showed two emulators and panasonic-3do six for one system. + One system per normalized id, as --system selects: counted per spelling, + nes and nintendo-nes were two systems and the README announced 550 + where --list-systems printed 513. """ - profiles = load_emulator_profiles(emulators_dir) - system_emus: dict[str, set[str]] = {} - spelling: dict[str, str] = {} + index: dict[str, tuple[str, set[str]]] = {} for name, p in sorted(profiles.items()): if p.get("type") in ("alias", "test", "launcher"): continue for sys_id in p.get("systems", []): key = _norm_system_id(sys_id) - system_emus.setdefault(key, set()).add(name) - spelling.setdefault(key, sys_id) - for key in sorted(system_emus, key=lambda k: spelling[k]): - count = len(system_emus[key]) - print(f" {spelling[key]:35s} ({count} emulator{'s' if count > 1 else ''})") + index.setdefault(key, (sys_id, set()))[1].add(name) + return index + + +def list_system_ids(emulators_dir: str) -> None: + """Print available system IDs with emulator count.""" + index = system_index(load_emulator_profiles(emulators_dir)) + for _key, (spelling, emulators) in sorted(index.items(), key=lambda kv: kv[1][0]): + count = len(emulators) + print(f" {spelling:35s} ({count} emulator{'s' if count > 1 else ''})") def list_platform_system_ids(platform_name: str, platforms_dir: str) -> None: diff --git a/scripts/generate_readme.py b/scripts/generate_readme.py index c7f1bc91..0eddc08d 100644 --- a/scripts/generate_readme.py +++ b/scripts/generate_readme.py @@ -28,6 +28,7 @@ from common import ( load_platform_config, load_platform_registry, resolve_platform_cores, + system_index, unique_emulator_profiles, write_if_changed, ) @@ -321,9 +322,7 @@ def generate_readme(db: dict, platforms_dir: str) -> str: ) acquirable_total = missing_total - unsourceable_total - system_ids: set[str] = set() - for p in profiles.values(): - system_ids.update(p.get("systems", [])) + system_ids = system_index(profiles) lines = [ '

', diff --git a/scripts/generate_site.py b/scripts/generate_site.py index d0f563fc..8ed33d86 100644 --- a/scripts/generate_site.py +++ b/scripts/generate_site.py @@ -257,11 +257,6 @@ def generate_home( cls = "other" classifications[cls] = classifications.get(cls, 0) + 1 - # Count total systems across all profiles - all_systems = set() - for p in unique.values(): - all_systems.update(p.get("systems", [])) - lines = [ '

', "", diff --git a/tests/test_audit_regressions.py b/tests/test_audit_regressions.py index 54383aef..f51ab6c2 100644 --- a/tests/test_audit_regressions.py +++ b/tests/test_audit_regressions.py @@ -4914,5 +4914,26 @@ class DocumentedExamplesHoldTogether(unittest.TestCase): self.assertEqual(done, downloading) + +class TheReadmeCountsSystemsAsListSystemsDoes(unittest.TestCase): + """The README counted every raw spelling (nes and nintendo-nes as two) + and announced 550 systems; the tool it points to listed 513.""" + + def test_one_count(self): + import io + import re + from contextlib import redirect_stdout + + from scripts.common import list_system_ids + + out = io.StringIO() + with redirect_stdout(out): + list_system_ids(str(ROOT / "emulators")) + listed = len(out.getvalue().splitlines()) + readme = (ROOT / "README.md").read_text(encoding="utf-8") + announced = int(re.search(r"\*\*(\d+) systems\*\*", readme).group(1)) + self.assertEqual(announced, listed) + + if __name__ == "__main__": unittest.main()