mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-11 12:33:53 -06:00
feat: record every core a rom folder offers
The rom folders files kept the default core of each folder only, so a core the user picks in the frontend's menu was not known to read its sets there. Each folder now lists its offers: every <core> of every <emulator> in AmberELEC's es_systems.cfg (arcade: packages/ui/emulationstation/config/es_systems.cfg:116-150), every core of EmuELEC's es_systems.json, every add_emu_core of ROCKNIX's emulators package.mk with the device case it is written under (arcade: 218-229), and the emulator of every ES-DE <command>. A core written under every branch of a case is offered unconditionally. The ES-XML, ES-JSON, ELEC-conf and ES-DE setups are rescraped at their pins: archr, arkos, chaos, darkos, jelos, kolos, pan4elec, retrobat and unofficialos gain the same records and nothing else. A libretro core offered for a folder reads the sets it loads beside the game there, as the default one does. Manifests, roms folders gaining sets read beside the game: - AmberELEC: +215 (293.1 MB) in fbneo, arcade and mame, for MAME 2010, 2015, 2016 and 2003-Plus. 1 omission leaves. - EmuELEC: +291 (232.0 MB) in cps1, cps2, cps3, mame, fbneo and arcade, for MAME 2010 and 2003-Plus. 47 omissions leave. - ROCKNIX: +501 (538.7 MB) in fbneo, neogeo, cps1, cps2, cps3, arcade and mame, for MAME 2010, 2015 and 2003-Plus. 120 omissions leave. - EmuDeck: +306 (251.2 MB) in cps, cps1, cps2, cps3, arcade and mame, for MAME 2000, 2003, 2003-Plus and 2010. 92 omissions leave. Each omission that leaves was a set named as read outside the BIOS folder. RetroDECK and the other platforms do not change. No file leaves, and verify --all keeps its severity and status counts.
This commit is contained in:
1 parent
01a422f86c
commit
801af113b7
21 files changed
+19272
-6
No files matched your search
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -120,6 +120,21 @@
|
||||
"type": "object",
|
||||
"additionalProperties": { "$ref": "#/$defs/board_default" }
|
||||
},
|
||||
"offers": {
|
||||
"type": "array",
|
||||
"description": "Every emulator and core the frontend can start for this folder, the default included, in the list's order. Absent when the default is the only one.",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["core"],
|
||||
"properties": {
|
||||
"emulator": { "type": "string", "minLength": 1 },
|
||||
"core": { "type": "string", "minLength": 1 },
|
||||
"when": { "type": "string", "minLength": 1, "description": "The build condition the offer is written under (a device case)." },
|
||||
"source_ref": { "$ref": "#/$defs/ref" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"arcade": { "type": "boolean" },
|
||||
"searched_by": {
|
||||
"type": "array",
|
||||
|
||||
+7
-5
@@ -3419,10 +3419,11 @@ def content_folder_tags(config: dict) -> set[str]:
|
||||
A core reading its BIOS sets beside the game (MAME 2003's osd_get_path
|
||||
gives FILETYPE_ROM the content directory) reads them in the folder of
|
||||
the system it is launched for. The folders come from the platform's own
|
||||
lists: ``libretro_systems``, the default core of each folder its rom
|
||||
folders file records, the cores a system offers under its own name, and
|
||||
the systems a RetroPie package registers. A platform with no roms root,
|
||||
or one keeping no folder per system (RetroArch, Lakka), gives none.
|
||||
lists: ``libretro_systems``, every core a folder of its rom folders file
|
||||
offers (its default, its board defaults, ``offers``), the cores a system
|
||||
offers under its own name, and the systems a RetroPie package registers.
|
||||
A platform with no roms root, or one keeping no folder per system
|
||||
(RetroArch, Lakka), gives none.
|
||||
"""
|
||||
if config.get("inherits_from") == "emulatorjs":
|
||||
# EmulatorJS writes the BIOS a frontend hands it and the game to
|
||||
@@ -3438,7 +3439,8 @@ def content_folder_tags(config: dict) -> set[str]:
|
||||
for core, systems in (config.get("libretro_systems") or {}).items():
|
||||
pairs |= {(_core_key(core), str(s)) for s in systems or []}
|
||||
for row in rows:
|
||||
for choice in [row, *(row.get("board_defaults") or {}).values()]:
|
||||
choices = [row, *(row.get("board_defaults") or {}).values(), *(row.get("offers") or [])]
|
||||
for choice in choices:
|
||||
if choice.get("core") and choice.get("emulator") in LIBRETRO_RUNNERS:
|
||||
pairs.add((_core_key(choice["core"]), str(row["folder"])))
|
||||
for system in (config.get("systems") or {}).values():
|
||||
|
||||
@@ -36,7 +36,7 @@ SETUP_KEYS = (
|
||||
)
|
||||
ROW_KEYS = (
|
||||
"folder", "name", "aliases", "extensions", "files", "core", "emulator", "source_ref",
|
||||
"board_defaults", "arcade", "searched_by", "note",
|
||||
"board_defaults", "offers", "arcade", "searched_by", "note",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -46,6 +46,44 @@ def _board(core: str | None, emulator: str | None, source_ref: str) -> dict:
|
||||
return {k: str(v) for k, v in out.items() if v}
|
||||
|
||||
|
||||
def _offers(choices: list[dict], core: str | None, emulator: str | None) -> list[dict]:
|
||||
"""Every (emulator, core) a folder can start, in the list's order, or
|
||||
none when the default is the only one.
|
||||
|
||||
A core written under several branches of a device case keeps the
|
||||
conditions it is written under, and none once a branch is the `*)` one
|
||||
beside another or no branch at all: every build then offers it.
|
||||
"""
|
||||
found: dict[tuple[str, str], dict] = {}
|
||||
whens: dict[tuple[str, str], set[str]] = {}
|
||||
lines: dict[tuple[str, str], list[str]] = {}
|
||||
for choice in choices:
|
||||
pair = (str(choice.get("emulator") or ""), str(choice.get("core") or choice.get("emulator") or ""))
|
||||
if not pair[1]:
|
||||
continue
|
||||
whens.setdefault(pair, set()).add(str(choice.get("when") or ""))
|
||||
ref = str(choice.get("source_ref") or "")
|
||||
if ref and pair in found and found[pair].get("source_ref", "").split(":")[0] == ref.split(":")[0]:
|
||||
lines[pair].append(ref.rsplit(":", 1)[-1])
|
||||
if pair not in found:
|
||||
found[pair] = {k: str(v) for k, v in {**choice, "core": pair[1]}.items()
|
||||
if v and k != "when"}
|
||||
lines[pair] = []
|
||||
out = []
|
||||
for pair, offer in found.items():
|
||||
if lines[pair] and offer.get("source_ref"):
|
||||
offer["source_ref"] = ",".join([offer["source_ref"], *lines[pair]])
|
||||
conditions = whens[pair]
|
||||
if "" not in conditions and not (
|
||||
len(conditions) > 1 and any(w.endswith(" *") for w in conditions)):
|
||||
offer = {**offer, "when": "; ".join(sorted(conditions))}
|
||||
offer = {k: offer[k] for k in ("emulator", "core", "when", "source_ref") if k in offer}
|
||||
out.append(offer)
|
||||
if [(o.get("emulator", ""), o["core"]) for o in out] == [(emulator or "", core or emulator or "")]:
|
||||
return []
|
||||
return out
|
||||
|
||||
|
||||
def _top_key_lines(text: str) -> dict[str, int]:
|
||||
"""First line of each top-level YAML key."""
|
||||
out: dict[str, int] = {}
|
||||
@@ -286,6 +324,9 @@ def _xml_rows(text: str, key: str, path: str, params: dict) -> list[dict]:
|
||||
"core": core or emulator,
|
||||
"emulator": emulator,
|
||||
"source_ref": _ref(key, path, row["line"]),
|
||||
"offers": _offers(
|
||||
[{"emulator": e, "core": c} for e, cores in emulators for c, _d in cores or [(e, False)]],
|
||||
core, emulator),
|
||||
"arcade": _is_arcade(system.findtext("hardware"), platform),
|
||||
"note": row.get("note"),
|
||||
})
|
||||
@@ -322,6 +363,9 @@ def es_json(spec: dict, fetcher: Fetcher) -> list[dict]:
|
||||
"core": core or emulator,
|
||||
"emulator": emulator,
|
||||
"source_ref": _ref(key, path, row["line"]),
|
||||
"offers": _offers(
|
||||
[{"emulator": e, "core": c} for e, cores in emulators for c, _d in cores or [(e, False)]],
|
||||
core, emulator),
|
||||
"arcade": _is_arcade(system.get("hardware"), system.get("platform")),
|
||||
"note": row.get("note"),
|
||||
})
|
||||
@@ -470,6 +514,12 @@ def elec_conf(spec: dict, fetcher: Fetcher) -> list[dict]:
|
||||
"emulator": pick["emulator"] if pick else None,
|
||||
"source_ref": "; ".join(refs),
|
||||
"board_defaults": board_defaults,
|
||||
"offers": _offers([
|
||||
{"emulator": c["emulator"], "core": c["core"],
|
||||
"when": ", ".join(f"{v} {p}".strip() for v, p in c["branches"]),
|
||||
"source_ref": _ref(key, package, c["line"])}
|
||||
for c in mine
|
||||
], pick and pick["core"], pick and pick["emulator"]),
|
||||
"arcade": values.get("SYSTEM_HARDWARE") == "arcade" or None,
|
||||
"note": note,
|
||||
})
|
||||
@@ -647,6 +697,7 @@ def esde(spec: dict, fetcher: Fetcher) -> list[dict]:
|
||||
"folder": folder, "extensions": extensions, "commands": commands,
|
||||
"ref": _ref(key, path, lines.get(name)),
|
||||
"searched_by": _searched_by(placed, key, path),
|
||||
"placed": [(*c, key, path) for c in placed],
|
||||
}
|
||||
alternatives: dict[str, tuple[str, int]] = {}
|
||||
alt = params.get("alternatives")
|
||||
@@ -669,6 +720,11 @@ def esde(spec: dict, fetcher: Fetcher) -> list[dict]:
|
||||
else:
|
||||
note = f"alternative emulator '{label}' names no command of this system"
|
||||
emulator, core = _command_core(chosen[1])
|
||||
offers = _offers([
|
||||
{"emulator": e, "core": c, "source_ref": _ref(key_, path_, line)}
|
||||
for _label, command, line, key_, path_ in system["placed"]
|
||||
for e, c in [_command_core(command)] if e
|
||||
], core, emulator)
|
||||
extensions = system["extensions"]
|
||||
if "." in extensions:
|
||||
# ES-DE's "." is a file without an extension (SystemData.cpp
|
||||
@@ -683,6 +739,7 @@ def esde(spec: dict, fetcher: Fetcher) -> list[dict]:
|
||||
"emulator": emulator,
|
||||
"source_ref": "; ".join(refs),
|
||||
"note": note,
|
||||
"offers": offers,
|
||||
"searched_by": system["searched_by"] or None,
|
||||
})
|
||||
return rows
|
||||
|
||||
@@ -1187,6 +1187,27 @@ class StandaloneMameOnEmuDeck(unittest.TestCase):
|
||||
self.assertIn(("roms", "adam/adam.zip"), places)
|
||||
|
||||
|
||||
class EveryCoreAFolderOffers(unittest.TestCase):
|
||||
"""AmberELEC's es_systems.cfg, EmuELEC's es_systems.json and ROCKNIX's
|
||||
add_emu_core calls offer several cores per folder, the user's choice
|
||||
starting any of them."""
|
||||
|
||||
def test_the_rom_folders_files_record_the_offers(self) -> None:
|
||||
for setup, folder, core in (("amberelec", "arcade", "mame2010"),
|
||||
("emuelec", "arcade", "mame2010"),
|
||||
("rocknix", "arcade", "mame2010")):
|
||||
with (REPO_ROOT / "platforms" / "romfolders" / f"{setup}.yml").open() as f:
|
||||
rows = {r["folder"]: r for r in yaml.safe_load(f)["folders"]}
|
||||
self.assertIn(core, {o["core"] for o in rows[folder].get("offers") or []}, setup)
|
||||
|
||||
def test_an_offered_core_reads_its_sets_in_that_folder(self) -> None:
|
||||
for platform in ("amberelec", "rocknix"):
|
||||
view = libretro_view("mame2010", platform)
|
||||
folders = {e["path"].split("/")[0] for e in view["files"]
|
||||
if e.get("category") == "bios_zip" and e.get("layout_path")}
|
||||
self.assertIn("arcade", folders, platform)
|
||||
|
||||
|
||||
def platform_manifest(platform: str) -> dict:
|
||||
from common import load_database
|
||||
|
||||
@@ -1214,6 +1235,16 @@ class ArchiveSharedByOfferedCores(unittest.TestCase):
|
||||
others = {c for a in entry.get("alternatives", []) for c in a["cores"]}
|
||||
self.assertIn("advancemame", others)
|
||||
|
||||
def test_offered_cores_join_the_default_one(self) -> None:
|
||||
# AmberELEC's arcade folder starts MAME 2003-Plus and offers MAME
|
||||
# 2000, 2010, 2015 and 2016 (es_systems.cfg:116-150).
|
||||
(entry,) = [f for f in platform_manifest("amberelec")["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.assertLessEqual({"mame2000", "mame2010", "mame2015", "mame2016"}, 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
|
||||
|
||||
@@ -133,6 +133,13 @@ class EsXmlFormat(unittest.TestCase):
|
||||
self.assertEqual(rows["psx"]["board_defaults"]["rk3326"]["core"], "duckstation")
|
||||
self.assertNotIn("board_defaults", rows["mame"])
|
||||
|
||||
def test_every_core_the_system_lists_is_offered(self) -> None:
|
||||
self.assertEqual(self.rows["psx"]["offers"], [
|
||||
{"emulator": "retroarch", "core": "pcsx_rearmed"},
|
||||
{"emulator": "retroarch", "core": "swanstation"}])
|
||||
# A default alone is no choice.
|
||||
self.assertNotIn("offers", self.rows["mame"])
|
||||
|
||||
def test_source_ref_points_at_the_name_line(self) -> None:
|
||||
self.assertEqual(self.rows["psx"]["source_ref"], "os es_systems.cfg:4")
|
||||
self.assertEqual(self.rows["advision"]["source_ref"], "os es_systems.cfg:20")
|
||||
@@ -173,6 +180,11 @@ class ElecConfFormat(unittest.TestCase):
|
||||
"DEVICE=RK3326": {"core": "pcsx_rearmed", "emulator": "retroarch",
|
||||
"source_ref": "os package.mk:11"}})
|
||||
|
||||
def test_a_core_under_every_branch_is_offered_unconditionally(self) -> None:
|
||||
self.assertEqual(self.rows["psx"]["offers"], [
|
||||
{"emulator": "retroarch", "core": "pcsx_rearmed", "source_ref": "os package.mk:11,16"},
|
||||
{"emulator": "retroarch", "core": "swanstation", "source_ref": "os package.mk:12,15"}])
|
||||
|
||||
def test_unconditional_default(self) -> None:
|
||||
self.assertEqual(self.rows["3do"]["core"], "opera")
|
||||
self.assertEqual(self.rows["3do"]["source_ref"],
|
||||
@@ -221,6 +233,11 @@ class EsdeFormat(unittest.TestCase):
|
||||
self.assertTrue(libretro["any_machine"])
|
||||
self.assertEqual(libretro["source_ref"], "os es_systems.xml:27")
|
||||
|
||||
def test_each_command_offers_its_emulator(self) -> None:
|
||||
self.assertEqual(self.rows["arcade"]["offers"], [
|
||||
{"emulator": "retroarch", "core": "mame", "source_ref": "os es_systems.xml:27"},
|
||||
{"emulator": "mame", "core": "mame", "source_ref": "os es_systems.xml:28"}])
|
||||
|
||||
def test_first_command_without_alternative(self) -> None:
|
||||
rows = by_folder(scrape("esde", "esde", {"systems": {"source": "os", "path": "es_systems.xml"}}))
|
||||
self.assertEqual((rows["psx"]["core"], rows["psx"]["emulator"]), ("mednafen_psx", "retroarch"))
|
||||
|
||||
Reference in new issue
Block a user