mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-15 06:23:47 -06:00
fix: scan placeholder BIOS folders in packs
LRPS2 and PCEE2 name their BIOS <bios>.bin, which resolves to no file, so the platform pack's agnostic scan never anchored pcsx2/bios/ for them. On RetroArch yaps2 anchored it and the files carried only yaps2 as their core. On Batocera and RetroBat, where yaps2 does not run, pcsx2/bios/ stayed empty or held only the platform's own list. The scan now takes the folders where the system's other profiles resolve a hashed dump of that shape, the evidence the emulator pack already used, and a companion (<bios>.nvm) looks where its image is proven.
This commit is contained in:
1 parent
12daa6698f
commit
871caf9cae
2 files changed
+112
-15
No files matched your search
+52
-15
@@ -513,6 +513,7 @@ def _agnostic_scan_extras(
|
||||
extras_prefix: str,
|
||||
standalone_set: set[str] | None = None,
|
||||
taken: dict[str, dict] | None = None,
|
||||
all_profiles: dict | None = None,
|
||||
) -> list[dict]:
|
||||
"""Every interchangeable candidate a filename-agnostic core accepts.
|
||||
|
||||
@@ -521,6 +522,8 @@ def _agnostic_scan_extras(
|
||||
The directory is the delicate part: anchoring it on a single
|
||||
filename walked into another emulator's tree, so the profile's own
|
||||
files have to agree before the scan follows an ambiguous name.
|
||||
``all_profiles`` are the profiles a placeholder's directory is proven
|
||||
from, ``profiles`` when not given.
|
||||
"""
|
||||
extras: list[dict] = []
|
||||
files_db = db.get("files", {})
|
||||
@@ -617,7 +620,21 @@ def _agnostic_scan_extras(
|
||||
resolved_dirs.get(id(image), "") if image
|
||||
else resolved_dirs.get(id(f), "")
|
||||
)
|
||||
if not path_prefix:
|
||||
# A placeholder (<bios>.bin) resolves to nothing and anchors no
|
||||
# directory. The folders where the system's other profiles
|
||||
# resolve hashed files of that shape do, those folders only:
|
||||
# LRPS2 and PCEE2 are made of placeholders, and on Batocera,
|
||||
# where no other libretro PS2 core anchors pcsx2/bios/, the
|
||||
# pack held nothing there. A companion (<bios>.nvm) looks
|
||||
# where its image is proven: RetroBat's pcsx2/bios/ had
|
||||
# scph39001.bin and its .nvm only at the BIOS root.
|
||||
exact_dirs: frozenset[str] = frozenset()
|
||||
anchor = image if image else f
|
||||
if not path_prefix and "<" in str(anchor.get("name", "")):
|
||||
exact_dirs = placeholder_directories(
|
||||
anchor, emu_name, profile, db, all_profiles or profiles
|
||||
)
|
||||
if not path_prefix and not exact_dirs:
|
||||
continue
|
||||
# A name matching one file identifies it. A name matching several
|
||||
# identifies nothing on its own, so the profile's other files have
|
||||
@@ -629,7 +646,11 @@ def _agnostic_scan_extras(
|
||||
# Scan DB for all files under this prefix matching size
|
||||
for sha1, entry in files_db.items():
|
||||
path = entry.get("path", "")
|
||||
if not path.startswith(path_prefix):
|
||||
if exact_dirs:
|
||||
if ("/" not in path or path.rsplit("/", 1)[0] not in exact_dirs
|
||||
or "/.variants/" in path):
|
||||
continue
|
||||
elif not path.startswith(path_prefix):
|
||||
continue
|
||||
if not companion_ext and not size_fits(f, entry.get("size", 0)):
|
||||
continue
|
||||
@@ -686,6 +707,31 @@ def _agnostic_scan_extras(
|
||||
return extras
|
||||
|
||||
|
||||
def placeholder_directories(
|
||||
seed: dict, emu_name: str, profile: dict, db: dict, all_profiles: dict,
|
||||
) -> frozenset[str]:
|
||||
"""The folders that hold the dumps a placeholder entry accepts.
|
||||
|
||||
Each folder where another profile of the same system resolves a hashed
|
||||
file of the seed's shape by content: PCSX2 proves bios/Sony/PlayStation 2/.
|
||||
Only that folder, not the ones below it (its DVD firmware).
|
||||
"""
|
||||
systems = set(profile.get("systems", []))
|
||||
directories: set[str] = set()
|
||||
for other_name, other in all_profiles.items():
|
||||
if other_name == emu_name or not systems & set(other.get("systems", [])):
|
||||
continue
|
||||
for f in other.get("files", []):
|
||||
if not isinstance(f, dict) or not any(f.get(h) for h in ("sha1", "md5", "sha256", "crc32")):
|
||||
continue
|
||||
if f.get("size") and not size_fits(seed, f["size"]):
|
||||
continue
|
||||
local, status = resolve_local_file({**f, "source_profile": other_name}, db)
|
||||
if local and "/" in local and resolution_is_hash_exact(status):
|
||||
directories.add(local.rsplit("/", 1)[0])
|
||||
return frozenset(directories)
|
||||
|
||||
|
||||
def _placeholder_scan(
|
||||
files: list[dict], emu_name: str, profile: dict, db: dict, standalone: bool,
|
||||
all_profiles: dict | None = None,
|
||||
@@ -699,7 +745,6 @@ def _placeholder_scan(
|
||||
(its DVD firmware). Nothing proven, nothing scanned.
|
||||
"""
|
||||
files_db = db.get("files", {})
|
||||
systems = set(profile.get("systems", []))
|
||||
extras: list[dict] = []
|
||||
for seed in files:
|
||||
name = str(seed.get("name", ""))
|
||||
@@ -707,18 +752,10 @@ def _placeholder_scan(
|
||||
continue
|
||||
if not (seed.get("size") or seed.get("min_size") or seed.get("max_size")):
|
||||
continue
|
||||
directories: set[str] = set()
|
||||
for other_name, other in (all_profiles or load_emulator_profiles("emulators")).items():
|
||||
if other_name == emu_name or not systems & set(other.get("systems", [])):
|
||||
continue
|
||||
for f in other.get("files", []):
|
||||
if not any(f.get(h) for h in ("sha1", "md5", "sha256", "crc32")):
|
||||
continue
|
||||
if f.get("size") and not size_fits(seed, f["size"]):
|
||||
continue
|
||||
local, status = resolve_local_file({**f, "source_profile": other_name}, db)
|
||||
if local and "/" in local and resolution_is_hash_exact(status):
|
||||
directories.add(local.rsplit("/", 1)[0])
|
||||
directories = placeholder_directories(
|
||||
seed, emu_name, profile, db,
|
||||
all_profiles or load_emulator_profiles("emulators"),
|
||||
)
|
||||
layout = (seed.get("standalone_path") if standalone else None) or seed.get("path") or ""
|
||||
folder = layout.rsplit("/", 1)[0] if "/" in layout else ""
|
||||
for sha1, entry in sorted(files_db.items(), key=lambda kv: kv[1].get("path", "")):
|
||||
|
||||
@@ -55,6 +55,66 @@ class AgnosticScan(unittest.TestCase):
|
||||
["pcsx2/bios/scph1.bin", "pcsx2/bios/scph2.bin"])
|
||||
|
||||
|
||||
def test_a_placeholder_scans_the_folder_another_profile_proves(self):
|
||||
"""LRPS2 and PCEE2 name their BIOS <bios>.bin, which resolves nowhere.
|
||||
|
||||
On RetroArch yaps2 anchored pcsx2/bios/ for them; on Batocera no
|
||||
other libretro PS2 core runs, and the pack held nothing there. The
|
||||
folder where another profile of the system resolves a hashed dump of
|
||||
that shape is the evidence, that folder only.
|
||||
"""
|
||||
from packextras import _agnostic_scan_extras
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
previous = os.getcwd()
|
||||
os.chdir(tmp)
|
||||
self.addCleanup(os.chdir, previous)
|
||||
files, by_name = {}, {}
|
||||
for path, data in (
|
||||
("bios/PS2/scph1.bin", b"a" * 64),
|
||||
("bios/PS2/scph2.bin", b"b" * 64),
|
||||
("bios/PS2/scph2.bin.rom1", b"r" * 8),
|
||||
("bios/PS2/scph1.nvm", b"n" * 8),
|
||||
("bios/PS2/.variants/scph3.bin", b"e" * 64),
|
||||
("bios/PS2/DVD/dvd.bin", b"f" * 64),
|
||||
):
|
||||
Path(path).parent.mkdir(parents=True, exist_ok=True)
|
||||
Path(path).write_bytes(data)
|
||||
sha1 = hashlib.sha1(data).hexdigest()
|
||||
name = path.rsplit("/", 1)[1]
|
||||
files[sha1] = {"path": path, "name": name, "size": len(data), "sha1": sha1}
|
||||
by_name.setdefault(name, []).append(sha1)
|
||||
db = {"files": files, "indexes": {"by_name": by_name, "by_md5": {}, "by_crc32": {},
|
||||
"by_path_suffix": {}}}
|
||||
placeholders = {"emulator": "L", "type": "libretro", "bios_mode": "agnostic",
|
||||
"bios_directory": "pcsx2/bios/", "systems": ["ps2"], "files": [
|
||||
{"name": "<bios>.bin", "found_by": "content",
|
||||
"path": "pcsx2/bios/<bios>.bin", "min_size": 32, "max_size": 64},
|
||||
{"name": "<bios>.rom1", "path": "pcsx2/bios/<bios>.rom1"},
|
||||
{"name": "<bios>.nvm", "path": "pcsx2/bios/<bios>.nvm", "size": 8},
|
||||
]}
|
||||
pinned = {"emulator": "S", "type": "standalone", "systems": ["ps2"], "files": [
|
||||
{"name": "scph1.bin", "size": 64,
|
||||
"sha1": hashlib.sha1(b"a" * 64).hexdigest()},
|
||||
]}
|
||||
extras = _agnostic_scan_extras(
|
||||
{"l": placeholders, "s": pinned}, {"l"}, db, by_name, set(), "")
|
||||
self.assertEqual(sorted(e["destination"] for e in extras), [
|
||||
"pcsx2/bios/scph1.bin", "pcsx2/bios/scph1.nvm", "pcsx2/bios/scph2.bin",
|
||||
"pcsx2/bios/scph2.bin.rom1",
|
||||
])
|
||||
self.assertTrue(all(e["source_profile"] == "l" for e in extras))
|
||||
# An image the platform already ships there still gets its
|
||||
# companion: RetroBat lists pcsx2/bios/scph39001.bin itself.
|
||||
shipped = {"pcsx2/bios/scph1.bin"}
|
||||
extras = _agnostic_scan_extras(
|
||||
{"l": placeholders, "s": pinned}, {"l"}, db, by_name, shipped, "")
|
||||
self.assertIn("pcsx2/bios/scph1.nvm", {e["destination"] for e in extras})
|
||||
# Nothing proves the folder: nothing is scanned.
|
||||
self.assertEqual(
|
||||
_agnostic_scan_extras({"l": placeholders}, {"l"}, db, by_name, set(), ""), [])
|
||||
|
||||
|
||||
class BundledHashIsProof(unittest.TestCase):
|
||||
"""A bundled file with a declared hash is served by those bytes or flagged.
|
||||
|
||||
|
||||
Reference in new issue
Block a user