fix: map a card's image name to its board

Batocera and KNULLI write the image name, not the board, to the card's board file (post-image-script.sh). The target scraper now records each board's images from Config.in, the target lists carry them, and the installer maps a card's image to its board. KNULLI's boot/knulli.board is read too.
This commit is contained in:
Abdessamad Derraz committed 2026-11-06 03:40:34 +01:00
1 parent 349914fda2
commit a2e8809945
14 files changed
+5236 -4691

No files matched your search

+1 -1
View File
@@ -16,7 +16,7 @@
# and Invoke-WebRequest throws on a cut transfer before the hash is read:
# the site's build rewrites this line there (scripts/sitebootstrap.py).
$mismatchAdvice = "The download was most likely cut short: try again. Within minutes of an update of this project the two files can come from caches of different ages. Waiting five minutes settles that."
$defaultInstallSha256 = "8e1b953a928a794c79d73105dc4b45dccd5a2f55651f8a262ac51783352f16c1"
$defaultInstallSha256 = "4d5fbd4fcf4fc42e4ae02ebfa568fd906ef673d003eefcae22e38e520d70c243"
$maximumInstallerBytes = 2MB
$installerExit = 0
$installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null }
+25 -3
View File
@@ -1488,8 +1488,11 @@ _BOARD_NAMES: dict[str, dict[str, str]] = {
# the FAT one beside SHARE (batocera-43.1, board/batocera/scripts/
# post-image-script.sh:60), and reads it there itself (package/batocera/
# core/batocera-scripts/scripts/batocera-info). batocera.arch is inside the
# image, out of a computer's reach.
_CARD_BOARD_FILES: dict[str, str] = {"batocera": "boot/batocera.board"}
# image, out of a computer's reach. KNULLI writes boot/knulli.board the same
# way (knulli-linux 20260511, board/scripts/post-image-script.sh:66). Both
# write the image's name, which the target list maps to its board.
_CARD_BOARD_FILES: dict[str, str] = {"batocera": "boot/batocera.board",
"knulli": "boot/knulli.board"}
def card_board(platform: str) -> str | None:
@@ -3905,9 +3908,26 @@ def _validate_targets(data: object) -> dict[str, dict]:
):
raise ValueError(f"invalid core list for target {target}")
normalized[target] = {"cores": cores}
images = value.get("images") if isinstance(value, dict) else None
if images is None:
continue
if not isinstance(images, list) or len(images) > 10_000 or not all(
isinstance(image, str) and 0 < len(image) <= 128 for image in images):
raise ValueError(f"invalid image list for target {target}")
normalized[target]["images"] = images
return normalized
def board_target(board: str, targets: dict[str, dict]) -> str | None:
"""The target a board name stands for: the name itself, else the one
target whose images carry it. A card names its image (Batocera writes
anbernic-rg35xx-plus for the h700 target, x86-64-v3 for zen3)."""
if board in targets:
return board
owners = [name for name, info in targets.items() if board in info.get("images", ())]
return owners[0] if len(owners) == 1 else None
NETWORK_ADVICE = (" Check the connection, and HTTPS_PROXY if this network needs a "
"proxy, then run the same command again.\n"
f" More help: {TROUBLESHOOTING_PAGE}")
@@ -15195,9 +15215,11 @@ def _install(args: argparse.Namespace, real_stdout: TextIO) -> None:
board, where = card_board(plat_name), "found on the card"
if board:
known = fetch_targets(plat_name) or {}
image, board = board, board_target(board, known) or board
if board in known:
target = board
words = f"Board {board} {where} (--every-board installs all boards)"
named = board if image == board else f"{board} ({image})"
words = f"Board {named} {where} (--every-board installs all boards)"
else:
words = (f"Board {board} {where} has no "
f"{PLATFORM_NAMES.get(plat_name, plat_name)} core list: "
+1 -1
View File
@@ -23,7 +23,7 @@ DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/ins
# curl or wget stop on a cut transfer before the hash is read: the site's
# build rewrites this line there (scripts/sitebootstrap.py).
MISMATCH_ADVICE="The download was most likely cut short: try again. Within minutes of an update of this project the two files can come from caches of different ages. Waiting five minutes settles that."
DEFAULT_INSTALL_SHA256="8e1b953a928a794c79d73105dc4b45dccd5a2f55651f8a262ac51783352f16c1"
DEFAULT_INSTALL_SHA256="4d5fbd4fcf4fc42e4ae02ebfa568fd906ef673d003eefcae22e38e520d70c243"
MAX_INSTALLER_BYTES=2097152
cleanup() {
+8 -8
View File
@@ -27,8 +27,8 @@
{
"dest": "roms/ports/retrobios/install.py",
"repo_path": "install.py",
"size": 1098291,
"sha256": "8e1b953a928a794c79d73105dc4b45dccd5a2f55651f8a262ac51783352f16c1",
"size": 1099572,
"sha256": "4d5fbd4fcf4fc42e4ae02ebfa568fd906ef673d003eefcae22e38e520d70c243",
"executable": false
},
{
@@ -71,8 +71,8 @@
"muos": {
"archive": {
"dest": "ARCHIVE/RetroBIOS.muxapp",
"size": 1151918,
"sha256": "4981792f43dd11f8bb09f0c9981abe721934a84d5d8a49c05bfca44d736ddd83"
"size": 1153199,
"sha256": "284143d1c9bed3becfbe1ecee2e690cd11dc0ee495c8beeb9d7dca70cd63622e"
},
"members": [
{
@@ -92,8 +92,8 @@
{
"name": "RetroBIOS/install.py",
"repo_path": "install.py",
"size": 1098291,
"sha256": "8e1b953a928a794c79d73105dc4b45dccd5a2f55651f8a262ac51783352f16c1",
"size": 1099572,
"sha256": "4d5fbd4fcf4fc42e4ae02ebfa568fd906ef673d003eefcae22e38e520d70c243",
"executable": false
},
{
@@ -166,8 +166,8 @@
{
"dest": ".config/modules/retrobios/install.py",
"repo_path": "install.py",
"size": 1098291,
"sha256": "8e1b953a928a794c79d73105dc4b45dccd5a2f55651f8a262ac51783352f16c1",
"size": 1099572,
"sha256": "4d5fbd4fcf4fc42e4ae02ebfa568fd906ef673d003eefcae22e38e520d70c243",
"executable": false
},
{
+3580 -3391
View File
File diff suppressed because it is too large. Load diff
+1343 -1274
View File
File diff suppressed because it is too large. Load diff
+124 -1
View File
@@ -1,6 +1,6 @@
platform: batocera
source: https://github.com/batocera-linux/batocera.linux/tree/batocera-43.1
scraped_at: '2026-11-05T10:55:18Z'
scraped_at: '2026-11-06T02:18:59Z'
targets:
a3gen2:
architecture: aarch64
@@ -169,6 +169,8 @@ targets:
- yabasanshiro
- yquake2
- zc210
images:
- khadas-vim4
bcm2711:
architecture: aarch64
cores:
@@ -1063,6 +1065,12 @@ targets:
- xvic
- yquake2
- zc210
images:
- bananapi-m2-zero
- cha
- orangepi-one
- orangepi-pc
- orangepi-plus2e
h5:
architecture: aarch64
cores:
@@ -1214,6 +1222,9 @@ targets:
- xvic
- yquake2
- zc210
images:
- orangepi-pc2
- tritium-h5
h6:
architecture: aarch64
cores:
@@ -1364,6 +1375,10 @@ targets:
- xvic
- yquake2
- zc210
images:
- orangepi-3
- orangepi-3-lts
- orangepi-one-plus
h616:
architecture: aarch64
cores:
@@ -1523,6 +1538,10 @@ targets:
- yabasanshiro
- yquake2
- zc210
images:
- orangepi-zero2
- orangepi-zero3
- x96-mate
h700:
architecture: aarch64
cores:
@@ -1695,6 +1714,22 @@ targets:
- yabasanshiro
- yquake2
- zc210
images:
- anbernic-rg28xx
- anbernic-rg34xx
- anbernic-rg34xx-sp
- anbernic-rg35xx-2024
- anbernic-rg35xx-2024-rev6-panel
- anbernic-rg35xx-h
- anbernic-rg35xx-h-rev6-panel
- anbernic-rg35xx-plus
- anbernic-rg35xx-plus-rev6-panel
- anbernic-rg35xx-pro
- anbernic-rg35xx-sp
- anbernic-rg35xx-sp-v2-panel
- anbernic-rg40xx-h
- anbernic-rg40xx-v
- anbernic-rgcubexx
jh7110:
architecture: riscv64
cores:
@@ -2328,6 +2363,8 @@ targets:
- ymir
- yquake2
- zc210
images:
- dragonq6a
rk3128:
architecture: arm
cores:
@@ -2471,6 +2508,10 @@ targets:
- xvic
- yquake2
- zc210
images:
- powkiddy_a13
- ps5000
- ps7000
rk3288:
architecture: arm
cores:
@@ -2626,6 +2667,9 @@ targets:
- yabasanshiro
- yquake2
- zc210
images:
- miqi
- tinkerboard
rk3326:
architecture: aarch64
cores:
@@ -2780,6 +2824,10 @@ targets:
- yabasanshiro
- yquake2
- zc210
images:
- r36s
- rg351v-v2
- rk3326
rk3328:
architecture: aarch64
cores:
@@ -2932,6 +2980,9 @@ targets:
- xvic
- yquake2
- zc210
images:
- roc-cc
- rock64
rk3399:
architecture: aarch64
cores:
@@ -3094,6 +3145,15 @@ targets:
- yabasanshiro
- yquake2
- zc210
images:
- hugsun-x99
- nanopi-m4v2
- orangepi-800
- orangepi4-lts
- rg552
- rock-pi-4
- rock960
- rockpro64
rk3568:
architecture: aarch64
cores:
@@ -3268,6 +3328,17 @@ targets:
- yabasanshiro
- yquake2
- zc210
images:
- anbernic-rgxx3
- firefly-station-m2
- firefly-station-p2
- odroid-m1
- odroid-m1s
- orangepi-3b
- powkiddy-x55
- radxa-zero-3w
- rock-3a
- rock-3c
rk3588-sdio:
architecture: aarch64
cores:
@@ -3455,6 +3526,9 @@ targets:
- ymir
- yquake2
- zc210
images:
- orangepi-5-max
- orangepi-5-ultra
rk3588:
architecture: aarch64
cores:
@@ -3642,6 +3716,25 @@ targets:
- ymir
- yquake2
- zc210
images:
- bananapi-m7
- coolpi-4b
- firefly-station-m3
- friendlyelec-cm3588-nas
- gameforce-ace
- indiedroid-nova
- khadas-edge-2
- mekotronics-r58
- nanopi-m6
- orangepi-5
- orangepi-5-plus
- orangepi-5-pro
- orangepi-5b
- quartzpro64
- rock-5a
- rock-5b
- rock-5b-plus
- rock-5c
s812:
architecture: arm
cores:
@@ -3940,6 +4033,14 @@ targets:
- xvic
- yquake2
- zc210
images:
- lepotato
- lepotatov2
- minix-neo-u1
- odroidc2
- p201
- tvbox
- vim
s905gen2:
architecture: aarch64
cores:
@@ -4102,6 +4203,9 @@ targets:
- yabasanshiro
- yquake2
- zc210
images:
- khadas-vim2
- radxa-zero
s905gen3:
architecture: aarch64
cores:
@@ -4264,6 +4368,10 @@ targets:
- yabasanshiro
- yquake2
- zc210
images:
- khadas-vim3l
- odroidc4
- tvbox-gen3
s922x:
architecture: aarch64
cores:
@@ -4431,6 +4539,15 @@ targets:
- yabasanshiro
- yquake2
- zc210
images:
- gtking
- gtkingpro
- odroidn2
- odroidn2l
- odroidn2plus
- ogu
- vim3
- zero2
s9gen4:
architecture: aarch64
cores:
@@ -4591,6 +4708,8 @@ targets:
- yabasanshiro
- yquake2
- zc210
images:
- khadas-vim1s
sm8250:
architecture: aarch64
cores:
@@ -5147,6 +5266,8 @@ targets:
- yabasanshiro
- yquake2
- zc210
images:
- orangepi-4a
x86_64:
architecture: x86_64
cores:
@@ -5599,3 +5720,5 @@ targets:
- ymir
- yquake2
- zc210
images:
- x86-64-v3
+43 -1
View File
@@ -1,6 +1,6 @@
platform: knulli
source: https://github.com/knulli-cfw/knulli-linux/tree/20260511
scraped_at: '2026-11-05T10:55:16Z'
scraped_at: '2026-11-06T02:19:00Z'
targets:
a133:
architecture: aarch64
@@ -165,6 +165,13 @@ targets:
- xvic
- yabasanshiro
- zc210
images:
- magicx-zero-28
- magicx-zero-40
- powkiddy-v20
- powkiddy-v90s
- trimui-brick
- trimui-smart-pro
a527:
architecture: aarch64
cores:
@@ -329,6 +336,9 @@ targets:
- xvic
- yabasanshiro
- zc210
images:
- helegany-action-pi
- trimui-smart-pro-s
atm7039:
architecture: arm
cores:
@@ -460,6 +470,8 @@ targets:
- xvic
- yabasanshiro
- zc210
images:
- rg35xx
h700:
architecture: aarch64
cores:
@@ -625,6 +637,17 @@ targets:
- xvic
- yabasanshiro
- zc210
images:
- rg-cubexx
- rg28xx
- rg34xx
- rg34xx-sp
- rg35xx-h
- rg35xx-plus
- rg35xx-pro
- rg35xx-sp
- rg40xx-h
- rg40xx-v
r16:
architecture: arm
cores:
@@ -762,6 +785,8 @@ targets:
- xscpu64
- xvic
- zc210
images:
- miyoo-a30
rk3128:
architecture: arm
cores:
@@ -901,6 +926,10 @@ targets:
- xvic
- yabasanshiro
- zc210
images:
- powkiddy_a13
- ps5000
- ps7000
rk3326:
architecture: aarch64
cores:
@@ -1052,6 +1081,11 @@ targets:
- xvic
- yabasanshiro
- zc210
images:
- g350
- magicx-mini-m
- pixel2
- r36s
rk3566:
architecture: aarch64
cores:
@@ -1211,6 +1245,12 @@ targets:
- xvic
- yabasanshiro
- zc210
images:
- miyoo-flip
- powkiddy-rgb30
- powkiddy-x55
- rg-arc-s
- rg-ds
rk3576:
architecture: aarch64
cores:
@@ -1369,6 +1409,8 @@ targets:
- xvic
- yabasanshiro
- zc210
images:
- rg-vita-pro
sm8250:
architecture: aarch64
cores:
+5 -1
View File
@@ -13,7 +13,11 @@
"additionalProperties": false,
"required": ["cores"],
"properties": {
"cores": { "type": "array", "items": { "type": "string", "minLength": 1 }, "uniqueItems": true }
"cores": { "type": "array", "items": { "type": "string", "minLength": 1 }, "uniqueItems": true },
"images": {
"description": "The image names the target builds, as a card's board file writes them (boot/batocera.board, boot/knulli.board).",
"type": "array", "items": { "type": "string", "minLength": 1 }, "uniqueItems": true
}
}
}
]
+14 -4
View File
@@ -2415,7 +2415,7 @@ def generate_target_manifests(targets_dir: str, output_dir: str) -> None:
print(f"Generated {count} target manifest(s) in {output_dir}")
def target_list(yml_file: Path) -> dict[str, list[str] | None]:
def target_list(yml_file: Path) -> dict[str, list[str] | dict | None]:
"""A platform's cores per hardware target, as install/targets/ publishes
them and a pack carries them: aliases included, overrides applied."""
targets_path = yml_file.parent
@@ -2443,7 +2443,7 @@ def target_list(yml_file: Path) -> dict[str, list[str] | None]:
if names:
alias_map[tname] = names
result: dict[str, list[str] | None] = {}
result: dict[str, list[str] | dict | None] = {}
for target_name, target_info in targets.items():
if not isinstance(target_name, str) or not target_name:
raise ValueError(f"{yml_file}: target names must be non-empty strings")
@@ -2468,7 +2468,15 @@ def target_list(yml_file: Path) -> dict[str, list[str] | None]:
resolved = load_target_config(
yml_file.stem, target_name, str(targets_path.parent)
)
result[target_name] = None if resolved is None else sorted(resolved)
listed = None if resolved is None else sorted(resolved)
images = target_info.get("images")
# The image names a card's board file holds (Batocera writes
# anbernic-rg35xx-plus for the h700 target): the installer reads
# the board of a card prepared on a computer through them.
result[target_name] = (
{"cores": listed, "images": images}
if images and listed is not None else listed
)
for target_name, names in alias_map.items():
if target_name not in result:
@@ -2478,7 +2486,9 @@ def target_list(yml_file: Path) -> dict[str, list[str] | None]:
raise ValueError(
f"{yml_file}: alias {alias!r} collides with a target name"
)
result[alias] = result[target_name]
# An alias names the board, not its images.
held = result[target_name]
result[alias] = held["cores"] if isinstance(held, dict) else held
return result
@@ -36,6 +36,35 @@ except ImportError:
_TARGET_FLAG_RE = re.compile(r"^(BR2_PACKAGE_BATOCERA_TARGET_\w+)=y", re.MULTILINE)
# The images a target flag builds, each a board directory whose last segment
# post-image-script.sh writes to boot/<system>.board on the card
# (batocera-43.1 board/batocera/scripts/post-image-script.sh:50,60, KNULLI
# 20260511 board/scripts/post-image-script.sh:57,66): Config.in lists them
# under BR2_TARGET_<SYSTEM>_IMAGES, one "default" per target flag.
_IMAGES_DEFAULT_RE = re.compile(
r'^\s*default\s+"([^"]*)"\s+if\s+(BR2_PACKAGE_BATOCERA_TARGET_\w+)\s*$')
def _parse_images(text: str) -> dict[str, list[str]]:
"""Each target flag's image names, in the words a card's board file uses."""
lines = text.replace("\\\n", " ").splitlines()
start = next((n for n, line in enumerate(lines)
if re.match(r"^\s*config\s+BR2_TARGET_[A-Z]+_IMAGES\s*$", line)), None)
if start is None:
return {}
images: dict[str, list[str]] = {}
for line in lines[start + 1:]:
if not line.strip():
continue
m = _IMAGES_DEFAULT_RE.match(line)
if m:
images.setdefault(m.group(2), []).extend(
path.rsplit("/", 1)[-1] for path in m.group(1).split())
elif not re.match(r"^\s*(string|default|help)\b", line):
break
return images
_IF_RE = re.compile(r"^if\s+(.*)$")
_DEFAULT_Y_RE = re.compile(r"^\s*default\s+y\s*(?:#.*)?$")
@@ -428,6 +457,9 @@ class Scraper(BaseTargetScraper):
)
if not package_to_emulators:
raise RuntimeError(f"no package->emulator mappings parsed from {es_systems_url}")
images_by_flag = _parse_images(config_in_text)
if not images_by_flag:
raise RuntimeError(f"no target image list parsed from {config_in_url}")
targets: dict[str, dict] = {}
for board_name in sorted(boards):
@@ -456,6 +488,9 @@ class Scraper(BaseTargetScraper):
"architecture": arch,
"cores": sorted(emulators),
}
images = sorted(set(images_by_flag.get(primary_flag, [])))
if images and images != [target_key]:
targets[target_key]["images"] = images
print(
f" {len(emulators)} emulators ({len(selected_packages)} packages selected)",
file=sys.stderr,
+40
View File
@@ -1584,6 +1584,8 @@ class TargetFlagIsAppliedOrRefused(unittest.TestCase):
with self.subTest(platform=platform):
self.assertEqual(sorted(targets), sorted(known))
for target, cores in targets.items():
# A target that names its card images carries them beside.
cores = cores["cores"] if isinstance(cores, dict) else cores
with self.subTest(platform=platform, target=target):
resolved = load_target_config(platform, target, platforms)
self.assertEqual(cores, None if resolved is None else sorted(resolved))
@@ -3347,6 +3349,44 @@ class ACardPreparedOnAComputerGetsItsBoard(unittest.TestCase):
files = {event["dest"] for event in events if event["event"] == "file"}
self.assertEqual(files, {"pi.bin"})
def test_a_card_naming_its_image_gets_the_board_of_that_image(self):
"""A Batocera RG35XX Plus card names anbernic-rg35xx-plus, a zen3 one
x86-64-v3: neither is a target name, and the card took every
board's files (9.0 GB where h700 reads 6.1)."""
(self.boot / "boot" / "batocera.board").write_text("anbernic-rg35xx-plus\n")
(self.list.parent / "targets" / "batocera.json").write_text(json.dumps({
"h700": {"cores": ["picore"], "images": ["anbernic-rg28xx",
"anbernic-rg35xx-plus"]},
"x86_64": ["pccore"]}))
out, events = self.run_main()
[board] = [event for event in events if event["event"] == "board"]
self.assertEqual((board["board"], board["applied"]), ("h700", True))
self.assertIn("Board h700 (anbernic-rg35xx-plus) found on the card", out)
files = {event["dest"] for event in events if event["event"] == "file"}
self.assertEqual(files, {"pi.bin"})
def test_an_image_two_boards_build_names_neither(self):
targets = install._validate_targets({
"zen3": {"cores": ["a"], "images": ["x86-64-v3"]},
"rk3326": {"cores": ["b"], "images": ["r36s"]},
"rk3566": {"cores": ["c"], "images": ["r36s"]}})
self.assertEqual(install.board_target("x86-64-v3", targets), "zen3")
self.assertEqual(install.board_target("zen3", targets), "zen3")
self.assertIsNone(install.board_target("r36s", targets))
def test_a_knulli_card_names_its_image_too(self):
(self.boot / "boot" / "knulli.board").write_text("rg35xx-plus\n")
with unittest.mock.patch.object(install, "_mounted_cards", return_value=[self.boot]):
self.assertEqual(install.card_board("knulli"), "rg35xx-plus")
published = json.loads((REPO_ROOT / "install" / "targets" / "knulli.json").read_text())
self.assertEqual(install.board_target(
"rg35xx-plus", install._validate_targets(published)), "h700")
batocera = install._validate_targets(json.loads(
(REPO_ROOT / "install" / "targets" / "batocera.json").read_text()))
for image, board in (("anbernic-rg35xx-plus", "h700"), ("x86-64-v3", "zen3"),
("odroidn2", "s922x"), ("bcm2711", "bcm2711")):
self.assertEqual(install.board_target(image, batocera), board)
def test_every_board_keeps_them_all(self):
_out, events = self.run_main("--every-board")
self.assertFalse([event for event in events if event["event"] == "board"])
+15 -4
View File
@@ -96,7 +96,9 @@ class FailedRequestsStopTheScrape(unittest.TestCase):
b"BR2_PACKAGE_BATOCERA_SYSTEM=y\n")
if url.endswith("Config.in"):
return _Body(b"config BR2_PACKAGE_BATOCERA_SYSTEM\n"
b"\tselect BR2_PACKAGE_MGBA\n")
b"\tselect BR2_PACKAGE_MGBA\n\n"
b"config BR2_TARGET_BATOCERA_IMAGES\n\tstring \"x\"\n"
b"\tdefault \"x86_64\" if BR2_PACKAGE_BATOCERA_TARGET_X86_64\n")
return _Body(
b"gba:\n emulators:\n libretro:\n"
b" mgba: {requireAnyOf: [BR2_PACKAGE_MGBA]}\n"
@@ -263,7 +265,14 @@ class KnulliPerBoard(unittest.TestCase):
"\tconfig BR2_PACKAGE_BATOCERA_GLES3\n\tdefault y\nendif\n\n"
"config BR2_PACKAGE_KNULLI_SYSTEM\n"
"\tselect BR2_PACKAGE_LIBRETRO_MGBA\n"
"\tselect BR2_PACKAGE_PPSSPP\tif BR2_PACKAGE_BATOCERA_GLES3\n"
"\tselect BR2_PACKAGE_PPSSPP\tif BR2_PACKAGE_BATOCERA_GLES3\n\n"
"config BR2_TARGET_KNULLI_IMAGES_DEFINITIONS\n"
"\tbool \"target images definitions\"\n"
"\tconfig BR2_TARGET_KNULLI_IMAGES\n\tstring \"target images\"\n"
"\tdefault \"allwinner/h700/rg35xx-sp allwinner/h700/rg35xx-plus\" \\\n"
"\t\tif BR2_PACKAGE_BATOCERA_TARGET_H700\n"
"\tdefault \"allwinner/r16/miyoo-a30\"\tif BR2_PACKAGE_BATOCERA_TARGET_SUNXI_R16\n\n"
"\tchoice\n\tprompt \"knulli target\"\n"
),
"package/emulationstation/knulli-es-system/es_systems.yml": (
"gba:\n emulators:\n libretro:\n"
@@ -316,10 +325,12 @@ class KnulliPerBoard(unittest.TestCase):
self.assertEqual(data["source"],
f"https://github.com/knulli-cfw/knulli-linux/tree/{pin}")
self.assertEqual(sorted(data["targets"]), ["h700", "r16"])
# The images name what a card's boot/knulli.board holds.
self.assertEqual(data["targets"]["h700"],
{"architecture": "aarch64", "cores": ["mgba", "ppsspp"]})
{"architecture": "aarch64", "cores": ["mgba", "ppsspp"],
"images": ["rg35xx-plus", "rg35xx-sp"]})
self.assertEqual(data["targets"]["r16"],
{"architecture": "arm", "cores": ["mgba"]})
{"architecture": "arm", "cores": ["mgba"], "images": ["miyoo-a30"]})
self.assertTrue(all(pin in url or "buildroot/c2d7985b" in url for url in seen), seen)
+2 -2
View File
@@ -961,7 +961,7 @@ what the options name, everything by default.
|--------|--------|
| <span id="option-platform"></span>`--platform NAME` | Install for this platform instead of the detected one. A system `--list-platforms` names under its own files (`muos`, `arkos`) stands for the file list it gets, and the run says which. ES-DE, LaunchBox and Android, which keep no BIOS folder, are answered with the run that fills each emulator's folder. Unknown names are refused with the closest ones |
| <span id="option-dest"></span>`--dest PATH` | Destination directory, overriding detection. Without `--platform`, the file list is the platform detected here: at a terminal the installer asks, Enter taking the one found, and otherwise it uses the one found, RetroArch's when nothing is found, and refuses when several are. The run prints whose list it is. A folder that does not exist is named with the nearest one that does, then created, except for a system that runs from its SD card, which refuses it. When more than its last level is missing (a mistyped path), a run at a terminal asks first, and the answer defaults to no |
| <span id="option-target"></span>`--target NAME` | Keep only the files the cores of that hardware target need, and the systems they emulate. An unknown target is refused rather than ignored, since carrying on would install everything. On Batocera, Recalbox, ROCKNIX, Lakka, KNULLI and RetroPie the board is read from the system (`batocera.arch`, `recalbox.arch`, `HW_DEVICE`, `LIBREELEC_DEVICE` or `LIBREELEC_PROJECT`, `knulli.arch`, the Pi model in `/proc/cpuinfo` where `/opt/retropie` exists) and applied when no `--target` is given, from a pack alone too when it carries the core list per board. On a computer, the board a Batocera card names on its BATOCERA partition (`boot/batocera.board`) is applied the same way, in line mode and on the full screen |
| <span id="option-target"></span>`--target NAME` | Keep only the files the cores of that hardware target need, and the systems they emulate. An unknown target is refused rather than ignored, since carrying on would install everything. On Batocera, Recalbox, ROCKNIX, Lakka, KNULLI and RetroPie the board is read from the system (`batocera.arch`, `recalbox.arch`, `HW_DEVICE`, `LIBREELEC_DEVICE` or `LIBREELEC_PROJECT`, `knulli.arch`, the Pi model in `/proc/cpuinfo` where `/opt/retropie` exists) and applied when no `--target` is given, from a pack alone too when it carries the core list per board. On a computer, the board a Batocera or KNULLI card names on its boot partition (`boot/batocera.board`, `boot/knulli.board`) is applied the same way, in line mode and on the full screen. The card names its image (`anbernic-rg35xx-plus`, `x86-64-v3`), which the target list maps to its board (`h700`, `zen3`) |
| <span id="option-every-board"></span>`--every-board` | Install the files of every board instead of the one the system names, for a card that moves between devices |
| <span id="option-installed-cores"></span>`--installed-cores` | Keep only what the cores already installed read (RetroArch, Lakka, RetroPie). See [Installed cores](#installed-cores) |
| <span id="option-cores-dir"></span>`--cores-dir PATH` | The folder RetroArch loads its cores from, for `--installed-cores` when it is not found. Implies `--installed-cores`. A folder that does not exist or cannot be opened is refused with the reason (code 1) |
@@ -1785,7 +1785,7 @@ other schemas under `api/v1/schemas/`.
| `error` | `stage`, `reason`, `platform`, `message` | The file list could not be read (`stage` is `manifest`): `reason` is `network`, `not_found` or `invalid`, `clock` when the server's certificate was refused and the system clock reads a date before the installer was written, `no_ca` when the machine holds no list of trusted certificates (`SSL_CERT_FILE` names one), `no_ssl` when this Python was built without its `ssl` module and cannot use HTTPS, or `offline` when `--offline` forbids the request. A folder named with `--from` cannot be read (`stage` is `from`, `reason` `not_found`, `not_a_folder` for a file that is no ZIP, `unreadable`, or `system` for the root of the system, `/proc`, `/sys` or `/dev`). The BIOS folder sits under a folder this user cannot open (`stage` is `dest`, `reason` `unreadable`), and the run ends with code 1 before anything is written. A platform, target, system, core or emulator named on the command line is unknown or cannot be used here (`stage` is `option`, `reason` `unknown`, `platform` the platform it was checked against or the name typed), refused before any download with code 2. The BIOS folder cannot be created (`stage` is `dest`, `reason` `not_writable`): a `summary` follows with what the run installed before it. Two systems are detected on one machine and no `--dest` names the folder (`stage` is `detect`, `reason` `conflict`). The platform named with `--platform` is found nowhere and no folder can be assumed for it: a card system with no card named, a portable emulator, Batocera or Recalbox named on a computer (their folder is the `bios` folder of their SHARE drive), or no usual folder on this system or one set aside behind a folder this user cannot open (`stage` is `detect`, `reason` `not_found`). So is a platform named by a file list or a pack, in a run without questions where it is found nowhere here. A `--dest` refused as the card of the system named carries the same `stage` and `reason`: another system's card, a folder inside a card, a drive with none of the files its card always has, or a folder that does not exist. `message` says it in words, and is the only thing that tells these cases apart |
| `scanned` | `folder`, `files`, `truncated`, `other_drives` | Each folder named with `--from`, once read and before `detect`. `files` counts the files found, `truncated` is true when the search stopped at 200,000 of them, and `other_drives` lists the folders below it on another drive, which were not entered |
| `version` | `platform`, `list_version`, `installed_version`, `message` | Before `choices`, when the system here runs another version than the one the list was built for: `list_version` is the list's, `installed_version` the one the system's version file states, and `message` says it and what it means. Not written when either version is unknown. The run goes on |
| `board` | `platform`, `board`, `applied`, `message` | Before `choices`, with no `--target` and no `--every-board`, when the system names its board or a Batocera card mounted here does (`boot/batocera.board` on its BATOCERA partition): `applied` is true when the run keeps that board's files only, false for a board the platform's list does not name, which keeps every board's files. `message` says it |
| `board` | `platform`, `board`, `applied`, `message` | Before `choices`, with no `--target` and no `--every-board`, when the system names its board or a Batocera or KNULLI card mounted here does (`boot/batocera.board` or `boot/knulli.board` on its boot partition, `board` then being the board its image maps to): `applied` is true when the run keeps that board's files only, false for a board the platform's list does not name, which keeps every board's files. `message` says it |
| `choices` | `platform`, `systems` | Before `checking`: the systems the run's list lets one choose among, sorted by id, `other` last for the files no system names. The run's other options apply (`--core`, `--region`, `--no-game-data`, the files left out for an unknown save directory), `--system` does not: these are the systems a screen lists to choose from, and the count a `--system` is out of |
| `set_aside` | `platform`, `dests` | Right after `choices`: the files of the list the run sets aside before any choice, in list order, those read from a save directory whose place is not known among them. A screen chooses its cores among the rest, the cores `--core` accepts |
| `file` | `platform`, `dest`, `systems`, `size`, `state` | Each file of the list after the check. `state` is `present` (hash verified), `wrong_hash` or `missing` |