mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-06 01:53:56 -06:00
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:
1 parent
349914fda2
commit
a2e8809945
14 files changed
+5236
-4691
No files matched your search
+1
-1
@@ -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
@@ -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
@@ -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() {
|
||||
|
||||
@@ -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
File diff suppressed because it is too large.
Load diff
+1343
-1274
File diff suppressed because it is too large.
Load diff
@@ -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
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"])
|
||||
|
||||
@@ -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
@@ -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` |
|
||||
|
||||
Reference in new issue
Block a user