From 7bcd41e0c40dea63fe22be24e7467f940607dd4c Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Wed, 21 Oct 2026 05:20:08 +0200 Subject: [PATCH] fix: let taiwan and asia-ntsc answer each other kronos tags the ST-V BIOS epr-19854.ic8 taiwan, mednafen_stv tags it asia-ntsc, the SMPC area Mednafen names for Taiwan (ss.cpp:1119, 2363-2364). As siblings under asia they never matched, so a request in one spelling dropped the file tagged in the other, and --region taiwan,us dropped the Saturn sega_101.bin. AREA_TERRITORIES relates each area to the territory it serves, china for asia-pal included, in region.py and its installer copy. --- install.ps1 | 2 +- install.py | 12 +++++++++++- install.sh | 2 +- scripts/region.py | 22 ++++++++++++++++++++-- tests/test_install_selection.py | 7 +++++-- tests/test_region.py | 14 ++++++++++++++ wiki/profiling.md | 5 ++++- 7 files changed, 56 insertions(+), 8 deletions(-) diff --git a/install.ps1 b/install.ps1 index 52ac1e68..33b617d8 100644 --- a/install.ps1 +++ b/install.ps1 @@ -8,7 +8,7 @@ param( $ErrorActionPreference = "Stop" $defaultInstallUrl = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.py" -$defaultInstallSha256 = "22737fbb470a63a8d3630f52ffee1535e57f96a2f1fea969e58368e06ca35f06" +$defaultInstallSha256 = "c1f70fb54ba84b75f434a04cdb41998e342210bb804276dda286a333efd80733" $maximumInstallerBytes = 2MB $installerExit = 0 $installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null } diff --git a/install.py b/install.py index 3cde59b4..bcad60d7 100644 --- a/install.py +++ b/install.py @@ -137,6 +137,10 @@ REGION_ALIASES: dict[str, str] = { "auto": WORLD_REGION, "gb": "uk", } +AREA_TERRITORIES: dict[str, frozenset] = { + "asia-ntsc": frozenset({"taiwan"}), + "asia-pal": frozenset({"china"}), +} _REGION_PARENT = { member: parent for parent, members in REGION_TREE.items() for member in members } @@ -2116,7 +2120,13 @@ def canonical_region(raw: str) -> str: def _region_comparable(a: str, b: str) -> bool: - return a == b or _REGION_PARENT.get(a) == b or _REGION_PARENT.get(b) == a + return ( + a == b + or _REGION_PARENT.get(a) == b + or _REGION_PARENT.get(b) == a + or a in AREA_TERRITORIES.get(b, ()) + or b in AREA_TERRITORIES.get(a, ()) + ) def _region_rank(file_regions: set[str], requested: list[str]) -> int: diff --git a/install.sh b/install.sh index 360630eb..431418a0 100755 --- a/install.sh +++ b/install.sh @@ -18,7 +18,7 @@ esac TEMP_INSTALLER="" TEMP_DIRECTORY="" DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/install.py" -DEFAULT_INSTALL_SHA256="22737fbb470a63a8d3630f52ffee1535e57f96a2f1fea969e58368e06ca35f06" +DEFAULT_INSTALL_SHA256="c1f70fb54ba84b75f434a04cdb41998e342210bb804276dda286a333efd80733" MAX_INSTALLER_BYTES=2097152 cleanup() { diff --git a/scripts/region.py b/scripts/region.py index 7c6f9257..a299a8a0 100644 --- a/scripts/region.py +++ b/scripts/region.py @@ -47,6 +47,17 @@ REGION_TREE: dict[str, frozenset[str]] = { "oceania": frozenset({"australia", "new-zealand"}), } +# Hardware areas that fuse territory and signal standard, with the territories +# the code serves through them. Mednafen's Saturn SMPC area 0x2 is "For TAIWAN +# and PHILIPINES" and its area option offers it as "Taiwan", area 0xA as +# "China" (mednafen/ss/ss.cpp:1119, 2363-2364 in the mednafen_stv port). +# kronos tags the ST-V BIOS epr-19854.ic8 taiwan and mednafen_stv tags it +# asia-ntsc: a request in either spelling has to reach the other. +AREA_TERRITORIES: dict[str, frozenset[str]] = { + "asia-ntsc": frozenset({"taiwan"}), + "asia-pal": frozenset({"china"}), +} + _PARENT: dict[str, str] = { member: parent for parent, members in REGION_TREE.items() for member in members } @@ -96,8 +107,15 @@ def _canonical(raw: str) -> str: def comparable(a: str, b: str) -> bool: - """True when two regions are equal or in a parent/child relation.""" - return a == b or _PARENT.get(a) == b or _PARENT.get(b) == a + """True when two regions are equal, parent and child, or an area and a + territory it serves.""" + return ( + a == b + or _PARENT.get(a) == b + or _PARENT.get(b) == a + or a in AREA_TERRITORIES.get(b, ()) + or b in AREA_TERRITORIES.get(a, ()) + ) def normalize_declared(raw: str | list | None) -> set[str]: diff --git a/tests/test_install_selection.py b/tests/test_install_selection.py index 66c8d9dc..a379e1a3 100644 --- a/tests/test_install_selection.py +++ b/tests/test_install_selection.py @@ -40,6 +40,7 @@ class OneRegionVocabulary(unittest.TestCase): def test_the_copy_matches(self): self.assertEqual(install.REGION_TREE, dict(region.REGION_TREE)) + self.assertEqual(install.AREA_TERRITORIES, dict(region.AREA_TERRITORIES)) self.assertEqual(install.REGION_ALIASES, region.ALIASES) self.assertEqual(install.WORLD_REGION, region.WORLD) self.assertEqual(install.REGIONS, region.REGIONS) @@ -49,7 +50,8 @@ class TheRegionRuleIsTheBuilders(unittest.TestCase): """Random groups, both implementations, one answer.""" POOL = ["north-america", "europe", "japan", "uk", "canada", "brazil", - "south-korea", "world", "asia", "oceania"] + "south-korea", "world", "asia", "oceania", "taiwan", "asia-ntsc", + "china", "asia-pal"] def _case(self, rng: random.Random): groups: dict[str, list[tuple[str, str]]] = {} @@ -79,7 +81,8 @@ class TheRegionRuleIsTheBuilders(unittest.TestCase): if not entry["regions"]: del entry["regions"], entry["region_groups"] entry.pop("priority", None) - requested = rng.sample(["north-america", "europe", "japan", "uk", "canada"], + requested = rng.sample(["north-america", "europe", "japan", "uk", "canada", + "taiwan", "asia-ntsc", "china", "asia-pal"], rng.randint(1, 3)) return groups, index, list(entries.values()), requested diff --git a/tests/test_region.py b/tests/test_region.py index 3c544724..a2e853bc 100644 --- a/tests/test_region.py +++ b/tests/test_region.py @@ -58,6 +58,17 @@ class TestComparable(unittest.TestCase): def test_saturn_fused_slug_is_under_asia(self): self.assertTrue(region.comparable("asia", "asia-ntsc")) + def test_an_area_matches_the_territory_it_serves(self): + """kronos tags the ST-V BIOS epr-19854.ic8 taiwan, mednafen_stv tags + it asia-ntsc, the SMPC area Mednafen names for Taiwan.""" + self.assertTrue(region.comparable("taiwan", "asia-ntsc")) + self.assertTrue(region.comparable("asia-ntsc", "taiwan")) + self.assertTrue(region.comparable("china", "asia-pal")) + + def test_an_area_does_not_match_other_territories(self): + self.assertFalse(region.comparable("japan", "asia-ntsc")) + self.assertFalse(region.comparable("taiwan", "asia-pal")) + class TestNormalizeDeclared(unittest.TestCase): def test_none_is_empty(self): @@ -889,6 +900,9 @@ class RegionalRequestsKeepWhatTheCodeLoads(unittest.TestCase): ("ace-dl", ["france"], "private/firmware/Amstrad_s4.cpr"), ("duckstation", ["brazil"], "scph5501.bin"), ("duckstation", ["south-korea"], "scph5500.bin"), + ("mednafen_stv", ["taiwan", "north-america"], "epr-19854.ic8"), + ("kronos", ["asia-ntsc", "north-america"], "epr-19854.ic8"), + ("beetle_saturn", ["taiwan", "north-america"], "sega_101.bin"), ("pcsx_rearmed", ["brazil", "japan"], "scph5501.bin"), ("pcsx_rearmed", ["brazil", "japan"], "scph1001.bin"), ("blastem", ["australia", "north-america"], "bios_CD_E.bin"), diff --git a/wiki/profiling.md b/wiki/profiling.md index d0d17163..d920f642 100644 --- a/wiki/profiling.md +++ b/wiki/profiling.md @@ -186,7 +186,10 @@ What does not, and why each trap is easy to fall into: `env.Loader.ReqSpec == "PAL"`, a video specification, and the Atari 2600 has no region lock. There is no signal field: drop `region:` rather than map it. The exception is hardware that fuses both axes in one register, such as the - Saturn SMPC area codes behind `asia-ntsc` and `asia-pal`. + Saturn SMPC area codes behind `asia-ntsc` and `asia-pal`. Mednafen names + them for Taiwan and China, so a `--region taiwan` request reaches an + `asia-ntsc` file and a `--region china` request an `asia-pal` one, and the + other way round (`AREA_TERRITORIES` in `scripts/region.py`). - **A single-region system.** When every file of a system shares one region, the pack filter keeps them all anyway, so annotating adds nothing. - **A language without a territory.** Arabic MSX variants, CJK fonts. The