mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-17 08:53:28 -05:00
fix: let target installs match their target pack
This commit is contained in:
1 parent
0a5e15789f
commit
e659228777
12 files changed
+1326
-70
No files matched your search
+1
-1
@@ -8,7 +8,7 @@ param(
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$defaultInstallUrl = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
|
||||
$defaultInstallSha256 = "e0d8fad4e7e55afa57c412e98726992d82b92286b752c9a8cce0978f0a355127"
|
||||
$defaultInstallSha256 = "953240dab33c25e52005155ecc8cd694269234697ca2c3f55939994fc6b61951"
|
||||
$maximumInstallerBytes = 2MB
|
||||
$installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null }
|
||||
$temporary = $null
|
||||
|
||||
+81
-7
@@ -1154,6 +1154,47 @@ def _check_download(entry: dict, where: str, dest: str) -> int:
|
||||
return size
|
||||
|
||||
|
||||
def _check_slot(entry: dict, where: str, dest: str) -> int:
|
||||
"""The copies a platform file takes as cores and systems drop; the largest size."""
|
||||
choices = entry.get("slot_choices", [])
|
||||
declared = entry.get("declared_choices")
|
||||
if declared is None and not choices:
|
||||
return 0
|
||||
if (
|
||||
entry.get("cores") is not None
|
||||
or not isinstance(choices, list)
|
||||
or not isinstance(declared, list)
|
||||
or not declared
|
||||
or len(choices) > MAX_SELECTION_TAGS
|
||||
or len(declared) > MAX_SELECTION_TAGS
|
||||
):
|
||||
raise ValueError(f"invalid slot choices for {dest}")
|
||||
size = 0
|
||||
for index, choice in enumerate(declared):
|
||||
systems = choice.get("systems") if isinstance(choice, dict) else None
|
||||
if (
|
||||
not isinstance(systems, list)
|
||||
or not systems
|
||||
or not all(isinstance(s, str) for s in systems)
|
||||
or not set(choice) <= (set(_COPY_FIELDS) - {"cores"}) | {"systems"}
|
||||
):
|
||||
raise ValueError(f"invalid declared choice for {dest}")
|
||||
size = max(size, _check_download(choice, f"{where}.declared_choices[{index}]", dest))
|
||||
for index, choice in enumerate(choices):
|
||||
if (
|
||||
not isinstance(choice, dict)
|
||||
or not choice.get("cores")
|
||||
or not set(choice) <= set(_COPY_FIELDS)
|
||||
):
|
||||
raise ValueError(f"invalid slot choice for {dest}")
|
||||
# A choice naming only cores leaves the platform's own answer.
|
||||
if set(choice) != {"cores"}:
|
||||
size = max(size, _check_download(
|
||||
choice, f"{where}.slot_choices[{index}]", dest
|
||||
))
|
||||
return size
|
||||
|
||||
|
||||
def _validate_manifest(data: object, plat: str) -> dict:
|
||||
"""Validate the untrusted install-manifest boundary using stdlib only."""
|
||||
if not isinstance(data, dict):
|
||||
@@ -1192,6 +1233,7 @@ def _validate_manifest(data: object, plat: str) -> dict:
|
||||
raise ValueError(f"invalid alternative for {dest}")
|
||||
# Narrowing can swap in any copy: the cap holds for the largest.
|
||||
size = max(size, _check_download(alternative, where, dest))
|
||||
size = max(size, _check_slot(entry, f"files[{index}]", dest))
|
||||
largest_total += size
|
||||
if largest_total > MAX_TOTAL_DOWNLOAD_SIZE:
|
||||
raise ValueError("manifest total size exceeds safety limit")
|
||||
@@ -1397,13 +1439,44 @@ def _for_cores(entry: dict, chosen: set[str]) -> dict | None:
|
||||
if chosen.intersection(alternative["cores"]):
|
||||
kept = {
|
||||
k: v for k, v in entry.items()
|
||||
if k not in _COPY_FIELDS and k != "alternatives"
|
||||
if k not in _COPY_FIELDS
|
||||
and k not in ("alternatives", "slot_choices", "declared_choices")
|
||||
}
|
||||
kept.update((k, alternative[k]) for k in _COPY_FIELDS if k in alternative)
|
||||
return kept
|
||||
return None
|
||||
|
||||
|
||||
def _settle_slot(entry: dict, chosen: set[str], kept_system=None) -> dict:
|
||||
"""A platform file as a pack built for the chosen cores carries it.
|
||||
|
||||
Where a core's own source contradicts the platform and the frontend only
|
||||
checks the path, the pack serves the core's copy: the first of
|
||||
`slot_choices` naming a chosen core decides. Otherwise the platform
|
||||
answers, through the first of `declared_choices` whose systems remain.
|
||||
"""
|
||||
declared = entry.get("declared_choices")
|
||||
if declared is None:
|
||||
return entry
|
||||
copy = next(
|
||||
(c for c in entry.get("slot_choices", []) if chosen.intersection(c["cores"])),
|
||||
None,
|
||||
)
|
||||
if copy is None or "sha1" not in copy:
|
||||
copy = next(
|
||||
(c for c in declared
|
||||
if kept_system is None or any(kept_system(s) for s in c["systems"])),
|
||||
declared[0],
|
||||
)
|
||||
kept = {
|
||||
k: v for k, v in entry.items()
|
||||
if k not in _COPY_FIELDS and k not in ("slot_choices", "declared_choices")
|
||||
}
|
||||
kept.update((k, copy[k]) for k in _COPY_FIELDS if k in copy and k != "cores")
|
||||
kept["cores"] = None
|
||||
return kept
|
||||
|
||||
|
||||
def _filter_by_target(
|
||||
files: list[dict], target_cores: list[str]
|
||||
) -> list[dict]:
|
||||
@@ -1412,7 +1485,7 @@ def _filter_by_target(
|
||||
result: list[dict] = []
|
||||
for f in files:
|
||||
if f.get("cores") is None:
|
||||
result.append(f)
|
||||
result.append(_settle_slot(f, chosen))
|
||||
continue
|
||||
kept = _for_cores(f, chosen)
|
||||
if kept is not None:
|
||||
@@ -1453,6 +1526,9 @@ def narrow_to_cores(
|
||||
cores = designated_cores(manifest, names)
|
||||
reach = manifest.get("system_cores") or {}
|
||||
|
||||
def kept_system(system: str) -> bool:
|
||||
return system not in reach or bool(cores.intersection(reach[system]))
|
||||
|
||||
def kept(entries: list[dict]) -> list[dict]:
|
||||
result: list[dict] = []
|
||||
for entry in entries:
|
||||
@@ -1464,10 +1540,8 @@ def narrow_to_cores(
|
||||
# The systems declaring the file, where they are fewer than the
|
||||
# ones reading it.
|
||||
systems = entry.get("declared_in") or entry_systems(entry)
|
||||
if not systems or any(
|
||||
s not in reach or cores.intersection(reach[s]) for s in systems
|
||||
):
|
||||
result.append(entry)
|
||||
if not systems or any(kept_system(s) for s in systems):
|
||||
result.append(_settle_slot(entry, cores, kept_system))
|
||||
continue
|
||||
# The system is dropped, which frees its destination for the
|
||||
# first named core claiming it, as in a pack built for them.
|
||||
@@ -1622,7 +1696,7 @@ def available_choices(files: list[dict], omitted: list[dict]) -> dict[str, list[
|
||||
ordered.append(OTHER_SYSTEMS)
|
||||
cores = {
|
||||
c for f in files
|
||||
for copy in [f, *f.get("alternatives", [])]
|
||||
for copy in [f, *f.get("alternatives", []), *f.get("slot_choices", [])]
|
||||
for c in (copy.get("cores") or [])
|
||||
}
|
||||
regions = {
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ esac
|
||||
TEMP_INSTALLER=""
|
||||
TEMP_DIRECTORY=""
|
||||
DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
|
||||
DEFAULT_INSTALL_SHA256="e0d8fad4e7e55afa57c412e98726992d82b92286b752c9a8cce0978f0a355127"
|
||||
DEFAULT_INSTALL_SHA256="953240dab33c25e52005155ecc8cd694269234697ca2c3f55939994fc6b61951"
|
||||
MAX_INSTALLER_BYTES=2097152
|
||||
|
||||
cleanup() {
|
||||
|
||||
@@ -1536,6 +1536,28 @@
|
||||
"systems": [
|
||||
"arcade"
|
||||
],
|
||||
"slot_choices": [
|
||||
{
|
||||
"sha1": "9f84f563df14ea1a1bcaaf05b720c3f5545e5a83",
|
||||
"sha256": "13d503ea6c7e7b9a2249837cf915bff6df862b7035f5c99b18240088ec10854a",
|
||||
"size": 3556,
|
||||
"repo_path": "bios/Arcade/Arcade/.variants/nmk004.zip.f70b6562",
|
||||
"cores": [
|
||||
"fbneo"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_choices": [
|
||||
{
|
||||
"sha1": "489256f5e2001070d2ad94c90d255282c71ed274",
|
||||
"sha256": "6ca3142ebe127f5d2cb755c53740a92d9bf1574bde8daa32642de2991af89aaa",
|
||||
"size": 3556,
|
||||
"repo_path": "bios/Arcade/Arcade/nmk004.zip",
|
||||
"systems": [
|
||||
"arcade"
|
||||
]
|
||||
}
|
||||
],
|
||||
"alternatives": [
|
||||
{
|
||||
"sha1": "9f84f563df14ea1a1bcaaf05b720c3f5545e5a83",
|
||||
@@ -1850,6 +1872,29 @@
|
||||
"arcade",
|
||||
"snk-neogeo-cd"
|
||||
],
|
||||
"slot_choices": [
|
||||
{
|
||||
"sha1": "985d935c06fe278ce39d7690aa6c53df88954cd6",
|
||||
"sha256": "4919ba2a7650b24d1bf2c9107c89a80d93b50703a0a371fa5113c791bd03e114",
|
||||
"size": 1184219,
|
||||
"repo_path": "bios/Arcade/Arcade/.variants/neocdz.zip.ba20a7be",
|
||||
"cores": [
|
||||
"fbneo",
|
||||
"fbneo_neogeo"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_choices": [
|
||||
{
|
||||
"sha1": "4f28af31ca0defdd73d80edec2fa296908e624dc",
|
||||
"sha256": "6aa507a5dc33c45d2592bf2bc77eb3e497762221ddd9225f18583a5c55d38667",
|
||||
"size": 214681,
|
||||
"repo_path": "bios/Arcade/Arcade/.variants/neocdz.zip.4f28af31",
|
||||
"systems": [
|
||||
"arcade"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_in": [
|
||||
"arcade"
|
||||
],
|
||||
@@ -2044,6 +2089,28 @@
|
||||
"atari-400-800",
|
||||
"atari-5200"
|
||||
],
|
||||
"slot_choices": [
|
||||
{
|
||||
"sha1": "db1031585968cfc6ec2ecda5c9a5a52f61444a3b",
|
||||
"sha256": "63973d5e86b1ae5b6fca5b119a7f7589e817e19acc3ca54e54462c9dfb619e73",
|
||||
"size": 10240,
|
||||
"repo_path": "bios/Atari/400-800/ROM_OS_B_NTSC",
|
||||
"cores": [
|
||||
"atari800"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_choices": [
|
||||
{
|
||||
"sha1": "f1f0741b1d34fb4350cf7cb8ab3b6ea11cdd8174",
|
||||
"sha256": "bc6012f8ca2f7dde21ae895899b1d2af572ffaa28f819b84911ddb6a0bd8a5c7",
|
||||
"size": 10240,
|
||||
"repo_path": "bios/Atari/400-800/ATARIOSB.ROM",
|
||||
"systems": [
|
||||
"atari-400-800"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_in": [
|
||||
"atari-400-800"
|
||||
],
|
||||
@@ -5224,6 +5291,38 @@
|
||||
"nintendo-ds",
|
||||
"nintendo-dsi"
|
||||
],
|
||||
"slot_choices": [
|
||||
{
|
||||
"sha1": "c7c7570bfe51c3c7c5da3b01331b94e7e7cb4f53",
|
||||
"sha256": "12a03016ba07cfd874fc002df49a2c6eb2414eb7cfa395c2043035ec5e9193e8",
|
||||
"size": 65536,
|
||||
"repo_path": "bios/Nintendo/DS/NDS_Bios7i.bin",
|
||||
"cores": [
|
||||
"melonds",
|
||||
"melonds_ds"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_choices": [
|
||||
{
|
||||
"sha1": "a3aa751eb6bdaaf8a827ba9e03576a6f1ab0f547",
|
||||
"sha256": "2946281e730e71f7cafdb125f5cb60fed944ca5d610ee1e082c441b602b5f4e2",
|
||||
"size": 65536,
|
||||
"repo_path": "bios/Nintendo/Nintendo DS/biosdsi7.bin",
|
||||
"systems": [
|
||||
"nintendo-dsi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"sha1": "d2b17224e156ee5e55b5d8d9f323b1c9b86dd951",
|
||||
"sha256": "28a13952901b4065ef3f08c3252b49a00b6525e301b7ddaa3a390b4c7cbef901",
|
||||
"size": 65536,
|
||||
"repo_path": "bios/Nintendo/DS/dsi_bios7.bin",
|
||||
"systems": [
|
||||
"nintendo-ds"
|
||||
]
|
||||
}
|
||||
],
|
||||
"alternatives": [
|
||||
{
|
||||
"sha1": "c7c7570bfe51c3c7c5da3b01331b94e7e7cb4f53",
|
||||
@@ -5248,6 +5347,38 @@
|
||||
"nintendo-ds",
|
||||
"nintendo-dsi"
|
||||
],
|
||||
"slot_choices": [
|
||||
{
|
||||
"sha1": "719b9eef33692406d7170ff526069615759c4dfc",
|
||||
"sha256": "6e9f70b51dc4f26289b597f14e2513620c2af964374a95f4c5a62b5cad199afc",
|
||||
"size": 65536,
|
||||
"repo_path": "bios/Nintendo/DS/NDS_Bios9i.bin",
|
||||
"cores": [
|
||||
"melonds",
|
||||
"melonds_ds"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_choices": [
|
||||
{
|
||||
"sha1": "7bf549b8be9e48ab0cdc9b0fdadd49a5131f97eb",
|
||||
"sha256": "47538922a8e8a8e79b922ff1203863ef5c40d9c54656a8d2c89c56ece52029ce",
|
||||
"size": 65536,
|
||||
"repo_path": "bios/Nintendo/Nintendo DS/biosdsi9.bin",
|
||||
"systems": [
|
||||
"nintendo-dsi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"sha1": "db61fa39ddbc5f5ed71fb19cda47609ef0201723",
|
||||
"sha256": "664c608bd6ccfe583e87b3b924afa00f8697dc7ca92eeb6d206d68abb03d2e10",
|
||||
"size": 65536,
|
||||
"repo_path": "bios/Nintendo/DS/dsi_bios9.bin",
|
||||
"systems": [
|
||||
"nintendo-ds"
|
||||
]
|
||||
}
|
||||
],
|
||||
"alternatives": [
|
||||
{
|
||||
"sha1": "719b9eef33692406d7170ff526069615759c4dfc",
|
||||
@@ -9539,6 +9670,38 @@
|
||||
"systems": [
|
||||
"snk-neogeo-cd"
|
||||
],
|
||||
"slot_choices": [
|
||||
{
|
||||
"sha1": "712816a3c41baf4129e23d645305a536e03d69de",
|
||||
"sha256": "f885f2cc79b5c0120ca316b0db360183c2b9a60bc727cbe211b7518e12bd5548",
|
||||
"size": 955352,
|
||||
"repo_path": "bios/SNK/Neo Geo CD/.variants/neocdz.zip.712816a3",
|
||||
"cores": [
|
||||
"geolith"
|
||||
]
|
||||
},
|
||||
{
|
||||
"sha1": "985d935c06fe278ce39d7690aa6c53df88954cd6",
|
||||
"sha256": "4919ba2a7650b24d1bf2c9107c89a80d93b50703a0a371fa5113c791bd03e114",
|
||||
"size": 1184219,
|
||||
"repo_path": "bios/Arcade/Arcade/.variants/neocdz.zip.ba20a7be",
|
||||
"cores": [
|
||||
"fbneo",
|
||||
"fbneo_neogeo"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_choices": [
|
||||
{
|
||||
"sha1": "9cab31cfe7eacb6871d36cee105d87b90fd85b64",
|
||||
"sha256": "d828241905d390c5de4867646296f615c95bca2f5d8f7e81d50e08717373a821",
|
||||
"size": 1181333,
|
||||
"repo_path": "bios/Arcade/Arcade/neocdz.zip",
|
||||
"systems": [
|
||||
"snk-neogeo-cd"
|
||||
]
|
||||
}
|
||||
],
|
||||
"alternatives": [
|
||||
{
|
||||
"sha1": "712816a3c41baf4129e23d645305a536e03d69de",
|
||||
@@ -9831,6 +9994,29 @@
|
||||
"region_system_groups": [
|
||||
"sony-playstation"
|
||||
],
|
||||
"slot_choices": [
|
||||
{
|
||||
"sha1": "ffa7f9a7fb19d773a0c3985a541c8e5623d2c30d",
|
||||
"sha256": "0c8359870cbac0ea091f1c87f188cd332dcc709753b91cafd9fd44a4a6188197",
|
||||
"size": 524288,
|
||||
"repo_path": "bios/Sony/PlayStation/scph5000.bin",
|
||||
"cores": [
|
||||
"duckstation",
|
||||
"rustation"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_choices": [
|
||||
{
|
||||
"sha1": "ffa7f9a7fb19d773a0c3985a541c8e5623d2c30d",
|
||||
"sha256": "0c8359870cbac0ea091f1c87f188cd332dcc709753b91cafd9fd44a4a6188197",
|
||||
"size": 524288,
|
||||
"repo_path": "bios/Sony/PlayStation/scph5000.bin",
|
||||
"systems": [
|
||||
"sony-playstation"
|
||||
]
|
||||
}
|
||||
],
|
||||
"alternatives": [
|
||||
{
|
||||
"sha1": "ffa7f9a7fb19d773a0c3985a541c8e5623d2c30d",
|
||||
@@ -11675,6 +11861,46 @@
|
||||
"systems": [
|
||||
"sony-playstation-2"
|
||||
],
|
||||
"slot_choices": [
|
||||
{
|
||||
"sha1": "72bfdf91fa11a86e2810a7c167cdcf11e20418a8",
|
||||
"sha256": "61c77b789ccde856096f024e4a3eef6891e15ca59c127e0dd13e762a7d338a7e",
|
||||
"size": 2690355,
|
||||
"repo_path": "bios/Other/armsx2/pcsx2/resources/GameIndex.yaml",
|
||||
"cores": [
|
||||
"armsx2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"sha1": "7efb2ed721cf0c0f0677324e09be311a8337e793",
|
||||
"sha256": "3bf3c54d6f16d88d3669be698179bb8c2fb981ee5ce71bcb952719429a838cc1",
|
||||
"size": 2693579,
|
||||
"repo_path": "bios/Other/yaps2/GameIndex.yaml",
|
||||
"cores": [
|
||||
"yaps2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"sha1": "72bfdf91fa11a86e2810a7c167cdcf11e20418a8",
|
||||
"sha256": "61c77b789ccde856096f024e4a3eef6891e15ca59c127e0dd13e762a7d338a7e",
|
||||
"size": 2690355,
|
||||
"repo_path": "bios/Other/armsx2/pcsx2/resources/GameIndex.yaml",
|
||||
"cores": [
|
||||
"lrps2"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_choices": [
|
||||
{
|
||||
"sha1": "57369df9ca82a460e596cea7835c5a0797dee668",
|
||||
"sha256": "efa94cb7b9208dc6812bb9407dc986560e523cce57f511eff30942f76b7288bf",
|
||||
"size": 1737799,
|
||||
"repo_path": "bios/Sony/PlayStation 2/.variants/GameIndex.yaml.57369df9",
|
||||
"systems": [
|
||||
"sony-playstation-2"
|
||||
]
|
||||
}
|
||||
],
|
||||
"alternatives": [
|
||||
{
|
||||
"sha1": "72bfdf91fa11a86e2810a7c167cdcf11e20418a8",
|
||||
|
||||
@@ -1775,6 +1775,28 @@
|
||||
"systems": [
|
||||
"arcade"
|
||||
],
|
||||
"slot_choices": [
|
||||
{
|
||||
"sha1": "9f84f563df14ea1a1bcaaf05b720c3f5545e5a83",
|
||||
"sha256": "13d503ea6c7e7b9a2249837cf915bff6df862b7035f5c99b18240088ec10854a",
|
||||
"size": 3556,
|
||||
"repo_path": "bios/Arcade/Arcade/.variants/nmk004.zip.f70b6562",
|
||||
"cores": [
|
||||
"fbneo"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_choices": [
|
||||
{
|
||||
"sha1": "489256f5e2001070d2ad94c90d255282c71ed274",
|
||||
"sha256": "6ca3142ebe127f5d2cb755c53740a92d9bf1574bde8daa32642de2991af89aaa",
|
||||
"size": 3556,
|
||||
"repo_path": "bios/Arcade/Arcade/nmk004.zip",
|
||||
"systems": [
|
||||
"arcade"
|
||||
]
|
||||
}
|
||||
],
|
||||
"alternatives": [
|
||||
{
|
||||
"sha1": "9f84f563df14ea1a1bcaaf05b720c3f5545e5a83",
|
||||
@@ -2089,6 +2111,29 @@
|
||||
"arcade",
|
||||
"snk-neogeo-cd"
|
||||
],
|
||||
"slot_choices": [
|
||||
{
|
||||
"sha1": "985d935c06fe278ce39d7690aa6c53df88954cd6",
|
||||
"sha256": "4919ba2a7650b24d1bf2c9107c89a80d93b50703a0a371fa5113c791bd03e114",
|
||||
"size": 1184219,
|
||||
"repo_path": "bios/Arcade/Arcade/.variants/neocdz.zip.ba20a7be",
|
||||
"cores": [
|
||||
"fbneo",
|
||||
"fbneo_neogeo"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_choices": [
|
||||
{
|
||||
"sha1": "4f28af31ca0defdd73d80edec2fa296908e624dc",
|
||||
"sha256": "6aa507a5dc33c45d2592bf2bc77eb3e497762221ddd9225f18583a5c55d38667",
|
||||
"size": 214681,
|
||||
"repo_path": "bios/Arcade/Arcade/.variants/neocdz.zip.4f28af31",
|
||||
"systems": [
|
||||
"arcade"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_in": [
|
||||
"arcade"
|
||||
],
|
||||
@@ -2283,6 +2328,28 @@
|
||||
"atari-400-800",
|
||||
"atari-5200"
|
||||
],
|
||||
"slot_choices": [
|
||||
{
|
||||
"sha1": "db1031585968cfc6ec2ecda5c9a5a52f61444a3b",
|
||||
"sha256": "63973d5e86b1ae5b6fca5b119a7f7589e817e19acc3ca54e54462c9dfb619e73",
|
||||
"size": 10240,
|
||||
"repo_path": "bios/Atari/400-800/ROM_OS_B_NTSC",
|
||||
"cores": [
|
||||
"atari800"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_choices": [
|
||||
{
|
||||
"sha1": "f1f0741b1d34fb4350cf7cb8ab3b6ea11cdd8174",
|
||||
"sha256": "bc6012f8ca2f7dde21ae895899b1d2af572ffaa28f819b84911ddb6a0bd8a5c7",
|
||||
"size": 10240,
|
||||
"repo_path": "bios/Atari/400-800/ATARIOSB.ROM",
|
||||
"systems": [
|
||||
"atari-400-800"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_in": [
|
||||
"atari-400-800"
|
||||
],
|
||||
@@ -5463,6 +5530,38 @@
|
||||
"nintendo-ds",
|
||||
"nintendo-dsi"
|
||||
],
|
||||
"slot_choices": [
|
||||
{
|
||||
"sha1": "c7c7570bfe51c3c7c5da3b01331b94e7e7cb4f53",
|
||||
"sha256": "12a03016ba07cfd874fc002df49a2c6eb2414eb7cfa395c2043035ec5e9193e8",
|
||||
"size": 65536,
|
||||
"repo_path": "bios/Nintendo/DS/NDS_Bios7i.bin",
|
||||
"cores": [
|
||||
"melonds",
|
||||
"melonds_ds"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_choices": [
|
||||
{
|
||||
"sha1": "a3aa751eb6bdaaf8a827ba9e03576a6f1ab0f547",
|
||||
"sha256": "2946281e730e71f7cafdb125f5cb60fed944ca5d610ee1e082c441b602b5f4e2",
|
||||
"size": 65536,
|
||||
"repo_path": "bios/Nintendo/Nintendo DS/biosdsi7.bin",
|
||||
"systems": [
|
||||
"nintendo-dsi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"sha1": "d2b17224e156ee5e55b5d8d9f323b1c9b86dd951",
|
||||
"sha256": "28a13952901b4065ef3f08c3252b49a00b6525e301b7ddaa3a390b4c7cbef901",
|
||||
"size": 65536,
|
||||
"repo_path": "bios/Nintendo/DS/dsi_bios7.bin",
|
||||
"systems": [
|
||||
"nintendo-ds"
|
||||
]
|
||||
}
|
||||
],
|
||||
"alternatives": [
|
||||
{
|
||||
"sha1": "c7c7570bfe51c3c7c5da3b01331b94e7e7cb4f53",
|
||||
@@ -5487,6 +5586,38 @@
|
||||
"nintendo-ds",
|
||||
"nintendo-dsi"
|
||||
],
|
||||
"slot_choices": [
|
||||
{
|
||||
"sha1": "719b9eef33692406d7170ff526069615759c4dfc",
|
||||
"sha256": "6e9f70b51dc4f26289b597f14e2513620c2af964374a95f4c5a62b5cad199afc",
|
||||
"size": 65536,
|
||||
"repo_path": "bios/Nintendo/DS/NDS_Bios9i.bin",
|
||||
"cores": [
|
||||
"melonds",
|
||||
"melonds_ds"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_choices": [
|
||||
{
|
||||
"sha1": "7bf549b8be9e48ab0cdc9b0fdadd49a5131f97eb",
|
||||
"sha256": "47538922a8e8a8e79b922ff1203863ef5c40d9c54656a8d2c89c56ece52029ce",
|
||||
"size": 65536,
|
||||
"repo_path": "bios/Nintendo/Nintendo DS/biosdsi9.bin",
|
||||
"systems": [
|
||||
"nintendo-dsi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"sha1": "db61fa39ddbc5f5ed71fb19cda47609ef0201723",
|
||||
"sha256": "664c608bd6ccfe583e87b3b924afa00f8697dc7ca92eeb6d206d68abb03d2e10",
|
||||
"size": 65536,
|
||||
"repo_path": "bios/Nintendo/DS/dsi_bios9.bin",
|
||||
"systems": [
|
||||
"nintendo-ds"
|
||||
]
|
||||
}
|
||||
],
|
||||
"alternatives": [
|
||||
{
|
||||
"sha1": "719b9eef33692406d7170ff526069615759c4dfc",
|
||||
@@ -9778,6 +9909,38 @@
|
||||
"systems": [
|
||||
"snk-neogeo-cd"
|
||||
],
|
||||
"slot_choices": [
|
||||
{
|
||||
"sha1": "712816a3c41baf4129e23d645305a536e03d69de",
|
||||
"sha256": "f885f2cc79b5c0120ca316b0db360183c2b9a60bc727cbe211b7518e12bd5548",
|
||||
"size": 955352,
|
||||
"repo_path": "bios/SNK/Neo Geo CD/.variants/neocdz.zip.712816a3",
|
||||
"cores": [
|
||||
"geolith"
|
||||
]
|
||||
},
|
||||
{
|
||||
"sha1": "985d935c06fe278ce39d7690aa6c53df88954cd6",
|
||||
"sha256": "4919ba2a7650b24d1bf2c9107c89a80d93b50703a0a371fa5113c791bd03e114",
|
||||
"size": 1184219,
|
||||
"repo_path": "bios/Arcade/Arcade/.variants/neocdz.zip.ba20a7be",
|
||||
"cores": [
|
||||
"fbneo",
|
||||
"fbneo_neogeo"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_choices": [
|
||||
{
|
||||
"sha1": "9cab31cfe7eacb6871d36cee105d87b90fd85b64",
|
||||
"sha256": "d828241905d390c5de4867646296f615c95bca2f5d8f7e81d50e08717373a821",
|
||||
"size": 1181333,
|
||||
"repo_path": "bios/Arcade/Arcade/neocdz.zip",
|
||||
"systems": [
|
||||
"snk-neogeo-cd"
|
||||
]
|
||||
}
|
||||
],
|
||||
"alternatives": [
|
||||
{
|
||||
"sha1": "712816a3c41baf4129e23d645305a536e03d69de",
|
||||
@@ -10070,6 +10233,29 @@
|
||||
"region_system_groups": [
|
||||
"sony-playstation"
|
||||
],
|
||||
"slot_choices": [
|
||||
{
|
||||
"sha1": "ffa7f9a7fb19d773a0c3985a541c8e5623d2c30d",
|
||||
"sha256": "0c8359870cbac0ea091f1c87f188cd332dcc709753b91cafd9fd44a4a6188197",
|
||||
"size": 524288,
|
||||
"repo_path": "bios/Sony/PlayStation/scph5000.bin",
|
||||
"cores": [
|
||||
"duckstation",
|
||||
"rustation"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_choices": [
|
||||
{
|
||||
"sha1": "ffa7f9a7fb19d773a0c3985a541c8e5623d2c30d",
|
||||
"sha256": "0c8359870cbac0ea091f1c87f188cd332dcc709753b91cafd9fd44a4a6188197",
|
||||
"size": 524288,
|
||||
"repo_path": "bios/Sony/PlayStation/scph5000.bin",
|
||||
"systems": [
|
||||
"sony-playstation"
|
||||
]
|
||||
}
|
||||
],
|
||||
"alternatives": [
|
||||
{
|
||||
"sha1": "ffa7f9a7fb19d773a0c3985a541c8e5623d2c30d",
|
||||
@@ -11914,6 +12100,46 @@
|
||||
"systems": [
|
||||
"sony-playstation-2"
|
||||
],
|
||||
"slot_choices": [
|
||||
{
|
||||
"sha1": "72bfdf91fa11a86e2810a7c167cdcf11e20418a8",
|
||||
"sha256": "61c77b789ccde856096f024e4a3eef6891e15ca59c127e0dd13e762a7d338a7e",
|
||||
"size": 2690355,
|
||||
"repo_path": "bios/Other/armsx2/pcsx2/resources/GameIndex.yaml",
|
||||
"cores": [
|
||||
"armsx2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"sha1": "7efb2ed721cf0c0f0677324e09be311a8337e793",
|
||||
"sha256": "3bf3c54d6f16d88d3669be698179bb8c2fb981ee5ce71bcb952719429a838cc1",
|
||||
"size": 2693579,
|
||||
"repo_path": "bios/Other/yaps2/GameIndex.yaml",
|
||||
"cores": [
|
||||
"yaps2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"sha1": "72bfdf91fa11a86e2810a7c167cdcf11e20418a8",
|
||||
"sha256": "61c77b789ccde856096f024e4a3eef6891e15ca59c127e0dd13e762a7d338a7e",
|
||||
"size": 2690355,
|
||||
"repo_path": "bios/Other/armsx2/pcsx2/resources/GameIndex.yaml",
|
||||
"cores": [
|
||||
"lrps2"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_choices": [
|
||||
{
|
||||
"sha1": "57369df9ca82a460e596cea7835c5a0797dee668",
|
||||
"sha256": "efa94cb7b9208dc6812bb9407dc986560e523cce57f511eff30942f76b7288bf",
|
||||
"size": 1737799,
|
||||
"repo_path": "bios/Sony/PlayStation 2/.variants/GameIndex.yaml.57369df9",
|
||||
"systems": [
|
||||
"sony-playstation-2"
|
||||
]
|
||||
}
|
||||
],
|
||||
"alternatives": [
|
||||
{
|
||||
"sha1": "72bfdf91fa11a86e2810a7c167cdcf11e20418a8",
|
||||
|
||||
@@ -6570,6 +6570,26 @@
|
||||
"arcade",
|
||||
"philips-cdi"
|
||||
],
|
||||
"declared_choices": [
|
||||
{
|
||||
"sha1": "e7d2a0dad62d6f75bc10f48a376da0a99b764571",
|
||||
"sha256": "aa8a33813531795de9739b06b65960e0f86cdec504c8452a956d184eac3cdcd4",
|
||||
"size": 468660,
|
||||
"repo_path": "bios/Arcade/Arcade/.variants/cdibios.zip.e7d2a0da",
|
||||
"systems": [
|
||||
"arcade"
|
||||
]
|
||||
},
|
||||
{
|
||||
"sha1": "16072afaa65d1b059346616ac5b5a600c63ff1d1",
|
||||
"sha256": "b53d206ae81607bf6866411b720ad82ce75122423058ce08c6df84cdc1ca3496",
|
||||
"size": 472798,
|
||||
"repo_path": "bios/Philips/CD-i/cdibios.zip",
|
||||
"systems": [
|
||||
"philips-cdi"
|
||||
]
|
||||
}
|
||||
],
|
||||
"alternatives": [
|
||||
{
|
||||
"sha1": "16072afaa65d1b059346616ac5b5a600c63ff1d1",
|
||||
@@ -17884,6 +17904,26 @@
|
||||
"arcade",
|
||||
"sega-saturn"
|
||||
],
|
||||
"declared_choices": [
|
||||
{
|
||||
"sha1": "330a2ab5ce2e2c29987b2086e021de7aa04aa102",
|
||||
"sha256": "bbdf6a9ef4bad6bd9ad16f9f7669071dd4ee3bb2387d49124f024ea8dd750f88",
|
||||
"size": 2699375,
|
||||
"repo_path": "bios/Arcade/MAME/.variants/stvbios.zip.330a2ab5",
|
||||
"systems": [
|
||||
"arcade"
|
||||
]
|
||||
},
|
||||
{
|
||||
"sha1": "7783ab663bb1a8d62ff60d583674355c2af556b1",
|
||||
"sha256": "38ceb2792c72770f56aadb1865cf66dc33a3017db1ee05e11c2b2dff397ad39e",
|
||||
"size": 2955630,
|
||||
"repo_path": "bios/Arcade/MAME/stvbios.zip",
|
||||
"systems": [
|
||||
"sega-saturn"
|
||||
]
|
||||
}
|
||||
],
|
||||
"alternatives": [
|
||||
{
|
||||
"sha1": "b14bbed4bf459439cf7e96d2f79d7adea5d31448",
|
||||
@@ -20260,6 +20300,26 @@
|
||||
"atari-400-800",
|
||||
"atari-5200"
|
||||
],
|
||||
"declared_choices": [
|
||||
{
|
||||
"sha1": "f1f0741b1d34fb4350cf7cb8ab3b6ea11cdd8174",
|
||||
"sha256": "bc6012f8ca2f7dde21ae895899b1d2af572ffaa28f819b84911ddb6a0bd8a5c7",
|
||||
"size": 10240,
|
||||
"repo_path": "bios/Atari/400-800/ATARIOSB.ROM",
|
||||
"systems": [
|
||||
"atari-400-800"
|
||||
]
|
||||
},
|
||||
{
|
||||
"sha1": "db1031585968cfc6ec2ecda5c9a5a52f61444a3b",
|
||||
"sha256": "63973d5e86b1ae5b6fca5b119a7f7589e817e19acc3ca54e54462c9dfb619e73",
|
||||
"size": 10240,
|
||||
"repo_path": "bios/Atari/400-800/ROM_OS_B_NTSC",
|
||||
"systems": [
|
||||
"atari-5200"
|
||||
]
|
||||
}
|
||||
],
|
||||
"alternatives": [
|
||||
{
|
||||
"sha1": "db1031585968cfc6ec2ecda5c9a5a52f61444a3b",
|
||||
@@ -23088,6 +23148,26 @@
|
||||
"systems": [
|
||||
"nec-pc-88",
|
||||
"tandy-coco"
|
||||
],
|
||||
"declared_choices": [
|
||||
{
|
||||
"sha1": "bb7103a0818850a039c67ff666a31ce49a8d516f",
|
||||
"sha256": "9dc63118728a171ce2ed437ad38ef280b8b7bcf4a5a95fedf18946501b010456",
|
||||
"size": 2048,
|
||||
"repo_path": "bios/NEC/PC-88/N88SUB.ROM",
|
||||
"systems": [
|
||||
"nec-pc-88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"sha1": "892b85cc96f0ff31cae8ad8d409b9f6c04b32e5e",
|
||||
"sha256": "b9b4bfe42b4957d4c6b532ceb5a1946c9e4d9d4f55a6e3eb600cc7f236e09ca9",
|
||||
"size": 8194,
|
||||
"repo_path": "bios/Tandy/CoCo/disk.rom",
|
||||
"systems": [
|
||||
"tandy-coco"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1553,6 +1553,28 @@
|
||||
"systems": [
|
||||
"arcade"
|
||||
],
|
||||
"slot_choices": [
|
||||
{
|
||||
"sha1": "9f84f563df14ea1a1bcaaf05b720c3f5545e5a83",
|
||||
"sha256": "13d503ea6c7e7b9a2249837cf915bff6df862b7035f5c99b18240088ec10854a",
|
||||
"size": 3556,
|
||||
"repo_path": "bios/Arcade/Arcade/.variants/nmk004.zip.f70b6562",
|
||||
"cores": [
|
||||
"fbneo"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_choices": [
|
||||
{
|
||||
"sha1": "489256f5e2001070d2ad94c90d255282c71ed274",
|
||||
"sha256": "6ca3142ebe127f5d2cb755c53740a92d9bf1574bde8daa32642de2991af89aaa",
|
||||
"size": 3556,
|
||||
"repo_path": "bios/Arcade/Arcade/nmk004.zip",
|
||||
"systems": [
|
||||
"arcade"
|
||||
]
|
||||
}
|
||||
],
|
||||
"alternatives": [
|
||||
{
|
||||
"sha1": "9f84f563df14ea1a1bcaaf05b720c3f5545e5a83",
|
||||
@@ -1867,6 +1889,29 @@
|
||||
"arcade",
|
||||
"snk-neogeo-cd"
|
||||
],
|
||||
"slot_choices": [
|
||||
{
|
||||
"sha1": "985d935c06fe278ce39d7690aa6c53df88954cd6",
|
||||
"sha256": "4919ba2a7650b24d1bf2c9107c89a80d93b50703a0a371fa5113c791bd03e114",
|
||||
"size": 1184219,
|
||||
"repo_path": "bios/Arcade/Arcade/.variants/neocdz.zip.ba20a7be",
|
||||
"cores": [
|
||||
"fbneo",
|
||||
"fbneo_neogeo"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_choices": [
|
||||
{
|
||||
"sha1": "4f28af31ca0defdd73d80edec2fa296908e624dc",
|
||||
"sha256": "6aa507a5dc33c45d2592bf2bc77eb3e497762221ddd9225f18583a5c55d38667",
|
||||
"size": 214681,
|
||||
"repo_path": "bios/Arcade/Arcade/.variants/neocdz.zip.4f28af31",
|
||||
"systems": [
|
||||
"arcade"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_in": [
|
||||
"arcade"
|
||||
],
|
||||
@@ -2061,6 +2106,28 @@
|
||||
"atari-400-800",
|
||||
"atari-5200"
|
||||
],
|
||||
"slot_choices": [
|
||||
{
|
||||
"sha1": "db1031585968cfc6ec2ecda5c9a5a52f61444a3b",
|
||||
"sha256": "63973d5e86b1ae5b6fca5b119a7f7589e817e19acc3ca54e54462c9dfb619e73",
|
||||
"size": 10240,
|
||||
"repo_path": "bios/Atari/400-800/ROM_OS_B_NTSC",
|
||||
"cores": [
|
||||
"atari800"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_choices": [
|
||||
{
|
||||
"sha1": "f1f0741b1d34fb4350cf7cb8ab3b6ea11cdd8174",
|
||||
"sha256": "bc6012f8ca2f7dde21ae895899b1d2af572ffaa28f819b84911ddb6a0bd8a5c7",
|
||||
"size": 10240,
|
||||
"repo_path": "bios/Atari/400-800/ATARIOSB.ROM",
|
||||
"systems": [
|
||||
"atari-400-800"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_in": [
|
||||
"atari-400-800"
|
||||
],
|
||||
@@ -2346,6 +2413,34 @@
|
||||
"systems": [
|
||||
"coleco-colecovision"
|
||||
],
|
||||
"slot_choices": [
|
||||
{
|
||||
"cores": [
|
||||
"jollycv",
|
||||
"openmsx"
|
||||
]
|
||||
},
|
||||
{
|
||||
"sha1": "99ba9be24ada3e86e5c17aeecb7a2d68c5edfe59",
|
||||
"sha256": "25cdb7ff4ad675ac25e11663e9e9472afc1c7cf59ba40c330081abda9881a968",
|
||||
"size": 8192,
|
||||
"repo_path": "bios/Coleco/ColecoVision/colecoa.rom",
|
||||
"cores": [
|
||||
"zesarux"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_choices": [
|
||||
{
|
||||
"sha1": "45bedc4cbdeac66c7df59e9e599195c778d86a92",
|
||||
"sha256": "990bf1956f10207d8781b619eb74f89b00d921c8d45c95c334c16c8cceca09ad",
|
||||
"size": 8192,
|
||||
"repo_path": "bios/Coleco/ColecoVision/BIOS.col",
|
||||
"systems": [
|
||||
"coleco-colecovision"
|
||||
]
|
||||
}
|
||||
],
|
||||
"alternatives": [
|
||||
{
|
||||
"sha1": "45bedc4cbdeac66c7df59e9e599195c778d86a92",
|
||||
@@ -4113,6 +4208,33 @@
|
||||
"systems": [
|
||||
"microsoft-msx"
|
||||
],
|
||||
"slot_choices": [
|
||||
{
|
||||
"cores": [
|
||||
"fmsx"
|
||||
]
|
||||
},
|
||||
{
|
||||
"sha1": "fec451b9256570a0e4869355a40329c57f40705f",
|
||||
"sha256": "a3a6a325935d031cee662b56e2c810c73d35a8375a2c5ffa03f6d27405bc569e",
|
||||
"size": 65536,
|
||||
"repo_path": "bios/Microsoft/MSX/openmsx/fmpac.rom",
|
||||
"cores": [
|
||||
"openmsx"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_choices": [
|
||||
{
|
||||
"sha1": "9d789166e3caf28e4742fe933d962e99618c633d",
|
||||
"sha256": "3cfe44646b69a622656b96305f1b975f48ca018839f7946895f7a1352d9720aa",
|
||||
"size": 65536,
|
||||
"repo_path": "bios/Microsoft/MSX/FMPAC.ROM",
|
||||
"systems": [
|
||||
"microsoft-msx"
|
||||
]
|
||||
}
|
||||
],
|
||||
"alternatives": [
|
||||
{
|
||||
"sha1": "9d789166e3caf28e4742fe933d962e99618c633d",
|
||||
@@ -4190,6 +4312,33 @@
|
||||
"systems": [
|
||||
"microsoft-msx"
|
||||
],
|
||||
"slot_choices": [
|
||||
{
|
||||
"cores": [
|
||||
"fmsx"
|
||||
]
|
||||
},
|
||||
{
|
||||
"sha1": "e998f0c441f4f1800ef44e42cd1659150206cf79",
|
||||
"sha256": "1c85dac5536fa3ba6f2cb70deba02ff680b34ac6cc787d2977258bd663a99555",
|
||||
"size": 32768,
|
||||
"repo_path": "bios/Microsoft/MSX/MSX_8020-20bios.rom",
|
||||
"cores": [
|
||||
"zesarux"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_choices": [
|
||||
{
|
||||
"sha1": "409e82adac40f6bdd18eb6c84e8b2fbdc7fb5498",
|
||||
"sha256": "999564a371dd2fdf7fbe8d853e82a68d557c27b7d87417639b2fa17704b83f78",
|
||||
"size": 32768,
|
||||
"repo_path": "bios/Microsoft/MSX/MSX.ROM",
|
||||
"systems": [
|
||||
"microsoft-msx"
|
||||
]
|
||||
}
|
||||
],
|
||||
"alternatives": [
|
||||
{
|
||||
"sha1": "409e82adac40f6bdd18eb6c84e8b2fbdc7fb5498",
|
||||
@@ -5250,6 +5399,38 @@
|
||||
"nintendo-ds",
|
||||
"nintendo-dsi"
|
||||
],
|
||||
"slot_choices": [
|
||||
{
|
||||
"sha1": "c7c7570bfe51c3c7c5da3b01331b94e7e7cb4f53",
|
||||
"sha256": "12a03016ba07cfd874fc002df49a2c6eb2414eb7cfa395c2043035ec5e9193e8",
|
||||
"size": 65536,
|
||||
"repo_path": "bios/Nintendo/DS/NDS_Bios7i.bin",
|
||||
"cores": [
|
||||
"melonds",
|
||||
"melonds_ds"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_choices": [
|
||||
{
|
||||
"sha1": "a3aa751eb6bdaaf8a827ba9e03576a6f1ab0f547",
|
||||
"sha256": "2946281e730e71f7cafdb125f5cb60fed944ca5d610ee1e082c441b602b5f4e2",
|
||||
"size": 65536,
|
||||
"repo_path": "bios/Nintendo/Nintendo DS/biosdsi7.bin",
|
||||
"systems": [
|
||||
"nintendo-dsi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"sha1": "d2b17224e156ee5e55b5d8d9f323b1c9b86dd951",
|
||||
"sha256": "28a13952901b4065ef3f08c3252b49a00b6525e301b7ddaa3a390b4c7cbef901",
|
||||
"size": 65536,
|
||||
"repo_path": "bios/Nintendo/DS/dsi_bios7.bin",
|
||||
"systems": [
|
||||
"nintendo-ds"
|
||||
]
|
||||
}
|
||||
],
|
||||
"alternatives": [
|
||||
{
|
||||
"sha1": "c7c7570bfe51c3c7c5da3b01331b94e7e7cb4f53",
|
||||
@@ -5274,6 +5455,38 @@
|
||||
"nintendo-ds",
|
||||
"nintendo-dsi"
|
||||
],
|
||||
"slot_choices": [
|
||||
{
|
||||
"sha1": "719b9eef33692406d7170ff526069615759c4dfc",
|
||||
"sha256": "6e9f70b51dc4f26289b597f14e2513620c2af964374a95f4c5a62b5cad199afc",
|
||||
"size": 65536,
|
||||
"repo_path": "bios/Nintendo/DS/NDS_Bios9i.bin",
|
||||
"cores": [
|
||||
"melonds",
|
||||
"melonds_ds"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_choices": [
|
||||
{
|
||||
"sha1": "7bf549b8be9e48ab0cdc9b0fdadd49a5131f97eb",
|
||||
"sha256": "47538922a8e8a8e79b922ff1203863ef5c40d9c54656a8d2c89c56ece52029ce",
|
||||
"size": 65536,
|
||||
"repo_path": "bios/Nintendo/Nintendo DS/biosdsi9.bin",
|
||||
"systems": [
|
||||
"nintendo-dsi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"sha1": "db61fa39ddbc5f5ed71fb19cda47609ef0201723",
|
||||
"sha256": "664c608bd6ccfe583e87b3b924afa00f8697dc7ca92eeb6d206d68abb03d2e10",
|
||||
"size": 65536,
|
||||
"repo_path": "bios/Nintendo/DS/dsi_bios9.bin",
|
||||
"systems": [
|
||||
"nintendo-ds"
|
||||
]
|
||||
}
|
||||
],
|
||||
"alternatives": [
|
||||
{
|
||||
"sha1": "719b9eef33692406d7170ff526069615759c4dfc",
|
||||
@@ -9587,6 +9800,38 @@
|
||||
"systems": [
|
||||
"snk-neogeo-cd"
|
||||
],
|
||||
"slot_choices": [
|
||||
{
|
||||
"sha1": "712816a3c41baf4129e23d645305a536e03d69de",
|
||||
"sha256": "f885f2cc79b5c0120ca316b0db360183c2b9a60bc727cbe211b7518e12bd5548",
|
||||
"size": 955352,
|
||||
"repo_path": "bios/SNK/Neo Geo CD/.variants/neocdz.zip.712816a3",
|
||||
"cores": [
|
||||
"geolith"
|
||||
]
|
||||
},
|
||||
{
|
||||
"sha1": "985d935c06fe278ce39d7690aa6c53df88954cd6",
|
||||
"sha256": "4919ba2a7650b24d1bf2c9107c89a80d93b50703a0a371fa5113c791bd03e114",
|
||||
"size": 1184219,
|
||||
"repo_path": "bios/Arcade/Arcade/.variants/neocdz.zip.ba20a7be",
|
||||
"cores": [
|
||||
"fbneo",
|
||||
"fbneo_neogeo"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_choices": [
|
||||
{
|
||||
"sha1": "9cab31cfe7eacb6871d36cee105d87b90fd85b64",
|
||||
"sha256": "d828241905d390c5de4867646296f615c95bca2f5d8f7e81d50e08717373a821",
|
||||
"size": 1181333,
|
||||
"repo_path": "bios/Arcade/Arcade/neocdz.zip",
|
||||
"systems": [
|
||||
"snk-neogeo-cd"
|
||||
]
|
||||
}
|
||||
],
|
||||
"alternatives": [
|
||||
{
|
||||
"sha1": "712816a3c41baf4129e23d645305a536e03d69de",
|
||||
@@ -9879,6 +10124,29 @@
|
||||
"region_system_groups": [
|
||||
"sony-playstation"
|
||||
],
|
||||
"slot_choices": [
|
||||
{
|
||||
"sha1": "ffa7f9a7fb19d773a0c3985a541c8e5623d2c30d",
|
||||
"sha256": "0c8359870cbac0ea091f1c87f188cd332dcc709753b91cafd9fd44a4a6188197",
|
||||
"size": 524288,
|
||||
"repo_path": "bios/Sony/PlayStation/scph5000.bin",
|
||||
"cores": [
|
||||
"duckstation",
|
||||
"rustation"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_choices": [
|
||||
{
|
||||
"sha1": "ffa7f9a7fb19d773a0c3985a541c8e5623d2c30d",
|
||||
"sha256": "0c8359870cbac0ea091f1c87f188cd332dcc709753b91cafd9fd44a4a6188197",
|
||||
"size": 524288,
|
||||
"repo_path": "bios/Sony/PlayStation/scph5000.bin",
|
||||
"systems": [
|
||||
"sony-playstation"
|
||||
]
|
||||
}
|
||||
],
|
||||
"alternatives": [
|
||||
{
|
||||
"sha1": "ffa7f9a7fb19d773a0c3985a541c8e5623d2c30d",
|
||||
@@ -11723,6 +11991,46 @@
|
||||
"systems": [
|
||||
"sony-playstation-2"
|
||||
],
|
||||
"slot_choices": [
|
||||
{
|
||||
"sha1": "72bfdf91fa11a86e2810a7c167cdcf11e20418a8",
|
||||
"sha256": "61c77b789ccde856096f024e4a3eef6891e15ca59c127e0dd13e762a7d338a7e",
|
||||
"size": 2690355,
|
||||
"repo_path": "bios/Other/armsx2/pcsx2/resources/GameIndex.yaml",
|
||||
"cores": [
|
||||
"armsx2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"sha1": "7efb2ed721cf0c0f0677324e09be311a8337e793",
|
||||
"sha256": "3bf3c54d6f16d88d3669be698179bb8c2fb981ee5ce71bcb952719429a838cc1",
|
||||
"size": 2693579,
|
||||
"repo_path": "bios/Other/yaps2/GameIndex.yaml",
|
||||
"cores": [
|
||||
"yaps2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"sha1": "72bfdf91fa11a86e2810a7c167cdcf11e20418a8",
|
||||
"sha256": "61c77b789ccde856096f024e4a3eef6891e15ca59c127e0dd13e762a7d338a7e",
|
||||
"size": 2690355,
|
||||
"repo_path": "bios/Other/armsx2/pcsx2/resources/GameIndex.yaml",
|
||||
"cores": [
|
||||
"lrps2"
|
||||
]
|
||||
}
|
||||
],
|
||||
"declared_choices": [
|
||||
{
|
||||
"sha1": "57369df9ca82a460e596cea7835c5a0797dee668",
|
||||
"sha256": "efa94cb7b9208dc6812bb9407dc986560e523cce57f511eff30942f76b7288bf",
|
||||
"size": 1737799,
|
||||
"repo_path": "bios/Sony/PlayStation 2/.variants/GameIndex.yaml.57369df9",
|
||||
"systems": [
|
||||
"sony-playstation-2"
|
||||
]
|
||||
}
|
||||
],
|
||||
"alternatives": [
|
||||
{
|
||||
"sha1": "72bfdf91fa11a86e2810a7c167cdcf11e20418a8",
|
||||
|
||||
@@ -482,6 +482,104 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"slot_choices": {
|
||||
"description": "On a platform file a core's own source contradicts while the frontend only checks the path: the copy each core's presence puts there, in the order the pack builder settles the slot. A narrowing serves the first whose cores remain; one naming only cores, or none remaining, leaves `declared_choices`.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"cores"
|
||||
],
|
||||
"properties": {
|
||||
"sha1": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-f0-9]{40}$"
|
||||
},
|
||||
"sha256": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-f0-9]{64}$"
|
||||
},
|
||||
"size": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"repo_path": {
|
||||
"type": "string"
|
||||
},
|
||||
"storage": {
|
||||
"const": "release"
|
||||
},
|
||||
"release_asset": {
|
||||
"type": "string",
|
||||
"pattern": "^[^/\\\\]+$"
|
||||
},
|
||||
"cores": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"minItems": 1,
|
||||
"uniqueItems": true
|
||||
},
|
||||
"executable": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"declared_choices": {
|
||||
"description": "The platform's own answer at the destination by the declaring systems that remain, in the order the pack builder takes the declarations; a narrowing serves the first whose systems remain.",
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"sha1",
|
||||
"sha256",
|
||||
"size",
|
||||
"repo_path",
|
||||
"systems"
|
||||
],
|
||||
"properties": {
|
||||
"sha1": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-f0-9]{40}$"
|
||||
},
|
||||
"sha256": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-f0-9]{64}$"
|
||||
},
|
||||
"size": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"repo_path": {
|
||||
"type": "string"
|
||||
},
|
||||
"storage": {
|
||||
"const": "release"
|
||||
},
|
||||
"release_asset": {
|
||||
"type": "string",
|
||||
"pattern": "^[^/\\\\]+$"
|
||||
},
|
||||
"executable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"systems": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"minItems": 1,
|
||||
"uniqueItems": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"executable": {
|
||||
"description": "The repository keeps the file executable; the installer gives it the execute bits the umask allows.",
|
||||
"type": "boolean"
|
||||
|
||||
+153
-36
@@ -3160,6 +3160,66 @@ def _attach_claims(
|
||||
entry.pop("alternatives", None)
|
||||
|
||||
|
||||
def _record_slot_choices(
|
||||
entry: dict,
|
||||
choices: list[tuple[str, str | None]],
|
||||
declarations: list[tuple[list[str], dict, str]],
|
||||
settle,
|
||||
dest: str,
|
||||
db: dict,
|
||||
repo_root: str,
|
||||
) -> None:
|
||||
"""Record what a pack built for fewer cores serves at a platform destination.
|
||||
|
||||
`slot_choices` lists, in the order slots decides, the cores whose claim
|
||||
settles the slot and the copy it serves, a choice without a copy leaving
|
||||
the platform's answer. `declared_choices` is the platform's answer by the
|
||||
declaring systems that remain, in the order the builder takes them.
|
||||
Nothing is recorded where every narrowing gets the entry's own copy.
|
||||
"""
|
||||
|
||||
def download(file_entry: dict, sys_id: str, override: str | None) -> dict | None:
|
||||
local_path, _status = settle(file_entry, sys_id, dest, override)
|
||||
if local_path is None:
|
||||
return None
|
||||
copy, _size = _manifest_entry(local_path, dest, None, db, repo_root, {})
|
||||
if copy is None:
|
||||
return None
|
||||
return {k: v for k, v in copy.items() if k not in ("dest", "cores")}
|
||||
|
||||
declared: list[dict] = []
|
||||
for systems, file_entry, sys_id in declarations:
|
||||
copy = download(file_entry, sys_id, None)
|
||||
if copy is None:
|
||||
# An omission is not a copy the installer can switch to.
|
||||
return
|
||||
if declared and declared[-1]["sha1"] == copy["sha1"]:
|
||||
declared[-1]["systems"].extend(systems)
|
||||
else:
|
||||
declared.append({**copy, "systems": list(systems)})
|
||||
_systems, first, first_sys = declarations[0]
|
||||
recorded: list[dict] = []
|
||||
seen: set[str] = set()
|
||||
for emulator, path in choices:
|
||||
if emulator in seen:
|
||||
continue
|
||||
seen.add(emulator)
|
||||
copy = {} if path is None else download(first, first_sys, path)
|
||||
if copy is None:
|
||||
return
|
||||
if recorded and recorded[-1].get("sha1") == copy.get("sha1"):
|
||||
recorded[-1]["cores"].append(emulator)
|
||||
else:
|
||||
recorded.append({**copy, "cores": [emulator]})
|
||||
while recorded and "sha1" not in recorded[-1]:
|
||||
recorded.pop()
|
||||
if not recorded and len(declared) == 1:
|
||||
return
|
||||
if recorded:
|
||||
entry["slot_choices"] = recorded
|
||||
entry["declared_choices"] = declared
|
||||
|
||||
|
||||
def _manifest_core_entries(
|
||||
core_files: list,
|
||||
config: dict,
|
||||
@@ -3441,6 +3501,13 @@ def generate_manifest(
|
||||
config, emu_profiles, db, zip_contents, data_registry,
|
||||
target_cores=target_cores, source=source,
|
||||
)
|
||||
# The full manifest records every core's answer at a contested slot, so a
|
||||
# target narrowed from it gets what that target's own pack serves.
|
||||
slot_choices = (
|
||||
slots.slot_choices(config, emu_profiles, db, zip_contents, data_registry)
|
||||
if target_cores is None and source != "platform" and emu_profiles
|
||||
else {}
|
||||
)
|
||||
|
||||
def manifest_destination(full_destination: str) -> str:
|
||||
if base_dest and full_destination.startswith(f"{base_dest}/"):
|
||||
@@ -3489,6 +3556,80 @@ def generate_manifest(
|
||||
pack_systems, db, bios_dir, base_dest, required_only,
|
||||
zip_contents, offline,
|
||||
)
|
||||
declarations: dict[str, dict[str, list[dict]]] = {}
|
||||
for sys_id, system in sorted(pack_systems.items()):
|
||||
for file_entry in system.get("files", []):
|
||||
if required_only and file_entry.get("required") is False:
|
||||
continue
|
||||
dest = sanitize_pack_path(file_entry.get("destination", file_entry["name"]))
|
||||
if dest and not (region_drops and dest in region_drops):
|
||||
full_dest = f"{base_dest}/{dest}" if base_dest else dest
|
||||
declarations.setdefault(full_dest, {}).setdefault(sys_id, []).append(
|
||||
file_entry
|
||||
)
|
||||
|
||||
def declaration_order(full_dest: str) -> list[tuple[list[str], dict, str]]:
|
||||
"""The declarations answering a destination as its systems drop.
|
||||
|
||||
A pack built for a target loses the systems no available core
|
||||
reads, and the next declaration takes the destination by the
|
||||
same rule: RetroArch's bare nintendo-ds dsi_bios9.bin once the
|
||||
pinned nintendo-dsi one is gone.
|
||||
"""
|
||||
remaining = dict(declarations.get(full_dest, {}))
|
||||
order: list[tuple[list[str], dict, str]] = []
|
||||
while remaining:
|
||||
winner = _preferred_entries(
|
||||
{sid: {"files": fes} for sid, fes in remaining.items()},
|
||||
db, bios_dir, base_dest, required_only, zip_contents, offline,
|
||||
).get(full_dest)
|
||||
first_sys, first = next(
|
||||
(sid, fe) for sid, fes in sorted(remaining.items()) for fe in fes
|
||||
if winner is None or id(fe) == winner
|
||||
)
|
||||
systems = sorted(
|
||||
sid for sid, fes in remaining.items()
|
||||
if any(fe is first for fe in fes)
|
||||
)
|
||||
order.append((systems, first, first_sys))
|
||||
for sid in systems:
|
||||
del remaining[sid]
|
||||
return order
|
||||
|
||||
def settle(file_entry: dict, sys_id: str, dest: str, override: str | None):
|
||||
"""The file served once the slot is settled, or None and why not."""
|
||||
local_path, status = slots.apply_override(override, *resolve_file(
|
||||
file_entry,
|
||||
db,
|
||||
bios_dir,
|
||||
zip_contents,
|
||||
data_dir_registry=data_registry,
|
||||
offline=offline,
|
||||
))
|
||||
if status == "not_found" and not reads_file_contents(verification_mode):
|
||||
found = agnostic_substitute(file_entry, sys_id, db, manifest_profiles)
|
||||
if found:
|
||||
local_path, status = found[0], "agnostic_fallback"
|
||||
if os.path.basename(local_path) != file_entry.get("name", ""):
|
||||
# The pack explains the rename in a note of its own.
|
||||
rename_notes.add(file_entry.get("name", ""))
|
||||
# The builder's verdict: a hash platform omits what its frontend
|
||||
# would reject, a zipped_file archive included when the member is
|
||||
# missing whatever the outer resolution said. An existence
|
||||
# platform never reads the bytes.
|
||||
status, refusal = baseline_verdict(
|
||||
file_entry, local_path, status, verification_mode
|
||||
)
|
||||
if refusal or status in ("not_found", "external"):
|
||||
return None, "hash_mismatch" if refusal else status
|
||||
# The variant the pack ships when an emulator rejects the
|
||||
# resolved file: the installer must place the same bytes.
|
||||
local_path, _disagreement = validated_choice(
|
||||
file_entry, local_path, db, manifest_validation, bios_dir,
|
||||
digest_algorithm(verification_mode), dest, manifest_owners,
|
||||
)
|
||||
return local_path, status
|
||||
|
||||
for sys_id, system in sorted(pack_systems.items()):
|
||||
for file_entry in system.get("files", []):
|
||||
if required_only and file_entry.get("required") is False:
|
||||
@@ -3524,48 +3665,24 @@ def generate_manifest(
|
||||
)
|
||||
continue
|
||||
|
||||
local_path, status = resolve_file(
|
||||
file_entry,
|
||||
db,
|
||||
bios_dir,
|
||||
zip_contents,
|
||||
data_dir_registry=data_registry,
|
||||
offline=offline,
|
||||
local_path, status = settle(
|
||||
file_entry, sys_id, dest, slot_overrides.get(full_dest)
|
||||
)
|
||||
local_path, status = slots.apply_override(
|
||||
slot_overrides.get(full_dest), local_path, status
|
||||
)
|
||||
if status == "not_found" and not reads_file_contents(verification_mode):
|
||||
found = agnostic_substitute(file_entry, sys_id, db, manifest_profiles)
|
||||
if found:
|
||||
local_path, status = found[0], "agnostic_fallback"
|
||||
if os.path.basename(local_path) != file_entry.get("name", ""):
|
||||
# The pack explains the rename in a note of its own.
|
||||
rename_notes.add(file_entry.get("name", ""))
|
||||
# The builder's verdict: a hash platform omits what its
|
||||
# frontend would reject, a zipped_file archive included when
|
||||
# the member is missing whatever the outer resolution said.
|
||||
# An existence platform never reads the bytes.
|
||||
status, refusal = baseline_verdict(
|
||||
file_entry, local_path, status, verification_mode
|
||||
)
|
||||
if refusal or status in ("not_found", "external"):
|
||||
record_omission(
|
||||
full_dest, file_entry, sys_id,
|
||||
"hash_mismatch" if refusal else status, None,
|
||||
)
|
||||
if local_path is None:
|
||||
record_omission(full_dest, file_entry, sys_id, status, None)
|
||||
continue
|
||||
# The variant the pack ships when an emulator rejects the
|
||||
# resolved file: the installer must place the same bytes.
|
||||
local_path, _disagreement = validated_choice(
|
||||
file_entry, local_path, db, manifest_validation, bios_dir,
|
||||
digest_algorithm(verification_mode), dest, manifest_owners,
|
||||
)
|
||||
|
||||
entry, file_size = _manifest_entry(
|
||||
local_path, dest, None, db, repo_root,
|
||||
selection.fields(dest, file_entry["name"]),
|
||||
)
|
||||
if entry is not None and target_cores is None:
|
||||
order = declaration_order(full_dest)
|
||||
if full_dest in slot_choices or len(order) > 1:
|
||||
_record_slot_choices(
|
||||
entry, slot_choices.get(full_dest, []), order,
|
||||
settle, dest, db, repo_root,
|
||||
)
|
||||
if entry is None:
|
||||
record_omission(full_dest, file_entry, sys_id, "not_found", None)
|
||||
if file_size and full_dest not in seen_destinations:
|
||||
@@ -3706,7 +3823,7 @@ def _manifest_core_index(
|
||||
}
|
||||
named = {
|
||||
c for entry in [*files, *omitted]
|
||||
for copy in [entry, *entry.get("alternatives", [])]
|
||||
for copy in [entry, *entry.get("alternatives", []), *entry.get("slot_choices", [])]
|
||||
for c in copy.get("cores") or []
|
||||
}
|
||||
named |= {c for cores in system_cores.values() for c in cores}
|
||||
|
||||
+79
-24
@@ -28,6 +28,7 @@ from common import (
|
||||
parse_md5_list,
|
||||
resolution_is_hash_exact,
|
||||
resolve_local_file,
|
||||
resolve_platform_cores,
|
||||
runs_standalone,
|
||||
)
|
||||
from packresolve import preferred_entries
|
||||
@@ -302,20 +303,16 @@ def _claim_rank(claim: Claim) -> int:
|
||||
return 1 if claim.is_proven else 0
|
||||
|
||||
|
||||
def find_conflicts(
|
||||
def _slot_claims(
|
||||
config: dict,
|
||||
profiles: dict,
|
||||
db: dict,
|
||||
base_dest: str = "",
|
||||
standalone_cores: set[str] | None = None,
|
||||
zip_contents: dict | None = None,
|
||||
data_dir_registry: dict | None = None,
|
||||
) -> list[Conflict]:
|
||||
"""Destinations where a proven profile claim contradicts what ships.
|
||||
|
||||
Only proven claims are compared. A claim resolved by filename alone
|
||||
asserts nothing about content and cannot contradict anything.
|
||||
"""
|
||||
base_dest: str,
|
||||
standalone_cores: set[str] | None,
|
||||
zip_contents: dict | None,
|
||||
data_dir_registry: dict | None,
|
||||
) -> tuple[dict[str, Claim], dict[str, list[Claim]]]:
|
||||
"""The platform claim and the proven profile claims at each destination."""
|
||||
fold = bool(config.get("case_insensitive_fs", False))
|
||||
# The builder's own choice among declarations sharing a destination,
|
||||
# read from the same function: the most accepting archive for zipped_file
|
||||
@@ -369,6 +366,27 @@ def find_conflicts(
|
||||
continue
|
||||
by_slot.setdefault(key, []).append(claim)
|
||||
|
||||
return by_dest, by_slot
|
||||
|
||||
|
||||
def find_conflicts(
|
||||
config: dict,
|
||||
profiles: dict,
|
||||
db: dict,
|
||||
base_dest: str = "",
|
||||
standalone_cores: set[str] | None = None,
|
||||
zip_contents: dict | None = None,
|
||||
data_dir_registry: dict | None = None,
|
||||
) -> list[Conflict]:
|
||||
"""Destinations where a proven profile claim contradicts what ships.
|
||||
|
||||
Only proven claims are compared. A claim resolved by filename alone
|
||||
asserts nothing about content and cannot contradict anything.
|
||||
"""
|
||||
by_dest, by_slot = _slot_claims(
|
||||
config, profiles, db, base_dest, standalone_cores, zip_contents,
|
||||
data_dir_registry,
|
||||
)
|
||||
# A declaration that lists several accepted hashes is satisfied by any of
|
||||
# them: Recalbox names both scph1001.bin revisions, and mesence names
|
||||
# four BS-X dumps among which System.dat pins one. The resolver stops on
|
||||
@@ -428,20 +446,45 @@ def pack_overrides(
|
||||
verify all read this: two of them deciding alone gave the one-line
|
||||
installer different bytes than the ZIP.
|
||||
"""
|
||||
from common import resolve_platform_cores
|
||||
|
||||
# A baseline-only pack serves what the platform declares, and a target's
|
||||
# pack answers to the cores that hardware builds: DuckStation does not
|
||||
# exist on any RetroArch target and still replaced its scph5000.bin.
|
||||
if not profiles or source == "platform":
|
||||
return {}
|
||||
platform_profiles = {
|
||||
name: profiles[name]
|
||||
for name in resolve_platform_cores(config, profiles, target_cores=target_cores)
|
||||
}
|
||||
mode = config.get("verification_mode", "existence")
|
||||
cores = set(resolve_platform_cores(config, profiles, target_cores=target_cores))
|
||||
overrides: dict[str, str] = {}
|
||||
for conflict in find_conflicts(
|
||||
for destination, choices in slot_choices(
|
||||
config, profiles, db, zip_contents, data_dir_registry
|
||||
).items():
|
||||
path = next((path for emulator, path in choices if emulator in cores), None)
|
||||
if path:
|
||||
overrides[destination] = path
|
||||
return overrides
|
||||
|
||||
|
||||
def slot_choices(
|
||||
config: dict,
|
||||
profiles: dict,
|
||||
db: dict,
|
||||
zip_contents: dict | None = None,
|
||||
data_dir_registry: dict | None = None,
|
||||
) -> dict[str, list[tuple[str, str | None]]]:
|
||||
"""Destination -> (core, the file it has served there) in decision order.
|
||||
|
||||
A pack built for a set of cores serves the first choice whose core is in
|
||||
it, None standing for the platform's own answer; no core in it leaves
|
||||
the platform's answer too. The order is arbitrate's: a core agreeing with
|
||||
the platform settles the slot whatever else is present, then the
|
||||
strongest proof decides. Read for one core set by pack_overrides and
|
||||
recorded whole in the install manifest, so a target narrowed from the
|
||||
full manifest gets the bytes the target's own pack carries.
|
||||
"""
|
||||
if nativemode.reads_file_contents(config.get("verification_mode", "existence")):
|
||||
return {}
|
||||
platform_profiles = {
|
||||
name: profiles[name] for name in resolve_platform_cores(config, profiles)
|
||||
}
|
||||
by_dest, by_slot = _slot_claims(
|
||||
config,
|
||||
platform_profiles,
|
||||
db,
|
||||
@@ -449,11 +492,23 @@ def pack_overrides(
|
||||
{str(c) for c in config.get("standalone_cores", [])},
|
||||
zip_contents,
|
||||
data_dir_registry,
|
||||
):
|
||||
decision = arbitrate(conflict, mode)
|
||||
if decision.serves_both and decision.winner.local_path:
|
||||
overrides[conflict.destination] = decision.winner.local_path
|
||||
return overrides
|
||||
)
|
||||
choices: dict[str, list[tuple[str, str | None]]] = {}
|
||||
for key, claims in sorted(by_slot.items()):
|
||||
platform = by_dest[key]
|
||||
agree = [_satisfies(c, platform, db) for c in claims]
|
||||
agreeing = [c for c, ok in zip(claims, agree) if ok]
|
||||
others = sorted(
|
||||
(c for c, ok in zip(claims, agree) if not ok),
|
||||
key=lambda claim: (-_claim_rank(claim), claims.index(claim)),
|
||||
)
|
||||
if not others:
|
||||
continue
|
||||
choices[platform.destination] = [
|
||||
*((c.emulator, None) for c in agreeing),
|
||||
*((c.emulator, c.local_path) for c in others),
|
||||
]
|
||||
return choices
|
||||
|
||||
|
||||
def apply_override(
|
||||
|
||||
@@ -244,6 +244,74 @@ class APlatformOmissionKeepsItsClaim(unittest.TestCase):
|
||||
)
|
||||
|
||||
|
||||
class APlatformFileSettledByCoresAndSystems(unittest.TestCase):
|
||||
"""RetroArch's dsi_bios9.bin ships melonds' copy where melonds runs; on
|
||||
a target without it the pack serves the platform's answer, and the
|
||||
pinned nintendo-dsi declaration gives way to the bare nintendo-ds one
|
||||
once its system drops. The installer kept melonds' copy on rpi4."""
|
||||
|
||||
def copy(self, letter: str) -> dict:
|
||||
return {"sha1": letter * 40, "size": 1, "repo_path": f"bios/{letter}.bin"}
|
||||
|
||||
def manifest(self) -> dict:
|
||||
entry = {
|
||||
"dest": "dsi_bios9.bin", **self.copy("a"), "cores": None,
|
||||
"systems": ["nintendo-ds", "nintendo-dsi"],
|
||||
"slot_choices": [{"cores": ["desmume"]}, {**self.copy("a"), "cores": ["melonds"]}],
|
||||
"declared_choices": [
|
||||
{**self.copy("c"), "systems": ["nintendo-dsi"]},
|
||||
{**self.copy("b"), "systems": ["nintendo-ds"]},
|
||||
],
|
||||
}
|
||||
return {
|
||||
"manifest_version": 2, "platform": "retroarch",
|
||||
"files": [entry], "omitted_files": [],
|
||||
"core_names": {},
|
||||
"system_cores": {"nintendo-ds": ["desmume", "melonds", "nds"],
|
||||
"nintendo-dsi": ["melonds", "dsi"]},
|
||||
}
|
||||
|
||||
def served(self, names: list[str]) -> str:
|
||||
files, _ = install.narrow_to_cores(self.manifest(), names)
|
||||
self.assertEqual(len(files), 1)
|
||||
self.assertNotIn("declared_choices", files[0])
|
||||
return files[0]["sha1"][0]
|
||||
|
||||
def test_the_first_chosen_core_decides(self):
|
||||
self.assertEqual(self.served(["melonds"]), "a")
|
||||
|
||||
def test_a_core_agreeing_with_the_platform_leaves_its_answer(self):
|
||||
self.assertEqual(self.served(["desmume", "melonds"]), "c")
|
||||
self.assertEqual(self.served(["desmume"]), "b")
|
||||
|
||||
def test_the_remaining_system_answers_without_the_cores(self):
|
||||
self.assertEqual(self.served(["dsi"]), "c")
|
||||
self.assertEqual(self.served(["nds"]), "b")
|
||||
|
||||
def test_the_boundary_checks_every_copy(self):
|
||||
install._validate_manifest(self.manifest(), "retroarch")
|
||||
bad_choices = (
|
||||
[{"cores": []}],
|
||||
[{"cores": ["x"], "repo_path": "../x", "sha1": "a" * 40, "size": 1}],
|
||||
[{"cores": ["x"], "dest": "../evil"}],
|
||||
)
|
||||
for choices in bad_choices:
|
||||
manifest = self.manifest()
|
||||
manifest["files"][0]["slot_choices"] = choices
|
||||
with self.subTest(choices=choices), self.assertRaises(ValueError):
|
||||
install._validate_manifest(manifest, "retroarch")
|
||||
for declared in ([], [self.copy("c")], [{**self.copy("c"), "systems": []}],
|
||||
[{**self.copy("c"), "systems": ["s"], "cores": ["x"]}]):
|
||||
manifest = self.manifest()
|
||||
manifest["files"][0]["declared_choices"] = declared
|
||||
with self.subTest(declared=declared), self.assertRaises(ValueError):
|
||||
install._validate_manifest(manifest, "retroarch")
|
||||
manifest = self.manifest()
|
||||
del manifest["files"][0]["declared_choices"]
|
||||
with self.assertRaises(ValueError):
|
||||
install._validate_manifest(manifest, "retroarch")
|
||||
|
||||
|
||||
class RefusedBeforeAnyInstall(unittest.TestCase):
|
||||
def test_a_platform_shipping_its_cores_stops_the_run_first(self):
|
||||
tmp = Path(tempfile.mkdtemp(dir=REPO_ROOT / "tmp"))
|
||||
|
||||
+5
-1
@@ -173,7 +173,11 @@ What stays is what a pack built for exactly those cores carries, the rule
|
||||
emulates it or reads its files, or when no core of the platform does; a
|
||||
file a core loads stays when that core is installed. Where two cores read
|
||||
one path and want different bytes, the copy of the first installed core in
|
||||
the builder's order is the one installed.
|
||||
the builder's order is the one installed. The same holds for a file of the
|
||||
platform's own list: where a core's source contradicts the platform and the
|
||||
frontend only checks the path, the core's copy is installed while that core
|
||||
is, and otherwise the platform's answer, read from the first declaring
|
||||
system that stays.
|
||||
|
||||
Batocera, Recalbox and the other distributions ship their cores with the
|
||||
system, so the option is refused there, as it is when no core is found or
|
||||
|
||||
Reference in new issue
Block a user