mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-10 13:33:24 -05:00
fix: place slot claims where the pack puts extras
This commit is contained in:
1 parent
2c2f284414
commit
3d0170264a
4 files changed
+89
-23
No files matched your search
+8
-4
@@ -1733,7 +1733,7 @@ def platform_declarations(
|
||||
declaring a file under a different name than the emulator profile
|
||||
(e.g. Batocera ROM1 vs gsplus ROM).
|
||||
"""
|
||||
prefix = "" if config.get("base_destination") else declared_root(config).lower()
|
||||
prefix = declared_root(config).lower()
|
||||
declared: dict[str, list[tuple[int | None, str]]] = {}
|
||||
by_md5 = db.get("indexes", {}).get("by_md5", {})
|
||||
files_db = db.get("files", {})
|
||||
@@ -1767,11 +1767,15 @@ def platform_declarations(
|
||||
|
||||
|
||||
def declared_root(config: dict) -> str:
|
||||
"""The directory nearly every declared destination starts with, if any.
|
||||
"""The BIOS directory a platform writes into every destination, if any.
|
||||
|
||||
A platform without base_destination writes it into each destination;
|
||||
core files go under the same directory.
|
||||
Only a platform whose base_destination is explicitly empty does that
|
||||
(RetroDECK writes bios/ in front of each file), and core files go under
|
||||
the same directory. A config that does not state a base says nothing,
|
||||
and its common folder is a system's, not the BIOS root.
|
||||
"""
|
||||
if config.get("base_destination", None) != "":
|
||||
return ""
|
||||
roots = [
|
||||
d.split("/", 1)[0]
|
||||
for system in config.get("systems", {}).values()
|
||||
|
||||
+49
-17
@@ -436,6 +436,45 @@ def _archive_prefix_extras(
|
||||
return extras
|
||||
|
||||
|
||||
class ExtraPlacement:
|
||||
"""Where a pack puts a core's file, decided once for every reader.
|
||||
|
||||
The builder, and slots.py judging what a destination holds, both read
|
||||
this: placed apart, slot arbitration compared RetroDECK's bios/ claims
|
||||
with core claims at the root and never saw them meet.
|
||||
"""
|
||||
|
||||
def __init__(self, config: dict, base_dest: str, profiles: dict) -> None:
|
||||
from common import _norm_system_id
|
||||
|
||||
self.prefix = _detect_extras_prefix(config, base_dest)
|
||||
self.slug_based, self.sys_to_slug = _detect_slug_structure(config)
|
||||
self.platform_systems = set(config.get("systems", {}).keys())
|
||||
self.norm_map = (
|
||||
{_norm_system_id(sid): sid for sid in self.platform_systems}
|
||||
if self.slug_based
|
||||
else {}
|
||||
)
|
||||
self.profiles = profiles
|
||||
|
||||
def place(self, entry: dict, dest: str) -> tuple[str, str] | None:
|
||||
"""(pack-relative destination, full destination), or None.
|
||||
|
||||
``entry`` names its profile and may name its own system. A
|
||||
slug-based platform cannot place a file of a system it has no
|
||||
folder for.
|
||||
"""
|
||||
if self.slug_based:
|
||||
slug = _slug_for(
|
||||
entry, self.profiles, self.platform_systems, self.norm_map,
|
||||
self.sys_to_slug,
|
||||
)
|
||||
if not slug:
|
||||
return None
|
||||
dest = f"{slug}/{dest}"
|
||||
return dest, (f"{self.prefix}/{dest}" if self.prefix else dest)
|
||||
|
||||
|
||||
def _collect_emulator_extras(
|
||||
config: dict,
|
||||
emulators_dir: str,
|
||||
@@ -469,7 +508,7 @@ def _collect_emulator_extras(
|
||||
|
||||
Works for ANY platform (RetroArch, Batocera, Recalbox, etc.)
|
||||
"""
|
||||
from common import _norm_system_id, resolve_platform_cores
|
||||
from common import resolve_platform_cores
|
||||
from verify import find_undeclared_files
|
||||
|
||||
profiles = (
|
||||
@@ -479,13 +518,9 @@ def _collect_emulator_extras(
|
||||
)
|
||||
|
||||
# Detect destination conventions for core extras
|
||||
extras_prefix = _detect_extras_prefix(config, base_dest)
|
||||
is_slug_based, sys_to_slug = _detect_slug_structure(config)
|
||||
platform_systems = set(config.get("systems", {}).keys())
|
||||
norm_map: dict[str, str] = {}
|
||||
if is_slug_based:
|
||||
for sid in platform_systems:
|
||||
norm_map[_norm_system_id(sid)] = sid
|
||||
placement = ExtraPlacement(config, base_dest, profiles)
|
||||
extras_prefix = placement.prefix
|
||||
is_slug_based = placement.slug_based
|
||||
|
||||
# Use strict YAML names (no DB alias enrichment) so that files known
|
||||
# under an alias still get packed at the emulator's expected path.
|
||||
@@ -508,15 +543,12 @@ def _collect_emulator_extras(
|
||||
dest = f"{raw_dest}{u['name']}" if raw_dest.endswith("/") else raw_dest
|
||||
|
||||
# Slug-based platforms: prefix dest with system slug
|
||||
if is_slug_based:
|
||||
slug = _slug_for(u, profiles, platform_systems, norm_map, sys_to_slug)
|
||||
if not slug:
|
||||
if unplaceable is not None:
|
||||
unplaceable.append(u)
|
||||
continue
|
||||
dest = f"{slug}/{dest}"
|
||||
|
||||
full_dest = f"{extras_prefix}/{dest}" if extras_prefix else dest
|
||||
placed = placement.place(u, dest)
|
||||
if placed is None:
|
||||
if unplaceable is not None:
|
||||
unplaceable.append(u)
|
||||
continue
|
||||
dest, full_dest = placed
|
||||
if claimants is not None:
|
||||
claimants[(u.get("emulator", ""), u.get("name", ""), u.get("path") or "")] = (
|
||||
sanitize_pack_path(dest)
|
||||
|
||||
+21
-2
@@ -136,9 +136,14 @@ def profile_claims(
|
||||
standalone_cores: set[str] | None = None,
|
||||
zip_contents: dict | None = None,
|
||||
data_dir_registry: dict | None = None,
|
||||
config: dict | None = None,
|
||||
) -> list[Claim]:
|
||||
"""What each emulator profile says belongs at each destination it names.
|
||||
|
||||
With the platform's config, a claim sits where the pack puts the core's
|
||||
file (ExtraPlacement): under RetroDECK's bios/, under RomM's system
|
||||
folder. A file the platform cannot place claims nothing.
|
||||
|
||||
A file can exist in one build of an emulator and not the other, and the
|
||||
two builds read from different directories. The mode is decided per
|
||||
emulator, not per pack: a platform runs some of its emulators as libretro
|
||||
@@ -148,6 +153,11 @@ def profile_claims(
|
||||
``standalone_path``. This is the gate verify already applies.
|
||||
"""
|
||||
standalone_cores = standalone_cores or set()
|
||||
placement = None
|
||||
if config is not None:
|
||||
from packextras import ExtraPlacement # noqa: PLC0415
|
||||
|
||||
placement = ExtraPlacement(config, base_dest, profiles)
|
||||
claims: list[Claim] = []
|
||||
for emu_name, profile in sorted(profiles.items()):
|
||||
if profile.get("type") in ("launcher", "alias"):
|
||||
@@ -174,7 +184,15 @@ def profile_claims(
|
||||
) or entry.get("name") or ""
|
||||
if not dest:
|
||||
continue
|
||||
full = f"{base_dest}/{dest}" if base_dest else dest
|
||||
if placement is None:
|
||||
full = f"{base_dest}/{dest}" if base_dest else dest
|
||||
else:
|
||||
placed = placement.place(
|
||||
{"profile": emu_name, "system": entry.get("system")}, dest
|
||||
)
|
||||
if placed is None:
|
||||
continue
|
||||
full = placed[1]
|
||||
# The owner's own copy is the one the builder ships.
|
||||
local, status = resolve_local_file(
|
||||
{**entry, "source_profile": emu_name},
|
||||
@@ -236,7 +254,8 @@ def find_conflicts(
|
||||
# claim at all matches what ships is a disagreement.
|
||||
by_slot: dict[str, list[Claim]] = {}
|
||||
for claim in profile_claims(
|
||||
profiles, db, base_dest, standalone_cores, zip_contents, data_dir_registry
|
||||
profiles, db, base_dest, standalone_cores, zip_contents, data_dir_registry,
|
||||
config=config,
|
||||
):
|
||||
key = _normalize(claim.destination)
|
||||
platform = by_dest.get(key)
|
||||
|
||||
Reference in new issue
Block a user