mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-26 05:37:20 -05:00
fix: offer only a pack that serves the system
Release packs now exist for six platforms only, and install.py refuses a pack named for another. build_port.py writes packs.txt from the registry's release_pack groups. Offline, the launcher takes only a pack spelling the detected system, names that pack, or says no release pack serves it and gives Wi-Fi or the computer route.
This commit is contained in:
1 parent
e6ce3085b1
commit
d614acbef3
4 files changed
+161
-20
No files matched your search
+33
-6
@@ -304,9 +304,10 @@ rb_detect() {
|
||||
done < <(rb_py "$RB_EVENTS" status "$RB_STATE/detect.jsonl" 2>> "$RB_LOG")
|
||||
}
|
||||
|
||||
# The first pack in RB_PACK_PATH, one whose name spells this system first:
|
||||
# install.py reads the platform from the same spelling (Name_With_Underscores
|
||||
# between two underscores).
|
||||
# The first pack in RB_PACK_PATH whose name spells this system: install.py
|
||||
# reads the platform from the same spelling (Name_With_Underscores between
|
||||
# two underscores) and refuses a pack named for another. Any pack only when
|
||||
# the system is not known, since install.py then reads it from the name.
|
||||
rb_find_pack() {
|
||||
local dir file base stem first="" want=""
|
||||
if [ -n "${RB_PLATFORM_NAME:-}" ]; then
|
||||
@@ -323,13 +324,27 @@ rb_find_pack() {
|
||||
printf '%s\n' "$file"
|
||||
return 0
|
||||
fi
|
||||
[ -z "$first" ] && first="$file"
|
||||
[ -z "$want" ] && [ -z "$first" ] && first="$file"
|
||||
done
|
||||
done
|
||||
[ -n "$first" ] && printf '%s\n' "$first"
|
||||
return 0
|
||||
}
|
||||
|
||||
# How the release pack that serves this system is named: the platforms of its
|
||||
# group, as packs.txt lists them. build_port.py writes packs.txt from the
|
||||
# registry's release_pack platforms, so a system with no line has no pack.
|
||||
rb_pack_label() {
|
||||
[ -n "${RB_PLATFORM:-}" ] && [ -f "$RB_DIR/packs.txt" ] || return 1
|
||||
awk -F'\t' -v p="$RB_PLATFORM" '$1 == p { print $2; found = 1; exit } END { exit !found }' \
|
||||
"$RB_DIR/packs.txt"
|
||||
}
|
||||
|
||||
# Whether the system is known to have no release pack.
|
||||
rb_no_pack_serves() {
|
||||
[ -n "${RB_PLATFORM:-}" ] && [ -f "$RB_DIR/packs.txt" ] && ! rb_pack_label > /dev/null
|
||||
}
|
||||
|
||||
rb_same_dir() {
|
||||
[ -n "$1" ] && [ -n "$2" ] || return 1
|
||||
[ "$(stat -L -c '%d:%i' "$1" 2>/dev/null)" = "$(stat -L -c '%d:%i' "$2" 2>/dev/null)" ]
|
||||
@@ -355,8 +370,14 @@ rb_other_card() {
|
||||
}
|
||||
|
||||
rb_pack_text() {
|
||||
local dir="${RB_PACK_PATH%%:*}" pack
|
||||
if [ -n "${RB_PLATFORM_NAME:-}" ]; then
|
||||
local dir="${RB_PACK_PATH%%:*}" pack label
|
||||
if rb_no_pack_serves; then
|
||||
printf '%s\n' "No network connection, and the releases carry no BIOS pack for ${RB_PLATFORM_NAME:-$RB_PLATFORM}: connect to Wi-Fi and start RetroBIOS again. $RB_PC_ROUTE"
|
||||
return 0
|
||||
fi
|
||||
if label=$(rb_pack_label); then
|
||||
pack="the $label BIOS pack (a file whose name starts with ${label}_ and ends in _BIOS_Pack.zip)"
|
||||
elif [ -n "${RB_PLATFORM_NAME:-}" ]; then
|
||||
pack="the BIOS pack whose name contains ${RB_PLATFORM_NAME// /_}"
|
||||
else
|
||||
pack="the BIOS pack for this system (a file whose name ends in _BIOS_Pack.zip)"
|
||||
@@ -752,6 +773,12 @@ rb_here() {
|
||||
printf 'No connection. Looking for the BIOS folder and a pack on the card...\n'
|
||||
# Detection also names the BIOS folder the other card must not be.
|
||||
rb_detect
|
||||
if rb_no_pack_serves; then
|
||||
# Copying from the other card needs a file list too: nothing to ask.
|
||||
rb_message "$(rb_pack_text)"
|
||||
printf '1\n' > "$RB_STATE/status"
|
||||
return 1
|
||||
fi
|
||||
other=$(rb_other_card)
|
||||
if [ -n "$other" ] && rb_ask "Copy the BIOS files from your other card? $other holds files. RetroBIOS copies the ones this system needs, each checked by its hash, and leaves that card as it is."; then
|
||||
args=(--from "$other")
|
||||
|
||||
+50
-6
@@ -46,6 +46,8 @@ MUOS_APP = "RetroBIOS.muxapp"
|
||||
TOOLS_ZIP = "RetroBIOS-tools.zip"
|
||||
# Every archive a release attaches, in the order the release notes list them.
|
||||
ARCHIVES = (PORT_ZIP, BATOCERA_ZIP, MUOS_APP, TOOLS_ZIP)
|
||||
# Which release pack serves each platform, read by tui.txt offline.
|
||||
PACKS_TABLE = "packs.txt"
|
||||
_PIN = re.compile(r'^DEFAULT_INSTALL_SHA256="([0-9a-f]{64})"$', re.MULTILINE)
|
||||
|
||||
|
||||
@@ -57,7 +59,7 @@ def pinned_installer_sha256(root: Path = REPO_ROOT) -> str:
|
||||
return found.group(1)
|
||||
|
||||
|
||||
def _engine(folder: str, root: Path, mapping: bool = True) -> list[tuple[str, Path]]:
|
||||
def _engine(folder: str, root: Path, mapping: bool = True) -> list[tuple[str, Path | None]]:
|
||||
"""What every launcher runs: the installer, the interface launcher, the
|
||||
event reader, the licenses, and the gptokeyb mapping where one is read."""
|
||||
shared = root / "ports" / "shared"
|
||||
@@ -67,14 +69,17 @@ def _engine(folder: str, root: Path, mapping: bool = True) -> list[tuple[str, Pa
|
||||
(f"{folder}/rbevents.py", shared / "rbevents.py"),
|
||||
(f"{folder}/licenses/LICENSE-retrobios.txt", root / "LICENSE"),
|
||||
(f"{folder}/licenses/NOTICE-retrobios.txt", root / "NOTICE"),
|
||||
# Written by build_archive from the registry.
|
||||
(f"{folder}/{PACKS_TABLE}", None),
|
||||
]
|
||||
if mapping:
|
||||
members.append((f"{folder}/retrobios.gptk", shared / "retrobios.gptk"))
|
||||
return members
|
||||
|
||||
|
||||
def archive_members(name: str, root: Path = REPO_ROOT) -> list[tuple[str, Path]]:
|
||||
"""(name in the archive, source file) for every member of *name*, sorted."""
|
||||
def archive_members(name: str, root: Path = REPO_ROOT) -> list[tuple[str, Path | None]]:
|
||||
"""(name in the archive, source file) for every member of *name*, sorted.
|
||||
The source is None for the packs table build_archive writes."""
|
||||
ports = root / "ports"
|
||||
if name == PORT_ZIP:
|
||||
source = ports / "RetroBIOS"
|
||||
@@ -121,6 +126,40 @@ def archive_members(name: str, root: Path = REPO_ROOT) -> list[tuple[str, Path]]
|
||||
return sorted(members)
|
||||
|
||||
|
||||
def release_pack_labels(root: Path = REPO_ROOT) -> dict[str, str]:
|
||||
"""Platform -> the platform part of the release pack that serves it.
|
||||
|
||||
Only the platforms the registry marks release_pack have a pack, grouped
|
||||
as the release builds them (pipeline.py --include-archived). install.py
|
||||
refuses a pack whose name does not spell the platform, so a platform
|
||||
outside these groups has no pack to copy, whatever its file list.
|
||||
"""
|
||||
from common import ( # noqa: PLC0415
|
||||
group_identical_platforms,
|
||||
list_release_pack_platforms,
|
||||
load_platform_config,
|
||||
)
|
||||
from generate_pack import _name_part, _platform_pack_stem # noqa: PLC0415
|
||||
|
||||
platforms_dir = str(root / "platforms")
|
||||
pool = list_release_pack_platforms(platforms_dir, include_archived=True)
|
||||
labels: dict[str, str] = {}
|
||||
for group, representative in group_identical_platforms(pool, platforms_dir):
|
||||
stem = _platform_pack_stem(group, representative, platforms_dir)
|
||||
config = load_platform_config(representative, platforms_dir)
|
||||
version = config.get("version", config.get("dat_version", ""))
|
||||
tag = f"_{_name_part(str(version))}" if version else ""
|
||||
label = stem[: -len(tag)] if tag and stem.endswith(tag) else stem
|
||||
for platform in group:
|
||||
labels[platform] = label
|
||||
return dict(sorted(labels.items()))
|
||||
|
||||
|
||||
def _packs_table(root: Path) -> bytes:
|
||||
return "".join(f"{platform}\t{label}\n"
|
||||
for platform, label in release_pack_labels(root).items()).encode()
|
||||
|
||||
|
||||
def build_archive(name: str, output: Path, root: Path = REPO_ROOT) -> Path:
|
||||
"""Write the launcher archive *name* to *output* and return it."""
|
||||
pinned = pinned_installer_sha256(root)
|
||||
@@ -137,9 +176,14 @@ def build_archive(name: str, output: Path, root: Path = REPO_ROOT) -> Path:
|
||||
members = archive_members(name, root)
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
scratch = output.with_name(f".{output.name}.tmp")
|
||||
with zipfile.ZipFile(scratch, "w", zipfile.ZIP_DEFLATED, compresslevel=9) as zf:
|
||||
for member, path in members:
|
||||
_add_pack_member(zf, str(path), member)
|
||||
table = output.with_name(f".{output.name}.{PACKS_TABLE}")
|
||||
table.write_bytes(_packs_table(root))
|
||||
try:
|
||||
with zipfile.ZipFile(scratch, "w", zipfile.ZIP_DEFLATED, compresslevel=9) as zf:
|
||||
for member, path in members:
|
||||
_add_pack_member(zf, str(path or table), member)
|
||||
finally:
|
||||
table.unlink()
|
||||
scratch.replace(output)
|
||||
return output
|
||||
|
||||
|
||||
@@ -28,6 +28,8 @@ import unittest
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
PORTS = REPO_ROOT / "ports"
|
||||
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
||||
@@ -235,6 +237,21 @@ class TheArchives(unittest.TestCase):
|
||||
second = build_port.build_archive(name, self.out / f"b-{name}").read_bytes()
|
||||
self.assertEqual(first, second)
|
||||
|
||||
def test_each_names_the_release_packs(self):
|
||||
"""packs.txt names the platforms a release publishes a pack for, as
|
||||
the registry marks them, and the pack's name for each."""
|
||||
registry = yaml.safe_load((REPO_ROOT / "platforms" / "_registry.yml").read_text())
|
||||
released = {name for name, entry in registry["platforms"].items()
|
||||
if entry.get("release_pack") is True}
|
||||
labels = build_port.release_pack_labels()
|
||||
self.assertEqual(set(labels), released)
|
||||
self.assertEqual(labels["retroarch"], "RetroArch")
|
||||
expected = "".join(f"{p}\t{label}\n" for p, label in sorted(labels.items()))
|
||||
for name, (_script, folder) in self.LAYOUT.items():
|
||||
with self.subTest(archive=name):
|
||||
with zipfile.ZipFile(build_port.build_archive(name, self.out / name)) as zf:
|
||||
self.assertEqual(zf.read(f"{folder}/packs.txt").decode(), expected)
|
||||
|
||||
def test_an_unpinned_installer_is_refused(self):
|
||||
root = _tempdir(self)
|
||||
for name in ("install.py", "install.sh", "LICENSE", "NOTICE"):
|
||||
@@ -296,10 +313,12 @@ if "--check" in sys.argv:
|
||||
if os.environ.get("FAKE_DETECT_HANG"):
|
||||
time.sleep(60)
|
||||
print(json.dumps({"event": "detect", "os": "linux", "os_name": "Fake OS",
|
||||
"platforms": [{"platform": "retroarch", "name": "RetroArch",
|
||||
"platforms": [{"platform": os.environ.get("FAKE_PLATFORM", "retroarch"),
|
||||
"name": os.environ.get("FAKE_PLATFORM_NAME", "RetroArch"),
|
||||
"bios_dir": "/roms/bios"}]}))
|
||||
print(json.dumps({"event": "error", "stage": "manifest", "reason": "network",
|
||||
"platform": "retroarch", "message": "unreachable"}))
|
||||
"platform": os.environ.get("FAKE_PLATFORM", "retroarch"),
|
||||
"message": "unreachable"}))
|
||||
print(json.dumps({"event": "end", "code": 1}))
|
||||
sys.exit(1)
|
||||
if os.environ.get("FAKE_REFUSE"):
|
||||
@@ -1249,6 +1268,24 @@ class TheMuosApp(unittest.TestCase):
|
||||
[call] = device.tui_calls()
|
||||
self.assertEqual(call["args"][:3], ["--tui", "--from-pack", str(pack)])
|
||||
|
||||
def test_offline_names_the_pack_that_serves_muos(self):
|
||||
"""muOS runs RetroArch, and install.py reads it as retroarch: the
|
||||
RetroArch pack serves it."""
|
||||
device, app = self.device(muxterm=True)
|
||||
proc = device.run([str(app / "mux_launch.sh"), str(app)], FAKE_CURL="7")
|
||||
self.assertEqual(proc.returncode, 1)
|
||||
# muxterm draws what its child writes: here the launcher's stdout.
|
||||
screen = " ".join(proc.stdout.decode().split())
|
||||
self.assertIn("download the RetroArch BIOS pack (a file whose name starts with "
|
||||
"RetroArch_ and ends in _BIOS_Pack.zip)", screen)
|
||||
|
||||
def test_offline_takes_no_pack_named_for_another_system(self):
|
||||
device, app = self.device(muxterm=True)
|
||||
(device.root / "mmc" / "Batocera_43.1_BIOS_Pack.zip").write_bytes(b"")
|
||||
proc = device.run([str(app / "mux_launch.sh"), str(app)], FAKE_CURL="7")
|
||||
self.assertEqual(proc.returncode, 1)
|
||||
self.assertEqual(device.tui_calls(), [])
|
||||
|
||||
def test_offline_reads_a_pack_on_the_second_card(self):
|
||||
device, app = self.device(muxterm=True)
|
||||
(device.root / "sdcard").mkdir()
|
||||
@@ -1445,6 +1482,22 @@ INI
|
||||
# Stopped without the sudo control.ini names.
|
||||
device.assert_mapper_stopped()
|
||||
|
||||
def test_rocknix_offline_names_no_pack_the_releases_lack(self):
|
||||
"""No release pack spells ROCKNIX, and install.py refuses one named
|
||||
for another system: the RetroArch pack on the card is not offered,
|
||||
and the message gives the computer route."""
|
||||
device, tools = self.device(FAKE_CURL="7")
|
||||
self.rocknix(device)
|
||||
(tools / "retrobios" / "RetroArch_v1.22.2_BIOS_Pack.zip").write_bytes(b"")
|
||||
proc = device.run(["bash", str(tools / "RetroBIOS.sh")], stdio_tty=True, TERM="xterm",
|
||||
FAKE_PLATFORM="rocknix", FAKE_PLATFORM_NAME="ROCKNIX")
|
||||
self.assertEqual(proc.returncode, 1)
|
||||
self.assertEqual(device.tui_calls(), [])
|
||||
screen = " ".join(device.screen.split())
|
||||
self.assertIn("the releases carry no BIOS pack for ROCKNIX", screen)
|
||||
self.assertIn("--dest", screen)
|
||||
self.assertNotIn("download", screen)
|
||||
|
||||
def test_rocknix_start_select_in_foot(self):
|
||||
device, tools = self.device()
|
||||
self.rocknix(device)
|
||||
@@ -1458,6 +1511,21 @@ INI
|
||||
|
||||
|
||||
class TheDocs(unittest.TestCase):
|
||||
def test_the_offline_packs_named_are_the_ones_that_serve(self):
|
||||
"""The wiki names the pack each firmware takes offline: the release
|
||||
pack of the platform install.py detects it as, or none."""
|
||||
labels = build_port.release_pack_labels()
|
||||
served = {}
|
||||
for rule in install._EMBEDDED_RULES:
|
||||
served.setdefault(rule.name, labels.get(rule.manifest))
|
||||
for name in ("muOS", "ArkOS", "dArkOS", "JELOS"):
|
||||
self.assertEqual(served[name], "RetroArch", name)
|
||||
self.assertEqual(served["Batocera"], "Batocera")
|
||||
for name in ("ROCKNIX", "KNULLI", "AmberELEC"):
|
||||
self.assertIsNone(served[name], name)
|
||||
text = (REPO_ROOT / "wiki" / "installer.md").read_text(encoding="utf-8")
|
||||
self.assertIn("The releases carry no pack for ROCKNIX,\nKnulli or AmberELEC", text)
|
||||
|
||||
def test_muos_paths_are_the_card_muos_mounts(self):
|
||||
"""muOS mounts the first card at /mnt/mmc (MustardOS internal
|
||||
mux_launch.sh, GET_VAR storage/rom/mount)."""
|
||||
|
||||
+8
-6
@@ -987,13 +987,15 @@ running install is then stopped the way B stops it, and the last screen says
|
||||
what it wrote. On muOS without PortMaster, muxterm reads the pad itself: Y
|
||||
ticks a system, Select shows its keyboard, and Menu quits.
|
||||
|
||||
**Without Wi-Fi.** On a computer, download the BIOS pack whose name contains
|
||||
your system (every part, for a pack in parts) and copy it next to RetroBIOS:
|
||||
into `ports/retrobios` for the PortMaster port and on Batocera, to the root
|
||||
of either card on muOS, into the `retrobios` folder of the Tools entry. With
|
||||
no connection, RetroBIOS installs from it, as
|
||||
**Without Wi-Fi.** On a computer, download the BIOS pack that serves your
|
||||
system (every part, for a pack in parts): RetroArch's on muOS, ArkOS,
|
||||
dArkOS and JELOS, Batocera's on Batocera. Copy it next to RetroBIOS: into
|
||||
`ports/retrobios` for the PortMaster port and on Batocera, to the root of
|
||||
either card on muOS, into the `retrobios` folder of the Tools entry. With no
|
||||
connection, RetroBIOS installs from it, as
|
||||
[`--from-pack`](#without-a-network-offline-install) does. With no pack there,
|
||||
it names the pack and the folder.
|
||||
it names the pack and the folder. The releases carry no pack for ROCKNIX,
|
||||
Knulli or AmberELEC: there RetroBIOS needs Wi-Fi, or the computer route.
|
||||
|
||||
On a two-card device, RetroBIOS without Wi-Fi first asks to **copy the BIOS
|
||||
files from your other card**: muOS's `MUOS/bios` on the card the system does
|
||||
|
||||
Reference in new issue
Block a user