From 481b8eb5f9a29b2b235feebd9b3dbc46d01c9320 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Sun, 18 Oct 2026 17:11:39 +0200 Subject: [PATCH] fix: show catalog names and flag bad dumps --- scripts/generate_site.py | 58 ++++++++++++++++++++++++++++++++------ tests/test_site_exports.py | 30 +++++++++++++++++++- 2 files changed, 79 insertions(+), 9 deletions(-) diff --git a/scripts/generate_site.py b/scripts/generate_site.py index 8ed33d86..b45fe615 100644 --- a/scripts/generate_site.py +++ b/scripts/generate_site.py @@ -1827,6 +1827,50 @@ def _prov_title(data: dict) -> str: return ": ".join(p for p in parts if p).replace('"', """) +# TOSEC dump flags that say the dump is not a clean copy of the chip +# (TOSEC Naming Convention, "Dump info flags"); [a] alternate and [!] +# verified good are not among them. +_TOSEC_BAD_FLAGS = { + "b": "bad dump", "o": "overdump", "h": "hacked", "cr": "cracked", + "f": "fixed", "t": "trained", "p": "pirate", +} +_TOSEC_FLAG_RE = re.compile(r"\[(b|o|h|cr|f|t|p)\d*(?:\s[^\]]*)?\]") + + +def _dump_flags(source: str, data: dict) -> list[str]: + """What a catalog's name says is wrong with the dump, as words.""" + if source != "tosec": + return [] + name = str(data.get("name") or data.get("description") or "") + seen: list[str] = [] + for flag in _TOSEC_FLAG_RE.findall(name): + word = _TOSEC_BAD_FLAGS[flag] + if word not in seen: + seen.append(word) + return seen + + +def _provenance_lines(provenance: dict, prefix: str) -> list[str]: + """One visible line per catalog: its badge and the entry's own name. + + The name was only a title attribute, unreadable on a touch screen and + absent from the search for 793 of 803 provenances; and a TOSEC entry + flagged [b] or [o] wore the same green badge as a clean dump. + """ + lines: list[str] = [] + for source, data in sorted(provenance.items()): + label = _PROVENANCE_LABELS.get(source, source) + flags = _dump_flags(source, data) + kind = "rb-badge-warning" if flags else "rb-badge-success" + badge = f'[{label}]({prefix}provenance.md#{source})' + name = str(data.get("name") or data.get("description") or "") + line = f"- Catalogued: {badge} {_code(name)}" if name else f"- Catalogued: {badge}" + if flags: + line += f" ({', '.join(flags)}, as {label} records it)" + lines.append(line) + return lines + + def load_derivable(path: str = "derivable.json") -> dict[str, dict]: """Missing sha1 -> how the collection produces it, as the pipeline found. @@ -1852,8 +1896,11 @@ def generate_provenance_page( "", f"**{matched_files:,}** of {total_files:,} system files match an entry " "in a dump-preservation catalog. Those files carry a " - 'Verified dump badge on ' - "the [system pages](systems/index.md). Arcade ROM sets and engine data " + 'catalog badge and the ' + "catalog entry's own name on the [system pages](systems/index.md), " + 'orange (TOSEC) where ' + "TOSEC flags the dump as bad, overdumped, hacked, cracked or fixed. " + "Arcade ROM sets and engine data " "sit outside what those catalogs index, so they are left out of the " "ratio rather than counted as failures.", "", @@ -2065,12 +2112,7 @@ def _system_file_card(f: dict, consumers: FileConsumers) -> list[str]: lines.append(f"- Emulators: {', '.join(_emulator_link(e, '../') for e in emus)}") provenance = f.get("provenance", {}) if provenance: - prov_badges = " ".join( - f'' - f"[{_PROVENANCE_LABELS.get(s, s)}](../provenance.md#{s})" - for s, data in sorted(provenance.items()) - ) - lines.append(f"- Verified dump: {prov_badges}") + lines.extend(_provenance_lines(provenance, "../")) lines.extend(["", "", ""]) return lines diff --git a/tests/test_site_exports.py b/tests/test_site_exports.py index 48629e11..b461dd1e 100644 --- a/tests/test_site_exports.py +++ b/tests/test_site_exports.py @@ -355,7 +355,35 @@ class AVariantShowsWhoPinsIt(unittest.TestCase): self.assertIn("[batocera](../platforms/batocera.md)", block) self.assertIn("[retrobat](../platforms/retrobat.md)", block) self.assertIn('`' + "c" * 40 + '`', block) - self.assertIn("Verified dump", block) + self.assertIn("Catalogued", block) + + +class AFlaggedDumpIsNotVerified(unittest.TestCase): + """scph5000.bin, which TOSEC names "...(JP)[b].bin", wore a green + "Verified dump: TOSEC" badge; the catalog name lived only in a title + attribute, outside the search and invisible on a touch screen.""" + + def test_flags_and_name(self): + from generate_site import _provenance_lines + + lines = _provenance_lines({ + "tosec": {"name": "Sony PlayStation BIOS (v2.2)(Sony)(JP)[b].bin"}, + "redump": {"name": "SCPH-5000 BIOS v2.2"}, + }, "../") + tosec = next(l for l in lines if "TOSEC" in l) + redump = next(l for l in lines if "Redump" in l) + self.assertIn("rb-badge-warning", tosec) + self.assertIn("bad dump", tosec) + self.assertIn("(JP)[b].bin", tosec) + self.assertIn("rb-badge-success", redump) + self.assertIn("SCPH-5000 BIOS v2.2", redump) + + def test_an_alternate_is_not_a_flag(self): + from generate_site import _dump_flags + + self.assertEqual(_dump_flags("tosec", {"name": "x (JP)[a].bin"}), []) + self.assertEqual(_dump_flags("tosec", {"name": "x [o][b].bin"}), ["overdump", "bad dump"]) + if __name__ == "__main__": unittest.main()