mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-11 12:33:53 -06:00
fix: give a shared roms archive the default copy
A pack writes one file per path, and the second pass of core extras gave a path read by several cores to the first profile in name order: EmuELEC's arcade/neogeo.zip took AdvanceMAME's copy although the folder starts MAME 2003-Plus by default (es_systems.json:82), and RetroDECK's cps folders took MAME 2000's neogeo.zip and MAME 2003's nss.zip, which lack members MAME 2003-Plus requires there. The copies cores ask for at one path are now gathered first. The copy of the core the roms folder starts by default goes first, then the one holding every required member of the most claims, then profile order. The others stay alternatives in the manifest. Manifests: EmuELEC arcade/neogeo.zip becomes neogeo.zip.74df3258 (MAME 2003-Plus, AdvanceMAME's 4ccf6682 an alternative). RetroDECK roms/cps, cps1, cps2 and cps3 get neogeo.zip.74df3258 and nss.zip.cba0bd8c. No file is added or removed. verify --all keeps its counts.
This commit is contained in:
1 parent
852ce19ca9
commit
01a422f86c
2 files changed
+94
-4
No files matched your search
+57
-4
@@ -10,7 +10,7 @@ from collections.abc import Iterable
|
||||
|
||||
from common import PROFILE_IDENTITY_FIELDS, _norm_system_id, entry_system_ids
|
||||
from alternatives import core_file_spares, required_only_spares, set_fields
|
||||
from common import archive_members, holds_required_members
|
||||
from common import archive_members, contents_members, holds_required_members
|
||||
from common import declared_root, expand_platform_declared_names, platform_declarations
|
||||
from common import load_emulator_profiles
|
||||
from common import resolution_is_hash_exact
|
||||
@@ -1447,6 +1447,8 @@ def _collect_emulator_extras(
|
||||
for e in extras:
|
||||
covered_names.add(e["name"])
|
||||
|
||||
# Destination -> the copies cores ask for there, in profile order.
|
||||
copied: dict[str, list[dict]] = {}
|
||||
for emu_name, profile in sorted(profiles.items()):
|
||||
if profile.get("type") in ("launcher", "alias"):
|
||||
continue
|
||||
@@ -1500,7 +1502,8 @@ def _collect_emulator_extras(
|
||||
)
|
||||
if full_dest in seen_dests and full_dest not in taken:
|
||||
continue
|
||||
if full_dest in taken and full_dest not in seen and fname not in platform_names:
|
||||
if (full_dest in taken or full_dest in copied) and full_dest not in seen \
|
||||
and fname not in platform_names:
|
||||
# Another core's extra holds this path and only that
|
||||
# extra covers the name: a pack without that core never
|
||||
# reaches this copy, so this core claims nothing here.
|
||||
@@ -1551,8 +1554,22 @@ def _collect_emulator_extras(
|
||||
if f.get("layout_path"):
|
||||
extra["layout_path"] = f["layout_path"]
|
||||
extra["claim_order"] = (2, 0, emu_name)
|
||||
if _take(extra, full_dest, seen_dests, taken):
|
||||
extras.append(extra)
|
||||
copied.setdefault(full_dest, []).append(extra)
|
||||
|
||||
# Several cores reading one archive in one roms folder: the copy of the
|
||||
# core the folder starts by default goes first, then the copy serving
|
||||
# the most of them. Five cores AmberELEC offers for roms/arcade read
|
||||
# neogeo.zip there, and the copy holding the 18 members its default
|
||||
# MAME 2003-Plus requires also holds the 3 MAME 2000 requires.
|
||||
defaults = _folder_defaults(config, profiles)
|
||||
for full_dest, claims in copied.items():
|
||||
if len(claims) > 1:
|
||||
claims = sorted(claims, key=lambda c: (
|
||||
not _starts_by_default(c, defaults), -_copy_serves(c, claims, db),
|
||||
))
|
||||
for extra in claims:
|
||||
if _take(extra, full_dest, seen_dests, taken):
|
||||
extras.append(extra)
|
||||
|
||||
extras.extend(
|
||||
_archive_prefix_extras(
|
||||
@@ -1656,6 +1673,42 @@ class _ClaimRanks:
|
||||
)
|
||||
|
||||
|
||||
def _folder_defaults(config: dict, profiles: dict) -> dict[str, set[str]]:
|
||||
"""Roms folder -> the profiles its default core or emulator names."""
|
||||
out: dict[str, set[str]] = {}
|
||||
for row in config.get("_rom_folders") or []:
|
||||
chosen = {str(row.get("core") or ""), str(row.get("emulator") or "")} - {""}
|
||||
keys = {c.lower().replace("-", "_") for c in chosen}
|
||||
out[str(row.get("folder"))] = {
|
||||
name for name, profile in profiles.items()
|
||||
if keys & {n.lower().replace("-", "_")
|
||||
for n in (name, *(str(c) for c in profile.get("cores") or []))}
|
||||
}
|
||||
return out
|
||||
|
||||
|
||||
def _starts_by_default(extra: dict, defaults: dict[str, set[str]]) -> bool:
|
||||
"""Whether the extra's core is the one its roms folder starts by default."""
|
||||
layout = str(extra.get("layout_path") or "")
|
||||
destination = str(extra.get("destination") or "")
|
||||
if not layout or not destination.endswith("/" + layout):
|
||||
return False
|
||||
folder = destination[: -len(layout) - 1].removeprefix("roms/")
|
||||
return extra.get("source_profile") in defaults.get(folder, set())
|
||||
|
||||
|
||||
def _copy_serves(extra: dict, claims: list[dict], db: dict) -> int:
|
||||
"""How many of the claims on one path the copy an extra resolves to
|
||||
holds every required member of."""
|
||||
wanted = [members for c in claims if (members := contents_members(c))]
|
||||
if not wanted:
|
||||
return 0
|
||||
path, _status = resolve_local_file(extra, db, dest_hint=extra.get("destination", ""))
|
||||
if not path:
|
||||
return 0
|
||||
return sum(1 for members in wanted if holds_required_members(path, members))
|
||||
|
||||
|
||||
def _names_content(entry: dict) -> bool:
|
||||
"""Whether a report entry declares the bytes it wants, not only a name.
|
||||
|
||||
|
||||
@@ -1187,6 +1187,43 @@ class StandaloneMameOnEmuDeck(unittest.TestCase):
|
||||
self.assertIn(("roms", "adam/adam.zip"), places)
|
||||
|
||||
|
||||
def platform_manifest(platform: str) -> dict:
|
||||
from common import load_database
|
||||
|
||||
db = load_database(str(REPO_ROOT / "database.json"))
|
||||
with redirect_stdout(io.StringIO()):
|
||||
return generate_manifest(
|
||||
platform, str(REPO_ROOT / "platforms"), db, str(REPO_ROOT / "bios"),
|
||||
str(REPO_ROOT / "platforms" / "_registry.yml"),
|
||||
emulators_dir=str(REPO_ROOT / "emulators"), offline=True,
|
||||
)
|
||||
|
||||
|
||||
class ArchiveSharedByOfferedCores(unittest.TestCase):
|
||||
"""One path holds one file: several cores reading an archive in one roms
|
||||
folder get the copy of the core the folder starts by default, then the
|
||||
copy serving the most of them."""
|
||||
|
||||
def test_the_default_core_copy_takes_the_folder(self) -> None:
|
||||
# EmuELEC's arcade folder starts MAME 2003-Plus by default
|
||||
# (es_systems.json:82), AdvanceMAME reads its sets there too.
|
||||
(entry,) = [f for f in platform_manifest("emuelec")["files"]
|
||||
if f["dest"] == "arcade/neogeo.zip"]
|
||||
self.assertEqual(entry["cores"], ["mame2003_plus"])
|
||||
self.assertTrue(entry["repo_path"].endswith("neogeo.zip.74df3258"))
|
||||
others = {c for a in entry.get("alternatives", []) for c in a["cores"]}
|
||||
self.assertIn("advancemame", others)
|
||||
|
||||
def test_the_copy_serving_most_cores_wins_without_a_default(self) -> None:
|
||||
# RetroDECK's cps folders start the libretro MAME core, whose sets are
|
||||
# in mame/bios. Of the cores reading nss.zip in cps1, MAME 2003-Plus
|
||||
# alone lists members, and only its copy holds them.
|
||||
(entry,) = [f for f in platform_manifest("retrodeck")["files"]
|
||||
if f["dest"] == "roms/cps1/nss.zip"]
|
||||
self.assertEqual(entry["cores"], ["mame2003_plus"])
|
||||
self.assertTrue(entry["repo_path"].endswith("nss.zip.cba0bd8c"))
|
||||
|
||||
|
||||
class MacBootDisksOnKnulli(unittest.TestCase):
|
||||
def test_the_hard_records_bind(self) -> None:
|
||||
# mameGenerator.py and libretroMAMEConfig.py pass the boot disk as
|
||||
|
||||
Reference in new issue
Block a user