fix: fill a launcher name listed under another

RetroPie lists KS 1.3 as kick34005.A500, and its fs-uae launcher
passes $biosdir/kick13.rom. The listed copy settles fs-uae's entry by
content, so the launcher layer never offered it, and kick13.rom
reached the pack only as a copy of UAE4All2's amiga/kick13.rom: the
x86 boards, without UAE4All2, lost it. An entry the list settles under
another name now answers the launcher's place by its own name.
This commit is contained in:
Abdessamad Derraz committed 2026-11-10 02:08:16 +01:00
1 parent 3d2629f652
commit 2eca218535
4 files changed
+59 -1

No files matched your search

+19 -1
View File
@@ -955,6 +955,18 @@ class LauncherLayer:
for entry in profiles[key].get("files") or []
])
self._db = db
self._declared_elsewhere: set[tuple[str, bool, str, str]] = set()
def declared_elsewhere(self, emulator: str, standalone: bool, entry: dict) -> None:
"""Record a core entry the platform's list settles under another
name. A launcher opening the entry's own name finds nothing there:
RetroPie lists KS 1.3 as kick34005.A500, and its fs-uae launcher
passes $biosdir/kick13.rom. A name the list holds somewhere is
copied to the launcher's place from that declaration."""
name = str(entry.get("name", ""))
if name.rsplit("/", 1)[-1].lower() in self.declared_names_folded:
return
self._declared_elsewhere.add((emulator, standalone, name, str(entry.get("path") or "")))
@staticmethod
def _profiles_named(name: str, profiles: dict, relevant: set[str]) -> list[str]:
@@ -1074,6 +1086,9 @@ class LauncherLayer:
if "<" not in str(entry.get("name", "")):
report["in_repo"] = resolve_held(report) is not None
report["unoffered"] = True
if (*layout, str(entry.get("name", "")),
str(entry.get("path") or "")) in self._declared_elsewhere:
report["declared_elsewhere"] = True
by_layout.setdefault(layout, []).append((entry, report))
placed: set[str] = set()
for (emulator, standalone), members in sorted(by_layout.items()):
@@ -1081,11 +1096,14 @@ class LauncherLayer:
# An entry the report left out answers only through the
# setting or the extended name: by its own name it was left
# out for a reason (a region variant, a settled duplicate).
# One the platform settles under another name still answers
# by its own: the place reads a name the list does not hold.
matched = [
(candidate, report)
for entry, report in members
if (candidate := self.match(place, entry)) is not None
and (candidate.by_setting_or_stem or not report.get("unoffered"))
and (candidate.by_setting_or_stem or not report.get("unoffered")
or report.get("declared_elsewhere"))
]
# A size names one entry only when no other of the core's
# entries takes that size too.
+3
View File
@@ -627,6 +627,9 @@ def find_undeclared_files(
)
if verdict == "settled":
seen_files.add(seen_key)
if (launcher_layer and not f.get("unsourceable") and not save_root
and f.get("root") not in OUTER_ROOTS and not f.get("unplaced")):
launcher_layer.declared_elsewhere(emu_name, is_standalone, f)
# Answered by the platform's own declaration, which its
# report covers: the set it belongs to is spoken for.
set_records.append((set_keys((emu_name, is_standalone), f), f, True, None))
+8
View File
@@ -984,6 +984,14 @@ class InstallMatchesATargetManifest(unittest.TestCase):
("batocera", "bcm2711"),
("retropie", "rpi4"),
("emudeck", "steamos"),
# A core only Batocera's x86_64 downloader brings, a missing file two
# cores read, a file another emulator installs itself, and a name a
# launcher opens that another core's copy used to fill.
("batocera", "jh7110"),
("knulli", "h700"),
("rocknix", "sdm845"),
("retropie", "x86"),
("retropie", "rpi1"),
)
@classmethod
+29
View File
@@ -233,6 +233,35 @@ class UAE4AllOnRetroPie(unittest.TestCase):
class FsUaeOnRetroPie(unittest.TestCase):
"""RetroPie lists KS 1.3 as kick34005.A500, and its fs-uae launcher
passes $biosdir/kick13.rom (fs-uae.sh:23-26). The listed copy settles
fs-uae's entry by content, and the launcher's name was filled only as
a copy of UAE4All2's amiga/kick13.rom: a board without UAE4All2 lost it."""
def test_the_launcher_name_is_filled_without_another_core(self) -> None:
import json
from common import load_target_config
db_path = REPO_ROOT / "database.json"
if not db_path.exists():
self.skipTest("database.json is not built")
db = json.loads(db_path.read_text(encoding="utf-8"))
cores = load_target_config("retropie", "x86", str(REPO_ROOT / "platforms"))
self.assertNotIn("uae4all", cores)
with redirect_stdout(io.StringIO()):
manifest = generate_manifest(
"retropie", str(REPO_ROOT / "platforms"), db, str(REPO_ROOT / "bios"),
str(REPO_ROOT / "platforms" / "_registry.yml"),
emulators_dir=str(REPO_ROOT / "emulators"), offline=True,
target_cores=set(cores), target_name="x86",
)
held = [(f["dest"], f["cores"]) for f in manifest["files"]
if f["dest"].rsplit("/", 1)[-1] == "kick13.rom"]
self.assertEqual(held, [("kick13.rom", ["fs-uae"])])
PROGRAM = b"PROGRAM BINARY"
PROGRAM_DATA = b"PROGRAM DATA"