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()