diff --git a/schemas/emulator.schema.json b/schemas/emulator.schema.json index 8d073676..bc6fa4a3 100644 --- a/schemas/emulator.schema.json +++ b/schemas/emulator.schema.json @@ -286,6 +286,11 @@ "items": {"type": "string"}, "description": "Platforms (registry keys) whose package leaves out a file the emulator's release bundles, so their packs bring it." }, + "unread_on": { + "type": "array", + "items": {"type": "string"}, + "description": "Platforms (registry keys) whose launcher points the emulator at other files, so this entry is never opened there." + }, "source_ref": { "type": "string" } @@ -470,6 +475,11 @@ "items": {"type": "string"}, "description": "Platforms (registry keys) whose package leaves out a file the emulator's release bundles, so their packs bring it." }, + "unread_on": { + "type": "array", + "items": {"type": "string"}, + "description": "Platforms (registry keys) whose launcher points the emulator at other files, so this entry is never opened there." + }, "config_key": { "type": "string" }, diff --git a/scripts/generate_site.py b/scripts/generate_site.py index ff77f7b5..22b6a062 100644 --- a/scripts/generate_site.py +++ b/scripts/generate_site.py @@ -2861,6 +2861,9 @@ def _render_emulator_file( if f.get("not_bundled_on"): details.append("Left out by the platform's package, so the pack brings it, on: " + ", ".join(sorted(f["not_bundled_on"]))) + if f.get("unread_on"): + details.append("Never opened on these platforms, whose launcher names other files: " + + ", ".join(sorted(f["unread_on"]))) if source_ref: asker = requested_by(profile, source_ref) if asker: diff --git a/scripts/validation.py b/scripts/validation.py index 9a6a5967..e3ba678c 100644 --- a/scripts/validation.py +++ b/scripts/validation.py @@ -539,6 +539,16 @@ def installed_with_emulator(entry: dict, platform: str = "") -> bool: return bool(entry.get("bundled")) and platform not in (entry.get("not_bundled_on") or []) +def unread_on(entry: dict, platform: str) -> bool: + """Whether this platform's launcher points the emulator at other files. + + RetroBat starts standalone BlastEm with absolute paths to its own BIOS + names in the config it writes, so the defaults the emulator would read + (cdbios.bin, colecovision_bios.col) are never opened there. + """ + return bool(platform) and platform in (entry.get("unread_on") or []) + + def deliverable(entries: list[dict], standalone: bool, platform: str = "") -> list[dict]: """The entries of one build a pack has to bring. @@ -549,7 +559,7 @@ def deliverable(entries: list[dict], standalone: bool, platform: str = "") -> li """ return [ e for e in filter_files_by_mode(entries, standalone) - if not installed_with_emulator(e, platform) + if not installed_with_emulator(e, platform) and not unread_on(e, platform) ] diff --git a/scripts/verify.py b/scripts/verify.py index f7fcbb9a..5735669d 100644 --- a/scripts/verify.py +++ b/scripts/verify.py @@ -97,6 +97,7 @@ from validation import ( deliverable, filter_files_by_mode, installed_with_emulator, + unread_on, outside_gap_scope, ) @@ -540,7 +541,7 @@ def find_undeclared_files( f.get("variant_group"), region_key, ) - if not fname: + if not fname or unread_on(f, config.get("_key", "")): continue first = reported.get(seen_key) duplicate = seen_key in seen_files diff --git a/tests/test_audit_regressions.py b/tests/test_audit_regressions.py index 2dda466b..2c500274 100644 --- a/tests/test_audit_regressions.py +++ b/tests/test_audit_regressions.py @@ -5774,5 +5774,25 @@ class TheApiSchemaHoldsTheFieldsItPromises(unittest.TestCase): jsonschema.validate(envelope, schema) +class AFileTheLauncherNeverOpens(unittest.TestCase): + """RetroBat hands standalone BlastEm absolute paths to its own BIOS + names, so the defaults the emulator would read (cdbios.bin) were listed + as required and missing on RetroBat although nothing there opens them.""" + + def test_unread_on_drops_the_entry_for_that_platform_only(self): + from validation import deliverable, unread_on + + entry = {"name": "cdbios.bin", "unread_on": ["retrobat"]} + self.assertTrue(unread_on(entry, "retrobat")) + self.assertFalse(unread_on(entry, "batocera")) + self.assertFalse(unread_on(entry, "")) + self.assertEqual(deliverable([entry], True, "retrobat"), []) + self.assertEqual(deliverable([entry], True, "batocera"), [entry]) + + def test_the_gap_report_reads_it(self): + source = (ROOT / "scripts" / "verify.py").read_text(encoding="utf-8") + self.assertIn('unread_on(f, config.get("_key", ""))', source) + + if __name__ == "__main__": unittest.main()