feat: read retrobat bios groups

emulatorLauncher 228731e0 lists ten PS2 BIOS in one group: one held file
is enough and Program.cs reports the others OPTIONAL. The scraper keeps
group and optional, verify and the pack check apply the same rule.
This commit is contained in:
Abdessamad Derraz committed 2026-11-09 07:22:06 +01:00
1 parent 14e85924b0
commit 77a8f171ba
6 files changed
+85 -1

No files matched your search

+10
View File
@@ -2576,6 +2576,7 @@ systems:
md5: 21038400dc633070a78ad53090c53017
native_system: ps2
native_path: bios/pcsx2/bios/ps2-0230a-20080220.bin
group: ps2
native_name: PS2
- name: ps2-0230e-20080220.bin
destination: pcsx2/bios/ps2-0230e-20080220.bin
@@ -2583,6 +2584,7 @@ systems:
md5: dc69f0643a3030aaa4797501b483d6c4
native_system: ps2
native_path: bios/pcsx2/bios/ps2-0230e-20080220.bin
group: ps2
native_name: PS2
- name: ps2-0230j-20080220.bin
destination: pcsx2/bios/ps2-0230j-20080220.bin
@@ -2590,6 +2592,7 @@ systems:
md5: 80ac46fa7e77b8ab4366e86948e54f83
native_system: ps2
native_path: bios/pcsx2/bios/ps2-0230j-20080220.bin
group: ps2
native_name: PS2
- name: ps2-0250e-20100415.bin
destination: pcsx2/bios/ps2-0250e-20100415.bin
@@ -2597,6 +2600,7 @@ systems:
md5: 93ea3bcee4252627919175ff1b16a1d9
native_system: ps2
native_path: bios/pcsx2/bios/ps2-0250e-20100415.bin
group: ps2
native_name: PS2
- name: SCPH30004R.bin
destination: pcsx2/bios/SCPH30004R.bin
@@ -2604,6 +2608,7 @@ systems:
md5: 28922c703cc7d2cf856f177f2985b3a9
native_system: ps2
native_path: bios/pcsx2/bios/SCPH30004R.bin
group: ps2
native_name: PS2
- name: scph39001.bin
destination: pcsx2/bios/scph39001.bin
@@ -2611,30 +2616,35 @@ systems:
md5: d5ce2c7d119f563ce04bc04dbc3a323e
native_system: ps2
native_path: bios/pcsx2/bios/scph39001.bin
group: ps2
native_name: PS2
- name: ps3_ps2_emu_bios.bin
destination: pcsx2/bios/ps3_ps2_emu_bios.bin
required: true
native_system: ps2
native_path: bios/pcsx2/bios/ps3_ps2_emu_bios.bin
group: ps2
native_name: PS2
- name: SCPH-39004_BIOS_V7_EUR_160.BIN
destination: pcsx2/bios/SCPH-39004_BIOS_V7_EUR_160.BIN
required: true
native_system: ps2
native_path: bios/pcsx2/bios/SCPH-39004_BIOS_V7_EUR_160.BIN
group: ps2
native_name: PS2
- name: SCPH-39001_BIOS_V7_USA_160.BIN
destination: pcsx2/bios/SCPH-39001_BIOS_V7_USA_160.BIN
required: true
native_system: ps2
native_path: bios/pcsx2/bios/SCPH-39001_BIOS_V7_USA_160.BIN
group: ps2
native_name: PS2
- name: SCPH-70000_BIOS_V12_JAP_200.BIN
destination: pcsx2/bios/SCPH-70000_BIOS_V12_JAP_200.BIN
required: true
native_system: ps2
native_path: bios/pcsx2/bios/SCPH-70000_BIOS_V12_JAP_200.BIN
group: ps2
native_name: PS2
name: PS2
sony-playstation:
+4
View File
@@ -518,6 +518,10 @@
"type": "string",
"description": "Display name the platform gives its own system"
},
"group": {
"type": "string",
"description": "RetroBat: files sharing a group are alternatives. One held is enough and the others are reported OPTIONAL"
},
"md5_list": {
"type": "boolean",
"description": "Batocera: the platform writes md5 as a list of strings, any of which matches. md5 holds the items comma-joined, in order"
+14
View File
@@ -774,6 +774,14 @@ def verify_pack_against_platform(
baseline_groups.setdefault(expected, []).append(fe)
baseline_checked = len(baseline_groups)
# RetroBat's group: one packed file of it is enough, as verify counts it.
packed_groups = {
(system_of[id(fe)], fe["group"])
for expected, declarations in baseline_groups.items()
if expected in zip_set or expected.lower() in zip_lower
for fe in declarations
if fe.get("group")
}
exclusion_index = build_zip_contents_index(db) if db is not None else {}
categories = EntryCategories(config, emu_profiles, data_registry)
for expected, declarations in baseline_groups.items():
@@ -795,6 +803,12 @@ def verify_pack_against_platform(
):
baseline_excluded += 1
continue
elif all(
fe.get("group") and (system_of[id(fe)], fe["group"]) in packed_groups
for fe in declarations
):
baseline_absent += 1
continue
elif db is not None and all(
categories.category(fe, fe.get("destination", fe.get("name", "")),
system_of[id(fe)]) != BIOS
+4 -1
View File
@@ -217,6 +217,9 @@ class Scraper(BaseScraper):
sys_name = sys_data.get("name", "") if isinstance(sys_data, dict) else ""
if sys_name:
native["native_name"] = sys_name
# One held file of a group is enough (Program.cs): verify reads it.
if bios.get("group"):
native["group"] = str(bios["group"])
requirements.append(
BiosRequirement(
@@ -224,7 +227,7 @@ class Scraper(BaseScraper):
system=SYSTEM_SLUG_MAP.get(sys_key, sys_key),
md5=md5 or None,
destination=file_path,
required=True,
required=not bios.get("optional", False),
native_id=sys_key,
native_path=declared_path,
native=native,
+19
View File
@@ -1424,6 +1424,25 @@ def verify_platform(
):
file_severity[dest] = sev
# RetroBat's group: one file of it held is enough, and the others are
# reported OPTIONAL (batocera-systems/Program.cs). None held, each stays
# required.
members: dict[tuple[str, str], list[str]] = {}
for sys_id, system in config.get("systems", {}).items():
for file_entry in system.get("files", []):
if file_entry.get("group"):
dest = _dest_key(file_entry, sys_id)
if dest in file_status:
members.setdefault((sys_id, file_entry["group"]), []).append(dest)
for dests in members.values():
if any(file_status[d] == Status.OK for d in dests):
for dest in dests:
if file_status[dest] == Status.MISSING:
file_required[dest] = False
file_severity[dest] = compute_severity(Status.MISSING, False, mode)
for result in results_at.get(dest, []):
result["required"] = False
shipped, occupied = _builder_placement(
packed_order, bool(config.get("case_insensitive_fs", False))
)
+34
View File
@@ -915,6 +915,40 @@ class TestE2E(unittest.TestCase):
c = result["severity_counts"]
self.assertGreater(c[Severity.WARNING], 0)
def test_24b_a_held_group_member_makes_the_others_optional(self):
"""RetroBat's group: one file held is enough (batocera-systems/Program.cs)."""
from common import _platform_config_cache
with open(os.path.join(self.platforms_dir, "test_md5.yml")) as fh:
config = yaml.safe_load(fh)
held_md5 = next(
f["md5"] for f in config["systems"]["sys-renamed"]["files"]
)
config["systems"] = {
"sys-group": {"files": [
{"name": "correct_hash.bin", "destination": "g/held.bin",
"md5": held_md5, "required": True, "group": "one"},
{"name": "absent_alt.bin", "destination": "g/absent_alt.bin",
"md5": "0" * 32, "required": True, "group": "one"},
{"name": "absent_a.bin", "destination": "g/absent_a.bin",
"md5": "1" * 32, "required": True, "group": "none"},
{"name": "absent_b.bin", "destination": "g/absent_b.bin",
"md5": "2" * 32, "required": True, "group": "none"},
]},
}
with open(os.path.join(self.platforms_dir, "grouped_md5.yml"), "w") as fh:
yaml.dump(config, fh)
_platform_config_cache.clear()
result = verify_platform(
load_platform_config("grouped_md5", self.platforms_dir), self.db, self.emulators_dir
)
by_name = {d["name"]: d for d in result["details"]}
self.assertEqual(by_name["correct_hash.bin"]["status"], Status.OK)
self.assertIs(by_name["absent_alt.bin"]["required"], False)
self.assertIs(by_name["absent_a.bin"]["required"], True)
self.assertIs(by_name["absent_b.bin"]["required"], True)
self.assertEqual(result["severity_counts"][Severity.CRITICAL], 2)
def test_25_verify_sha1_platform(self):
config = load_platform_config("test_sha1", self.platforms_dir)
result = verify_platform(config, self.db, self.emulators_dir)