From f69bd7cbd50425734f2230e95caaadd095a63c5f Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Thu, 5 Nov 2026 11:44:40 +0100 Subject: [PATCH] fix: read the database size of a declared hash A platform declaration that names only an md5 left the size check undecided, and an undecided same-named match overrode a profile's system:. The size the database holds for that md5 now refutes it: clk's Oric basic10.rom no longer joins Batocera's BK list. --- scripts/truth.py | 19 ++++++++++++++----- tests/test_truth_layout.py | 20 ++++++++++++++++++++ 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/scripts/truth.py b/scripts/truth.py index 302d6562e..bc1e3c98c 100644 --- a/scripts/truth.py +++ b/scripts/truth.py @@ -508,14 +508,19 @@ def _sizes_clash(a: dict, b: dict) -> bool: return False -def _identity_values(entry: dict, db: dict | None) -> set[str]: - """Every hash an entry declares, with the siblings the database knows.""" +def _identity(entry: dict, db: dict | None) -> dict: + """The entry with the hashes and size the database knows for its hash.""" probe = {k: entry[k] for k in ("sha1", "md5", "crc32", "size") if entry.get(k)} if db and probe and not any(isinstance(v, list) for v in probe.values()): _enrich_hashes(probe, db) + return {**entry, **{k: v for k, v in probe.items() if not entry.get(k)}} + + +def _identity_values(entry: dict) -> set[str]: + """Every hash an identity declares.""" values: set[str] = set() for field in ("sha1", "md5", "sha256", "crc32"): - values |= _declared(entry, field) | _declared(probe, field) + values |= _declared(entry, field) return values @@ -525,10 +530,14 @@ def _declaration_verdict(entry: dict, declaration: dict, db: dict | None) -> str A shared hash proves it. Sizes no value of which agrees refute it, as name_match_size_ok refutes a candidate. Two hashes at one size are two revisions of one slot, as slots.py reads them, so they refute nothing. + The size the database holds for a declared hash counts as declared: + Batocera's BK BASIC10.ROM names only an md5, of a 24576-byte file, and + clk's 16 KB Oric basic10.rom was filed under the BK beside it. """ - if _identity_values(entry, db) & _identity_values(declaration, db): + ours, theirs = _identity(entry, db), _identity(declaration, db) + if _identity_values(ours) & _identity_values(theirs): return "same" - if _sizes_clash(entry, declaration): + if _sizes_clash(ours, theirs): return "other" return "undecided" diff --git a/tests/test_truth_layout.py b/tests/test_truth_layout.py index d644350aa..4c187b261 100644 --- a/tests/test_truth_layout.py +++ b/tests/test_truth_layout.py @@ -454,6 +454,26 @@ class TheModelIsAFunctionOfItsInputs(unittest.TestCase): ) +class DatabaseSizeRefutesASameNamedDeclaration(unittest.TestCase): + """Batocera's BK system declares BASIC10.ROM by md5 alone. clk's Oric + basic10.rom, 16 KB with its own system:, was filed under the BK and + added to its list: the 24576 bytes the database holds for that md5 + were never compared.""" + + def test_the_size_behind_a_declared_hash_counts(self): + bk_md5, oric_md5 = "3" * 32, "e" * 32 + db = {"files": {"1" * 40: {"sha1": "1" * 40, "md5": bk_md5, "size": 24576}}, + "indexes": {"by_md5": {bk_md5: "1" * 40}}} + profiles = {"clk": {"emulator": "clk", "type": "libretro", "systems": ["oric"], + "files": [{"name": "basic10.rom", "path": "Oric/basic10.rom", + "system": "oric", "size": 16384, "md5": oric_md5}]}} + config = {"cores": ["clk"], "systems": {"elektronika-bk": {"files": [ + {"name": "BASIC10.ROM", "md5": bk_md5}]}}} + truth = generate_platform_truth("p", config, {}, profiles, db=db) + self.assertNotIn("basic10.rom", { + f["name"] for f in truth["systems"].get("elektronika-bk", {}).get("files", [])}) + + class RegionFollowsEveryReader(unittest.TestCase): """The first core to tag a file set its region in the truth: the ST-V BIOS kept beetle_saturn's Americas and lost mednafen_stv's Korea, and