feat: honour the boards a folder offers a core on

ROCKNIX writes some add_emu_core calls under a case on DEVICE, and the
rom folders file keeps that branch as the offer's `when` (uae4arm for
amiga, emulators/package.mk:188). A board's core list names such a core
when another folder offers it there, so --target placed its files in a
folder the board does not offer it for.

offer_boards() reads `when` and board_defaults against the platform's
targets, aliases included. A board build (generate_pack and generate
--manifest with --target, verify --target) carries the board in the
config (TARGET_KEY), and content_folder_tags leaves out the pairs the
board is not offered. A manifest for every board lists in
offered_boards each profile placed in such a folder, and install.py
--target drops its files there on the other boards.

Manifests: no file moves. The cores ROCKNIX offers under a device case
read no file beside the game today, so no manifest carries
offered_boards yet.
This commit is contained in:
Abdessamad Derraz committed 2026-11-11 13:46:57 +01:00
1 parent 389ebfa132
commit 05f3f5c702
10 files changed
+383 -41

No files matched your search

+45 -2
View File
@@ -4277,6 +4277,21 @@ def _validate_manifest(data: object, plat: str) -> dict:
_check_tags(mapping, key, field)
if mapping[key] is None:
raise ValueError(f"invalid {field} for {key}")
offered = data.get("offered_boards")
if offered is not None:
if not isinstance(offered, dict) or len(offered) > MAX_MANIFEST_FILES:
raise ValueError("invalid offered_boards")
for key, places in offered.items():
if not isinstance(key, str) or not 0 < len(key) <= 256:
raise ValueError("invalid offered_boards key")
if not isinstance(places, list) or not all(
isinstance(p, dict) and isinstance(p.get("folder"), str)
and p.get("root") in ("roms", "install")
and isinstance(p.get("boards"), list)
and all(isinstance(b, str) for b in p["boards"])
for p in places
):
raise ValueError(f"invalid offered_boards for {key}")
titles = data.get("system_titles")
if titles is not None:
if not isinstance(titles, dict) or len(titles) > MAX_MANIFEST_FILES:
@@ -4883,6 +4898,33 @@ def designated_cores(
return chosen
def offered_here(
manifest: dict, entry: dict, cores: set[str], target: str | None
) -> set[str]:
"""The cores a board reads this file for.
A roms folder can offer a core on some boards only (ROCKNIX writes
uae4arm for roms/amiga under the devices it builds it for): on another
board, that core reads nothing there, even when the board runs it for
another folder. `offered_boards` gives those boards.
"""
offered = manifest.get("offered_boards") or {}
if target is None or not offered:
return cores
tree = {"roms": "roms", None: "install"}.get(entry.get("root"))
if tree is None:
return cores
dest = str(entry.get("dest", ""))
return {
core for core in cores
if not any(
place["root"] == tree and dest.startswith(place["folder"] + "/")
and target not in place["boards"]
for place in offered.get(core) or []
)
}
def narrow_to_cores(
manifest: dict,
names: list[str],
@@ -4919,7 +4961,8 @@ def narrow_to_cores(
result.append(_for_cores(entry, cores) or _settle_slot(entry, cores))
continue
if entry.get("cores") is not None:
copy = _for_cores(entry, cores)
here = offered_here(manifest, entry, cores, target)
copy = _for_cores(entry, here)
if copy is not None:
result.append(copy)
continue
@@ -4927,7 +4970,7 @@ def narrow_to_cores(
# wants the slot and has nothing held is a file left out.
claim = next(
(c for c in entry.get("absent_claims", [])
if cores.intersection(c.get("cores") or [])),
if here.intersection(c.get("cores") or [])),
None,
)
if claim is not None: