feat: detect knulli, amberelec and emuelec

KNULLI keeps its markers and now installs its own list. AmberELEC is
found by NAME in os-release (its ID is coreelec), EmuELEC by ID.
This commit is contained in:
Abdessamad Derraz committed 2026-10-22 03:42:25 +02:00
1 parent 2bdac466a9
commit d488b14a7f
4 files changed
+33 -14

No files matched your search

+1 -1
View File
@@ -11,7 +11,7 @@
$ErrorActionPreference = "Stop"
$defaultInstallUrl = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
$defaultInstallSha256 = "fe8de4eda0919c268d4d2c0d571c7d0dc1cf675d7604263766f5f99a35aa3724"
$defaultInstallSha256 = "a5437191c86a4ecd9042443c3d58a501bd007b851603f7e778c179f31f6c2463"
$maximumInstallerBytes = 2MB
$installerExit = 0
$installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null }
+17 -7
View File
@@ -185,7 +185,7 @@ OTHER_SYSTEMS = "other"
AVAILABLE_PLATFORMS = (
"retroarch", "batocera", "recalbox", "retrobat", "emudeck",
"lakka", "retrodeck", "rocknix", "romm", "bizhawk", "misterfpga",
"retropie",
"retropie", "amberelec", "emuelec", "knulli",
)
# The names the frontends give themselves, as each manifest's display_name
@@ -197,6 +197,7 @@ PLATFORM_NAMES = {
"retrobat": "RetroBat", "emudeck": "EmuDeck", "lakka": "Lakka",
"retrodeck": "RetroDECK", "rocknix": "ROCKNIX", "romm": "RomM",
"bizhawk": "BizHawk", "misterfpga": "MiSTer FPGA", "retropie": "RetroPie",
"amberelec": "AmberELEC", "emuelec": "EmuELEC", "knulli": "KNULLI",
}
SITE_URL = "https://abdess.github.io/retrobios/"
@@ -213,6 +214,10 @@ DEFAULT_DESTS = {
"retrodeck": Path.home() / "retrodeck",
"emudeck": Path.home() / "Emulation" / "bios",
"rocknix": Path("/storage/roms/bios"),
# Their BIOS lists name files under /storage/roms beside bios/.
"amberelec": Path("/storage/roms"),
"emuelec": Path("/storage/roms"),
"knulli": Path("/userdata/bios"),
"misterfpga": Path("/media/fat/games"),
"retropie": Path.home() / "RetroPie" / "BIOS",
"romm": Path("/romm/library/bios"),
@@ -745,13 +750,12 @@ _BOARD_SOURCES: dict[str, tuple[str, str]] = {
}
# A derivative installed under a platform's name, with its own board file.
# A platform that names its board without publishing a core list per board.
# Knulli moves /usr/share/batocera to /usr/share/knulli (knulli-linux
# 57afa662, board/scripts/post-build-script.sh:264-278) and writes its board
# to knulli.arch (package/system/knulli-system/knulli-system.mk:98-99). Its
# packages are its own, so Batocera's per-board core lists do not apply.
# to knulli.arch (package/system/knulli-system/knulli-system.mk:98-99).
_DERIVATIVE_BOARDS: dict[str, tuple[str, str]] = {
"batocera": ("Knulli", "/usr/share/knulli/knulli.arch"),
"knulli": ("Knulli", "/usr/share/knulli/knulli.arch"),
}
@@ -827,8 +831,14 @@ _EMBEDDED_RULES: tuple[tuple[str, str, tuple[str, ...], str], ...] = (
# Knulli (knulli-linux 57afa662): OS_NAME in os-release
# (board/scripts/post-build-script.sh:233-236) and knulli.version
# (package/system/knulli-system/knulli-system.mk:98-102).
("batocera", "os_release", ("OS_NAME=knulli",), "/userdata/bios"),
("batocera", "file_exists", ("/usr/share/knulli/knulli.version",), "/userdata/bios"),
("knulli", "os_release", ("OS_NAME=knulli",), "/userdata/bios"),
("knulli", "file_exists", ("/usr/share/knulli/knulli.version",), "/userdata/bios"),
# scripts/image writes NAME from DISTRONAME. AmberELEC a21975bf:
# scripts/image:152 (ID is "coreelec", line 154) and
# distributions/AmberELEC/options:17. EmuELEC v4.8: scripts/image:174-176
# (ID is DISTRONAME lowercased) and distributions/EmuELEC/options:4.
("amberelec", "os_release", ("NAME=AmberELEC",), "/storage/roms"),
("emuelec", "os_release", ("ID=emuelec",), "/storage/roms"),
("lakka", "os_release", ("ID=lakka",), "/storage/system"),
# batocera.linux 38845725, batocera-system.mk:21-22 (the same lines in
# batocera-30): batocera.arch and batocera.version.
+1 -1
View File
@@ -18,7 +18,7 @@ esac
TEMP_INSTALLER=""
TEMP_DIRECTORY=""
DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
DEFAULT_INSTALL_SHA256="fe8de4eda0919c268d4d2c0d571c7d0dc1cf675d7604263766f5f99a35aa3724"
DEFAULT_INSTALL_SHA256="a5437191c86a4ecd9042443c3d58a501bd007b851603f7e778c179f31f6c2463"
MAX_INSTALLER_BYTES=2097152
cleanup() {
+14 -5
View File
@@ -1078,7 +1078,7 @@ class DetectionFollowsTheRegistry(unittest.TestCase):
lambda p: str(p) in ("/usr/share/knulli/knulli.version",
"/usr/share/batocera/batocera.version"),
):
self.assertEqual(install._detect_embedded(), [("batocera", Path("/userdata/bios"))])
self.assertEqual(install._detect_embedded(), [("knulli", Path("/userdata/bios"))])
# What ROCKNIX writes since 2025: OS_NAME and HW_DEVICE, no ID.
with unittest.mock.patch.object(install, "_parse_os_release",
return_value={"OS_NAME": "ROCKNIX", "HW_DEVICE": "RK3566"}):
@@ -4264,16 +4264,25 @@ class BatoceraAndKnulliByWhatTheirBuildsWrite(unittest.TestCase):
def test_knulli_by_its_os_release_or_version_file(self):
self.assertEqual(self._detect({"OS_NAME": "knulli"}, ()),
[("batocera", Path("/userdata/bios"))])
[("knulli", Path("/userdata/bios"))])
self.assertEqual(self._detect({}, ("/usr/share/knulli/knulli.version",)),
[("batocera", Path("/userdata/bios"))])
[("knulli", Path("/userdata/bios"))])
def test_amberelec_and_emuelec_by_their_os_release(self):
"""Both write os-release from DISTRONAME in scripts/image. AmberELEC
keeps CoreELEC's ID, so its NAME tells it apart."""
self.assertEqual(self._detect({"NAME": "AmberELEC", "ID": "coreelec"}, ()),
[("amberelec", Path("/storage/roms"))])
self.assertEqual(self._detect({"NAME": "EmuELEC", "ID": "emuelec"}, ()),
[("emuelec", Path("/storage/roms"))])
self.assertEqual(self._detect({"NAME": "CoreELEC", "ID": "coreelec"}, ()), [])
def test_a_knulli_board_is_named_and_not_narrowed(self):
board = Path(_tempdir(self)) / "knulli.arch"
board.write_text("a133")
with unittest.mock.patch.dict(install._DERIVATIVE_BOARDS,
{"batocera": ("Knulli", str(board))}):
self.assertEqual(install.derivative_board("batocera"), ("Knulli", "a133"))
{"knulli": ("Knulli", str(board))}):
self.assertEqual(install.derivative_board("knulli"), ("Knulli", "a133"))
self.assertIsNone(install.derivative_board("recalbox"))
def test_the_run_says_why_every_board_is_installed(self):