mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-10 13:33:24 -05:00
fix: settle a core file only by its size or slot
This commit is contained in:
1 parent
be45eda62d
commit
f6da2cff3a
10 files changed
+321
-135
No files matched your search
+41
-22
@@ -1670,40 +1670,59 @@ def filter_systems_by_target(
|
||||
return filtered
|
||||
|
||||
|
||||
def expand_platform_declared_names(config: dict, db: dict) -> set[str]:
|
||||
"""Build set of file names declared by a platform config.
|
||||
def platform_declarations(
|
||||
config: dict, db: dict, *, enrich: bool = True
|
||||
) -> dict[str, list[tuple[int | None, str]]]:
|
||||
"""Every file name a platform declares, with each declaration's size and destination.
|
||||
|
||||
Enriches the set with canonical names and aliases from the database
|
||||
by resolving each platform file's MD5 through by_md5. This handles
|
||||
cases where a platform declares a file under a different name than
|
||||
the emulator profile (e.g. Batocera ROM1 vs gsplus ROM).
|
||||
A name alone does not say which file a declaration is: RetroArch declares
|
||||
galaksija/ROM1.BIN, a 4 KiB ROM, and DOSBox reads a 32 KiB SC-55 ROM1.BIN.
|
||||
The size, from the platform entry or else from the file its hash names,
|
||||
tells a declaration of the same file from a same-named one; None stands
|
||||
for a size nobody knows. The destination, sanitized and case-folded, is
|
||||
the slot the declaration fills.
|
||||
|
||||
With ``enrich``, a declaration also answers for the canonical name and
|
||||
the aliases the database knows its MD5 under. This handles a platform
|
||||
declaring a file under a different name than the emulator profile
|
||||
(e.g. Batocera ROM1 vs gsplus ROM).
|
||||
"""
|
||||
declared: set[str] = set()
|
||||
declared: dict[str, list[tuple[int | None, str]]] = {}
|
||||
by_md5 = db.get("indexes", {}).get("by_md5", {})
|
||||
files_db = db.get("files", {})
|
||||
for system in config.get("systems", {}).values():
|
||||
for fe in system.get("files", []):
|
||||
name = fe.get("name", "")
|
||||
dest = sanitize_pack_path(fe.get("destination") or name).lower()
|
||||
# A zipped_file entry pins a member of the archive it names: its
|
||||
# md5 and its size, when stated, are not the archive's.
|
||||
by_hash: dict = {}
|
||||
size = None
|
||||
if not fe.get("zipped_file"):
|
||||
md5 = (fe.get("md5") or "").lower()
|
||||
if md5 and "," not in md5:
|
||||
by_hash = files_db.get(by_md5.get(md5, ""), {})
|
||||
size = fe.get("size")
|
||||
if size is None:
|
||||
size = files_db.get(
|
||||
(fe.get("sha1") or "").lower(), by_hash
|
||||
).get("size")
|
||||
if name:
|
||||
declared.add(name)
|
||||
md5 = fe.get("md5", "")
|
||||
if not md5:
|
||||
declared.setdefault(name, []).append((size, dest))
|
||||
if not enrich:
|
||||
continue
|
||||
# Skip multi-hash and zipped_file entries (inner ROM MD5, not file MD5)
|
||||
if "," in md5 or fe.get("zipped_file"):
|
||||
continue
|
||||
sha1 = by_md5.get(md5.lower())
|
||||
if not sha1:
|
||||
continue
|
||||
entry = files_db.get(sha1, {})
|
||||
db_name = entry.get("name", "")
|
||||
if db_name:
|
||||
declared.add(db_name)
|
||||
for alias in entry.get("aliases", []):
|
||||
declared.add(alias)
|
||||
known = by_hash
|
||||
for other in (known.get("name", ""), *known.get("aliases", [])):
|
||||
if other:
|
||||
declared.setdefault(other, []).append((known.get("size"), dest))
|
||||
return declared
|
||||
|
||||
|
||||
def expand_platform_declared_names(config: dict, db: dict) -> set[str]:
|
||||
"""The names a platform declares, enriched as `platform_declarations`."""
|
||||
return set(platform_declarations(config, db))
|
||||
|
||||
|
||||
import re
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ this module builds so the report and the pack withdraw the same files."""
|
||||
from __future__ import annotations
|
||||
|
||||
from common import PROFILE_IDENTITY_FIELDS
|
||||
from common import expand_platform_declared_names
|
||||
from common import expand_platform_declared_names, platform_declarations
|
||||
from common import load_emulator_profiles
|
||||
from common import resolution_is_hash_exact
|
||||
from common import resolve_local_file
|
||||
@@ -494,12 +494,7 @@ def _collect_emulator_extras(
|
||||
|
||||
# Use strict YAML names (no DB alias enrichment) so that files known
|
||||
# under an alias still get packed at the emulator's expected path.
|
||||
yaml_names: set[str] = set()
|
||||
for system in config.get("systems", {}).values():
|
||||
for fe in system.get("files", []):
|
||||
name = fe.get("name", "")
|
||||
if name:
|
||||
yaml_names.add(name)
|
||||
yaml_names = platform_declarations(config, db, enrich=False)
|
||||
|
||||
undeclared = find_undeclared_files(
|
||||
config, emulators_dir, db, emu_profiles, target_cores=target_cores,
|
||||
|
||||
+31
-10
@@ -29,6 +29,7 @@ import json
|
||||
import os
|
||||
import sys
|
||||
import zipfile
|
||||
from collections.abc import Mapping
|
||||
|
||||
import slots
|
||||
|
||||
@@ -43,7 +44,8 @@ from common import (
|
||||
check_inside_zip,
|
||||
compute_hashes,
|
||||
expand_directory_entries,
|
||||
expand_platform_declared_names,
|
||||
name_match_size_ok,
|
||||
platform_declarations,
|
||||
filter_systems_by_target,
|
||||
group_identical_platforms,
|
||||
list_emulator_profiles,
|
||||
@@ -396,7 +398,8 @@ def _candidate_verdict(
|
||||
fname: str,
|
||||
is_standalone: bool,
|
||||
include_all: bool,
|
||||
declared_names: set,
|
||||
declared_names: Mapping[str, list[tuple[int | None, str]]] | set[str],
|
||||
dest: str = "",
|
||||
) -> str:
|
||||
"""Whether a profile entry can be a gap, and whether it is settled.
|
||||
|
||||
@@ -415,9 +418,27 @@ def _candidate_verdict(
|
||||
# A platform declaring any name the core answers to has met the
|
||||
# requirement: quasi88 reads n88sub.rom or disk.rom, and System.dat
|
||||
# names disk.rom. Without the aliases the pack carried the ROM twice.
|
||||
answers = {fname, file_entry.get("archive") or "", *file_entry.get("aliases", [])}
|
||||
if answers & declared_names:
|
||||
# The archive answers by its name. A loose file answers where the
|
||||
# declaration fills the entry's own slot, whose content the slot
|
||||
# arbitration decides, or elsewhere at a size the core accepts:
|
||||
# Galaksija's 4 KiB ROM1.BIN does not stand for DOSBox's 32 KiB
|
||||
# SC-55 one.
|
||||
if file_entry.get("archive") in declared_names:
|
||||
return "settled"
|
||||
slot = sanitize_pack_path(dest).lower()
|
||||
for name in (fname, *file_entry.get("aliases", [])):
|
||||
if name not in declared_names:
|
||||
continue
|
||||
declarations = (
|
||||
declared_names[name]
|
||||
if isinstance(declared_names, Mapping)
|
||||
else [(None, "")]
|
||||
)
|
||||
if any(
|
||||
(slot and where == slot) or name_match_size_ok(file_entry, size)
|
||||
for size, where in declarations
|
||||
):
|
||||
return "settled"
|
||||
return "keep"
|
||||
|
||||
|
||||
@@ -438,16 +459,16 @@ def find_undeclared_files(
|
||||
target_cores: set[str] | None = None,
|
||||
data_names: set[str] | None = None,
|
||||
include_all: bool = False,
|
||||
declared_names: set[str] | None = None,
|
||||
declared_names: Mapping[str, list[tuple[int | None, str]]] | None = None,
|
||||
) -> list[dict]:
|
||||
"""Find files needed by cores but not declared in platform config.
|
||||
|
||||
declared_names overrides the default enriched set from
|
||||
expand_platform_declared_names. Pass a strict set (YAML names only)
|
||||
when building packs so alias-only names still get packed.
|
||||
declared_names overrides the default enriched declarations from
|
||||
platform_declarations. Pass the strict ones (YAML names only) when
|
||||
building packs so alias-only names still get packed.
|
||||
"""
|
||||
if declared_names is None:
|
||||
declared_names = expand_platform_declared_names(config, db)
|
||||
declared_names = platform_declarations(config, db)
|
||||
|
||||
# Whether the builder drops a file whose local copy contradicts its
|
||||
# declared hash, which decides if such a copy counts as held here.
|
||||
@@ -523,7 +544,7 @@ def find_undeclared_files(
|
||||
if not fname or seen_key in seen_files:
|
||||
continue
|
||||
verdict = _candidate_verdict(
|
||||
f, fname, is_standalone, include_all, declared_names
|
||||
f, fname, is_standalone, include_all, declared_names, dest
|
||||
)
|
||||
if verdict == "settled":
|
||||
seen_files.add(seen_key)
|
||||
|
||||
Reference in new issue
Block a user