fix: group a system once whatever its spelling

The platform's files and the core extras for the Master System sat
in two region groups, sega-master-system and sega-mastersystem,
reduced apart. A US pack kept the declared Japanese BIOS. A core
extra's system group now takes the platform's spelling, in the pack,
the report and the installer's manifest fields alike.
This commit is contained in:
Abdessamad Derraz committed 2026-10-18 03:51:57 +02:00
1 parent 97c973c7e2
commit a40a68c1f7
4 files changed
+100 -9

No files matched your search

+5 -2
View File
@@ -19,7 +19,7 @@ from dataclasses import dataclass
import region as region_mod
from common import _norm_system_id, sanitize_pack_path
from packextras import _extra_system_ids, _kept, extra_region_groups
from packextras import _extra_system_ids, _kept, extra_region_groups, system_spellings
def _dest(entry: dict) -> str:
@@ -58,11 +58,14 @@ class SelectionIndex:
owned_by: dict[str, set[str]] = {}
extra_groups: dict[str, set[str]] = {}
spellings = system_spellings(pack_systems)
for extra in extras:
dest = _dest(extra)
if not dest:
continue
extra_groups.setdefault(dest, set()).update(extra_region_groups(extra))
extra_groups.setdefault(dest, set()).update(
extra_region_groups(extra, spellings)
)
# A profile system the platform does not have is no system the
# pack's --system accepts: it would offer what the pack refuses.
for sys_id in _extra_system_ids(extra):
+32 -6
View File
@@ -6,7 +6,9 @@ this module builds so the report and the pack withdraw the same files."""
from __future__ import annotations
from common import PROFILE_IDENTITY_FIELDS
from collections.abc import Iterable
from common import PROFILE_IDENTITY_FIELDS, _norm_system_id
from common import archive_members
from common import declared_root, expand_platform_declared_names, platform_declarations
from common import load_emulator_profiles
@@ -948,6 +950,7 @@ def platform_region_groups(
absent=absent,
)
held = {id(extra) for extra in extras}
spellings = system_spellings(systems)
for extra in _kept(extras + absent, required_only):
dest = sanitize_pack_path(extra.get("destination", extra.get("name", "")))
if not dest:
@@ -965,7 +968,7 @@ def platform_region_groups(
# looked up by name its scph50006 took DuckStation's [japan], which
# describes DuckStation's own PS1-on-PS2 choice.
competes = not extra.get("agnostic_scan") or extra.get("region")
for group_id in extra_region_groups(extra) if competes else ():
for group_id in extra_region_groups(extra, spellings) if competes else ():
groups.setdefault(group_id, []).append((dest, name))
if extras_out is not None and id(extra) in held:
extras_out.append(extra)
@@ -974,8 +977,31 @@ def platform_region_groups(
return groups, extra_dests
def extra_region_groups(extra: dict) -> list[str]:
"""The region groups a core extra competes in."""
def system_spellings(systems: Iterable[str]) -> dict[str, str]:
"""A platform's own spelling of each system, keyed the way ids compare.
RetroDECK files the Master System as sega-master-system, the profiles as
sega-mastersystem. Grouped under both, the declared bios/bios_J.sms and
the core's bios_J.sms were reduced apart and a --region us pack kept the
Japanese BIOS.
"""
spellings: dict[str, str] = {}
for sys_id in sorted(systems):
spellings.setdefault(_norm_system_id(sys_id), sys_id)
return spellings
def extra_region_groups(
extra: dict, spellings: dict[str, str] | None = None
) -> list[str]:
"""The region groups a core extra competes in.
A group named after a system takes the platform's spelling of it, so the
extra competes beside the files the platform declares for that system.
"""
def system_group(sys_id: str) -> str:
return (spellings or {}).get(_norm_system_id(sys_id), sys_id)
emulator = extra.get("source_emulator", "")
variant = extra.get("variant_group")
if variant:
@@ -986,10 +1012,10 @@ def extra_region_groups(extra: dict) -> list[str]:
return [f"{emulator}:variant:{variant}"]
explicit = extra.get("source_system")
if explicit:
return [str(explicit)]
return [system_group(str(explicit))]
systems = [str(s) for s in extra.get("source_systems", []) if s]
if len(systems) == 1:
return systems
return [system_group(systems[0])]
# No system names the machine this file is chosen for. Filed under every
# system of a 30-system profile, CLK's American MSX ROM made the
# Enterprise group drop its German ROM. Alone, it drops nothing and is
+38
View File
@@ -6251,6 +6251,44 @@ struct BurnDriver BurnDrvneogeo = {
("declared.bin", "declared.bin")],
)
def test_a_system_spelled_two_ways_is_one_group(self):
"""RetroDECK files the Master System as sega-master-system, the
profiles as sega-mastersystem. Two groups reduced apart, and a
--region us pack kept the declared Japanese BIOS."""
import hashlib # noqa: PLC0415
from packextras import platform_region_groups # noqa: PLC0415
files, by_name = {}, {}
for name in ("bios_U.sms", "bios_J.sms"):
payload = name.encode()
path = os.path.join(self.bios_dir, name)
with open(path, "wb") as handle:
handle.write(payload)
sha1 = hashlib.sha1(payload).hexdigest()
files[sha1] = {"path": path, "name": name, "size": len(payload),
"md5": hashlib.md5(payload).hexdigest()}
by_name[name] = [sha1]
db = {"files": files, "indexes": {"by_name": by_name, "by_md5": {},
"by_crc32": {}, "by_path_suffix": {}}}
profiles = {"smscore": {
"emulator": "SMS Core", "type": "libretro",
"systems": ["sega-mastersystem"],
"files": [{"name": "bios_U.sms", "region": ["north-america"]},
{"name": "bios_J.sms", "region": ["japan"]}],
}}
config = {"platform": "P", "cores": ["smscore"], "base_destination": "system",
"systems": {"sega-master-system": {"files": [
{"name": "bios_J.sms", "destination": "bios/bios_J.sms"}]}}}
groups, _ = platform_region_groups(
config, config["systems"], self.emulators_dir, db, "system", profiles,
)
self.assertNotIn("sega-mastersystem", groups)
self.assertEqual(
sorted(dest for dest, _ in groups["sega-master-system"]),
["bios/bios_J.sms", "bios_J.sms", "bios_U.sms"],
)
def test_slot_narrowed_pack_gets_its_own_name(self):
names = {
os.path.basename(self._region_pack()),
+25 -1
View File
@@ -14,7 +14,7 @@ from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(REPO_ROOT / "scripts"))
from packextras import extra_region_groups # noqa: E402
from packextras import extra_region_groups, system_spellings # noqa: E402
class TheGroupFollowsWhatTheProfileSays(unittest.TestCase):
@@ -63,5 +63,29 @@ class TheEnterpriseKeepsItsRom(unittest.TestCase):
self.assertNotIn("bios/brd.rom", drops)
class TheGroupTakesThePlatformSpelling(unittest.TestCase):
def test_a_profile_spelling_maps_to_the_platform_one(self):
spellings = system_spellings(["sega-master-system", "sony-playstation"])
self.assertEqual(
extra_region_groups({"source_system": "sega-mastersystem"}, spellings),
["sega-master-system"],
)
def test_a_system_the_platform_lacks_keeps_its_name(self):
spellings = system_spellings(["sony-playstation"])
self.assertEqual(
extra_region_groups({"source_systems": ["sega-saturn"]}, spellings),
["sega-saturn"],
)
def test_file_and_variant_groups_are_untouched(self):
spellings = system_spellings(["sega-megacd"])
self.assertEqual(
extra_region_groups({"source_emulator": "picodrive", "variant_group": "mcd",
"source_system": "sega-megacd"}, spellings),
["picodrive:variant:mcd"],
)
if __name__ == "__main__":
unittest.main()