mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-23 20:29:06 -05:00
fix: settle a slot in any case where case folds
declaration_settles tested the exact-case name before the slot, so on a filesystem that folds case a declaration spelled otherwise never settled the core's file: RetroBat's 32X_G_BIOS.BIN left BlastEm's 32X_G_BIOS.bin undeclared, and verify counted ten such entries as core files. A slot a case-insensitive platform fills now settles the entry whatever the spelling (case_folded_slots), the slot arbitration judging its bytes as for the exact name. Lakka and RetroPie, which keep both names, are unchanged. Measured: 10 entries settle on RetroBat, 21 on RetroArch. generate_site.py builds its gap report through cross_reference() without the folded slots; it needs folded_declared_slots() passed in the same way as the command line now does.
This commit is contained in:
1 parent
184c822e6e
commit
82a3e44afd
5 files changed
+101
-16
No files matched your search
@@ -44,7 +44,7 @@ from common import (
|
||||
select_emulator_profiles,
|
||||
)
|
||||
from hashing import parse_hash_list
|
||||
from validation import declaration_settles, outside_gap_scope
|
||||
from validation import case_folded_slots, declaration_settles, outside_gap_scope
|
||||
|
||||
yaml = require_yaml()
|
||||
|
||||
@@ -105,6 +105,25 @@ def declared_names(
|
||||
)
|
||||
|
||||
|
||||
def folded_declared_slots(
|
||||
platforms_dir: str, platforms: list[str] | None, db: dict
|
||||
) -> frozenset[str]:
|
||||
"""The slots platforms whose filesystem folds case fill, any spelling.
|
||||
|
||||
RetroBat's 32X_G_BIOS.BIN is the file BlastEm opens as 32X_G_BIOS.bin,
|
||||
and the exact-case name test called it undeclared.
|
||||
"""
|
||||
from common import platform_declarations
|
||||
|
||||
slots: set[str] = set()
|
||||
for platform_name in platforms or list_registered_platforms(
|
||||
platforms_dir, include_archived=True
|
||||
):
|
||||
config = load_platform_config(platform_name, platforms_dir)
|
||||
slots |= case_folded_slots(config, platform_declarations(config, db))
|
||||
return frozenset(slots)
|
||||
|
||||
|
||||
def _build_supplemental_index(
|
||||
data_root: str = "data", bios_root: str = "bios"
|
||||
) -> set[str]:
|
||||
@@ -631,7 +650,9 @@ def _cross_reference_profile(
|
||||
# Check platform declaration: by name, by an alias the core answers
|
||||
# to (RetroArch lists amiberry's cd32.rom as kick40060.CD32), or by
|
||||
# archive.
|
||||
in_platform = declaration_settles(f, fname, platform_names, effective_path)
|
||||
in_platform = declaration_settles(
|
||||
f, fname, platform_names, effective_path, index.get("folded_slots", frozenset())
|
||||
)
|
||||
|
||||
if in_platform:
|
||||
seen_files.add(seen_key)
|
||||
@@ -738,6 +759,7 @@ def cross_reference(
|
||||
data_names: set[str] | None = None,
|
||||
all_declared: dict[str, list[tuple[int | None, str]]] | None = None,
|
||||
standalone_cores: set[str] | None = None,
|
||||
folded_slots: frozenset[str] = frozenset(),
|
||||
) -> dict:
|
||||
"""Compare emulator profiles against platform declarations.
|
||||
|
||||
@@ -774,6 +796,7 @@ def cross_reference(
|
||||
"db_files": db_files,
|
||||
"data_names": data_names,
|
||||
"all_declared": all_declared,
|
||||
"folded_slots": folded_slots,
|
||||
"bios_dir": db.get("bios_dir"),
|
||||
}
|
||||
for emu_name, profile in profiles.items():
|
||||
@@ -958,6 +981,7 @@ def main() -> None:
|
||||
profiles, declared, db, plat_data_dirs, data_names,
|
||||
all_declared=declared_names(args.platforms_dir, selected, db),
|
||||
standalone_cores=standalone_cores,
|
||||
folded_slots=folded_declared_slots(args.platforms_dir, selected, db),
|
||||
)
|
||||
|
||||
if args.json:
|
||||
|
||||
+19
-1
@@ -465,11 +465,25 @@ def outside_gap_scope(file_entry: dict, is_standalone: bool) -> bool:
|
||||
)
|
||||
|
||||
|
||||
def case_folded_slots(
|
||||
config: dict, declarations: Mapping[str, list[tuple[int | None, str]]],
|
||||
) -> frozenset[str]:
|
||||
"""The slots a platform whose filesystem folds case fills, any spelling.
|
||||
|
||||
On RetroBat the 32X_G_BIOS.BIN it declares is the file BlastEm opens as
|
||||
32X_G_BIOS.bin. On Lakka bios.col and BIOS.col are two files: nothing.
|
||||
"""
|
||||
if not config.get("case_insensitive_fs", False):
|
||||
return frozenset()
|
||||
return frozenset(where for places in declarations.values() for _size, where in places)
|
||||
|
||||
|
||||
def declaration_settles(
|
||||
file_entry: dict,
|
||||
fname: str,
|
||||
declared_names: Mapping[str, list[tuple[int | None, str]]] | set[str],
|
||||
dest: str = "",
|
||||
folded_slots: frozenset[str] = frozenset(),
|
||||
) -> bool:
|
||||
"""Whether a platform declaration meets a core's requirement.
|
||||
|
||||
@@ -486,12 +500,16 @@ def declaration_settles(
|
||||
carries no slot or size and answers by name.
|
||||
|
||||
Read by verify and by the gap report, which settled on the name alone
|
||||
and left DOSBox's SC-55 ROMs off the gaps page.
|
||||
and left DOSBox's SC-55 ROMs off the gaps page. ``folded_slots``
|
||||
(case_folded_slots) are slots a declaration fills in another spelling on
|
||||
a filesystem that folds case: the core opens that file there.
|
||||
"""
|
||||
if file_entry.get("archive") in declared_names:
|
||||
return True
|
||||
slot = sanitize_pack_path(dest or fname).lower()
|
||||
directory = slot if dest.endswith("/") else slot.rpartition("/")[0]
|
||||
if not dest.endswith("/") and slot in folded_slots:
|
||||
return True
|
||||
for name in (fname, *file_entry.get("aliases", [])):
|
||||
if name not in declared_names:
|
||||
continue
|
||||
|
||||
+6
-2
@@ -86,6 +86,7 @@ from nativemode import (
|
||||
)
|
||||
from validation import (
|
||||
baseline_verdict,
|
||||
case_folded_slots,
|
||||
declaration_settles,
|
||||
frontend_digest_matches,
|
||||
existence_discrepancy,
|
||||
@@ -420,6 +421,7 @@ def _candidate_verdict(
|
||||
include_all: bool,
|
||||
declared_names: Mapping[str, list[tuple[int | None, str]]] | set[str],
|
||||
dest: str = "",
|
||||
folded_slots: frozenset[str] = frozenset(),
|
||||
) -> str:
|
||||
"""Whether a profile entry can be a gap, and whether it is settled.
|
||||
|
||||
@@ -435,7 +437,7 @@ def _candidate_verdict(
|
||||
if outside_gap_scope(file_entry, is_standalone):
|
||||
return "skip"
|
||||
if not include_all and declaration_settles(
|
||||
file_entry, fname, declared_names, dest
|
||||
file_entry, fname, declared_names, dest, folded_slots
|
||||
):
|
||||
return "settled"
|
||||
return "keep"
|
||||
@@ -479,6 +481,7 @@ def find_undeclared_files(
|
||||
"""
|
||||
if declared_names is None:
|
||||
declared_names = platform_declarations(config, db)
|
||||
folded_slots = case_folded_slots(config, declared_names)
|
||||
|
||||
# Whether the builder drops a file whose local copy contradicts its
|
||||
# declared hash, which decides if such a copy counts as held here.
|
||||
@@ -568,7 +571,8 @@ def find_undeclared_files(
|
||||
if duplicate and (first is None or first["profile"] == emu_name):
|
||||
continue
|
||||
verdict = _candidate_verdict(
|
||||
f, fname, is_standalone, include_all, declared_names, dest
|
||||
f, fname, is_standalone, include_all, declared_names, dest,
|
||||
folded_slots,
|
||||
)
|
||||
if verdict == "settled":
|
||||
seen_files.add(seen_key)
|
||||
|
||||
@@ -15,7 +15,7 @@ REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
||||
|
||||
from cross_reference import cross_reference # noqa: E402
|
||||
from validation import declaration_settles # noqa: E402
|
||||
from validation import case_folded_slots, declaration_settles # noqa: E402
|
||||
|
||||
SC55 = {"name": "ROM1.BIN", "path": "SC-55/ROM1.BIN", "size": 32768,
|
||||
"validation": ["size"], "required": True}
|
||||
@@ -45,6 +45,33 @@ class OneRule(unittest.TestCase):
|
||||
self.assertIn("declaration_settles(", source, module)
|
||||
|
||||
|
||||
class ACaseFoldingFilesystem(unittest.TestCase):
|
||||
"""RetroBat declares 32X_G_BIOS.BIN, BlastEm opens 32X_G_BIOS.bin at the
|
||||
same place. The exact-case name test reported it undeclared before the
|
||||
slot was ever compared."""
|
||||
|
||||
declared = {"32X_G_BIOS.BIN": [(256, "32x_g_bios.bin")]}
|
||||
entry = {"name": "32X_G_BIOS.bin"}
|
||||
|
||||
def test_the_slot_settles_it_where_case_folds(self):
|
||||
folded = case_folded_slots({"case_insensitive_fs": True}, self.declared)
|
||||
self.assertTrue(
|
||||
declaration_settles(self.entry, "32X_G_BIOS.bin", self.declared, "", folded)
|
||||
)
|
||||
|
||||
def test_two_spellings_are_two_files_elsewhere(self):
|
||||
folded = case_folded_slots({"case_insensitive_fs": False}, self.declared)
|
||||
self.assertFalse(
|
||||
declaration_settles(self.entry, "32X_G_BIOS.bin", self.declared, "", folded)
|
||||
)
|
||||
|
||||
def test_another_place_settles_nothing(self):
|
||||
folded = case_folded_slots({"case_insensitive_fs": True}, self.declared)
|
||||
self.assertFalse(declaration_settles(
|
||||
self.entry, "32X_G_BIOS.bin", self.declared, "blastem/32X_G_BIOS.bin", folded
|
||||
))
|
||||
|
||||
|
||||
class TheRunningBuildPlacesTheGap(unittest.TestCase):
|
||||
"""Batocera runs Cemu standalone, which reads keys.txt at its
|
||||
standalone_path. The gap report gave the libretro Cemu/keys.txt while
|
||||
|
||||
+22
-10
@@ -98,7 +98,7 @@ class TheReportSaysSo(unittest.TestCase):
|
||||
name: verify_platform(
|
||||
load_platform_config(name, "platforms"), db, "emulators", profiles
|
||||
)
|
||||
for name in ("retroarch", "retrobat")
|
||||
for name in ("retroarch", "retrobat", "lakka")
|
||||
}
|
||||
finally:
|
||||
os.chdir(previous)
|
||||
@@ -109,16 +109,28 @@ class TheReportSaysSo(unittest.TestCase):
|
||||
return u
|
||||
self.skipTest(f"{emulator} {path} is not in the cross-reference")
|
||||
|
||||
def test_a_case_twin_with_other_bytes_is_left_out(self):
|
||||
entry = self._entry("NP2kai", "np2kai/bios.rom", "retrobat")
|
||||
self.assertIs(entry.get("in_pack"), False)
|
||||
self.assertIn("another case", entry.get("omitted", ""))
|
||||
def _listed(self, emulator: str, path: str, platform: str) -> bool:
|
||||
return any(
|
||||
u.get("emulator") == emulator and (u.get("path") or u.get("name")) == path
|
||||
for u in self.results[platform]["undeclared_files"]
|
||||
)
|
||||
|
||||
def test_a_twin_with_the_same_bytes_serves_the_core(self):
|
||||
# MesenCE opens bios.col, and RetroArch's pack folds it onto BIOS.col,
|
||||
# the same bytes, which serves the core on a folding filesystem.
|
||||
entry = self._entry("MesenCE", "bios.col")
|
||||
self.assertIsNot(entry.get("in_pack"), False)
|
||||
def test_a_declaration_in_another_case_settles_the_slot(self):
|
||||
"""On a filesystem that folds case the platform's file at the slot is
|
||||
the one the core opens, whatever its spelling, and the slot
|
||||
arbitration judges its bytes. Listed as a core file, RetroBat's
|
||||
32X_G_BIOS.BIN counted BlastEm's 32X_G_BIOS.bin in the pack twice."""
|
||||
for platform, emulator, path in (
|
||||
("retrobat", "NP2kai", "np2kai/bios.rom"),
|
||||
("retrobat", "BlastEm", "32X_G_BIOS.bin"),
|
||||
("retroarch", "MesenCE", "bios.col"),
|
||||
):
|
||||
with self.subTest(platform=platform, path=path):
|
||||
self.assertFalse(self._listed(emulator, path, platform))
|
||||
|
||||
def test_a_filesystem_keeping_both_names_reports_the_other(self):
|
||||
"""Lakka runs on Linux, where bios.col and BIOS.col are two files."""
|
||||
self.assertTrue(self._listed("MesenCE", "bios.col", "lakka"))
|
||||
|
||||
def test_a_directory_under_a_packed_file_is_left_out(self):
|
||||
entry = self._entry("nSide (SFC Balanced)", "SGB1.sfc/lr35902.boot.rom")
|
||||
|
||||
Reference in new issue
Block a user