fix: one file under two spellings keeps both cores

BlastEm opens 32X_M_BIOS.bin and PicoDrive 32X_M_BIOS.BIN. On a case-insensitive platform the manifest kept the first and dropped the second core's claim, so an install narrowed to PicoDrive (RetroArch on Switch) lost the 32X BIOS its pack carries. The claim now joins the entry kept.
This commit is contained in:
Abdessamad Derraz committed 2026-10-20 11:27:08 +02:00
1 parent 312ba968bb
commit 6a852737a8
2 files changed
+39 -5

No files matched your search

+31 -3
View File
@@ -3658,6 +3658,29 @@ def _manifest_core_entries(
else:
full_dest = dest
if (
case_insensitive
and full_dest not in seen_destinations
and full_dest.lower() in seen_lower
):
# One file under two spellings: BlastEm opens 32X_M_BIOS.bin and
# PicoDrive 32X_M_BIOS.BIN. The second core's claim joins the
# entry kept, or a pack built without the first core loses the
# file the installer would still have to place. Collisions are
# rare, so the entry is looked up only when one happens.
held = next(
(e for e in manifest_files
if e["dest"].lower() == _manifest_dest_of(full_dest, base_dest).lower()),
None,
)
if held is not None and held.get("cores") is not None:
_attach_claims(
held, [fe], db, bios_dir, zip_contents, offline, required_only,
validation_index, owners, dest, held["dest"], repo_root,
data_registry,
)
continue
if placement_block(
full_dest, seen_destinations, seen_lower, seen_parents,
case_insensitive,
@@ -3700,9 +3723,7 @@ def _manifest_core_entries(
# Manifest dests are relative to base_destination. Keep the inferred
# extras prefix when it is an internal layout dir (RetroDECK bios/).
manifest_dest = full_dest
if base_dest and manifest_dest.startswith(f"{base_dest}/"):
manifest_dest = manifest_dest[len(base_dest) + 1:]
manifest_dest = _manifest_dest_of(full_dest, base_dest)
entry, file_size = _manifest_entry(
local_path, manifest_dest, [source_emu] if source_emu else [],
@@ -3746,6 +3767,13 @@ def _manifest_core_entries(
return total_size
def _manifest_dest_of(full_dest: str, base_dest: str) -> str:
"""A pack destination as the manifest writes it, below base_destination."""
if base_dest and full_dest.startswith(f"{base_dest}/"):
return full_dest[len(base_dest) + 1:]
return full_dest
def _claim_platform_omission(held: dict | None, emulator: str) -> bool:
"""Record a core's claim on a destination the platform already omits.
+8 -2
View File
@@ -634,9 +634,15 @@ class InstallMatchesATargetManifest(unittest.TestCase):
full = self._manifest(platform)
built = self._manifest(platform, set(cores), target)
files, omitted = install.narrow_to_cores(full, cores)
# A case-insensitive platform reads one file under both
# spellings (BlastEm's 32X_M_BIOS.bin, PicoDrive's .BIN).
from common import load_platform_config
config = load_platform_config(platform, str(REPO_ROOT / "platforms"))
fold = str.lower if config.get("case_insensitive_fs") else str
self.assertEqual(
{(f["dest"], f["sha1"]) for f in files},
{(f["dest"], f["sha1"]) for f in built["files"]},
{(fold(f["dest"]), f["sha1"]) for f in files},
{(fold(f["dest"]), f["sha1"]) for f in built["files"]},
)
self.assertEqual(
{o["dest"] for o in omitted},