From cd7d993cacf557f0eb1186c10686fbd4f03835f6 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Thu, 22 Oct 2026 06:30:56 +0200 Subject: [PATCH] feat: settle entries by their system's cores A system that lists the cores it runs is settled by those cores alone, so an unprofiled core of another system no longer leaves its entries open. Readers whose core runs the entry's system are named first, and cores that only share the file name are listed as such. --- scripts/exporter/candidates.py | 34 +++++++++++++++++++++++++++++++--- scripts/generate_site.py | 12 +++++++++--- scripts/verify.py | 11 ++++++++--- tests/test_entry_categories.py | 21 ++++++++++++++++++++- 4 files changed, 68 insertions(+), 10 deletions(-) diff --git a/scripts/exporter/candidates.py b/scripts/exporter/candidates.py index ce89495a..8b8c2b66 100644 --- a/scripts/exporter/candidates.py +++ b/scripts/exporter/candidates.py @@ -242,6 +242,10 @@ class EntryCategories: and every entry of a platform with no core to examine (BizHawk, MiSTer), stays firmware. + A system that carries its own `cores:` list is settled by those cores: + the cores examined are the ones it runs, and only its own unprofiled + cores leave it open. Otherwise the platform's whole list applies. + `explain` returns the category with its evidence: the profile entries that read the file, or the cores of its system that were examined. """ @@ -275,6 +279,20 @@ class EntryCategories: for key in sorted(examined): for sid in profiles[key].get("systems") or []: self._by_system.setdefault(_norm_system_id(str(sid)), []).append(key) + # Where the platform says which cores run a system, those settle its + # entries: an unprofiled core of another system has no say. + self._system_runs: dict[str, tuple[list[str], list[str]]] = {} + for sys_id, system in (config.get("systems") or {}).items(): + names = system.get("cores") if isinstance(system, dict) else None + if names: + own = {**config, "cores": list(names)} + for key in ("standalone_cores", "dual_mode_cores"): + if key in config: + own[key] = [n for n in config.get(key) or [] if n in names] + self._system_runs[sys_id] = ( + sorted(resolve_platform_cores(own, profiles)), + sorted(unresolved_platform_cores(own, profiles)), + ) def _matching(self, key: str, kind: str, entry: dict) -> list[dict]: """The profile entries of `key` that name the platform entry.""" @@ -304,7 +322,17 @@ class EntryCategories: }) if self._launchers.proof(entry, destination) is not None: readers[BIOS].append({"emulator": "launcher", "proof": "launcher", "entries": []}) - system_cores = self._by_system.get(_norm_system_id(system), []) + if system in self._system_runs: + system_cores, unresolved = self._system_runs[system] + else: + system_cores = self._by_system.get(_norm_system_id(system), []) + unresolved = self._unresolved + # A reader that runs the entry's system comes first and says so: a + # core of another system may only share the file's name. + for found in readers.values(): + for reader in found: + reader["runs_system"] = reader["emulator"] in system_cores + found.sort(key=lambda r: not r["runs_system"]) if readers[BIOS]: return {"category": BIOS, "read_by": readers[BIOS]} if readers[GAME_DATA]: @@ -312,10 +340,10 @@ class EntryCategories: examined = {"cores_examined": system_cores} if self._guessed: return {"category": BIOS, "unsettled": "the platform declares no core list", **examined} - if self._unresolved: + if unresolved: return {"category": BIOS, "unsettled": "cores the platform names have no profile: " - + ", ".join(self._unresolved), **examined} + + ", ".join(unresolved), **examined} if not system_cores: return {"category": BIOS, "unsettled": "no examined core emulates this system", **examined} diff --git a/scripts/generate_site.py b/scripts/generate_site.py index 740ccd40..4c42541f 100644 --- a/scripts/generate_site.py +++ b/scripts/generate_site.py @@ -2064,7 +2064,7 @@ def _platform_data_links(name: str) -> str: def _category_evidence_md(evidence: dict) -> str: """What establishes a missing entry's category, as the page shows it.""" if evidence.get("read_by"): - readers = [] + groups: dict[bool, list[str]] = {True: [], False: []} for reader in evidence["read_by"]: refs = [ f"`{e['name']}` at `{e['source_ref']}`" if e.get("source_ref") else f"`{e['name']}`" @@ -2072,8 +2072,14 @@ def _category_evidence_md(evidence: dict) -> str: ] name = (_emulator_link(reader["emulator"], "../") if reader["emulator"] != "launcher" else "the platform's launcher") - readers.append(f"{name} ({', '.join(refs)})" if refs else f"{name} (by {reader['proof']})") - return "Read by " + ", ".join(readers) + groups[bool(reader.get("runs_system"))].append( + f"{name} ({', '.join(refs)})" if refs else f"{name} (by {reader['proof']})") + if not groups[True]: + return "Read by " + ", ".join(groups[False]) + text = "Read by " + ", ".join(groups[True]) + if groups[False]: + text += ", also named by " + ", ".join(groups[False]) + return text examined = evidence.get("cores_examined") or [] cores = ("cores examined: " + ", ".join(_emulator_link(c, "../") for c in examined) if examined else "no core examined") diff --git a/scripts/verify.py b/scripts/verify.py index b080f7f7..2b0430d0 100644 --- a/scripts/verify.py +++ b/scripts/verify.py @@ -1634,17 +1634,22 @@ _NOT_FIRMWARE = { def category_evidence_text(evidence: dict) -> str: """One line saying what establishes a missing entry's category.""" if evidence.get("read_by"): - readers = [] + groups: dict[bool, list[str]] = {True: [], False: []} for reader in evidence["read_by"]: refs = [ f"{e['name']} at {e['source_ref']}" if e.get("source_ref") else e["name"] for e in reader.get("entries", []) ] - readers.append( + groups[bool(reader.get("runs_system"))].append( f"{reader['emulator']} ({', '.join(refs)})" if refs else f"{reader['emulator']} (by {reader['proof']})" ) - return "read by " + ", ".join(readers) + if not groups[True]: + return "read by " + ", ".join(groups[False]) + text = "read by " + ", ".join(groups[True]) + if groups[False]: + text += ", also named by " + ", ".join(groups[False]) + return text examined = evidence.get("cores_examined") or [] cores = f"cores examined: {', '.join(examined)}" if examined else "no core examined" if evidence.get("unsettled"): diff --git a/tests/test_entry_categories.py b/tests/test_entry_categories.py index c14b5735..d6cbb324 100644 --- a/tests/test_entry_categories.py +++ b/tests/test_entry_categories.py @@ -82,7 +82,7 @@ class Evidence(unittest.TestCase): found = self.cats.explain({"name": "diabdat.mpq"}, "ports/diablo/diabdat.mpq", "diablo") self.assertEqual(found["category"], GAME_DATA) self.assertEqual(found["read_by"], [{ - "emulator": "devilutionx", "proof": "name", + "emulator": "devilutionx", "proof": "name", "runs_system": True, "entries": [{"name": "diabdat.mpq", "source_ref": "Source/init.cpp:120"}]}]) self.assertEqual(verify.category_evidence_text(found), "read by devilutionx (diabdat.mpq at Source/init.cpp:120)") @@ -91,6 +91,25 @@ class Evidence(unittest.TestCase): found = self.cats.explain({"name": "hardwareconfig.xml"}, "bios/Machines/MSX/hardwareconfig.xml", "microsoft-msx") self.assertEqual(found, {"category": UNREAD, "cores_examined": ["bluemsx"]}) + + def test_the_system_s_own_core_reads_first(self): + profiles = {**PROFILES, "other": { + "emulator": "Other", "type": "game", "cores": ["other"], "systems": ["elsewhere"], + "files": [{"name": "diabdat.mpq", "category": "game_data"}]}} + cats = EntryCategories(_config(["bluemsx", "devilutionx", "other"], []), profiles) + found = cats.explain({"name": "diabdat.mpq"}, "ports/diablo/diabdat.mpq", "diablo") + self.assertEqual([r["emulator"] for r in found["read_by"]], ["devilutionx", "other"]) + self.assertEqual(verify.category_evidence_text(found), + "read by devilutionx (diabdat.mpq at Source/init.cpp:120), " + "also named by other (diabdat.mpq)") + + def test_a_system_s_own_cores_settle_it(self): + config = _config(["bluemsx", "devilutionx", "mystery"], [], system="microsoft-msx") + config["systems"]["microsoft-msx"]["cores"] = ["bluemsx"] + found = EntryCategories(config, PROFILES).explain( + {"name": "hardwareconfig.xml"}, "bios/Machines/MSX/hardwareconfig.xml", + "microsoft-msx") + self.assertEqual(found, {"category": UNREAD, "cores_examined": ["bluemsx"]}) self.assertEqual(verify.category_evidence_text(found), "no reader found (cores examined: bluemsx)")