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.
This commit is contained in:
Abdessamad Derraz committed 2026-10-21 05:20:08 +02:00
1 parent e318bb4503
commit 7bcd41e0c4
7 files changed
+56 -8

No files matched your search

+1 -1
View File
@@ -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 }
+11 -1
View File
@@ -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:
+1 -1
View File
@@ -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() {
+20 -2
View File
@@ -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]:
+5 -2
View File
@@ -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
+14
View File
@@ -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"),
+4 -1
View File
@@ -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