From 44565a07bb87342733be26ae77658f1ff71e4315 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Fri, 6 Nov 2026 12:09:09 +0100 Subject: [PATCH] feat: read the EmuELEC device in the installer EmuELEC's scripts/image writes COREELEC_DEVICE to os-release (v4.8, line 189), as a GameForce image shows. The installer reads it the way it does for AmberELEC and applies install/targets/emuelec.json. MiSTer FPGA takes EmuELEC's place in the test of a platform without targets. --- install.ps1 | 2 +- install.py | 8 +- install.sh | 2 +- install/launchers/index.json | 16 +- install/targets/emuelec.json | 492 +++++++++++++++++++++++++++++++++++ tests/test_install.py | 24 +- wiki/installer.md | 2 +- 7 files changed, 526 insertions(+), 20 deletions(-) create mode 100644 install/targets/emuelec.json diff --git a/install.ps1 b/install.ps1 index 3b53ab0a0..28de54233 100644 --- a/install.ps1 +++ b/install.ps1 @@ -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 = "b532d7f12f92dc5934e210b7359e849e796abf39d9d1598eac14fe91dc8c86ac" + $defaultInstallSha256 = "57e3cb6739a701181908773e603f17d2b10e1b0e2a5cde5cc4d5c97426da5dd4" $maximumInstallerBytes = 2MB $installerExit = 0 $installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null } diff --git a/install.py b/install.py index bd32bf94b..04a697af5 100644 --- a/install.py +++ b/install.py @@ -109,8 +109,8 @@ TARGETS_URL = f"{BASE_URL}/install/targets/{{platform}}.json" # has no hardware targets, where the run sent the user after a list that # exists nowhere. TARGET_PLATFORMS = frozenset({ - "amberelec", "batocera", "emudeck", "knulli", "lakka", "recalbox", "retroarch", "retrobat", - "retrodeck", "retropie", "rocknix", "romm", + "amberelec", "batocera", "emudeck", "emuelec", "knulli", "lakka", "recalbox", "retroarch", + "retrobat", "retrodeck", "retropie", "rocknix", "romm", }) RAW_FILE_URL = f"{BASE_URL}/{{path}}" RELEASE_URL = ( @@ -1484,9 +1484,11 @@ def _parse_bash_var(path: Path, key: str) -> str | None: # board/scripts/post-build-script.sh:264-278) and writes KNULLI_SYSTEM_ARCH # to knulli.arch (package/system/knulli-system/knulli-system.mk:98-99). # AmberELEC's scripts/image writes COREELEC_DEVICE (a21975bf, line 168), the -# DEVICE its Makefile builds (RG351P). +# DEVICE its Makefile builds (RG351P). EmuELEC's writes the same line (v4.8, +# line 189), the DEVICE of the release image (OdroidGoAdvance). _BOARD_SOURCES: dict[str, tuple[str, str]] = { "amberelec": ("os_release", "COREELEC_DEVICE"), + "emuelec": ("os_release", "COREELEC_DEVICE"), "batocera": ("file", "/usr/share/batocera/batocera.arch"), "recalbox": ("file", "/recalbox/recalbox.arch"), "rocknix": ("os_release", "HW_DEVICE"), diff --git a/install.sh b/install.sh index 8e65e5916..a03428f05 100755 --- a/install.sh +++ b/install.sh @@ -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="b532d7f12f92dc5934e210b7359e849e796abf39d9d1598eac14fe91dc8c86ac" +DEFAULT_INSTALL_SHA256="57e3cb6739a701181908773e603f17d2b10e1b0e2a5cde5cc4d5c97426da5dd4" MAX_INSTALLER_BYTES=2097152 cleanup() { diff --git a/install/launchers/index.json b/install/launchers/index.json index 428c911fc..f6dc104a6 100644 --- a/install/launchers/index.json +++ b/install/launchers/index.json @@ -27,8 +27,8 @@ { "dest": "roms/ports/retrobios/install.py", "repo_path": "install.py", - "size": 1112275, - "sha256": "b532d7f12f92dc5934e210b7359e849e796abf39d9d1598eac14fe91dc8c86ac", + "size": 1112438, + "sha256": "57e3cb6739a701181908773e603f17d2b10e1b0e2a5cde5cc4d5c97426da5dd4", "executable": false }, { @@ -71,8 +71,8 @@ "muos": { "archive": { "dest": "ARCHIVE/RetroBIOS.muxapp", - "size": 1165902, - "sha256": "30b3cd30b3a75c4e2fb0784589f1b12d3ac2fb6ec345e874a2a8d58681c236c8" + "size": 1166065, + "sha256": "1c9e5737a0b32c85108f556c2799cf0ebb5ce52cbd2bf501bc24783fa3a04b9a" }, "members": [ { @@ -92,8 +92,8 @@ { "name": "RetroBIOS/install.py", "repo_path": "install.py", - "size": 1112275, - "sha256": "b532d7f12f92dc5934e210b7359e849e796abf39d9d1598eac14fe91dc8c86ac", + "size": 1112438, + "sha256": "57e3cb6739a701181908773e603f17d2b10e1b0e2a5cde5cc4d5c97426da5dd4", "executable": false }, { @@ -166,8 +166,8 @@ { "dest": ".config/modules/retrobios/install.py", "repo_path": "install.py", - "size": 1112275, - "sha256": "b532d7f12f92dc5934e210b7359e849e796abf39d9d1598eac14fe91dc8c86ac", + "size": 1112438, + "sha256": "57e3cb6739a701181908773e603f17d2b10e1b0e2a5cde5cc4d5c97426da5dd4", "executable": false }, { diff --git a/install/targets/emuelec.json b/install/targets/emuelec.json new file mode 100644 index 000000000..c3363d940 --- /dev/null +++ b/install/targets/emuelec.json @@ -0,0 +1,492 @@ +{ + "amlogic-ng": [ + "2048", + "81", + "AMIBERRY", + "AdvanceMame", + "DOSBOXSDL2", + "Duckstation", + "ECWolf", + "FBNEOSA", + "HATARISA", + "HYPSEUS", + "OPENBOR", + "PPSSPPSDL", + "Pico-8", + "SCUMMVMSA", + "STELLASA", + "Solarus", + "a5200", + "atari800", + "bgdi", + "bk", + "bluemsx", + "cannonball", + "cap32", + "daphne", + "dinothawr", + "dolphin", + "dosbox_pure", + "dosbox_svn", + "easyrpg", + "fake08", + "fbalpha2012", + "fbneo", + "fceumm", + "fceumm_mod", + "flycast", + "flycast_32b", + "flycastsa", + "fmsx", + "freeintv", + "freej2me", + "fuse", + "gambatte", + "gearboy", + "gearcoleco", + "geargrafx", + "gearlynx", + "gearsystem", + "genesis_plus_gx", + "genesis_plus_gx_cart_special", + "genesis_plus_gx_wide", + "geolith", + "gpsp", + "gw", + "handy", + "hatari", + "ikemen", + "jzintv", + "mame", + "mame2003_plus", + "mame2010", + "mednafen_lynx", + "mednafen_ngp", + "mednafen_pce_fast", + "mednafen_pcfx", + "mednafen_saturn", + "mednafen_supafaust", + "mednafen_supergrafx", + "mednafen_vb", + "mednafen_wswan", + "mesen", + "mesen-s", + "mgba", + "mrboom", + "mupen64plus_32b", + "mupen64plus_next", + "mupen64plus_next_alt", + "mupen64plussa", + "nekop2", + "neocd", + "nestopia", + "np2kai", + "nxengine", + "o2em", + "opera", + "parallel_n64_32b", + "pcsx_rearmed", + "pcsx_rearmed_32b", + "picodrive", + "pocketcdg", + "pokemini", + "potator", + "prboom", + "prosystem", + "puae", + "puae2021", + "px68k", + "reminiscence", + "same_cdi", + "sameboy", + "sameduck", + "scummvm", + "snes9x", + "snes9x2002", + "snes9x2005_plus", + "snes9x2010", + "stella", + "swanstation", + "tgbdual", + "tic80", + "tyrquake", + "uae4arm_32b", + "uzem", + "vba_next", + "vbam", + "vecx", + "vice_x128", + "vice_x64", + "vice_x64sc", + "vice_xplus4", + "vice_xvic", + "virtualjaguar", + "x1", + "xrick", + "yabasanshiro" + ], + "gameforce": [ + "2048", + "81", + "AMIBERRY", + "AdvanceMame", + "DOSBOXSDL2", + "ECWolf", + "FBNEOSA", + "HATARISA", + "HYPSEUS", + "OPENBOR", + "PPSSPPSDL", + "Pico-8", + "SCUMMVMSA", + "STELLASA", + "Solarus", + "a5200", + "atari800", + "bgdi", + "bk", + "bluemsx", + "cannonball", + "cap32", + "daphne", + "dinothawr", + "dosbox_pure", + "dosbox_svn", + "easyrpg", + "fake08", + "fbalpha2012", + "fbneo", + "fceumm", + "fceumm_mod", + "flycast", + "flycast_32b", + "flycastsa", + "fmsx", + "freeintv", + "freej2me", + "fuse", + "gambatte", + "gearboy", + "gearcoleco", + "geargrafx", + "gearlynx", + "gearsystem", + "genesis_plus_gx", + "genesis_plus_gx_cart_special", + "genesis_plus_gx_wide", + "geolith", + "gpsp", + "gw", + "handy", + "hatari", + "ikemen", + "jzintv", + "mame2003_plus", + "mame2010", + "mednafen_lynx", + "mednafen_ngp", + "mednafen_pce_fast", + "mednafen_pcfx", + "mednafen_supafaust", + "mednafen_supergrafx", + "mednafen_vb", + "mednafen_wswan", + "mgba", + "mrboom", + "mupen64plus_32b", + "mupen64plus_next", + "mupen64plus_next_alt", + "mupen64plussa", + "nekop2", + "neocd", + "nestopia", + "np2kai", + "nxengine", + "o2em", + "opera", + "parallel_n64_32b", + "pcsx_rearmed", + "pcsx_rearmed_32b", + "picodrive", + "pocketcdg", + "pokemini", + "potator", + "prboom", + "prosystem", + "puae", + "puae2021", + "px68k", + "reminiscence", + "same_cdi", + "sameboy", + "sameduck", + "scummvm", + "snes9x", + "snes9x2002", + "snes9x2005_plus", + "snes9x2010", + "stella", + "swanstation", + "tgbdual", + "tic80", + "tyrquake", + "uae4arm_32b", + "uzem", + "vba_next", + "vbam", + "vecx", + "vice_x128", + "vice_x64", + "vice_x64sc", + "vice_xplus4", + "vice_xvic", + "x1", + "xrick", + "yabasanshiro" + ], + "odroidgoadvance": [ + "2048", + "81", + "AMIBERRY", + "AdvanceMame", + "DOSBOXSDL2", + "ECWolf", + "FBNEOSA", + "HATARISA", + "HYPSEUS", + "OPENBOR", + "PPSSPPSDL", + "Pico-8", + "SCUMMVMSA", + "STELLASA", + "Solarus", + "a5200", + "atari800", + "bgdi", + "bk", + "bluemsx", + "cannonball", + "cap32", + "daphne", + "dinothawr", + "dosbox_pure", + "dosbox_svn", + "easyrpg", + "fake08", + "fbalpha2012", + "fbneo", + "fceumm", + "fceumm_mod", + "flycast", + "flycast_32b", + "flycastsa", + "fmsx", + "freeintv", + "freej2me", + "fuse", + "gambatte", + "gearboy", + "gearcoleco", + "geargrafx", + "gearlynx", + "gearsystem", + "genesis_plus_gx", + "genesis_plus_gx_cart_special", + "genesis_plus_gx_wide", + "geolith", + "gpsp", + "gw", + "handy", + "hatari", + "ikemen", + "jzintv", + "mame2003_plus", + "mame2010", + "mednafen_lynx", + "mednafen_ngp", + "mednafen_pce_fast", + "mednafen_pcfx", + "mednafen_supafaust", + "mednafen_supergrafx", + "mednafen_vb", + "mednafen_wswan", + "mgba", + "mrboom", + "mupen64plus_32b", + "mupen64plus_next", + "mupen64plus_next_alt", + "mupen64plussa", + "nekop2", + "neocd", + "nestopia", + "np2kai", + "nxengine", + "o2em", + "opera", + "parallel_n64_32b", + "pcsx_rearmed", + "pcsx_rearmed_32b", + "picodrive", + "pocketcdg", + "pokemini", + "potator", + "prboom", + "prosystem", + "puae", + "puae2021", + "px68k", + "reminiscence", + "same_cdi", + "sameboy", + "sameduck", + "scummvm", + "snes9x", + "snes9x2002", + "snes9x2005_plus", + "snes9x2010", + "stella", + "swanstation", + "tgbdual", + "tic80", + "tyrquake", + "uae4arm_32b", + "uzem", + "vba_next", + "vbam", + "vecx", + "vice_x128", + "vice_x64", + "vice_x64sc", + "vice_xplus4", + "vice_xvic", + "x1", + "xrick", + "yabasanshiro" + ], + "odroidm1": [ + "2048", + "81", + "AMIBERRY", + "AdvanceMame", + "DOSBOXSDL2", + "Duckstation", + "ECWolf", + "FBNEOSA", + "HATARISA", + "HYPSEUS", + "OPENBOR", + "PPSSPPSDL", + "Pico-8", + "SCUMMVMSA", + "STELLASA", + "Solarus", + "a5200", + "atari800", + "bgdi", + "bk", + "bluemsx", + "cannonball", + "cap32", + "daphne", + "dinothawr", + "dolphin", + "dosbox_pure", + "dosbox_svn", + "easyrpg", + "fake08", + "fbalpha2012", + "fbneo", + "fceumm", + "fceumm_mod", + "flycast", + "flycast_32b", + "flycastsa", + "fmsx", + "freeintv", + "freej2me", + "fuse", + "gambatte", + "gearboy", + "gearcoleco", + "geargrafx", + "gearlynx", + "gearsystem", + "genesis_plus_gx", + "genesis_plus_gx_cart_special", + "genesis_plus_gx_wide", + "geolith", + "gpsp", + "gw", + "handy", + "hatari", + "ikemen", + "jzintv", + "mame", + "mame2003_plus", + "mame2010", + "mednafen_lynx", + "mednafen_ngp", + "mednafen_pce_fast", + "mednafen_pcfx", + "mednafen_saturn", + "mednafen_supafaust", + "mednafen_supergrafx", + "mednafen_vb", + "mednafen_wswan", + "mesen", + "mesen-s", + "mgba", + "mrboom", + "mupen64plus_32b", + "mupen64plus_next", + "mupen64plus_next_alt", + "mupen64plussa", + "nekop2", + "neocd", + "nestopia", + "np2kai", + "nxengine", + "o2em", + "opera", + "parallel_n64_32b", + "pcsx_rearmed", + "pcsx_rearmed_32b", + "picodrive", + "pocketcdg", + "pokemini", + "potator", + "prboom", + "prosystem", + "puae", + "puae2021", + "px68k", + "reminiscence", + "same_cdi", + "sameboy", + "sameduck", + "scummvm", + "snes9x", + "snes9x2002", + "snes9x2005_plus", + "snes9x2010", + "stella", + "swanstation", + "tgbdual", + "tic80", + "tyrquake", + "uae4arm_32b", + "uzem", + "vba_next", + "vbam", + "vecx", + "vice_x128", + "vice_x64", + "vice_x64sc", + "vice_xplus4", + "vice_xvic", + "virtualjaguar", + "x1", + "xrick", + "yabasanshiro" + ] +} diff --git a/tests/test_install.py b/tests/test_install.py index 7e1659a07..252f8a4c8 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -3552,7 +3552,7 @@ class ACardPreparedOnAComputerGetsItsBoard(unittest.TestCase): class APlatformWithoutTargetsSaysSoOffline(unittest.TestCase): """Offline, --target on AmberELEC asked for install/targets/amberelec.json, which existed nowhere: online the same run said it had no targets. - EmuELEC has none still.""" + MiSTer FPGA has none still.""" def test_the_list_matches_the_published_targets(self): published = {path.stem for path in (REPO_ROOT / "install" / "targets").glob("*.json")} @@ -3560,16 +3560,16 @@ class APlatformWithoutTargetsSaysSoOffline(unittest.TestCase): def test_offline_says_there_are_no_targets(self): dest = Path(_tempdir(self)) - for extra in ([], ["--manifest", str(REPO_ROOT / "install" / "emuelec.json")]): + for extra in ([], ["--manifest", str(REPO_ROOT / "install" / "misterfpga.json")]): with self.subTest(manifest=bool(extra)): proc = subprocess.run( - [sys.executable, str(REPO_ROOT / "install.py"), "--platform", "emuelec", - "--dest", str(dest), "--target", "rk3326", "--offline", "--no-input", + [sys.executable, str(REPO_ROOT / "install.py"), "--platform", "misterfpga", + "--dest", str(dest), "--target", "de10-nano", "--offline", "--no-input", "--dry-run", *extra], capture_output=True, text=True, timeout=120, stdin=subprocess.DEVNULL) self.assertEqual(proc.returncode, 2, proc.stderr) - self.assertIn("EmuELEC has no hardware targets", proc.stderr) - self.assertNotIn("targets/emuelec.json", proc.stderr) + self.assertIn("MiSTer FPGA has no hardware targets", proc.stderr) + self.assertNotIn("targets/misterfpga.json", proc.stderr) class TheBoardTheSystemNamesIsTheTarget(unittest.TestCase): @@ -3635,6 +3635,18 @@ class TheBoardTheSystemNamesIsTheTarget(unittest.TestCase): .read_text(encoding="utf-8")) self.assertIn("rg351p", targets) + def test_emuelec_names_its_device(self): + # An OdroidGoAdvance image carries no DuckStation, Dolphin or MAME + # core, and the full list was installed: EmuELEC had no target list. + with unittest.mock.patch.object( + install, "_parse_os_release", return_value={"COREELEC_DEVICE": "OdroidGoAdvance"} + ): + self.assertEqual(install.detect_board("emuelec"), "odroidgoadvance") + targets = json.loads((REPO_ROOT / "install" / "targets" / "emuelec.json") + .read_text(encoding="utf-8")) + self.assertIn("odroidgoadvance", targets) + self.assertNotIn("Duckstation", targets["odroidgoadvance"]) + def test_lakka_names_its_device_or_its_project(self): # An RPi4 Lakka installed 5.7 GB of every board's files: its board # was in os-release and never read. diff --git a/wiki/installer.md b/wiki/installer.md index d7061b97d..6378b191a 100644 --- a/wiki/installer.md +++ b/wiki/installer.md @@ -968,7 +968,7 @@ what the options name, everything by default. |--------|--------| | `--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 | | `--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 | -| `--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`) | +| `--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, AmberELEC, EmuELEC and RetroPie the board is read from the system (`batocera.arch`, `recalbox.arch`, `HW_DEVICE`, `LIBREELEC_DEVICE` or `LIBREELEC_PROJECT`, `knulli.arch`, `COREELEC_DEVICE`, 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`) | | `--every-board` | Install the files of every board instead of the one the system names, for a card that moves between devices | | `--installed-cores` | Keep only what the cores already installed read (RetroArch, Lakka, RetroPie). See [Installed cores](#installed-cores) | | `--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) |