From f362a54b12f2bc57a3e1bb72b1c1bac87babdda4 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Mon, 9 Nov 2026 21:26:09 +0100 Subject: [PATCH 1/6] fix: list only what a knulli board builds KNULLI's es_systems.yml is Batocera's model: it lists every emulator its configgen knows, and its Config.in selects many of them only on x86_64 boards, which KNULLI has none of. The platform list now holds what at least one board selects, read by the targets scraper, plus a program the launcher runs from the BIOS folder (lexaloffle). 59 cores and 44 standalone emulators leave the list, with their launcher records. The targets scraper follows Config.in and every file it sources, so a select a package declares itself counts (Batocera's mesa3d-rpi4 gives the Pi 4 OpenGL). A sourced file absent at the pin is named and read as empty: KNULLI 20260511 sources package/utils/vtree/Config.in, added in June. --- platforms/knulli.yml | 323 ------------------ scripts/scraper/batocera_scraper.py | 118 +++---- scripts/scraper/knulli_scraper.py | 21 +- .../targets/batocera_targets_scraper.py | 139 +++++++- .../scraper/targets/knulli_targets_scraper.py | 3 + tests/test_elec_scrapers.py | 10 +- tests/test_launcher_paths.py | 73 +++- tests/test_standalone_links.py | 6 +- tests/test_target_scrapers.py | 26 +- 9 files changed, 284 insertions(+), 435 deletions(-) diff --git a/platforms/knulli.yml b/platforms/knulli.yml index d2aa23488..c6923e911 100644 --- a/platforms/knulli.yml +++ b/platforms/knulli.yml @@ -12,55 +12,36 @@ cores: - advanced_drastic - aethersx2 - amiberry -- applewin - arduous - atari800 - azahar - beetle-saturn - bennugd -- bigpemu -- blastem - bluemsx -- boom3 - bsnes -- bsnes_hd - bstone - cannonball - cap32 -- catacombgl - cdogs - cemu - cgenius -- citra -- citron - clk - corsixth -- demul -- desmume - devilutionx - dhewm3 -- dice - dolphin -- dolphin_triforce -- dosbox - dosbox-x - dosbox_pure - dosbox_staging - dosboxx -- drastic -- duckstation -- duckstation-legacy - dxx-rebirth - easyrpg -- ecwolf - eduke32 -- eka2l1 - emuscv - etlegacy - fake08 - fallout1-ce - fallout2-ce -- fba2x - fbalpha - fbneo - fceumm @@ -68,20 +49,15 @@ cores: - flycast-xtreme - flycastvl - fmsx -- fpinball - freechaf - freeintv -- freej2me -- fsuae - fury - fuse - gambatte - gearsystem - genesisplusgx -- genesisplusgx-expanded - genesisplusgx-wide - geolith -- gong - gpsp - gsplus - gw @@ -92,15 +68,9 @@ cores: - hcl - holani - hurrican -- hypseus-singe -- ikemen -- imame4all - ioquake3 - iortcw -- jazz2-native -- kronos - lightspark -- lindbergh-loader - lowresnx - lutro - mame @@ -117,7 +87,6 @@ cores: - mesen-s - mgba - minivmac -- model2emu - moonlight - mpv - mrboom @@ -134,21 +103,14 @@ cores: - openbor7142 - openbor7530 - openjazz -- openjk -- openjkdf2 -- openlara -- openmohaa -- openmsx - opera - parallel_n64 - pce - pce_fast - pcfx -- pcsx2 - pcsx_rearmed - picodrive - play -- pocketsnes - pokemini - potator - ppsspp @@ -158,20 +120,15 @@ cores: - puae2021 - px68k - pygame -- pyxel - quasi88 - raze -- redream - reminiscence - rpcs3 -- ruffle -- ryujinx - samcoupe - same_cdi - sameduck - scummvm - sdlpop -- shadps4 - smsplus - snes9x - snes9x_next @@ -180,31 +137,21 @@ cores: - sonic2013 - sonic3-air - soniccd -- squirreljme - stella -- stella2014 - superbroswar -- supermodel -- supermodel-legacy - swanstation - taradino - tgbdual -- theforceengine - theodore - thextech - tic80 -- tr1x -- tr2x -- tsugaru - tyrian - tyrquake - uae4arm -- uqm - uzem - vb - vba-m - vecx -- vemulator - vice - vice_x128 - vice_x64 @@ -221,24 +168,18 @@ cores: - vkquake2 - vkquake3 - vox_official -- vpinball -- wasm4 - x1 - x128 - x16emu - x64 - x64dtv -- xash3d_fwgs - xemu -- xenia -- xenia-canary - xpet - xplus4 - xrick - xscpu64 - xvic - yabasanshiro -- ymir - zc210 systems: atari-400-800: @@ -4879,61 +4820,39 @@ standalone_cores: - advanced_drastic - aethersx2 - amiberry -- applewin - azahar -- bigpemu - bstone - cannonball -- catacombgl - cdogs - cemu - cgenius -- citra -- citron - clk - corsixth -- demul - devilutionx - dhewm3 - dolphin -- dolphin_triforce -- dosbox - dosbox-x - dosbox_staging - dosboxx -- drastic -- duckstation -- duckstation-legacy - dxx-rebirth - easyrpg -- ecwolf - eduke32 -- eka2l1 - etlegacy - fallout1-ce - fallout2-ce -- fba2x - flycast -- fpinball -- fsuae - fury - gsplus - gzdoom - hatari - hcl - hurrican -- hypseus-singe -- ikemen - ioquake3 - iortcw -- jazz2-native -- kodi - lexaloffle - lightspark -- lindbergh-loader - mame - melonds -- model2emu - moonlight - mpv - mupen64plus @@ -4943,71 +4862,46 @@ standalone_cores: - openbor7142 - openbor7530 - openjazz -- openjk -- openjkdf2 -- openmohaa -- openmsx -- pcsx2 - play - ppsspp - pygame -- pyxel - raze -- redream - rpcs3 -- ruffle -- ryujinx - samcoupe - scummvm - sdlpop -- shadps4 - solarus - sonic-mania - sonic2013 - sonic3-air - soniccd - stella -- supermodel -- supermodel-legacy - taradino -- theforceengine - thextech -- tr1x -- tr2x -- tsugaru - tyrian -- uqm - vice - vita3k - vkquake - vkquake2 - vkquake3 - vox_official -- vpinball - x128 - x16emu - x64 - x64dtv -- xash3d_fwgs - xemu -- xenia -- xenia-canary - xpet - xplus4 - xscpu64 - xvic - yabasanshiro -- ymir dual_mode_cores: -- applewin -- citra - dolphin - easyrpg - flycast - hatari - mame - melonds -- pcsx2 - play - ppsspp - scummvm @@ -5393,129 +5287,6 @@ libretro_systems: zc210: - zc210 launchers: -- emulator: duckstation - build: standalone - kind: names_searched - slot: NTSCU - names: - - scph101.bin - - scph1001.bin - - scph5501.bin - - scph7001.bin - - scph7501.bin - case_insensitive: true - search_dir: /userdata/bios - passes_to: "[BIOS] PathNTSCU" - fails_when_missing: No PSX1 BIOS found - fails_scope: emulator - source_ref: "package/system/knulli-configgen/configgen/configgen/generators/duckstation/duckstationGenerator.py:119-143,\ - \ 589-611" -- emulator: duckstation - build: standalone - kind: names_searched - slot: PAL - names: - - scph1002.bin - - scph5502.bin - - scph5552.bin - - scph7002.bin - - scph7502.bin - - scph9002.bin - - scph102a.bin - - scph102b.bin - case_insensitive: true - search_dir: /userdata/bios - passes_to: "[BIOS] PathPAL" - fails_when_missing: No PSX1 BIOS found - fails_scope: emulator - source_ref: "package/system/knulli-configgen/configgen/configgen/generators/duckstation/duckstationGenerator.py:119-143,\ - \ 589-611" -- emulator: duckstation - build: standalone - kind: names_searched - slot: NTSCJ - names: - - scph100.bin - - scph1000.bin - - scph3000.bin - - scph3500.bin - - scph5500.bin - - scph7000.bin - - scph7003.bin - case_insensitive: true - search_dir: /userdata/bios - passes_to: "[BIOS] PathNTSCJ" - fails_when_missing: No PSX1 BIOS found - fails_scope: emulator - source_ref: "package/system/knulli-configgen/configgen/configgen/generators/duckstation/duckstationGenerator.py:119-143,\ - \ 589-611" -- emulator: duckstation - build: standalone - kind: names_searched - slot: Uni - names: - - psxonpsp660.bin - - ps1_rom.bin - case_insensitive: true - search_dir: /userdata/bios - passes_to: "[BIOS] PathNTSCU, PathPAL, PathNTSCJ" - fails_when_missing: No PSX1 BIOS found - fails_scope: emulator - source_ref: "package/system/knulli-configgen/configgen/configgen/generators/duckstation/duckstationGenerator.py:119-143,\ - \ 589-611" -- emulator: duckstation-legacy - build: standalone - kind: names_searched - slot: NTSCU - names: - - scph101.bin - - scph1001.bin - - scph5501.bin - - scph7001.bin - - scph7501.bin - case_insensitive: false - search_dir: /userdata/bios - passes_to: "[BIOS] PathNTSCU" - fails_when_missing: No PSX1 BIOS found - fails_scope: emulator - source_ref: "package/system/knulli-configgen/configgen/configgen/generators/duckstation_legacy/duckstationLegacyGenerator.py:116-153" -- emulator: duckstation-legacy - build: standalone - kind: names_searched - slot: PAL - names: - - scph1002.bin - - scph5502.bin - - scph5552.bin - - scph7002.bin - - scph7502.bin - - scph9002.bin - - scph102a.bin - - scph102b.bin - case_insensitive: false - search_dir: /userdata/bios - passes_to: "[BIOS] PathPAL" - fails_when_missing: No PSX1 BIOS found - fails_scope: emulator - source_ref: "package/system/knulli-configgen/configgen/configgen/generators/duckstation_legacy/duckstationLegacyGenerator.py:116-153" -- emulator: duckstation-legacy - build: standalone - kind: names_searched - slot: NTSCJ - names: - - scph100.bin - - scph1000.bin - - scph3000.bin - - scph3500.bin - - scph5500.bin - - scph7000.bin - - scph7003.bin - case_insensitive: false - search_dir: /userdata/bios - passes_to: "[BIOS] PathNTSCJ" - fails_when_missing: No PSX1 BIOS found - fails_scope: emulator - source_ref: "package/system/knulli-configgen/configgen/configgen/generators/duckstation_legacy/duckstationLegacyGenerator.py:116-153" - emulator: hatari build: standalone kind: names_searched @@ -5683,33 +5454,6 @@ launchers: passes_to: environment FLYCAST_BIOS_PATH fails_when_missing: false source_ref: "package/system/knulli-configgen/configgen/configgen/generators/flycast/flycastGenerator.py:239" -- emulator: shadps4 - build: standalone - kind: passes_path - names: [] - search_dir: /userdata/system/configs/shadps4 - passes_to: working directory of shadps4Generator.py - fails_when_missing: false - source_ref: "package/system/knulli-configgen/configgen/configgen/generators/shadps4/shadps4Generator.py:33,\ - \ 164" -- emulator: openmsx - build: standalone - kind: passes_path - names: [] - search_dir: /userdata/bios/Machines - passes_to: "written: filepool add -path /userdata/bios/Machines -types system_rom\ - \ -position 1" - fails_when_missing: false - source_ref: "package/system/knulli-configgen/configgen/configgen/generators/openmsx/openmsxGenerator.py:95" -- emulator: openmsx - build: standalone - kind: passes_path - names: [] - search_dir: /userdata/bios/openmsx - passes_to: "written: filepool add -path /userdata/bios/openmsx -types system_rom\ - \ -position 2" - fails_when_missing: false - source_ref: "package/system/knulli-configgen/configgen/configgen/generators/openmsx/openmsxGenerator.py:96" - emulator: amiberry build: standalone kind: passes_path @@ -5765,15 +5509,6 @@ launchers: depends_on_option: neocd_bios source_ref: "package/system/knulli-configgen/configgen/configgen/generators/libretro/libretroOptions.py:2558-2559,\ \ 2566-2569" -- emulator: pcsx2 - build: standalone - kind: passes_path - names: [] - search_dir: /userdata/bios/ps2 - passes_to: "inis/PCSX2.ini [Folders] Bios = ../../../bios/ps2, relative to /userdata/system/configs/PCSX2" - fails_when_missing: false - source_ref: "package/system/knulli-configgen/configgen/configgen/generators/pcsx2/pcsx2Generator.py:92,\ - \ 176, 213" - emulator: aethersx2 build: standalone kind: passes_path @@ -5919,29 +5654,6 @@ launchers: fails_when_missing: false depends_on_option: "Rpcs3Generator.getFirmwareVersion() is None" source_ref: "package/system/knulli-configgen/configgen/configgen/generators/rpcs3/rpcs3Generator.py:355-358" -- emulator: ryujinx - build: standalone - kind: copies - names: - - switch/prod.keys - case_insensitive: false - search_dir: /userdata/bios - passes_to: /userdata/system/configs/Ryujinx/system/prod.keys - fails_when_missing: false - depends_on_option: "ryujinxKeys.exists()" - source_ref: "package/system/knulli-configgen/configgen/configgen/generators/ryujinx/ryujinxGenerator.py:107-109" -- emulator: pcsx2 - build: standalone - kind: copies - names: - - patches.zip - case_insensitive: false - search_dir: /usr/share/knulli/datainit/bios/ps2 - passes_to: /userdata/bios/ps2/patches.zip - fails_when_missing: false - depends_on_option: "not pcsx2Patches.exists()" - source_ref: "package/system/knulli-configgen/configgen/configgen/generators/pcsx2/pcsx2Generator.py:78,\ - \ 121-122" - emulator: aethersx2 build: standalone kind: copies @@ -5962,14 +5674,6 @@ launchers: passes_to: g_cfg_rom_path fails_when_missing: false source_ref: "package/system/knulli-configgen/configgen/configgen/generators/gsplus/gsplusGenerator.py:106" -- emulator: tsugaru - build: standalone - kind: passes_path - names: [] - search_dir: /userdata/bios/fmtowns - passes_to: command line argument 1 - fails_when_missing: false - source_ref: "package/system/knulli-configgen/configgen/configgen/generators/tsugaru/tsugaruGenerator.py:18-19" - emulator: scummvm build: standalone kind: passes_path @@ -5978,14 +5682,6 @@ launchers: passes_to: command line --extrapath fails_when_missing: false source_ref: "package/system/knulli-configgen/configgen/configgen/generators/scummvm/scummvmGenerator.py:117-123" -- emulator: fsuae - build: standalone - kind: passes_path - names: [] - search_dir: /userdata/bios/amiga - passes_to: command line --kickstarts_dir - fails_when_missing: false - source_ref: "package/system/knulli-configgen/configgen/configgen/generators/fsuae/fsuaeGenerator.py:63-69" - emulator: mame build: standalone kind: passes_path @@ -6039,16 +5735,6 @@ launchers: passes_to: command line -crosshairpath fails_when_missing: false source_ref: "package/system/knulli-configgen/configgen/configgen/generators/mame/mameGenerator.py:180" -- emulator: fpinball - build: standalone - kind: passes_path - names: [] - search_dir: /userdata/bios - passes_to: environment W_CACHE - fails_when_missing: false - depends_on_option: "not wsh57_done.exists()" - source_ref: "package/system/knulli-configgen/configgen/configgen/generators/fpinball/fpinballGenerator.py:37,\ - \ 39-43" - emulator: retroarch build: libretro kind: passes_path @@ -6142,15 +5828,6 @@ launchers: depends_on_option: not system.name == 'ti99' source_ref: "package/system/knulli-configgen/configgen/configgen/generators/libretro/libretroMAMEConfig.py:431-432,\ \ 435" -- emulator: citron - build: standalone - kind: passes_path - names: [] - search_dir: /userdata/bios/switch - passes_to: "CitronPath::KeysDir in src/common/fs/path_util.cpp returns it, in place\ - \ of citron_path / KEYS_DIR" - fails_when_missing: false - source_ref: "batocera/package/batocera/emulators/citron/002-adjust-paths.patch:9-10" configs_root: path: /userdata/system/configs from_bios: ../system/configs diff --git a/scripts/scraper/batocera_scraper.py b/scripts/scraper/batocera_scraper.py index 8ef10db16..84a22c2b3 100644 --- a/scripts/scraper/batocera_scraper.py +++ b/scripts/scraper/batocera_scraper.py @@ -14,7 +14,7 @@ import re import sys import urllib.error import urllib.request -from collections.abc import Callable, Iterable +from collections.abc import Callable from pathlib import Path @@ -30,6 +30,8 @@ from .base_scraper import ( requirement_entry, ) from .batocera_launchers import Configgen, configs_root, extract_launchers +from .targets.batocera_targets_scraper import BuildSelection +from .targets.batocera_targets_scraper import Scraper as BoardTargets PLATFORM_NAME = "batocera" @@ -489,76 +491,51 @@ def configgen_cores( for GameCube). `dropped` names what es_systems.yml files under an emulator without being one. - A default es_systems.yml does not offer names an emulator the build may - lack: KNULLI's defaults pick suyu for the Switch, and no package of its - tree, its batocera submodule or its buildroot builds suyu. `built` says - whether the build has a standalone emulator (packages_and_programs); a - default it answers no for is not run, nor its core. + `built` says whether some board builds a name (boards_build). What it + answers no for is not run: KNULLI's es_systems lists citron and shadps4, + which no KNULLI board selects, and its defaults pick suyu, which no + package builds. A default's core follows a standalone emulator the + build runs (vox_official is lexaloffle's Voxatron build). """ data = yaml_load_upstream(defaults_text) cores, standalone = es_systems_emulators(es_systems_text) - offered = set(cores) libretro = es_systems_libretro(es_systems_text) if not cores: raise RuntimeError("es_systems.yml names no emulator") + if built is not None: + cores = {name for name in cores if built(name)} + standalone &= cores for system, cfg in data.items(): if system == "default" or not isinstance(cfg, dict): continue emulator = cfg.get("emulator", "") core = cfg.get("core", "") - if (built is not None and emulator and emulator != "libretro" - and emulator not in offered and not built(emulator)): - continue - if core: - cores.add(core) if emulator and emulator != "libretro": + if built is not None and not built(emulator): + continue standalone.add(emulator) if core and core != emulator: standalone.add(core) + elif built is not None and core and not built(core): + continue + if core: + cores.add(core) dual = (standalone & libretro) - dropped return sorted(cores - dropped), sorted(standalone - dropped), sorted(dual) -BATOCERA_REPO = "batocera-linux/batocera.linux" +def boards_build(selection: BuildSelection, launchers: list[dict]) -> Callable[[str], bool]: + """Whether some board of a build runs an emulator. - -def package_names(paths: Iterable[str]) -> set[str]: - """The buildroot packages a tree defines: each folder holding a Config.in.""" - return { - path.rsplit("/", 2)[-2] - for path in paths - if path.endswith("/Config.in") and path.count("/") >= 1 - } - - -def repo_tree(repo: str, ref: str) -> list[str]: - """Every path of a GitHub repository at a revision.""" - url = f"https://api.github.com/repos/{repo}/git/trees/{ref}?recursive=1" - req = urllib.request.Request(url, headers=github_headers()) - with urllib.request.urlopen(req, timeout=60) as resp: - found = json.loads(resp.read().decode("utf-8")) - if found.get("truncated"): - raise RuntimeError(f"{url}: tree truncated") - return [entry["path"] for entry in found.get("tree", [])] - - -def submodule_revision(repo: str, path: str, ref: str) -> str: - """The commit a repository's submodule at `path` pins at a revision.""" - url = f"https://api.github.com/repos/{repo}/contents/{path}?ref={ref}" - req = urllib.request.Request(url, headers=github_headers()) - with urllib.request.urlopen(req, timeout=30) as resp: - found = json.loads(resp.read().decode("utf-8")) - if not isinstance(found, dict) or found.get("type") != "submodule" or not found.get("sha"): - raise RuntimeError(f"{url}: {path} is not a submodule") - return str(found["sha"]) - - -def packages_and_programs(packages: set[str], launchers: list[dict]) -> Callable[[str], bool]: - """Whether a build has a standalone emulator. - - It does when a package of that name exists, or when its launcher runs a - program the user puts in the BIOS folder (Batocera's lexaloffle runs - /userdata/bios/pico-8/pico8), which no package builds. + es_systems.yml ties each name to the packages that build it + (requireAnyOf), and a name it lists is built when a board selects one of + them: KNULLI lists citron and shadps4, and its Config.in selects them + only on x86_64 boards, which it has none of. A name only + configgen-defaults.yml gives is built when a board selects the package + of that name: the symbol its Config.in defines, else the one Buildroot + derives from the folder (BR2_PACKAGE_, package/pkg-generic.mk). A + program the launcher runs from the BIOS folder (lexaloffle, + /userdata/bios/pico-8/pico8) needs no package. """ supplied = { name.strip() @@ -567,7 +544,27 @@ def packages_and_programs(packages: set[str], launchers: list[dict]) -> Callable and str(record.get("search_dir") or "").startswith("/userdata/bios") for name in str(record.get("emulator", "")).split(",") } - return lambda emulator: emulator in packages or emulator in supplied + + def built(name: str) -> bool: + if name in selection.emulators or name in supplied: + return True + if name in selection.listed: + return False + symbol = selection.packages.get(name) or ( + "BR2_PACKAGE_" + name.upper().replace("-", "_")) + return symbol in selection.symbols + + return built + + +def records_run(launchers: list[dict], built: Callable[[str], bool]) -> list[dict]: + """The launcher records of emulators the build runs. + + configgen keeps the generators of emulators a build does not select + (KNULLI's citron, pcsx2, shadps4): what they read is never read on the + device, so the records do not describe the platform. + """ + return [r for r in launchers if any(built(n) for n in str(r["emulator"]).split(", "))] class Scraper(BaseScraper): @@ -578,20 +575,13 @@ class Scraper(BaseScraper): super().__init__(url=SOURCE_URL.format(tag=self.tag)) def _fetch_cores( - self, launchers: list[dict] | None = None + self, built: Callable[[str], bool] | None = None ) -> tuple[list[str], list[str], list[str]]: """Core names, standalone cores and dual-mode cores at the pinned tag. - A default es_systems.yml does not offer counts when a package of - Batocera's tree or its buildroot builds it, or when its launcher - runs a program from the BIOS folder. + With `built` (boards_build), a name counts when some board builds + it or its launcher runs a program from the BIOS folder. """ - built = None - if launchers is not None: - paths = repo_tree(BATOCERA_REPO, self.tag) - buildroot = submodule_revision(BATOCERA_REPO, "buildroot", self.tag) - paths += repo_tree("batocera-linux/buildroot", buildroot) - built = packages_and_programs(package_names(paths), launchers) return configgen_cores( self._fetch_text(CONFIGGEN_DEFAULTS_URL.format(tag=self.tag)), self._fetch_text(ES_SYSTEMS_URL.format(tag=self.tag)), @@ -633,7 +623,9 @@ class Scraper(BaseScraper): launchers = extract_launchers( lambda path: self._fetch_text(f"{_RAW_BASE}/{self.tag}/{path}") ) - cores, standalone, dual = self._fetch_cores(launchers) + built = boards_build(BoardTargets().build_selection(self.tag), launchers) + launchers = records_run(launchers, built) + cores, standalone, dual = self._fetch_cores(built) libretro_systems = es_systems_libretro_systems( self._fetch_text(ES_SYSTEMS_URL.format(tag=self.tag)), _not_cores() ) diff --git a/scripts/scraper/knulli_scraper.py b/scripts/scraper/knulli_scraper.py index 763c25ec4..f94f56704 100644 --- a/scripts/scraper/knulli_scraper.py +++ b/scripts/scraper/knulli_scraper.py @@ -37,18 +37,17 @@ from .batocera_scraper import ( ) from .batocera_scraper import ( _not_cores, + boards_build, configgen_cores, es_systems_by_system, es_systems_libretro_systems, extract_systems_dict, - package_names, - packages_and_programs, platform_systems, - repo_tree, + records_run, requirements_from_systems, - submodule_revision, ) from .es_emulators import attach_system_cores +from .targets.knulli_targets_scraper import Scraper as BoardTargets from .batocera_launchers import configs_root from .knulli_launchers import BATOCERA_SUBMODULE, extract_launchers from .knulli_launchers import configgen as knulli_configgen @@ -134,15 +133,11 @@ class Scraper(BaseScraper): lambda path: self._fetch_text(_RAW.format(ref=self.tag) + path), lambda path: self._fetch_text(_BATOCERA_RAW.format(ref=revision) + path), ) - # The packages KNULLI builds come from its own tree, its batocera - # submodule and its buildroot submodule: a default no package of - # them builds (suyu) is not run. - paths = repo_tree(REPO, self.tag) - paths += repo_tree("batocera-linux/batocera.linux", revision) - paths += repo_tree( - "knulli-cfw/buildroot", submodule_revision(REPO, "buildroot", self.tag) - ) - built = packages_and_programs(package_names(paths), launchers) + # What runs is what at least one board builds, read by the targets + # scraper from the Kconfig of KNULLI's tree and its batocera and + # buildroot submodules (boards_build). + built = boards_build(BoardTargets().build_selection(self.tag), launchers) + launchers = records_run(launchers, built) cores, standalone, dual = configgen_cores(defaults, es_systems, dropped, built) # Each system's own list (psx, es_systems.yml:640-653: libretro # pcsx_rearmed, swanstation, mednafen_psx, then duckstation and diff --git a/scripts/scraper/targets/batocera_targets_scraper.py b/scripts/scraper/targets/batocera_targets_scraper.py index f13615ccb..ce272de03 100644 --- a/scripts/scraper/targets/batocera_targets_scraper.py +++ b/scripts/scraper/targets/batocera_targets_scraper.py @@ -3,6 +3,8 @@ Sources (batocera-linux/batocera.linux): - configs/batocera-*.board -- board definitions, each sets BR2_PACKAGE_BATOCERA_TARGET_* - package/batocera/core/batocera-system/Config.in -- select PACKAGE if CONDITION lines + - Config.in and every Kconfig file it sources -- the selects a package + declares itself (advanced_drastic selects drastic) - package/batocera/emulationstation/batocera-es-system/es_systems.yml -- emulator requireAnyOf flag mapping Sources (batocera-linux/buildroot, at the submodule commit the tag pins): @@ -19,6 +21,8 @@ import re import sys import urllib.error import urllib.request +from concurrent.futures import ThreadPoolExecutor +from dataclasses import dataclass from datetime import datetime, timezone import yaml @@ -79,6 +83,16 @@ def _fetch(url: str, headers: dict | None = None) -> str: raise RuntimeError(f"cannot fetch {url}: {e}") from e +def _fetch_present(url: str) -> str | None: + """A file's text, or None when the server answers that it does not exist.""" + try: + return _fetch(url) + except RuntimeError as e: + if isinstance(e.__cause__, urllib.error.HTTPError) and e.__cause__.code == 404: + return None + raise + + def _fetch_json(url: str) -> list | dict: text = _fetch(url, headers=github_headers()) try: @@ -210,6 +224,7 @@ BUILDROOT_KCONFIG = ( ) _CONFIG_LINE_RE = re.compile(r"^\s*(?:menu)?config\s+(BR2_\w+)") +_SOURCE_RE = re.compile(r'^\s*source\s+"\$BR2_EXTERNAL_\w+_PATH/([^"]+)"') _OWNED_SELECT_RE = re.compile(r"^\s+select\s+(BR2_\w+)(?:\s+if\s+([^#]+))?") _BOARD_FLAG_RE = re.compile(r"^(BR2_\w+)=y\s*$", re.MULTILINE) _BOARD_INCLUDE_RE = re.compile(r"^(-?)include\s+(\S+)", re.MULTILINE) @@ -342,6 +357,43 @@ def _arch_from_flag(flag: str, board_flags: set[str] | frozenset[str] = frozense return "aarch64" +@dataclass(frozen=True) +class BuildSelection: + """What the boards of a build turn on, read once from its Kconfig. + + `targets` is each board's entry of the targets file. `emulators` holds + every es_systems name some board builds and `listed` every name + es_systems ties to a package, `symbols` every symbol some board ends up + with, and `packages` the config symbol each package folder of the tree + defines. + """ + + targets: dict[str, dict] + emulators: frozenset[str] + listed: frozenset[str] + symbols: frozenset[str] + packages: dict[str, str] + + +def _sourced(text: str) -> list[str]: + """The files a Kconfig file sources, relative to the external tree.""" + return [m.group(1) for line in text.splitlines() if (m := _SOURCE_RE.match(line))] + + +def _package_symbols(files: dict[str, str]) -> dict[str, str]: + """package folder -> the first config symbol its Config.in defines.""" + found: dict[str, str] = {} + for path, text in files.items(): + parts = path.split("/") + if len(parts) < 3 or parts[-1] != "Config.in" or "package" not in parts: + continue + config = next((m.group(1) for line in text.splitlines() + if (m := _CONFIG_LINE_RE.match(line))), None) + if config: + found.setdefault(parts[-2], config) + return found + + class Scraper(BaseTargetScraper): """Cross-references Batocera boards, Config.in, and es_systems to build target lists. @@ -357,6 +409,9 @@ class Scraper(BaseTargetScraper): ES_SYSTEMS_PATH = "package/batocera/emulationstation/batocera-es-system/es_systems.yml" SYSTEM_SYMBOL = "BR2_PACKAGE_BATOCERA_SYSTEM" BUILDROOT_REPO = "batocera-linux/buildroot" + # Path prefix -> repository of a submodule the root Config.in sources + # from: KNULLI reads Batocera's packages as batocera/package/... + SUBMODULES: dict[str, str] = {} def __init__(self, url: str = "") -> None: super().__init__(url=url or f"https://github.com/{self.REPO}") @@ -411,6 +466,45 @@ class Scraper(BaseTargetScraper): pending += [(inc, bool(dash)) for dash, inc in _BOARD_INCLUDE_RE.findall(text)] return target, flags + def _kconfig_files(self) -> dict[str, str]: + """The root Config.in and every file it sources, recursively. + + The build reads these and nothing else: a package's own Config.in + can select another package (advanced_drastic selects drastic), and + such a select sits in no system Config.in. A sourced file absent at + the pin defines nothing and is named: KNULLI 20260511 sources + package/utils/vtree/Config.in, a package its tree gained later. + """ + bases: list[tuple[str, str]] = [] + for prefix, repo in self.SUBMODULES.items(): + pin = _fetch_json(self._api(prefix.rstrip("/"))) + sha = pin.get("sha") if isinstance(pin, dict) else None + if not sha: + raise RuntimeError(f"no {prefix.rstrip('/')} submodule commit at {self.tag}") + bases.append((prefix, f"https://raw.githubusercontent.com/{repo}/{sha}/")) + + def url(path: str) -> str: + for prefix, base in bases: + if path.startswith(prefix): + return base + path.removeprefix(prefix) + return self._raw(path) + + files: dict[str, str] = {} + absent: list[str] = [] + pending = ["Config.in"] + with ThreadPoolExecutor(max_workers=8) as pool: + while pending: + found = dict(zip(pending, pool.map(lambda path: _fetch_present(url(path)), pending))) + absent += [path for path, text in found.items() if text is None] + files.update((path, text or "") for path, text in found.items()) + pending = sorted({src for text in found.values() if text + for src in _sourced(text)} - set(files)) + if "Config.in" in absent or len(files) - len(absent) < 2: + raise RuntimeError(f"Config.in at {self.tag} sources no readable Kconfig file") + for path in absent: + print(f" sourced but absent at {self.tag}: {path}", file=sys.stderr) + return files + def _buildroot_rules(self) -> list[tuple[str, str, str]]: """Buildroot's selects for the symbols the tree tests, at its pin.""" pin = _fetch_json(self._api("buildroot")) @@ -423,26 +517,32 @@ class Scraper(BaseTargetScraper): rules += _parse_owned_selects(_fetch(f"{raw}/{path}")) return rules - def fetch_targets(self) -> dict: - """Build per-board emulator availability map.""" - self.tag = self.pinned_revision(self.PLATFORM) + def build_selection(self, tag: str | None = None) -> BuildSelection: + """Each board's selection, from the Kconfig the build reads. + + `tag` is the revision to read, by default the one the platform's + YAML was scraped at: a platform scraper moving to a new release + passes its own. + """ + self.tag = tag or self.pinned_revision(self.PLATFORM) print(" fetching board list...", file=sys.stderr) boards = self._list_boards() - print(" fetching Config.in...", file=sys.stderr) - config_in_url = self._raw(self.CONFIG_IN_PATH) - config_in_text = _fetch(config_in_url) + print(" fetching Kconfig files...", file=sys.stderr) + files = self._kconfig_files() + config_in_text = files.get(self.CONFIG_IN_PATH) if not config_in_text: - raise RuntimeError(f"empty Config.in from {config_in_url}") + raise RuntimeError(f"Config.in at {self.tag} does not source {self.CONFIG_IN_PATH}") meta_rules = _parse_meta_flags(config_in_text) - rules = _parse_owned_selects(config_in_text) + rules = [rule for text in files.values() for rule in _parse_owned_selects(text)] print( - f" parsed {len(meta_rules)} meta-flag rules, {len(rules)} select lines", + f" parsed {len(meta_rules)} meta-flag rules, {len(rules)} select lines " + f"in {len(files)} Kconfig files", file=sys.stderr, ) - if not rules: - raise RuntimeError(f"no 'select BR2_PACKAGE_*' lines parsed from {config_in_url}") + if not _parse_owned_selects(config_in_text): + raise RuntimeError(f"no 'select BR2_PACKAGE_*' lines parsed from {self.CONFIG_IN_PATH}") rules += self._buildroot_rules() print(" fetching es_systems.yml...", file=sys.stderr) @@ -459,9 +559,10 @@ class Scraper(BaseTargetScraper): 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}") + raise RuntimeError(f"no target image list parsed from {self.CONFIG_IN_PATH}") targets: dict[str, dict] = {} + symbols: set[str] = set() for board_name in sorted(boards): target_key = board_name.removeprefix(self.BOARD_PREFIX).removesuffix(".board") print(f" processing {target_key}...", file=sys.stderr) @@ -476,6 +577,7 @@ class Scraper(BaseTargetScraper): if self.SYSTEM_SYMBOL not in selected_packages: print(f" {board_name} builds no system image", file=sys.stderr) continue + symbols |= selected_packages # Map selected packages to emulator names via es_systems.yml emulators: set[str] = set() @@ -496,11 +598,22 @@ class Scraper(BaseTargetScraper): file=sys.stderr, ) + return BuildSelection( + targets=targets, + emulators=frozenset(c for entry in targets.values() for c in entry["cores"]), + listed=frozenset(n for names in package_to_emulators.values() for n in names), + symbols=frozenset(symbols), + packages=_package_symbols(files), + ) + + def fetch_targets(self) -> dict: + """Build per-board emulator availability map.""" + selection = self.build_selection() return { "platform": self.PLATFORM, "source": f"{self.url}/tree/{self.tag}", "scraped_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"), - "targets": targets, + "targets": selection.targets, } diff --git a/scripts/scraper/targets/knulli_targets_scraper.py b/scripts/scraper/targets/knulli_targets_scraper.py index e19f72f27..16de256e7 100644 --- a/scripts/scraper/targets/knulli_targets_scraper.py +++ b/scripts/scraper/targets/knulli_targets_scraper.py @@ -9,6 +9,8 @@ the tag platforms/knulli.yml names): the root Config.in in place of Batocera's batocera-system/Config.in - package/emulationstation/knulli-es-system/es_systems.yml -- emulator requireAnyOf flag mapping + - Config.in and what it sources, Batocera's packages read from the + batocera submodule at the commit the tag pins Sources (knulli-cfw/buildroot, at the submodule commit the tag pins): - arch/Config.in, package/mesa3d/Config.in, package/libglvnd/Config.in @@ -32,6 +34,7 @@ class Scraper(BatoceraScraper): ES_SYSTEMS_PATH = "package/emulationstation/knulli-es-system/es_systems.yml" SYSTEM_SYMBOL = "BR2_PACKAGE_KNULLI_SYSTEM" BUILDROOT_REPO = "knulli-cfw/buildroot" + SUBMODULES = {"batocera/": "batocera-linux/batocera.linux"} def main() -> None: diff --git a/tests/test_elec_scrapers.py b/tests/test_elec_scrapers.py index 0ee2f1950..da854d70c 100644 --- a/tests/test_elec_scrapers.py +++ b/tests/test_elec_scrapers.py @@ -284,7 +284,10 @@ class TheExportKeepsTheirShape(unittest.TestCase): class TheCommittedListsAgree(unittest.TestCase): """A system's cores are a part of the platform's: a name only the system - carries would escape every reader of the platform list.""" + carries would escape every reader of the platform list. A system keeps + the list its ES file writes, which says whose files its entries ask for, + so the one name it may carry beyond the platform's is one no device + image builds (EmuELEC's VVVVVV port engine, KNULLI's duckstation).""" def test_system_cores_are_platform_cores(self): import yaml @@ -292,10 +295,13 @@ class TheCommittedListsAgree(unittest.TestCase): for name in ("amberelec", "emuelec", "knulli"): with self.subTest(platform=name): config = yaml.safe_load((REPO_ROOT / "platforms" / f"{name}.yml").read_text()) + targets = yaml.safe_load( + (REPO_ROOT / "platforms" / "targets" / f"{name}.yml").read_text())["targets"] + built = {c for entry in targets.values() for c in entry["cores"]} platform = {str(c) for c in config["cores"]} listed = {str(c) for s in config["systems"].values() for c in s.get("cores", [])} self.assertTrue(listed, "no system carries its cores") - self.assertEqual(listed - platform, set()) + self.assertEqual(listed - platform - (listed - built), set()) if __name__ == "__main__": diff --git a/tests/test_launcher_paths.py b/tests/test_launcher_paths.py index 55564f564..1a37a4fba 100644 --- a/tests/test_launcher_paths.py +++ b/tests/test_launcher_paths.py @@ -231,15 +231,20 @@ class APortableUserFolder(unittest.TestCase): -class ADefaultTheBuildLacksIsNotRun(unittest.TestCase): - """KNULLI's configgen-defaults.yml picks suyu for the Switch, which its - es_systems.yml does not offer and no package builds.""" +class OnlyWhatABoardBuildsIsRun(unittest.TestCase): + """KNULLI's es_systems.yml lists citron and shadps4, which its Config.in + selects only on x86_64 boards (it has none), and its + configgen-defaults.yml picks suyu, which no package builds.""" ES = textwrap.dedent("""\ switch: emulators: citron: citron: { requireAnyOf: [BR2_PACKAGE_CITRON] } + gba: + emulators: + libretro: + mgba: { requireAnyOf: [BR2_PACKAGE_LIBRETRO_MGBA] } voxatron: emulators: {} """) @@ -253,31 +258,66 @@ class ADefaultTheBuildLacksIsNotRun(unittest.TestCase): kodi: emulator: kodi core: kodi + mpv: + emulator: mpv + core: mpv """) LAUNCHERS = [{"emulator": "lexaloffle", "kind": "fixed_name", "passes_to": "exec", "search_dir": "/userdata/bios"}] - def test_only_what_a_package_or_the_user_supplies_is_run(self): + def selection(self, symbols: set[str]): + from scraper.targets.batocera_targets_scraper import BuildSelection + + return BuildSelection( + targets={}, emulators=frozenset({"mgba"}), + listed=frozenset({"citron", "mgba"}), symbols=frozenset(symbols), + packages={"kodi": "BR2_PACKAGE_KODI"}, + ) + + def test_only_what_a_board_builds_or_the_user_supplies_is_run(self): from scraper import batocera_scraper as bs - paths = ["package/batocera/emulators/citron/Config.in", "package/kodi/Config.in"] - built = bs.packages_and_programs(bs.package_names(paths), self.LAUNCHERS) + # A board selects kodi's package and buildroot's mpv; citron is + # listed by es_systems, so the symbol of a folder of that name does + # not count for it. + built = bs.boards_build( + self.selection({"BR2_PACKAGE_KODI", "BR2_PACKAGE_MPV", "BR2_PACKAGE_CITRON_X"}), + self.LAUNCHERS, + ) cores, standalone, _dual = bs.configgen_cores(self.DEFAULTS, self.ES, set(), built) - self.assertEqual(sorted(cores), ["citron", "kodi", "vox_official"]) - self.assertIn("lexaloffle", standalone) - self.assertNotIn("suyu", standalone) + self.assertEqual(sorted(cores), ["kodi", "mgba", "mpv", "vox_official"]) + self.assertEqual(sorted(standalone), ["kodi", "lexaloffle", "mpv", "vox_official"]) + # Each system keeps the list es_systems.yml writes (whose files its + # entries ask for), less a default no package builds. by_system = bs.es_systems_by_system(self.ES, self.DEFAULTS, set(), built) self.assertEqual(by_system["switch"], ["citron"]) - # Without the packages, every default counts, as before. + self.assertEqual(by_system["voxatron"], ["lexaloffle", "vox_official"]) + self.assertEqual(by_system["gba"], ["mgba"]) + + def test_a_listed_name_is_built_only_through_its_packages(self): + from scraper import batocera_scraper as bs + + built = bs.boards_build(self.selection({"BR2_PACKAGE_CITRON"}), []) + self.assertFalse(built("citron")) + self.assertTrue(built("mgba")) + self.assertFalse(built("lexaloffle")) + # Without a selection, every name counts, as before. self.assertIn("suyu", bs.configgen_cores(self.DEFAULTS, self.ES, set())[0]) - def test_the_real_knulli_list_names_no_suyu(self): + def test_the_real_lists_name_only_what_a_board_builds(self): import yaml - config = yaml.safe_load((REPO_ROOT / "platforms" / "knulli.yml").read_text()) - self.assertNotIn("suyu", config["cores"]) - self.assertNotIn("suyu", config.get("standalone_cores") or []) - + for platform, absent in (("knulli", ("suyu", "citron", "shadps4", "pcsx2")),): + config = yaml.safe_load((REPO_ROOT / "platforms" / f"{platform}.yml").read_text()) + names = set(config["cores"]) | set(config.get("standalone_cores") or []) + targets = yaml.safe_load( + (REPO_ROOT / "platforms" / "targets" / f"{platform}.yml").read_text())["targets"] + boards = {c for entry in targets.values() for c in entry["cores"]} + with self.subTest(platform=platform): + self.assertFalse(names & set(absent)) + # What a board builds and the list once named is still there, + # and what no board builds is a program the user supplies. + self.assertLessEqual(names - boards, {"kodi", "lexaloffle", "mpv", "vox_official"}) class AUserFolderInTheConfigsFolder(unittest.TestCase): @@ -289,7 +329,8 @@ class AUserFolderInTheConfigsFolder(unittest.TestCase): from common import standalone_set profile = load_emulator_profiles(str(REPO_ROOT / "emulators"))["shadps4"] - for platform in ("batocera", "knulli"): + # KNULLI's generator does the same, but no KNULLI board builds shadps4. + for platform in ("batocera",): config = load_platform_config(platform, str(REPO_ROOT / "platforms")) ((view, _standalone),) = platform_layouts("shadps4", profile, standalone_set(config)) with self.subTest(platform=platform): diff --git a/tests/test_standalone_links.py b/tests/test_standalone_links.py index 1fcd5eedd..7638dfafa 100644 --- a/tests/test_standalone_links.py +++ b/tests/test_standalone_links.py @@ -548,12 +548,12 @@ class OpenmsxOnEachPlatform(unittest.TestCase): """openMSX finds system ROMs by SHA1 in share/systemroms below its user directory and in every filepool entry. RetroBat sets OPENMSX_HOME to bios/openmsx, RetroPie links share/systemroms of its user directory to - BIOS/openmsx, Batocera and KNULLI add bios/Machines and bios/openmsx to - the filepool and list their own files in bios/openmsx.""" + BIOS/openmsx, Batocera adds bios/Machines and bios/openmsx to the + filepool and lists its own files in bios/openmsx. KNULLI's generator + does the same, but no KNULLI board builds openmsx.""" EXPECTED = { "batocera": {"share/systemroms": ("", "openmsx")}, - "knulli": {"share/systemroms": ("", "openmsx")}, "retrobat": {"": ("", "openmsx")}, "retropie": {"share/systemroms": ("", "openmsx")}, } diff --git a/tests/test_target_scrapers.py b/tests/test_target_scrapers.py index d55033cff..81c389d5d 100644 --- a/tests/test_target_scrapers.py +++ b/tests/test_target_scrapers.py @@ -97,6 +97,9 @@ class FailedRequestsStopTheScrape(unittest.TestCase): _refuse() return _Body(b"BR2_PACKAGE_BATOCERA_TARGET_X86_64=y\n" b"BR2_PACKAGE_BATOCERA_SYSTEM=y\n") + if url.startswith("https://raw.githubusercontent.com/") and url.count("/") == 6: + return _Body(b'source "$BR2_EXTERNAL_BATOCERA_PATH/' + b'package/batocera/core/batocera-system/Config.in"\n') if url.endswith("Config.in"): return _Body(b"config BR2_PACKAGE_BATOCERA_SYSTEM\n" b"\tselect BR2_PACKAGE_MGBA\n\n" @@ -262,6 +265,15 @@ class KnulliPerBoard(unittest.TestCase): "BR2_arm=y\nBR2_PACKAGE_BATOCERA_TARGET_H700=y\n" ), "configs/knulli-h700_armhf_libs.packages": "BR2_PACKAGE_SDL2=y\n", + # The root Config.in sources KNULLI's system and Batocera's packages + # from the batocera submodule, where a package selects another. + "Config.in": ( + 'source "$BR2_EXTERNAL_KNULLI_PATH/package/system/knulli-system/Config.in"\n' + 'source "$BR2_EXTERNAL_KNULLI_PATH/batocera/package/batocera/emulators/' + 'ppsspp/Config.in"\n' + '#source "$BR2_EXTERNAL_KNULLI_PATH/package/utils/off/Config.in"\n' + 'source "$BR2_EXTERNAL_KNULLI_PATH/package/utils/vtree/Config.in"\n' + ), "package/system/knulli-system/Config.in": ( "if BR2_PACKAGE_BATOCERA_TARGET_H700 || \\\n" " BR2_PACKAGE_BATOCERA_TARGET_A133\n" @@ -282,6 +294,8 @@ class KnulliPerBoard(unittest.TestCase): " mgba: {requireAnyOf: [BR2_PACKAGE_LIBRETRO_MGBA]}\n" "psp:\n emulators:\n ppsspp:\n" " ppsspp: {requireAnyOf: [BR2_PACKAGE_PPSSPP]}\n" + "pspassets:\n emulators:\n libretro:\n" + " assets: {requireAnyOf: [BR2_PACKAGE_PPSSPP_ASSETS]}\n" ), } @@ -314,6 +328,13 @@ class KnulliPerBoard(unittest.TestCase): return _Body(json.dumps([{"name": n} for n in names])) if url == f"{api}buildroot?ref={pin}": return _Body('{"sha": "c2d7985b"}') + if url == f"{api}batocera?ref={pin}": + return _Body('{"sha": "487014d9"}') + if url == ("https://raw.githubusercontent.com/batocera-linux/batocera.linux/" + "487014d9/package/batocera/emulators/ppsspp/Config.in"): + return _Body("config BR2_PACKAGE_PPSSPP\n\tselect BR2_PACKAGE_PPSSPP_ASSETS\n") + if url == f"{raw}package/utils/vtree/Config.in": + raise urllib.error.HTTPError(url, 404, "Not Found", {}, None) if url.startswith("https://raw.githubusercontent.com/knulli-cfw/buildroot/c2d7985b/"): return _Body("config BR2_aarch64\n\tselect BR2_ARCH_IS_64\n") path = url.removeprefix(raw) @@ -330,11 +351,12 @@ class KnulliPerBoard(unittest.TestCase): 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": ["assets", "mgba", "ppsspp"], "images": ["rg35xx-plus", "rg35xx-sp"]}) self.assertEqual(data["targets"]["r16"], {"architecture": "arm", "cores": ["mgba"], "images": ["miyoo-a30"]}) - self.assertTrue(all(pin in url or "buildroot/c2d7985b" in url for url in seen), seen) + self.assertTrue(all(pin in url or "buildroot/c2d7985b" in url + or "batocera.linux/487014d9" in url for url in seen), seen) class RocknixPerDevice(unittest.TestCase): From 0aa551dba73d0548f91b61c0be6d71d93c7c6a01 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Mon, 9 Nov 2026 21:26:40 +0100 Subject: [PATCH 2/6] fix: list only what a batocera board builds No Batocera board turns on BATOCERA_CORES_STILL_COMMERCIALIZED or BATOCERA_CORES_EXPERIMENTAL (Config.in:109, 1670), so citron, ryujinx, applewin and eka2l1 are not built, and freej2me, gong, squirreljme, uqm and vemulator are selected by no Kconfig file at batocera-43.1. Read with every sourced Kconfig file, the Pi 4 boards gain dxx-rebirth, sonic2013, soniccd and xroar (mesa3d-rpi4 gives them OpenGL). --- install/targets/batocera.json | 16 ++++++++++++++++ platforms/batocera.yml | 24 ------------------------ platforms/targets/batocera.yml | 12 +++++++++++- tests/test_launcher_paths.py | 3 ++- 4 files changed, 29 insertions(+), 26 deletions(-) diff --git a/install/targets/batocera.json b/install/targets/batocera.json index 3b38510ca..755702725 100644 --- a/install/targets/batocera.json +++ b/install/targets/batocera.json @@ -184,6 +184,7 @@ "dosbox_staging", "dosboxx", "duckstation", + "dxx-rebirth", "easyrpg", "ecwolf", "eduke32", @@ -277,7 +278,9 @@ "snes9x_next", "solarus", "sonic-mania", + "sonic2013", "sonic3-air", + "soniccd", "stella", "superbroswar", "supermodel", @@ -319,6 +322,7 @@ "xpet", "xplus4", "xrick", + "xroar", "xscpu64", "xvic", "yabasanshiro", @@ -902,6 +906,7 @@ "dosbox_staging", "dosboxx", "duckstation", + "dxx-rebirth", "easyrpg", "ecwolf", "eduke32", @@ -995,7 +1000,9 @@ "snes9x_next", "solarus", "sonic-mania", + "sonic2013", "sonic3-air", + "soniccd", "stella", "superbroswar", "supermodel", @@ -1037,6 +1044,7 @@ "xpet", "xplus4", "xrick", + "xroar", "xscpu64", "xvic", "yabasanshiro", @@ -4530,6 +4538,7 @@ "dosbox_staging", "dosboxx", "duckstation", + "dxx-rebirth", "easyrpg", "ecwolf", "eduke32", @@ -4623,7 +4632,9 @@ "snes9x_next", "solarus", "sonic-mania", + "sonic2013", "sonic3-air", + "soniccd", "stella", "superbroswar", "supermodel", @@ -4665,6 +4676,7 @@ "xpet", "xplus4", "xrick", + "xroar", "xscpu64", "xvic", "yabasanshiro", @@ -4695,6 +4707,7 @@ "dosbox_staging", "dosboxx", "duckstation", + "dxx-rebirth", "easyrpg", "ecwolf", "eduke32", @@ -4788,7 +4801,9 @@ "snes9x_next", "solarus", "sonic-mania", + "sonic2013", "sonic3-air", + "soniccd", "stella", "superbroswar", "supermodel", @@ -4830,6 +4845,7 @@ "xpet", "xplus4", "xrick", + "xroar", "xscpu64", "xvic", "yabasanshiro", diff --git a/platforms/batocera.yml b/platforms/batocera.yml index fc772a2fe..66f677c5c 100644 --- a/platforms/batocera.yml +++ b/platforms/batocera.yml @@ -10,7 +10,6 @@ cores: - a5200 - abuse - amiberry -- applewin - arduous - atari800 - azahar @@ -30,7 +29,6 @@ cores: - cdogs - cemu - cgenius -- citron - clk - corsixth - demul @@ -50,7 +48,6 @@ cores: - easyrpg - ecwolf - eduke32 -- eka2l1 - emuscv - ep128emu-core - etlegacy @@ -66,7 +63,6 @@ cores: - fmsx - freechaf - freeintv -- freej2me - fsuae - fury - fuse @@ -76,7 +72,6 @@ cores: - genesisplusgx - genesisplusgx-expanded - genesisplusgx-wide -- gong - gpsp - gsplus - gw @@ -158,7 +153,6 @@ cores: - reminiscence - rpcs3 - ruffle -- ryujinx - samcoupe - same_cdi - sameduck @@ -173,7 +167,6 @@ cores: - sonic2013 - sonic3-air - soniccd -- squirreljme - stella - stella2014 - superbroswar @@ -192,12 +185,10 @@ cores: - tyrian - tyrquake - uae4arm -- uqm - uzem - vb - vba-m - vecx -- vemulator - vice - vice_x128 - vice_x64 @@ -5683,7 +5674,6 @@ systems: standalone_cores: - abuse - amiberry -- applewin - azahar - bigpemu - bstone @@ -5692,7 +5682,6 @@ standalone_cores: - cdogs - cemu - cgenius -- citron - clk - corsixth - demul @@ -5709,7 +5698,6 @@ standalone_cores: - easyrpg - ecwolf - eduke32 -- eka2l1 - etlegacy - fallout1-ce - fallout2-ce @@ -5755,7 +5743,6 @@ standalone_cores: - redream - rpcs3 - ruffle -- ryujinx - samcoupe - scummvm - sdlpop @@ -5776,7 +5763,6 @@ standalone_cores: - tr2x - tsugaru - tyrian -- uqm - vice - vita3k - vkquake @@ -5800,7 +5786,6 @@ standalone_cores: - ymir - yquake2 dual_mode_cores: -- applewin - dolphin - easyrpg - flycast @@ -6624,15 +6609,6 @@ launchers: fails_when_missing: false source_ref: "package/batocera/core/batocera-configgen/configgen/configgen/generators/shadps4/shadps4Generator.py:46,\ \ 228" -- emulator: citron - build: standalone - kind: passes_path - names: [] - search_dir: /userdata/bios/switch - passes_to: "CitronPath::KeysDir in src/common/fs/path_util.cpp returns it, in place\ - \ of citron_path / KEYS_DIR" - fails_when_missing: false - source_ref: "package/batocera/emulators/citron/002-adjust-paths.patch:9-10" configs_root: path: /userdata/system/configs from_bios: ../system/configs diff --git a/platforms/targets/batocera.yml b/platforms/targets/batocera.yml index 4127e3b01..a4fbf1d0e 100644 --- a/platforms/targets/batocera.yml +++ b/platforms/targets/batocera.yml @@ -1,6 +1,6 @@ platform: batocera source: https://github.com/batocera-linux/batocera.linux/tree/batocera-43.1 -scraped_at: '2026-11-06T02:18:59Z' +scraped_at: '2026-11-09T19:49:31Z' targets: a3gen2: architecture: aarch64 @@ -203,6 +203,7 @@ targets: - dosbox_staging - dosboxx - duckstation + - dxx-rebirth - easyrpg - ecwolf - eduke32 @@ -300,7 +301,9 @@ targets: - snes9x_next - solarus - sonic-mania + - sonic2013 - sonic3-air + - soniccd - stella - superbroswar - supermodel @@ -342,6 +345,7 @@ targets: - xpet - xplus4 - xrick + - xroar - xscpu64 - xvic - yabasanshiro @@ -5379,6 +5383,7 @@ targets: - model2emu - moonlight - mrboom + - mugen - mupen64plus - mupen64plus-next - neocd @@ -5475,6 +5480,8 @@ targets: - vkquake3 - vpinball - wasm4 + - wine + - wine-tkg - x1 - x128 - x16emu @@ -5605,6 +5612,7 @@ targets: - model2emu - moonlight - mrboom + - mugen - mupen64plus - mupen64plus-next - neocd @@ -5701,6 +5709,8 @@ targets: - vkquake3 - vpinball - wasm4 + - wine + - wine-tkg - x1 - x128 - x16emu diff --git a/tests/test_launcher_paths.py b/tests/test_launcher_paths.py index 1a37a4fba..eb2730746 100644 --- a/tests/test_launcher_paths.py +++ b/tests/test_launcher_paths.py @@ -307,7 +307,8 @@ class OnlyWhatABoardBuildsIsRun(unittest.TestCase): def test_the_real_lists_name_only_what_a_board_builds(self): import yaml - for platform, absent in (("knulli", ("suyu", "citron", "shadps4", "pcsx2")),): + for platform, absent in (("knulli", ("suyu", "citron", "shadps4", "pcsx2")), + ("batocera", ("citron", "ryujinx", "applewin"))): config = yaml.safe_load((REPO_ROOT / "platforms" / f"{platform}.yml").read_text()) names = set(config["cores"]) | set(config.get("standalone_cores") or []) targets = yaml.safe_load( From ebba4c9c890f7676ab4146cc8fc6721fdc362b54 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Mon, 9 Nov 2026 21:27:38 +0100 Subject: [PATCH 3/6] fix: list only what an emuelec image carries The 29 port engines depend on emuelec-ports, which no package of a released image depends on, and flycast-dojo is in no device list: the targets scraper already says so (UNBUILT). The platform list and the launcher records now hold what at least one device image carries. Each system keeps the list es_systems.json gives it, which says whose files its entries ask for: VVVVVV's data.zip is still never served xrick's. --- platforms/emuelec.yml | 69 ------------------- scripts/scraper/emuelec_scraper.py | 33 +++++++-- .../targets/emuelec_targets_scraper.py | 7 +- tests/test_elec_scrapers.py | 1 + tests/test_game_data_homonyms.py | 2 + 5 files changed, 38 insertions(+), 74 deletions(-) diff --git a/platforms/emuelec.yml b/platforms/emuelec.yml index 0e208d183..a9cbdf1ee 100644 --- a/platforms/emuelec.yml +++ b/platforms/emuelec.yml @@ -11,56 +11,38 @@ cores: - "81" - AMIBERRY - AdvanceMame -- CGeniusExe -- Chocolate-Doom - DOSBOXSDL2 - Duckstation - ECWolf - FBNEOSA -- GZDoom - HATARISA - HYPSEUS -- LZDoom - OPENBOR -- OpenJazz - PPSSPPSDL - Pico-8 -- RigelEngine - SCUMMVMSA - STELLASA - Solarus -- VVVVVV - a5200 -- abuse - atari800 - bgdi - bk - bluemsx -- bs -- bstone - cannonball - cap32 -- ccleste -- cdogs-sdl - daphne -- devilutionx - dinothawr - dolphin - dosbox_pure - dosbox_svn - easyrpg -- eduke32 - fake08 -- fallout-ce -- fallout2-ce - fbalpha2012 - fbneo - fceumm - fceumm_mod -- fheroes2 - flycast - flycast_32b -- flycast_dojo - flycastsa - fmsx - freeintv @@ -80,9 +62,6 @@ cores: - gw - handy - hatari -- hcl -- hode -- hurrican - ikemen - jzintv - mame @@ -111,7 +90,6 @@ cores: - np2kai - nxengine - o2em -- opentyrian - opera - parallel_n64_32b - pcsx_rearmed @@ -121,7 +99,6 @@ cores: - pokemini - potator - prboom -- prince - prosystem - puae - puae2021 @@ -131,17 +108,11 @@ cores: - sameboy - sameduck - scummvm -- smw - snes9x - snes9x2002 - snes9x2005_plus - snes9x2010 -- sonic2013 -- soniccd -- sonicmania - stella -- supertux2 -- supertuxkart - swanstation - tgbdual - tic80 @@ -158,60 +129,29 @@ cores: - vice_xvic - virtualjaguar - x1 -- xash3d - xrick - yabasanshiro standalone_cores: - AMIBERRY - AdvanceMame -- CGeniusExe -- Chocolate-Doom - DOSBOXSDL2 - Duckstation - ECWolf - FBNEOSA -- GZDoom - HATARISA - HYPSEUS -- LZDoom - OPENBOR -- OpenJazz - PPSSPPSDL - Pico-8 -- RigelEngine - SCUMMVMSA - STELLASA - Solarus -- VVVVVV -- abuse - bgdi -- bs -- bstone -- ccleste -- cdogs-sdl -- devilutionx - dolphin -- eduke32 -- fallout-ce -- fallout2-ce -- fheroes2 -- flycast_dojo - flycastsa -- hcl -- hode -- hurrican - ikemen - jzintv - mupen64plussa -- opentyrian -- prince -- smw -- sonic2013 -- soniccd -- sonicmania -- supertux2 -- supertuxkart -- xash3d - yabasanshiro dual_mode_cores: - FBNEOSA @@ -4412,15 +4352,6 @@ launchers: fails_when_missing: false source_ref: "packages/sx05re/emuelec/bin/emuelecRunEmu.sh:198-200, packages/sx05re/emulators/flycastsa/scripts/flycast.sh:11,\ \ 14" -- emulator: flycast_dojo - build: standalone - kind: links - names: [] - search_dir: /storage/roms/bios/dc_dojo - passes_to: symlink /storage/.local/share/flycast-dojo -> /storage/roms/bios/dc_dojo - fails_when_missing: false - source_ref: "packages/sx05re/emuelec/bin/emuelecRunEmu.sh:201-203, packages/sx05re/emulators/flycast-dojo/scripts/flycastdojo.sh:11-12,\ - \ 28-30" - emulator: amiberry build: standalone kind: links diff --git a/scripts/scraper/emuelec_scraper.py b/scripts/scraper/emuelec_scraper.py index a0729952e..7e7c21c96 100644 --- a/scripts/scraper/emuelec_scraper.py +++ b/scripts/scraper/emuelec_scraper.py @@ -209,7 +209,18 @@ class Scraper(BaseScraper): ) not_cores = _not_cores(PLATFORM_NAME) cores, standalone, dual = es_cores(es_systems, RUNNERS, BUILDS_OF, not_cores) + # Each system keeps the list es_systems.json gives it: it says whose + # files a platform entry asks for. What runs is what an image carries. attach_system_cores(systems, system_cores(es_systems, RUNNERS, not_cores)) + built = self._built() + unbuilt = {n for n in cores if n not in built} + cores, standalone, dual = ([n for n in names if n in built] for names in (cores, standalone, dual)) + # Runners (retroarch, retrorun) and the launcher's own spellings are + # no ES core name: only a record of an emulator no image carries goes. + launchers = [ + r for r in extract_launchers(RepoTree.github(REPO, launcher_revision(self.url))) + if not set(str(r["emulator"]).split(", ")) <= unbuilt + ] return { "platform": "EmuELEC", "version": self.tag.removeprefix("v"), @@ -225,12 +236,26 @@ class Scraper(BaseScraper): "standalone_cores": standalone, "dual_mode_cores": dual, "systems": systems, - # Read at the revision the BIOS list comes from. - "launchers": extract_launchers( - RepoTree.github(REPO, launcher_revision(self.url)) - ), + # Read at the revision the BIOS list comes from, for the + # emulators an image carries. + "launchers": launchers, } + def _built(self) -> set[str]: + """The cores at least one released device image carries. + + Read by the targets scraper: a device keeps a core when a package it + builds installs it and its es_systems.cfg still offers it. The port + engines depend on emuelec-ports, which no package of an image + depends on, and flycast-dojo is in no device list (UNBUILT there). + """ + try: + from .targets.emuelec_targets_scraper import Scraper as DeviceTargets + except ImportError: + from scraper.targets.emuelec_targets_scraper import Scraper as DeviceTargets + targets = DeviceTargets().device_targets(self.tag)["targets"] + return {name for entry in targets.values() for name in entry["cores"]} + def main() -> None: from .base_scraper import scraper_cli diff --git a/scripts/scraper/targets/emuelec_targets_scraper.py b/scripts/scraper/targets/emuelec_targets_scraper.py index aa1faf6c1..21b196b90 100644 --- a/scripts/scraper/targets/emuelec_targets_scraper.py +++ b/scripts/scraper/targets/emuelec_targets_scraper.py @@ -610,7 +610,12 @@ class Scraper(BaseTargetScraper): return fetch_bytes(RAW.format(ref=self.ref, path=path)).decode("utf-8") def fetch_targets(self) -> dict: - self.ref = self.pinned_revision(PLATFORM_NAME) + return self.device_targets() + + def device_targets(self, ref: str | None = None) -> dict: + """The targets file, read at `ref`, by default the revision + platforms/emuelec.yml was scraped at.""" + self.ref = ref or self.pinned_revision(PLATFORM_NAME) release = json.loads( fetch_bytes(RELEASE_API.format(ref=self.ref), headers=github_headers()) ) diff --git a/tests/test_elec_scrapers.py b/tests/test_elec_scrapers.py index da854d70c..c6ee5c34d 100644 --- a/tests/test_elec_scrapers.py +++ b/tests/test_elec_scrapers.py @@ -302,6 +302,7 @@ class TheCommittedListsAgree(unittest.TestCase): listed = {str(c) for s in config["systems"].values() for c in s.get("cores", [])} self.assertTrue(listed, "no system carries its cores") self.assertEqual(listed - platform - (listed - built), set()) + self.assertEqual(platform - built - {"lexaloffle", "mpv", "vox_official"}, set()) if __name__ == "__main__": diff --git a/tests/test_game_data_homonyms.py b/tests/test_game_data_homonyms.py index c39d95e43..80ba9f87b 100644 --- a/tests/test_game_data_homonyms.py +++ b/tests/test_game_data_homonyms.py @@ -207,6 +207,8 @@ class APlatformEntryTakesNoOtherEmulatorsTree(unittest.TestCase): config = load_platform_config("emuelec", str(REPO_ROOT / "platforms")) entry = config["systems"]["vvvvvv"]["files"][0] + # No image builds the VVVVVV port engine, but es_systems.json still + # names it for the system: its tree is the one the entry asks for. self.assertEqual(entry["platform_cores"], ["VVVVVV"]) db = load_database(str(REPO_ROOT / "database.json")) path, _status = resolve_local_file( From a7e757598f19a9acc07f05369f59607d094a06eb Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Mon, 9 Nov 2026 21:27:51 +0100 Subject: [PATCH 4/6] fix: list the libretro cores rocknix installs ROCKNIX listed retroarch, which stood for every libretro profile (351 resolved), while its devices build 124 libretro packages. The platform list now names the cores those packages install on at least one device. The targets scraper read a package's name as its core: vice-lr installs vice_x128, vice_x64, vice_xpet, vice_xplus4 and vice_xvic, which reached no device. It now reads each package's makeinstall_target, the file copied to ${INSTALL}/usr/lib/libretro under its target name, through TARGET_ARCH and ENABLE_32BIT (pcsx_rearmed-lr, desmume-lr). 155 profiles resolve, each built by a device, and no device loses one. --- install/targets/rocknix.json | 536 +++++++++++++----- platforms/rocknix.yml | 127 ++++- platforms/targets/rocknix.yml | 294 ++++++---- scripts/scraper/rocknix_scraper.py | 25 +- .../targets/rocknix_targets_scraper.py | 144 ++++- tests/test_launchers_rocknix.py | 4 +- tests/test_scraper_contract.py | 5 +- tests/test_target_scrapers.py | 65 ++- 8 files changed, 909 insertions(+), 291 deletions(-) diff --git a/install/targets/rocknix.json b/install/targets/rocknix.json index d6e122564..ad091575a 100644 --- a/install/targets/rocknix.json +++ b/install/targets/rocknix.json @@ -1,6 +1,7 @@ { "amd64": [ "81", + "DoubleCherryGB", "a5200", "amiberry", "arduous", @@ -27,11 +28,12 @@ "bk", "bluemsx", "boom3", + "boom3_xp", "bsnes", "bsnes2014_accuracy", "bsnes2014_balanced", "bsnes2014_performance", - "bsnes_hd", + "bsnes_hd_beta", "bsnes_mercury_accuracy", "bsnes_mercury_balanced", "bsnes_mercury_performance", @@ -44,7 +46,6 @@ "dolphin-sa", "dosbox_core", "dosbox_pure", - "doublecherrygb", "duckstation", "duckstation-sa", "easyrpg", @@ -58,7 +59,6 @@ "flycast", "flycast-sa", "fmsx", - "freechaf", "freeintv", "freej2me", "fuse", @@ -88,9 +88,9 @@ "mednafen", "melonds", "melonds-sa", - "melonds_ds", + "melondsds", "mesen", - "mesen_s", + "mesen-s", "mgba", "minivmac", "mojozork", @@ -98,7 +98,7 @@ "mu", "mupen64plus", "mupen64plus-sa", - "mupen64plus_nx", + "mupen64plus_next", "nanoboyadvance", "nanoboyadvance-sa", "neocd", @@ -130,7 +130,7 @@ "scummvm-sa", "skyemu", "skyemu-sa", - "smsplus_gx", + "smsplus", "snes9x", "snes9x2002", "snes9x2005_plus", @@ -150,6 +150,11 @@ "vecx", "vice", "vice-sa", + "vice_x128", + "vice_x64", + "vice_xpet", + "vice_xplus4", + "vice_xvic", "vircon32", "virtualjaguar", "vita3k", @@ -157,14 +162,15 @@ "vitaquake2", "vitaquake3", "wasm4", + "x1", "xemu", "xemu-sa", - "xmil", "yabasanshiro", "yabasanshiro-sa" ], "h700": [ "81", + "DoubleCherryGB", "a5200", "amiberry", "arduous", @@ -194,7 +200,6 @@ "desmume", "dosbox_core", "dosbox_pure", - "doublecherrygb", "drastic", "drastic-sa", "dsperate", @@ -213,7 +218,6 @@ "flycast-sa", "flycast2021", "fmsx", - "freechaf", "freeintv", "freej2me", "fuse", @@ -241,9 +245,9 @@ "mame2015", "mednafen", "melonds", - "melonds_ds", + "melondsds", "mesen", - "mesen_s", + "mesen-s", "mgba", "minivmac", "mojozork", @@ -251,7 +255,7 @@ "mu", "mupen64plus", "mupen64plus-sa", - "mupen64plus_nx", + "mupen64plus_next", "neocd", "nestopia", "np2kai", @@ -260,6 +264,7 @@ "opera", "parallel_n64", "pcsx_rearmed", + "pcsx_rearmed32", "pico-8", "picodrive", "pokemini", @@ -280,7 +285,7 @@ "scummvm", "scummvm-sa", "skyemu", - "smsplus_gx", + "smsplus", "snes9x", "snes9x2002", "snes9x2005_plus", @@ -301,16 +306,23 @@ "vecx", "vice", "vice-sa", + "vice_x128", + "vice_x64", + "vice_xpet", + "vice_xplus4", + "vice_xvic", "vircon32", "virtualjaguar", "vitaquake2", "wasm4", - "xmil", + "x1", + "yabadabadoo", "yabasanshiro", "yabasanshiro-sa" ], "pc": [ "81", + "DoubleCherryGB", "a5200", "amiberry", "arduous", @@ -337,11 +349,12 @@ "bk", "bluemsx", "boom3", + "boom3_xp", "bsnes", "bsnes2014_accuracy", "bsnes2014_balanced", "bsnes2014_performance", - "bsnes_hd", + "bsnes_hd_beta", "bsnes_mercury_accuracy", "bsnes_mercury_balanced", "bsnes_mercury_performance", @@ -354,7 +367,6 @@ "dolphin-sa", "dosbox_core", "dosbox_pure", - "doublecherrygb", "duckstation", "duckstation-sa", "easyrpg", @@ -368,7 +380,6 @@ "flycast", "flycast-sa", "fmsx", - "freechaf", "freeintv", "freej2me", "fuse", @@ -398,9 +409,9 @@ "mednafen", "melonds", "melonds-sa", - "melonds_ds", + "melondsds", "mesen", - "mesen_s", + "mesen-s", "mgba", "minivmac", "mojozork", @@ -408,7 +419,7 @@ "mu", "mupen64plus", "mupen64plus-sa", - "mupen64plus_nx", + "mupen64plus_next", "nanoboyadvance", "nanoboyadvance-sa", "neocd", @@ -440,7 +451,7 @@ "scummvm-sa", "skyemu", "skyemu-sa", - "smsplus_gx", + "smsplus", "snes9x", "snes9x2002", "snes9x2005_plus", @@ -460,6 +471,11 @@ "vecx", "vice", "vice-sa", + "vice_x128", + "vice_x64", + "vice_xpet", + "vice_xplus4", + "vice_xvic", "vircon32", "virtualjaguar", "vita3k", @@ -467,14 +483,15 @@ "vitaquake2", "vitaquake3", "wasm4", + "x1", "xemu", "xemu-sa", - "xmil", "yabasanshiro", "yabasanshiro-sa" ], "rk3326": [ "81", + "DoubleCherryGB", "a5200", "amiberry", "arduous", @@ -504,7 +521,6 @@ "desmume", "dosbox_core", "dosbox_pure", - "doublecherrygb", "drastic", "drastic-sa", "dsperate", @@ -523,7 +539,6 @@ "flycast-sa", "flycast2021", "fmsx", - "freechaf", "freeintv", "freej2me", "fuse", @@ -551,9 +566,9 @@ "mame2015", "mednafen", "melonds", - "melonds_ds", + "melondsds", "mesen", - "mesen_s", + "mesen-s", "mgba", "minivmac", "mojozork", @@ -561,7 +576,7 @@ "mu", "mupen64plus", "mupen64plus-sa", - "mupen64plus_nx", + "mupen64plus_next", "neocd", "nestopia", "np2kai", @@ -570,6 +585,7 @@ "opera", "parallel_n64", "pcsx_rearmed", + "pcsx_rearmed32", "pico-8", "picodrive", "pokemini", @@ -590,7 +606,7 @@ "scummvm", "scummvm-sa", "skyemu", - "smsplus_gx", + "smsplus", "snes9x", "snes9x2002", "snes9x2005_plus", @@ -611,19 +627,24 @@ "vecx", "vice", "vice-sa", + "vice_x128", + "vice_x64", + "vice_xpet", + "vice_xplus4", + "vice_xvic", "vircon32", "virtualjaguar", "vitaquake2", "wasm4", - "xmil", + "x1", + "yabadabadoo", "yabasanshiro", "yabasanshiro-sa" ], "rk3399": [ "81", + "DoubleCherryGB", "a5200", - "aethersx2", - "aethersx2-sa", "amiberry", "arduous", "armsx2", @@ -647,7 +668,7 @@ "bsnes2014_accuracy", "bsnes2014_balanced", "bsnes2014_performance", - "bsnes_hd", + "bsnes_hd_beta", "bsnes_mercury_accuracy", "bsnes_mercury_balanced", "bsnes_mercury_performance", @@ -659,7 +680,6 @@ "dolphin-sa", "dosbox_core", "dosbox_pure", - "doublecherrygb", "drastic", "drastic-sa", "dsperate", @@ -678,7 +698,6 @@ "flycast-sa", "flycast2021", "fmsx", - "freechaf", "freeintv", "freej2me", "fuse", @@ -707,9 +726,9 @@ "mednafen", "melonds", "melonds-sa", - "melonds_ds", + "melondsds", "mesen", - "mesen_s", + "mesen-s", "mgba", "minivmac", "mojozork", @@ -717,7 +736,7 @@ "mu", "mupen64plus", "mupen64plus-sa", - "mupen64plus_nx", + "mupen64plus_next", "nanoboyadvance", "nanoboyadvance-sa", "neocd", @@ -728,6 +747,7 @@ "opera", "parallel_n64", "pcsx_rearmed", + "pcsx_rearmed32", "pico-8", "picodrive", "pokemini", @@ -748,7 +768,7 @@ "scummvm", "scummvm-sa", "skyemu", - "smsplus_gx", + "smsplus", "snes9x", "snes9x2002", "snes9x2005_plus", @@ -769,19 +789,24 @@ "vecx", "vice", "vice-sa", + "vice_x128", + "vice_x64", + "vice_xpet", + "vice_xplus4", + "vice_xvic", "vircon32", "virtualjaguar", "vitaquake2", "wasm4", - "xmil", + "x1", + "yabadabadoo", "yabasanshiro", "yabasanshiro-sa" ], "rk3566": [ "81", + "DoubleCherryGB", "a5200", - "aethersx2", - "aethersx2-sa", "amiberry", "arduous", "armsx2", @@ -816,7 +841,6 @@ "dolphin-sa", "dosbox_core", "dosbox_pure", - "doublecherrygb", "drastic", "drastic-sa", "dsperate", @@ -835,7 +859,6 @@ "flycast-sa", "flycast2021", "fmsx", - "freechaf", "freeintv", "freej2me", "fuse", @@ -864,9 +887,9 @@ "mednafen", "melonds", "melonds-sa", - "melonds_ds", + "melondsds", "mesen", - "mesen_s", + "mesen-s", "mgba", "minivmac", "mojozork", @@ -874,7 +897,7 @@ "mu", "mupen64plus", "mupen64plus-sa", - "mupen64plus_nx", + "mupen64plus_next", "neocd", "nestopia", "np2kai", @@ -883,6 +906,7 @@ "opera", "parallel_n64", "pcsx_rearmed", + "pcsx_rearmed32", "pico-8", "picodrive", "pokemini", @@ -903,7 +927,7 @@ "scummvm", "scummvm-sa", "skyemu", - "smsplus_gx", + "smsplus", "snes9x", "snes9x2002", "snes9x2005_plus", @@ -924,21 +948,26 @@ "vecx", "vice", "vice-sa", + "vice_x128", + "vice_x64", + "vice_xpet", + "vice_xplus4", + "vice_xvic", "vircon32", "virtualjaguar", "vita3k", "vita3k-sa", "vitaquake2", "wasm4", - "xmil", + "x1", + "yabadabadoo", "yabasanshiro", "yabasanshiro-sa" ], "rk3576": [ "81", + "DoubleCherryGB", "a5200", - "aethersx2", - "aethersx2-sa", "amiberry", "arduous", "armsx2", @@ -973,7 +1002,6 @@ "dolphin-sa", "dosbox_core", "dosbox_pure", - "doublecherrygb", "drastic", "drastic-sa", "dsperate", @@ -992,7 +1020,6 @@ "flycast-sa", "flycast2021", "fmsx", - "freechaf", "freeintv", "freej2me", "fuse", @@ -1021,9 +1048,9 @@ "mednafen", "melonds", "melonds-sa", - "melonds_ds", + "melondsds", "mesen", - "mesen_s", + "mesen-s", "mgba", "minivmac", "mojozork", @@ -1031,7 +1058,7 @@ "mu", "mupen64plus", "mupen64plus-sa", - "mupen64plus_nx", + "mupen64plus_next", "neocd", "nestopia", "np2kai", @@ -1040,6 +1067,7 @@ "opera", "parallel_n64", "pcsx_rearmed", + "pcsx_rearmed32", "pico-8", "picodrive", "pokemini", @@ -1060,7 +1088,7 @@ "scummvm", "scummvm-sa", "skyemu", - "smsplus_gx", + "smsplus", "snes9x", "snes9x2002", "snes9x2005_plus", @@ -1081,21 +1109,26 @@ "vecx", "vice", "vice-sa", + "vice_x128", + "vice_x64", + "vice_xpet", + "vice_xplus4", + "vice_xvic", "vircon32", "virtualjaguar", "vita3k", "vita3k-sa", "vitaquake2", "wasm4", - "xmil", + "x1", + "yabadabadoo", "yabasanshiro", "yabasanshiro-sa" ], "rk3588": [ "81", + "DoubleCherryGB", "a5200", - "aethersx2", - "aethersx2-sa", "amiberry", "arduous", "armsx2", @@ -1122,7 +1155,7 @@ "bsnes2014_accuracy", "bsnes2014_balanced", "bsnes2014_performance", - "bsnes_hd", + "bsnes_hd_beta", "bsnes_mercury_accuracy", "bsnes_mercury_balanced", "bsnes_mercury_performance", @@ -1134,7 +1167,6 @@ "dolphin-sa", "dosbox_core", "dosbox_pure", - "doublecherrygb", "drastic", "drastic-sa", "dsperate", @@ -1153,7 +1185,6 @@ "flycast-sa", "flycast2021", "fmsx", - "freechaf", "freeintv", "freej2me", "fuse", @@ -1182,9 +1213,9 @@ "mednafen", "melonds", "melonds-sa", - "melonds_ds", + "melondsds", "mesen", - "mesen_s", + "mesen-s", "mgba", "minivmac", "mojozork", @@ -1192,7 +1223,7 @@ "mu", "mupen64plus", "mupen64plus-sa", - "mupen64plus_nx", + "mupen64plus_next", "neocd", "nestopia", "np2kai", @@ -1201,6 +1232,7 @@ "opera", "parallel_n64", "pcsx_rearmed", + "pcsx_rearmed32", "pico-8", "picodrive", "pokemini", @@ -1221,7 +1253,7 @@ "scummvm", "scummvm-sa", "skyemu", - "smsplus_gx", + "smsplus", "snes9x", "snes9x2002", "snes9x2005_plus", @@ -1244,21 +1276,26 @@ "vecx", "vice", "vice-sa", + "vice_x128", + "vice_x64", + "vice_xpet", + "vice_xplus4", + "vice_xvic", "vircon32", "virtualjaguar", "vita3k", "vita3k-sa", "vitaquake2", "wasm4", - "xmil", + "x1", + "yabadabadoo", "yabasanshiro", "yabasanshiro-sa" ], "s922x": [ "81", + "DoubleCherryGB", "a5200", - "aethersx2", - "aethersx2-sa", "amiberry", "arduous", "armsx2", @@ -1287,7 +1324,7 @@ "bsnes2014_accuracy", "bsnes2014_balanced", "bsnes2014_performance", - "bsnes_hd", + "bsnes_hd_beta", "bsnes_mercury_accuracy", "bsnes_mercury_balanced", "bsnes_mercury_performance", @@ -1298,7 +1335,6 @@ "dolphin-sa", "dosbox_core", "dosbox_pure", - "doublecherrygb", "drastic", "drastic-sa", "dsperate", @@ -1317,7 +1353,6 @@ "flycast-sa", "flycast2021", "fmsx", - "freechaf", "freeintv", "freej2me", "fuse", @@ -1344,9 +1379,9 @@ "mame2015", "melonds", "melonds-sa", - "melonds_ds", + "melondsds", "mesen", - "mesen_s", + "mesen-s", "mgba", "minivmac", "mojozork", @@ -1354,7 +1389,7 @@ "mu", "mupen64plus", "mupen64plus-sa", - "mupen64plus_nx", + "mupen64plus_next", "neocd", "nestopia", "np2kai", @@ -1363,6 +1398,7 @@ "opera", "parallel_n64", "pcsx_rearmed", + "pcsx_rearmed32", "pico-8", "picodrive", "pokemini", @@ -1383,7 +1419,7 @@ "scummvm", "scummvm-sa", "skyemu", - "smsplus_gx", + "smsplus", "snes9x", "snes9x2002", "snes9x2005_plus", @@ -1404,21 +1440,203 @@ "vecx", "vice", "vice-sa", + "vice_x128", + "vice_x64", + "vice_xpet", + "vice_xplus4", + "vice_xvic", "vircon32", "virtualjaguar", "vita3k", "vita3k-sa", "vitaquake2", "wasm4", - "xmil", + "x1", + "yabasanshiro", + "yabasanshiro-sa" + ], + "sdm845": [ + "81", + "DoubleCherryGB", + "a5200", + "amiberry", + "arduous", + "armsx2", + "armsx2-sa", + "atari800", + "azahar", + "azahar-sa", + "b2", + "beetle_gba", + "beetle_lynx", + "beetle_ngp", + "beetle_pce", + "beetle_pce_fast", + "beetle_pcfx", + "beetle_psx", + "beetle_saturn", + "beetle_supafaust", + "beetle_supergrafx", + "beetle_vb", + "beetle_wswan", + "bigpemu", + "bigpemu-sa", + "bk", + "bluemsx", + "bsnes", + "bsnes2014_accuracy", + "bsnes2014_balanced", + "bsnes2014_performance", + "bsnes_hd_beta", + "bsnes_mercury_accuracy", + "bsnes_mercury_balanced", + "bsnes_mercury_performance", + "cap32", + "cemu", + "cemu-sa", + "crocods", + "daedalusx64", + "daedalusx64-sa", + "daphne", + "desmume", + "dolphin", + "dolphin-sa", + "dosbox_core", + "dosbox_pure", + "dsperate", + "dsperate-sa", + "duckstation", + "duckstation-sa", + "easyrpg", + "ecwolf", + "emuscv", + "fake08", + "fbalpha2012", + "fbalpha2019", + "fbneo", + "fceumm", + "flycast", + "flycast-sa", + "flycast2021", + "fmsx", + "freeintv", + "freej2me", + "fuse", + "gambatte", + "gearboy", + "gearcoleco", + "geargrafx", + "gearlynx", + "gearsystem", + "genesis_plus_gx", + "genesis_plus_gx_wide", + "geolith", + "gpsp", + "gw", + "gzdoom", + "gzdoom-sa", + "handy", + "hatari", + "hatarisa", + "hypseus-singe", + "jaxe", + "kronos", + "mame", + "mame2003_plus", + "mame2010", + "mame2015", + "mednafen", + "melonds", + "melonds-sa", + "melondsds", + "mesen", + "mesen-s", + "mgba", + "minivmac", + "mojozork", + "moonlight", + "mu", + "mupen64plus", + "mupen64plus-sa", + "mupen64plus_next", + "nanoboyadvance", + "nanoboyadvance-sa", + "neocd", + "nestopia", + "np2kai", + "o2em", + "openbor", + "opera", + "parallel_n64", + "pcsx_rearmed", + "pcsx_rearmed32", + "pico-8", + "picodrive", + "pokemini", + "potator", + "ppsspp", + "ppsspp-sa", + "prboom", + "prosystem", + "puae", + "puae2021", + "px68k", + "quasi88", + "quicknes", + "race", + "rpcs3", + "rpcs3-sa", + "same_cdi", + "sameboy", + "sameduck", + "scummvm", + "scummvm-sa", + "skyemu", + "skyemu-sa", + "smsplus", + "snes9x", + "snes9x2002", + "snes9x2005_plus", + "snes9x2010", + "stella", + "supermodel", + "supermodel-sa", + "supersnes9x", + "swanstation", + "tgbdual", + "theodore", + "tic80", + "touchhle", + "touchhle-sa", + "tyrquake", + "uae4arm", + "uzem", + "vba_next", + "vbam", + "vecx", + "vice", + "vice-sa", + "vice_x128", + "vice_x64", + "vice_xpet", + "vice_xplus4", + "vice_xvic", + "vircon32", + "virtualjaguar", + "vita3k", + "vita3k-sa", + "vitaquake2", + "wasm4", + "x1", + "xemu", + "xemu-sa", "yabasanshiro", "yabasanshiro-sa" ], "sm4450": [ "81", + "DoubleCherryGB", "a5200", - "aethersx2", - "aethersx2-sa", "amiberry", "arduous", "armsx2", @@ -1447,7 +1665,7 @@ "bsnes2014_accuracy", "bsnes2014_balanced", "bsnes2014_performance", - "bsnes_hd", + "bsnes_hd_beta", "bsnes_mercury_accuracy", "bsnes_mercury_balanced", "bsnes_mercury_performance", @@ -1463,7 +1681,6 @@ "dolphin-sa", "dosbox_core", "dosbox_pure", - "doublecherrygb", "dsperate", "dsperate-sa", "duckstation", @@ -1480,7 +1697,6 @@ "flycast-sa", "flycast2021", "fmsx", - "freechaf", "freeintv", "freej2me", "fuse", @@ -1510,9 +1726,9 @@ "mednafen", "melonds", "melonds-sa", - "melonds_ds", + "melondsds", "mesen", - "mesen_s", + "mesen-s", "mgba", "minivmac", "mojozork", @@ -1520,7 +1736,7 @@ "mu", "mupen64plus", "mupen64plus-sa", - "mupen64plus_nx", + "mupen64plus_next", "nanoboyadvance", "nanoboyadvance-sa", "neocd", @@ -1531,6 +1747,7 @@ "opera", "parallel_n64", "pcsx_rearmed", + "pcsx_rearmed32", "pico-8", "picodrive", "pokemini", @@ -1552,7 +1769,7 @@ "scummvm-sa", "skyemu", "skyemu-sa", - "smsplus_gx", + "smsplus", "snes9x", "snes9x2002", "snes9x2005_plus", @@ -1575,23 +1792,28 @@ "vecx", "vice", "vice-sa", + "vice_x128", + "vice_x64", + "vice_xpet", + "vice_xplus4", + "vice_xvic", "vircon32", "virtualjaguar", "vita3k", "vita3k-sa", "vitaquake2", "wasm4", + "x1", "xemu", "xemu-sa", - "xmil", + "yabadabadoo", "yabasanshiro", "yabasanshiro-sa" ], "sm6115": [ "81", + "DoubleCherryGB", "a5200", - "aethersx2", - "aethersx2-sa", "amiberry", "arduous", "armsx2", @@ -1618,7 +1840,7 @@ "bsnes2014_accuracy", "bsnes2014_balanced", "bsnes2014_performance", - "bsnes_hd", + "bsnes_hd_beta", "bsnes_mercury_accuracy", "bsnes_mercury_balanced", "bsnes_mercury_performance", @@ -1630,7 +1852,6 @@ "dolphin-sa", "dosbox_core", "dosbox_pure", - "doublecherrygb", "drastic", "drastic-sa", "dsperate", @@ -1649,7 +1870,6 @@ "flycast-sa", "flycast2021", "fmsx", - "freechaf", "freeintv", "freej2me", "fuse", @@ -1678,9 +1898,9 @@ "mednafen", "melonds", "melonds-sa", - "melonds_ds", + "melondsds", "mesen", - "mesen_s", + "mesen-s", "mgba", "minivmac", "mojozork", @@ -1688,7 +1908,7 @@ "mu", "mupen64plus", "mupen64plus-sa", - "mupen64plus_nx", + "mupen64plus_next", "neocd", "nestopia", "np2kai", @@ -1697,6 +1917,7 @@ "opera", "parallel_n64", "pcsx_rearmed", + "pcsx_rearmed32", "pico-8", "picodrive", "pokemini", @@ -1717,7 +1938,7 @@ "scummvm", "scummvm-sa", "skyemu", - "smsplus_gx", + "smsplus", "snes9x", "snes9x2002", "snes9x2005_plus", @@ -1740,21 +1961,26 @@ "vecx", "vice", "vice-sa", + "vice_x128", + "vice_x64", + "vice_xpet", + "vice_xplus4", + "vice_xvic", "vircon32", "virtualjaguar", "vita3k", "vita3k-sa", "vitaquake2", "wasm4", - "xmil", + "x1", + "yabadabadoo", "yabasanshiro", "yabasanshiro-sa" ], "sm8250": [ "81", + "DoubleCherryGB", "a5200", - "aethersx2", - "aethersx2-sa", "amiberry", "arduous", "armsx2", @@ -1783,7 +2009,7 @@ "bsnes2014_accuracy", "bsnes2014_balanced", "bsnes2014_performance", - "bsnes_hd", + "bsnes_hd_beta", "bsnes_mercury_accuracy", "bsnes_mercury_balanced", "bsnes_mercury_performance", @@ -1799,7 +2025,6 @@ "dolphin-sa", "dosbox_core", "dosbox_pure", - "doublecherrygb", "dsperate", "dsperate-sa", "duckstation", @@ -1816,7 +2041,6 @@ "flycast-sa", "flycast2021", "fmsx", - "freechaf", "freeintv", "freej2me", "fuse", @@ -1846,9 +2070,9 @@ "mednafen", "melonds", "melonds-sa", - "melonds_ds", + "melondsds", "mesen", - "mesen_s", + "mesen-s", "mgba", "minivmac", "mojozork", @@ -1856,7 +2080,7 @@ "mu", "mupen64plus", "mupen64plus-sa", - "mupen64plus_nx", + "mupen64plus_next", "nanoboyadvance", "nanoboyadvance-sa", "neocd", @@ -1867,6 +2091,7 @@ "opera", "parallel_n64", "pcsx_rearmed", + "pcsx_rearmed32", "pico-8", "picodrive", "pokemini", @@ -1890,7 +2115,7 @@ "scummvm-sa", "skyemu", "skyemu-sa", - "smsplus_gx", + "smsplus", "snes9x", "snes9x2002", "snes9x2005_plus", @@ -1913,23 +2138,27 @@ "vecx", "vice", "vice-sa", + "vice_x128", + "vice_x64", + "vice_xpet", + "vice_xplus4", + "vice_xvic", "vircon32", "virtualjaguar", "vita3k", "vita3k-sa", "vitaquake2", "wasm4", + "x1", "xemu", "xemu-sa", - "xmil", "yabasanshiro", "yabasanshiro-sa" ], "sm8550": [ "81", + "DoubleCherryGB", "a5200", - "aethersx2", - "aethersx2-sa", "amiberry", "arduous", "ares", @@ -1960,7 +2189,7 @@ "bsnes2014_accuracy", "bsnes2014_balanced", "bsnes2014_performance", - "bsnes_hd", + "bsnes_hd_beta", "bsnes_mercury_accuracy", "bsnes_mercury_balanced", "bsnes_mercury_performance", @@ -1976,7 +2205,6 @@ "dolphin-sa", "dosbox_core", "dosbox_pure", - "doublecherrygb", "drastic", "drastic-sa", "dsperate", @@ -1995,7 +2223,6 @@ "flycast-sa", "flycast2021", "fmsx", - "freechaf", "freeintv", "freej2me", "fuse", @@ -2027,9 +2254,9 @@ "mednafen", "melonds", "melonds-sa", - "melonds_ds", + "melondsds", "mesen", - "mesen_s", + "mesen-s", "mgba", "minivmac", "mojozork", @@ -2037,7 +2264,7 @@ "mu", "mupen64plus", "mupen64plus-sa", - "mupen64plus_nx", + "mupen64plus_next", "nanoboyadvance", "nanoboyadvance-sa", "neocd", @@ -2048,6 +2275,7 @@ "opera", "parallel_n64", "pcsx_rearmed", + "pcsx_rearmed32", "pico-8", "picodrive", "pokemini", @@ -2071,7 +2299,7 @@ "scummvm-sa", "skyemu", "skyemu-sa", - "smsplus_gx", + "smsplus", "snes9x", "snes9x2002", "snes9x2005_plus", @@ -2094,23 +2322,27 @@ "vecx", "vice", "vice-sa", + "vice_x128", + "vice_x64", + "vice_xpet", + "vice_xplus4", + "vice_xvic", "vircon32", "virtualjaguar", "vita3k", "vita3k-sa", "vitaquake2", "wasm4", + "x1", "xemu", "xemu-sa", - "xmil", "yabasanshiro", "yabasanshiro-sa" ], "sm8650": [ "81", + "DoubleCherryGB", "a5200", - "aethersx2", - "aethersx2-sa", "amiberry", "arduous", "ares", @@ -2141,7 +2373,7 @@ "bsnes2014_accuracy", "bsnes2014_balanced", "bsnes2014_performance", - "bsnes_hd", + "bsnes_hd_beta", "bsnes_mercury_accuracy", "bsnes_mercury_balanced", "bsnes_mercury_performance", @@ -2154,7 +2386,6 @@ "dolphin-sa", "dosbox_core", "dosbox_pure", - "doublecherrygb", "dsperate", "dsperate-sa", "duckstation", @@ -2171,7 +2402,6 @@ "flycast-sa", "flycast2021", "fmsx", - "freechaf", "freeintv", "freej2me", "fuse", @@ -2202,9 +2432,9 @@ "mednafen", "melonds", "melonds-sa", - "melonds_ds", + "melondsds", "mesen", - "mesen_s", + "mesen-s", "mgba", "minivmac", "mojozork", @@ -2212,7 +2442,7 @@ "mu", "mupen64plus", "mupen64plus-sa", - "mupen64plus_nx", + "mupen64plus_next", "nanoboyadvance", "nanoboyadvance-sa", "neocd", @@ -2223,6 +2453,7 @@ "opera", "parallel_n64", "pcsx_rearmed", + "pcsx_rearmed32", "pico-8", "picodrive", "pokemini", @@ -2246,7 +2477,7 @@ "scummvm-sa", "skyemu", "skyemu-sa", - "smsplus_gx", + "smsplus", "snes9x", "snes9x2002", "snes9x2005_plus", @@ -2269,23 +2500,27 @@ "vecx", "vice", "vice-sa", + "vice_x128", + "vice_x64", + "vice_xpet", + "vice_xplus4", + "vice_xvic", "vircon32", "virtualjaguar", "vita3k", "vita3k-sa", "vitaquake2", "wasm4", + "x1", "xemu", "xemu-sa", - "xmil", "yabasanshiro", "yabasanshiro-sa" ], "sm8750": [ "81", + "DoubleCherryGB", "a5200", - "aethersx2", - "aethersx2-sa", "amiberry", "arduous", "ares", @@ -2316,7 +2551,7 @@ "bsnes2014_accuracy", "bsnes2014_balanced", "bsnes2014_performance", - "bsnes_hd", + "bsnes_hd_beta", "bsnes_mercury_accuracy", "bsnes_mercury_balanced", "bsnes_mercury_performance", @@ -2329,7 +2564,6 @@ "dolphin-sa", "dosbox_core", "dosbox_pure", - "doublecherrygb", "dsperate", "dsperate-sa", "duckstation", @@ -2346,7 +2580,6 @@ "flycast-sa", "flycast2021", "fmsx", - "freechaf", "freeintv", "freej2me", "fuse", @@ -2377,9 +2610,9 @@ "mednafen", "melonds", "melonds-sa", - "melonds_ds", + "melondsds", "mesen", - "mesen_s", + "mesen-s", "mgba", "minivmac", "mojozork", @@ -2387,7 +2620,7 @@ "mu", "mupen64plus", "mupen64plus-sa", - "mupen64plus_nx", + "mupen64plus_next", "nanoboyadvance", "nanoboyadvance-sa", "neocd", @@ -2398,6 +2631,7 @@ "opera", "parallel_n64", "pcsx_rearmed", + "pcsx_rearmed32", "pico-8", "picodrive", "pokemini", @@ -2421,7 +2655,7 @@ "scummvm-sa", "skyemu", "skyemu-sa", - "smsplus_gx", + "smsplus", "snes9x", "snes9x2002", "snes9x2005_plus", @@ -2444,20 +2678,26 @@ "vecx", "vice", "vice-sa", + "vice_x128", + "vice_x64", + "vice_xpet", + "vice_xplus4", + "vice_xvic", "vircon32", "virtualjaguar", "vita3k", "vita3k-sa", "vitaquake2", "wasm4", + "x1", "xemu", "xemu-sa", - "xmil", "yabasanshiro", "yabasanshiro-sa" ], "x86_64": [ "81", + "DoubleCherryGB", "a5200", "amiberry", "arduous", @@ -2484,11 +2724,12 @@ "bk", "bluemsx", "boom3", + "boom3_xp", "bsnes", "bsnes2014_accuracy", "bsnes2014_balanced", "bsnes2014_performance", - "bsnes_hd", + "bsnes_hd_beta", "bsnes_mercury_accuracy", "bsnes_mercury_balanced", "bsnes_mercury_performance", @@ -2501,7 +2742,6 @@ "dolphin-sa", "dosbox_core", "dosbox_pure", - "doublecherrygb", "duckstation", "duckstation-sa", "easyrpg", @@ -2515,7 +2755,6 @@ "flycast", "flycast-sa", "fmsx", - "freechaf", "freeintv", "freej2me", "fuse", @@ -2545,9 +2784,9 @@ "mednafen", "melonds", "melonds-sa", - "melonds_ds", + "melondsds", "mesen", - "mesen_s", + "mesen-s", "mgba", "minivmac", "mojozork", @@ -2555,7 +2794,7 @@ "mu", "mupen64plus", "mupen64plus-sa", - "mupen64plus_nx", + "mupen64plus_next", "nanoboyadvance", "nanoboyadvance-sa", "neocd", @@ -2587,7 +2826,7 @@ "scummvm-sa", "skyemu", "skyemu-sa", - "smsplus_gx", + "smsplus", "snes9x", "snes9x2002", "snes9x2005_plus", @@ -2607,6 +2846,11 @@ "vecx", "vice", "vice-sa", + "vice_x128", + "vice_x64", + "vice_xpet", + "vice_xplus4", + "vice_xvic", "vircon32", "virtualjaguar", "vita3k", @@ -2614,9 +2858,9 @@ "vitaquake2", "vitaquake3", "wasm4", + "x1", "xemu", "xemu-sa", - "xmil", "yabasanshiro", "yabasanshiro-sa" ] diff --git a/platforms/rocknix.yml b/platforms/rocknix.yml index 88a0f42fa..ed9e6620f 100644 --- a/platforms/rocknix.yml +++ b/platforms/rocknix.yml @@ -6,66 +6,191 @@ base_destination: bios hash_type: md5 verification_mode: md5 cores: -- retroarch +- "81" +- DoubleCherryGB +- a5200 - amiberry +- arduous - ares - ares-sa - armsx2 - armsx2-sa +- atari800 - azahar - azahar-sa +- b2 +- beetle_gba +- beetle_lynx +- beetle_ngp +- beetle_pce +- beetle_pce_fast +- beetle_pcfx +- beetle_psx +- beetle_saturn +- beetle_supafaust +- beetle_supergrafx +- beetle_vb +- beetle_wswan - bigpemu - bigpemu-sa +- bk +- bluemsx +- boom3 +- boom3_xp +- bsnes +- bsnes2014_accuracy +- bsnes2014_balanced +- bsnes2014_performance +- bsnes_hd_beta +- bsnes_mercury_accuracy +- bsnes_mercury_balanced +- bsnes_mercury_performance +- cap32 - cemu - cemu-sa +- crocods - daedalusx64 - daedalusx64-sa +- daphne +- desmume - dolphin - dolphin-sa +- dosbox_core +- dosbox_pure - drastic - drastic-sa - dsperate - dsperate-sa - duckstation - duckstation-sa +- easyrpg +- ecwolf +- emuscv +- fake08 +- fbalpha2012 +- fbalpha2019 +- fbneo +- fceumm - flycast - flycast-sa +- flycast2021 +- fmsx +- freeintv +- freej2me +- fuse +- gambatte +- gearboy +- gearcoleco +- geargrafx +- gearlynx +- gearsystem +- genesis_plus_gx +- genesis_plus_gx_wide +- geolith - gopher64 - gopher64-sa +- gpsp +- gw - gzdoom - gzdoom-sa +- handy - hatari - hatarisa - heroic - hypseus-singe +- jaxe +- kronos +- mame +- mame2003_plus +- mame2010 +- mame2015 - mednafen - melonds - melonds-sa +- melondsds +- mesen +- mesen-s +- mgba +- minivmac +- mojozork +- mu - mupen64plus - mupen64plus-sa +- mupen64plus_next - nanoboyadvance - nanoboyadvance-sa +- neocd +- nestopia +- np2kai +- o2em - openbor +- opera +- parallel_n64 +- pcsx_rearmed +- pcsx_rearmed32 - pico-8 +- picodrive +- pokemini +- potator - ppsspp - ppsspp-sa +- prboom +- prosystem +- puae +- puae2021 +- px68k +- quasi88 +- quicknes +- race - rpcs3 - rpcs3-sa +- same_cdi +- sameboy +- sameduck - scummvm - scummvm-sa - skyemu - skyemu-sa +- smsplus +- snes9x +- snes9x2002 +- snes9x2005_plus +- snes9x2010 - steam +- stella - supermodel - supermodel-sa +- supersnes9x +- swanstation +- tgbdual +- theodore +- tic80 - touchhle - touchhle-sa +- tyrquake +- uae4arm +- uzem +- vba_next +- vbam +- vecx - vice - vice-sa +- vice_x128 +- vice_x64 +- vice_xpet +- vice_xplus4 +- vice_xvic +- vircon32 +- virtualjaguar - vita3k - vita3k-sa +- vitaquake2 +- vitaquake3 +- wasm4 +- x1 - xemu - xemu-sa +- yabadabadoo - yabasanshiro - yabasanshiro-sa standalone_cores: diff --git a/platforms/targets/rocknix.yml b/platforms/targets/rocknix.yml index c5eb2d604..35113c4fa 100644 --- a/platforms/targets/rocknix.yml +++ b/platforms/targets/rocknix.yml @@ -1,11 +1,12 @@ platform: rocknix source: https://github.com/ROCKNIX/distribution/tree/20261101/projects/ROCKNIX/packages/virtual/emulators/package.mk -scraped_at: '2026-11-09T16:01:58Z' +scraped_at: '2026-11-09T20:12:32Z' targets: amd64: architecture: x86_64 cores: - '81' + - DoubleCherryGB - a5200 - amiberry - arduous @@ -32,11 +33,12 @@ targets: - bk - bluemsx - boom3 + - boom3_xp - bsnes - bsnes2014_accuracy - bsnes2014_balanced - bsnes2014_performance - - bsnes_hd + - bsnes_hd_beta - bsnes_mercury_accuracy - bsnes_mercury_balanced - bsnes_mercury_performance @@ -49,7 +51,6 @@ targets: - dolphin-sa - dosbox_core - dosbox_pure - - doublecherrygb - duckstation - duckstation-sa - easyrpg @@ -92,9 +93,9 @@ targets: - mednafen - melonds - melonds-sa - - melonds_ds + - melondsds - mesen - - mesen_s + - mesen-s - mgba - minivmac - mojozork @@ -102,7 +103,7 @@ targets: - mu - mupen64plus - mupen64plus-sa - - mupen64plus_nx + - mupen64plus_next - nanoboyadvance - nanoboyadvance-sa - neocd @@ -134,7 +135,7 @@ targets: - scummvm-sa - skyemu - skyemu-sa - - smsplus_gx + - smsplus - snes9x - snes9x2002 - snes9x2005_plus @@ -154,6 +155,11 @@ targets: - vecx - vice - vice-sa + - vice_x128 + - vice_x64 + - vice_xpet + - vice_xplus4 + - vice_xvic - vircon32 - virtualjaguar - vita3k @@ -162,15 +168,16 @@ targets: - vitaquake3 - wasm4 - wine + - x1 - xemu - xemu-sa - - xmil - yabasanshiro - yabasanshiro-sa h700: architecture: aarch64 cores: - '81' + - DoubleCherryGB - a5200 - amiberry - arduous @@ -202,7 +209,6 @@ targets: - desmume - dosbox_core - dosbox_pure - - doublecherrygb - drastic - drastic-sa - dsperate @@ -248,9 +254,9 @@ targets: - mame2015 - mednafen - melonds - - melonds_ds + - melondsds - mesen - - mesen_s + - mesen-s - mgba - minivmac - mojozork @@ -258,7 +264,7 @@ targets: - mu - mupen64plus - mupen64plus-sa - - mupen64plus_nx + - mupen64plus_next - neocd - nestopia - np2kai @@ -267,6 +273,7 @@ targets: - opera - parallel_n64 - pcsx_rearmed + - pcsx_rearmed32 - pico-8 - picodrive - pokemini @@ -288,7 +295,7 @@ targets: - scummvm - scummvm-sa - skyemu - - smsplus_gx + - smsplus - snes9x - snes9x2002 - snes9x2005_plus @@ -309,12 +316,17 @@ targets: - vecx - vice - vice-sa + - vice_x128 + - vice_x64 + - vice_xpet + - vice_xplus4 + - vice_xvic - vircon32 - virtualjaguar - vitaquake2 - wasm4 - wine - - xmil + - x1 - yabadabadoo - yabasanshiro - yabasanshiro-sa @@ -322,6 +334,7 @@ targets: architecture: aarch64 cores: - '81' + - DoubleCherryGB - a5200 - amiberry - arduous @@ -353,7 +366,6 @@ targets: - desmume - dosbox_core - dosbox_pure - - doublecherrygb - drastic - drastic-sa - dsperate @@ -399,9 +411,9 @@ targets: - mame2015 - mednafen - melonds - - melonds_ds + - melondsds - mesen - - mesen_s + - mesen-s - mgba - minivmac - mojozork @@ -409,7 +421,7 @@ targets: - mu - mupen64plus - mupen64plus-sa - - mupen64plus_nx + - mupen64plus_next - neocd - nestopia - np2kai @@ -418,6 +430,7 @@ targets: - opera - parallel_n64 - pcsx_rearmed + - pcsx_rearmed32 - pico-8 - picodrive - pokemini @@ -439,7 +452,7 @@ targets: - scummvm - scummvm-sa - skyemu - - smsplus_gx + - smsplus - snes9x - snes9x2002 - snes9x2005_plus @@ -460,12 +473,17 @@ targets: - vecx - vice - vice-sa + - vice_x128 + - vice_x64 + - vice_xpet + - vice_xplus4 + - vice_xvic - vircon32 - virtualjaguar - vitaquake2 - wasm4 - wine - - xmil + - x1 - yabadabadoo - yabasanshiro - yabasanshiro-sa @@ -473,6 +491,7 @@ targets: architecture: aarch64 cores: - '81' + - DoubleCherryGB - a5200 - amiberry - arduous @@ -499,7 +518,7 @@ targets: - bsnes2014_accuracy - bsnes2014_balanced - bsnes2014_performance - - bsnes_hd + - bsnes_hd_beta - bsnes_mercury_accuracy - bsnes_mercury_balanced - bsnes_mercury_performance @@ -511,7 +530,6 @@ targets: - dolphin-sa - dosbox_core - dosbox_pure - - doublecherrygb - drastic - drastic-sa - dsperate @@ -558,9 +576,9 @@ targets: - mednafen - melonds - melonds-sa - - melonds_ds + - melondsds - mesen - - mesen_s + - mesen-s - mgba - minivmac - mojozork @@ -568,7 +586,7 @@ targets: - mu - mupen64plus - mupen64plus-sa - - mupen64plus_nx + - mupen64plus_next - nanoboyadvance - nanoboyadvance-sa - neocd @@ -579,6 +597,7 @@ targets: - opera - parallel_n64 - pcsx_rearmed + - pcsx_rearmed32 - pico-8 - picodrive - pokemini @@ -600,7 +619,7 @@ targets: - scummvm - scummvm-sa - skyemu - - smsplus_gx + - smsplus - snes9x - snes9x2002 - snes9x2005_plus @@ -621,12 +640,17 @@ targets: - vecx - vice - vice-sa + - vice_x128 + - vice_x64 + - vice_xpet + - vice_xplus4 + - vice_xvic - vircon32 - virtualjaguar - vitaquake2 - wasm4 - wine - - xmil + - x1 - yabadabadoo - yabasanshiro - yabasanshiro-sa @@ -634,6 +658,7 @@ targets: architecture: aarch64 cores: - '81' + - DoubleCherryGB - a5200 - amiberry - arduous @@ -671,7 +696,6 @@ targets: - dolphin-sa - dosbox_core - dosbox_pure - - doublecherrygb - drastic - drastic-sa - dsperate @@ -718,9 +742,9 @@ targets: - mednafen - melonds - melonds-sa - - melonds_ds + - melondsds - mesen - - mesen_s + - mesen-s - mgba - minivmac - mojozork @@ -728,7 +752,7 @@ targets: - mu - mupen64plus - mupen64plus-sa - - mupen64plus_nx + - mupen64plus_next - neocd - nestopia - np2kai @@ -737,6 +761,7 @@ targets: - opera - parallel_n64 - pcsx_rearmed + - pcsx_rearmed32 - pico-8 - picodrive - pokemini @@ -758,7 +783,7 @@ targets: - scummvm - scummvm-sa - skyemu - - smsplus_gx + - smsplus - snes9x - snes9x2002 - snes9x2005_plus @@ -779,6 +804,11 @@ targets: - vecx - vice - vice-sa + - vice_x128 + - vice_x64 + - vice_xpet + - vice_xplus4 + - vice_xvic - vircon32 - virtualjaguar - vita3k @@ -786,7 +816,7 @@ targets: - vitaquake2 - wasm4 - wine - - xmil + - x1 - yabadabadoo - yabasanshiro - yabasanshiro-sa @@ -794,6 +824,7 @@ targets: architecture: aarch64 cores: - '81' + - DoubleCherryGB - a5200 - amiberry - arduous @@ -831,7 +862,6 @@ targets: - dolphin-sa - dosbox_core - dosbox_pure - - doublecherrygb - drastic - drastic-sa - dsperate @@ -878,9 +908,9 @@ targets: - mednafen - melonds - melonds-sa - - melonds_ds + - melondsds - mesen - - mesen_s + - mesen-s - mgba - minivmac - mojozork @@ -888,7 +918,7 @@ targets: - mu - mupen64plus - mupen64plus-sa - - mupen64plus_nx + - mupen64plus_next - neocd - nestopia - np2kai @@ -897,6 +927,7 @@ targets: - opera - parallel_n64 - pcsx_rearmed + - pcsx_rearmed32 - pico-8 - picodrive - pokemini @@ -918,7 +949,7 @@ targets: - scummvm - scummvm-sa - skyemu - - smsplus_gx + - smsplus - snes9x - snes9x2002 - snes9x2005_plus @@ -939,6 +970,11 @@ targets: - vecx - vice - vice-sa + - vice_x128 + - vice_x64 + - vice_xpet + - vice_xplus4 + - vice_xvic - vircon32 - virtualjaguar - vita3k @@ -946,7 +982,7 @@ targets: - vitaquake2 - wasm4 - wine - - xmil + - x1 - yabadabadoo - yabasanshiro - yabasanshiro-sa @@ -954,6 +990,7 @@ targets: architecture: aarch64 cores: - '81' + - DoubleCherryGB - a5200 - amiberry - arduous @@ -983,7 +1020,7 @@ targets: - bsnes2014_accuracy - bsnes2014_balanced - bsnes2014_performance - - bsnes_hd + - bsnes_hd_beta - bsnes_mercury_accuracy - bsnes_mercury_balanced - bsnes_mercury_performance @@ -995,7 +1032,6 @@ targets: - dolphin-sa - dosbox_core - dosbox_pure - - doublecherrygb - drastic - drastic-sa - dsperate @@ -1042,9 +1078,9 @@ targets: - mednafen - melonds - melonds-sa - - melonds_ds + - melondsds - mesen - - mesen_s + - mesen-s - mgba - minivmac - mojozork @@ -1052,7 +1088,7 @@ targets: - mu - mupen64plus - mupen64plus-sa - - mupen64plus_nx + - mupen64plus_next - neocd - nestopia - np2kai @@ -1061,6 +1097,7 @@ targets: - opera - parallel_n64 - pcsx_rearmed + - pcsx_rearmed32 - pico-8 - picodrive - pokemini @@ -1082,7 +1119,7 @@ targets: - scummvm - scummvm-sa - skyemu - - smsplus_gx + - smsplus - snes9x - snes9x2002 - snes9x2005_plus @@ -1105,6 +1142,11 @@ targets: - vecx - vice - vice-sa + - vice_x128 + - vice_x64 + - vice_xpet + - vice_xplus4 + - vice_xvic - vircon32 - virtualjaguar - vita3k @@ -1112,7 +1154,7 @@ targets: - vitaquake2 - wasm4 - wine - - xmil + - x1 - yabadabadoo - yabasanshiro - yabasanshiro-sa @@ -1120,6 +1162,7 @@ targets: architecture: aarch64 cores: - '81' + - DoubleCherryGB - a5200 - amiberry - arduous @@ -1151,7 +1194,7 @@ targets: - bsnes2014_accuracy - bsnes2014_balanced - bsnes2014_performance - - bsnes_hd + - bsnes_hd_beta - bsnes_mercury_accuracy - bsnes_mercury_balanced - bsnes_mercury_performance @@ -1162,7 +1205,6 @@ targets: - dolphin-sa - dosbox_core - dosbox_pure - - doublecherrygb - drastic - drastic-sa - dsperate @@ -1207,9 +1249,9 @@ targets: - mame2015 - melonds - melonds-sa - - melonds_ds + - melondsds - mesen - - mesen_s + - mesen-s - mgba - minivmac - mojozork @@ -1217,7 +1259,7 @@ targets: - mu - mupen64plus - mupen64plus-sa - - mupen64plus_nx + - mupen64plus_next - neocd - nestopia - np2kai @@ -1226,6 +1268,7 @@ targets: - opera - parallel_n64 - pcsx_rearmed + - pcsx_rearmed32 - pico-8 - picodrive - pokemini @@ -1247,7 +1290,7 @@ targets: - scummvm - scummvm-sa - skyemu - - smsplus_gx + - smsplus - snes9x - snes9x2002 - snes9x2005_plus @@ -1268,6 +1311,11 @@ targets: - vecx - vice - vice-sa + - vice_x128 + - vice_x64 + - vice_xpet + - vice_xplus4 + - vice_xvic - vircon32 - virtualjaguar - vita3k @@ -1275,13 +1323,14 @@ targets: - vitaquake2 - wasm4 - wine - - xmil + - x1 - yabasanshiro - yabasanshiro-sa sdm845: architecture: aarch64 cores: - '81' + - DoubleCherryGB - a5200 - amiberry - arduous @@ -1313,7 +1362,7 @@ targets: - bsnes2014_accuracy - bsnes2014_balanced - bsnes2014_performance - - bsnes_hd + - bsnes_hd_beta - bsnes_mercury_accuracy - bsnes_mercury_balanced - bsnes_mercury_performance @@ -1329,7 +1378,6 @@ targets: - dolphin-sa - dosbox_core - dosbox_pure - - doublecherrygb - dsperate - dsperate-sa - duckstation @@ -1376,9 +1424,9 @@ targets: - mednafen - melonds - melonds-sa - - melonds_ds + - melondsds - mesen - - mesen_s + - mesen-s - mgba - minivmac - mojozork @@ -1386,7 +1434,7 @@ targets: - mu - mupen64plus - mupen64plus-sa - - mupen64plus_nx + - mupen64plus_next - nanoboyadvance - nanoboyadvance-sa - neocd @@ -1397,6 +1445,7 @@ targets: - opera - parallel_n64 - pcsx_rearmed + - pcsx_rearmed32 - pico-8 - picodrive - pokemini @@ -1421,7 +1470,7 @@ targets: - scummvm-sa - skyemu - skyemu-sa - - smsplus_gx + - smsplus - snes9x - snes9x2002 - snes9x2005_plus @@ -1445,6 +1494,11 @@ targets: - vecx - vice - vice-sa + - vice_x128 + - vice_x64 + - vice_xpet + - vice_xplus4 + - vice_xvic - vircon32 - virtualjaguar - vita3k @@ -1452,15 +1506,16 @@ targets: - vitaquake2 - wasm4 - wine + - x1 - xemu - xemu-sa - - xmil - yabasanshiro - yabasanshiro-sa sm4450: architecture: aarch64 cores: - '81' + - DoubleCherryGB - a5200 - amiberry - arduous @@ -1492,7 +1547,7 @@ targets: - bsnes2014_accuracy - bsnes2014_balanced - bsnes2014_performance - - bsnes_hd + - bsnes_hd_beta - bsnes_mercury_accuracy - bsnes_mercury_balanced - bsnes_mercury_performance @@ -1508,7 +1563,6 @@ targets: - dolphin-sa - dosbox_core - dosbox_pure - - doublecherrygb - dsperate - dsperate-sa - duckstation @@ -1555,9 +1609,9 @@ targets: - mednafen - melonds - melonds-sa - - melonds_ds + - melondsds - mesen - - mesen_s + - mesen-s - mgba - minivmac - mojozork @@ -1565,7 +1619,7 @@ targets: - mu - mupen64plus - mupen64plus-sa - - mupen64plus_nx + - mupen64plus_next - nanoboyadvance - nanoboyadvance-sa - neocd @@ -1576,6 +1630,7 @@ targets: - opera - parallel_n64 - pcsx_rearmed + - pcsx_rearmed32 - pico-8 - picodrive - pokemini @@ -1598,7 +1653,7 @@ targets: - scummvm-sa - skyemu - skyemu-sa - - smsplus_gx + - smsplus - snes9x - snes9x2002 - snes9x2005_plus @@ -1622,6 +1677,11 @@ targets: - vecx - vice - vice-sa + - vice_x128 + - vice_x64 + - vice_xpet + - vice_xplus4 + - vice_xvic - vircon32 - virtualjaguar - vita3k @@ -1629,9 +1689,9 @@ targets: - vitaquake2 - wasm4 - wine + - x1 - xemu - xemu-sa - - xmil - yabadabadoo - yabasanshiro - yabasanshiro-sa @@ -1639,6 +1699,7 @@ targets: architecture: aarch64 cores: - '81' + - DoubleCherryGB - a5200 - amiberry - arduous @@ -1668,7 +1729,7 @@ targets: - bsnes2014_accuracy - bsnes2014_balanced - bsnes2014_performance - - bsnes_hd + - bsnes_hd_beta - bsnes_mercury_accuracy - bsnes_mercury_balanced - bsnes_mercury_performance @@ -1680,7 +1741,6 @@ targets: - dolphin-sa - dosbox_core - dosbox_pure - - doublecherrygb - drastic - drastic-sa - dsperate @@ -1727,9 +1787,9 @@ targets: - mednafen - melonds - melonds-sa - - melonds_ds + - melondsds - mesen - - mesen_s + - mesen-s - mgba - minivmac - mojozork @@ -1737,7 +1797,7 @@ targets: - mu - mupen64plus - mupen64plus-sa - - mupen64plus_nx + - mupen64plus_next - neocd - nestopia - np2kai @@ -1746,6 +1806,7 @@ targets: - opera - parallel_n64 - pcsx_rearmed + - pcsx_rearmed32 - pico-8 - picodrive - pokemini @@ -1767,7 +1828,7 @@ targets: - scummvm - scummvm-sa - skyemu - - smsplus_gx + - smsplus - snes9x - snes9x2002 - snes9x2005_plus @@ -1790,6 +1851,11 @@ targets: - vecx - vice - vice-sa + - vice_x128 + - vice_x64 + - vice_xpet + - vice_xplus4 + - vice_xvic - vircon32 - virtualjaguar - vita3k @@ -1797,7 +1863,7 @@ targets: - vitaquake2 - wasm4 - wine - - xmil + - x1 - yabadabadoo - yabasanshiro - yabasanshiro-sa @@ -1805,6 +1871,7 @@ targets: architecture: aarch64 cores: - '81' + - DoubleCherryGB - a5200 - amiberry - arduous @@ -1836,7 +1903,7 @@ targets: - bsnes2014_accuracy - bsnes2014_balanced - bsnes2014_performance - - bsnes_hd + - bsnes_hd_beta - bsnes_mercury_accuracy - bsnes_mercury_balanced - bsnes_mercury_performance @@ -1852,7 +1919,6 @@ targets: - dolphin-sa - dosbox_core - dosbox_pure - - doublecherrygb - dsperate - dsperate-sa - duckstation @@ -1899,9 +1965,9 @@ targets: - mednafen - melonds - melonds-sa - - melonds_ds + - melondsds - mesen - - mesen_s + - mesen-s - mgba - minivmac - mojozork @@ -1909,7 +1975,7 @@ targets: - mu - mupen64plus - mupen64plus-sa - - mupen64plus_nx + - mupen64plus_next - nanoboyadvance - nanoboyadvance-sa - neocd @@ -1920,6 +1986,7 @@ targets: - opera - parallel_n64 - pcsx_rearmed + - pcsx_rearmed32 - pico-8 - picodrive - pokemini @@ -1944,7 +2011,7 @@ targets: - scummvm-sa - skyemu - skyemu-sa - - smsplus_gx + - smsplus - snes9x - snes9x2002 - snes9x2005_plus @@ -1968,6 +2035,11 @@ targets: - vecx - vice - vice-sa + - vice_x128 + - vice_x64 + - vice_xpet + - vice_xplus4 + - vice_xvic - vircon32 - virtualjaguar - vita3k @@ -1975,15 +2047,16 @@ targets: - vitaquake2 - wasm4 - wine + - x1 - xemu - xemu-sa - - xmil - yabasanshiro - yabasanshiro-sa sm8550: architecture: aarch64 cores: - '81' + - DoubleCherryGB - a5200 - amiberry - arduous @@ -2017,7 +2090,7 @@ targets: - bsnes2014_accuracy - bsnes2014_balanced - bsnes2014_performance - - bsnes_hd + - bsnes_hd_beta - bsnes_mercury_accuracy - bsnes_mercury_balanced - bsnes_mercury_performance @@ -2033,7 +2106,6 @@ targets: - dolphin-sa - dosbox_core - dosbox_pure - - doublecherrygb - drastic - drastic-sa - dsperate @@ -2084,9 +2156,9 @@ targets: - mednafen - melonds - melonds-sa - - melonds_ds + - melondsds - mesen - - mesen_s + - mesen-s - mgba - minivmac - mojozork @@ -2094,7 +2166,7 @@ targets: - mu - mupen64plus - mupen64plus-sa - - mupen64plus_nx + - mupen64plus_next - nanoboyadvance - nanoboyadvance-sa - neocd @@ -2105,6 +2177,7 @@ targets: - opera - parallel_n64 - pcsx_rearmed + - pcsx_rearmed32 - pico-8 - picodrive - pokemini @@ -2129,7 +2202,7 @@ targets: - scummvm-sa - skyemu - skyemu-sa - - smsplus_gx + - smsplus - snes9x - snes9x2002 - snes9x2005_plus @@ -2153,6 +2226,11 @@ targets: - vecx - vice - vice-sa + - vice_x128 + - vice_x64 + - vice_xpet + - vice_xplus4 + - vice_xvic - vircon32 - virtualjaguar - vita3k @@ -2160,15 +2238,16 @@ targets: - vitaquake2 - wasm4 - wine + - x1 - xemu - xemu-sa - - xmil - yabasanshiro - yabasanshiro-sa sm8650: architecture: aarch64 cores: - '81' + - DoubleCherryGB - a5200 - amiberry - arduous @@ -2201,7 +2280,7 @@ targets: - bsnes2014_accuracy - bsnes2014_balanced - bsnes2014_performance - - bsnes_hd + - bsnes_hd_beta - bsnes_mercury_accuracy - bsnes_mercury_balanced - bsnes_mercury_performance @@ -2214,7 +2293,6 @@ targets: - dolphin-sa - dosbox_core - dosbox_pure - - doublecherrygb - dsperate - dsperate-sa - duckstation @@ -2262,9 +2340,9 @@ targets: - mednafen - melonds - melonds-sa - - melonds_ds + - melondsds - mesen - - mesen_s + - mesen-s - mgba - minivmac - mojozork @@ -2272,7 +2350,7 @@ targets: - mu - mupen64plus - mupen64plus-sa - - mupen64plus_nx + - mupen64plus_next - nanoboyadvance - nanoboyadvance-sa - neocd @@ -2283,6 +2361,7 @@ targets: - opera - parallel_n64 - pcsx_rearmed + - pcsx_rearmed32 - pico-8 - picodrive - pokemini @@ -2307,7 +2386,7 @@ targets: - scummvm-sa - skyemu - skyemu-sa - - smsplus_gx + - smsplus - snes9x - snes9x2002 - snes9x2005_plus @@ -2331,6 +2410,11 @@ targets: - vecx - vice - vice-sa + - vice_x128 + - vice_x64 + - vice_xpet + - vice_xplus4 + - vice_xvic - vircon32 - virtualjaguar - vita3k @@ -2338,15 +2422,16 @@ targets: - vitaquake2 - wasm4 - wine + - x1 - xemu - xemu-sa - - xmil - yabasanshiro - yabasanshiro-sa sm8750: architecture: aarch64 cores: - '81' + - DoubleCherryGB - a5200 - amiberry - arduous @@ -2379,7 +2464,7 @@ targets: - bsnes2014_accuracy - bsnes2014_balanced - bsnes2014_performance - - bsnes_hd + - bsnes_hd_beta - bsnes_mercury_accuracy - bsnes_mercury_balanced - bsnes_mercury_performance @@ -2392,7 +2477,6 @@ targets: - dolphin-sa - dosbox_core - dosbox_pure - - doublecherrygb - dsperate - dsperate-sa - duckstation @@ -2440,9 +2524,9 @@ targets: - mednafen - melonds - melonds-sa - - melonds_ds + - melondsds - mesen - - mesen_s + - mesen-s - mgba - minivmac - mojozork @@ -2450,7 +2534,7 @@ targets: - mu - mupen64plus - mupen64plus-sa - - mupen64plus_nx + - mupen64plus_next - nanoboyadvance - nanoboyadvance-sa - neocd @@ -2461,6 +2545,7 @@ targets: - opera - parallel_n64 - pcsx_rearmed + - pcsx_rearmed32 - pico-8 - picodrive - pokemini @@ -2485,7 +2570,7 @@ targets: - scummvm-sa - skyemu - skyemu-sa - - smsplus_gx + - smsplus - snes9x - snes9x2002 - snes9x2005_plus @@ -2509,6 +2594,11 @@ targets: - vecx - vice - vice-sa + - vice_x128 + - vice_x64 + - vice_xpet + - vice_xplus4 + - vice_xvic - vircon32 - virtualjaguar - vita3k @@ -2516,8 +2606,8 @@ targets: - vitaquake2 - wasm4 - wine + - x1 - xemu - xemu-sa - - xmil - yabasanshiro - yabasanshiro-sa diff --git a/scripts/scraper/rocknix_scraper.py b/scripts/scraper/rocknix_scraper.py index dc7019ea6..b4aa7e4ab 100644 --- a/scripts/scraper/rocknix_scraper.py +++ b/scripts/scraper/rocknix_scraper.py @@ -190,6 +190,25 @@ class Scraper(BaseScraper): self.tag = None if url else fetch_github_latest_version(GITHUB_REPO) super().__init__(url=url or SOURCE_TEMPLATE.format(ref=self.tag)) + def _built_cores(self, standalone: list[str]) -> tuple[list[str], list[str]]: + """(cores, standalone cores) at least one device image carries. + + RetroArch runs the libretro cores the packages of its emulators + metapackage install per device + (projects/ROCKNIX/packages/virtual/emulators/package.mk), read by + the targets scraper, where every libretro profile counted before. A + standalone package no device carries is not run either. + """ + try: + from .targets.rocknix_targets_scraper import Scraper as DeviceTargets + except ImportError: + from scraper.targets.rocknix_targets_scraper import Scraper as DeviceTargets + builds = DeviceTargets().device_builds(self.tag or launcher_revision(self.url)) + libretro = sorted({core for _arch, lr, _sa in builds.values() for core in lr}) + carried = set(standalone_emulators(sorted({p for *_rest, sa in builds.values() for p in sa}))) + standalone = [name for name in standalone if name in carried] + return sorted({*libretro, *standalone}), standalone + def _fetch_standalone(self) -> list[str]: """The packages of projects/ROCKNIX/packages/emulators/standalone.""" import json @@ -281,7 +300,7 @@ class Scraper(BaseScraper): ) entry["files"].append(requirement_entry(req)) - standalone = self._fetch_standalone() + cores, standalone = self._built_cores(self._fetch_standalone()) return { "platform": "ROCKNIX", "version": self.tag or fetch_github_latest_version(GITHUB_REPO), @@ -290,9 +309,7 @@ class Scraper(BaseScraper): "base_destination": "bios", "hash_type": "md5", "verification_mode": "md5", - # RetroArch with every libretro core, plus the standalone - # emulators ROCKNIX packages beside it. - "cores": ["retroarch", *standalone], + "cores": cores, "standalone_cores": standalone, # Launched both ways only where package.mk also adds the # emulator as a RetroArch core. diff --git a/scripts/scraper/targets/rocknix_targets_scraper.py b/scripts/scraper/targets/rocknix_targets_scraper.py index 9966d11c1..e9b5f7dd3 100644 --- a/scripts/scraper/targets/rocknix_targets_scraper.py +++ b/scripts/scraper/targets/rocknix_targets_scraper.py @@ -5,6 +5,8 @@ Sources (ROCKNIX/distribution, at the release tag platforms/rocknix.yml names): image carries, those aarch64 adds, and a `case "${DEVICE}"` block per SoC - projects/ROCKNIX/devices//options -- ENABLE_32BIT, which the 32-bit cores of a device depend on (distributions/ROCKNIX/options defaults it on) + - each libretro package's package.mk -- the cores its makeinstall_target + copies to ${INSTALL}/usr/lib/libretro (vice-lr installs ten of them) An H700 or RK3326 handheld carries neither Dolphin nor AetherSX2: without a target list, its pack shipped the PS2, GameCube and PS3 firmware anyway. @@ -13,6 +15,7 @@ target list, its pack shipped the PS2, GameCube and PS3 firmware anyway. from __future__ import annotations import argparse +import json import re import sys from datetime import datetime, timezone @@ -89,18 +92,90 @@ def parse_device_cores( return libretro, standalone -def core_names(libretro: list[str], standalone: list[str]) -> list[str]: - """Package names as the profiles' cores: lists spell them. +_INSTALL_BODY_RE = re.compile(r"^makeinstall_target\(\)\s*\{\n(.*?)^\}", re.M | re.S) +_COPY_RE = re.compile(r"\bcp\s+(.*?)\s+\$\{?INSTALL\}?/usr/lib/libretro(?:/(\S*))?") +_CORE_FILE_RE = re.compile(r"([\w+.-]+)_libretro\.(?:so\b|\{[^}]*\bso\b[^}]*\})") +_TEST_RE = re.compile(r'\[\s*"\$\{(\w+)\}"\s*=\s*"([^"]*)"\s*\]') - A libretro package is the core's name with `-lr` (`beetle-psx-lr`, - `neocd_lr`), spelled with underscores as the buildbot names it. A - standalone package is kept under both spellings, as the platform - scraper keeps it: `vice-sa` is the only name vice-standalone answers - to, `vice` that of the libretro profile. + +def installed_cores(package_mk: str, arch: str, with_32bit: bool) -> list[str]: + """The cores a libretro package installs on a device. + + The name is that of the file makeinstall_target copies to + ${INSTALL}/usr/lib/libretro ($INSTALL in some), renamed when the copy names its target: + beetle-psx-lr copies mednafen_psx_libretro.so as beetle_psx_libretro.so, + vice-lr ten EMUTYPE builds. pcsx_rearmed-lr picks its file by + TARGET_ARCH and adds the 32-bit build when ENABLE_32BIT is on, desmume-lr + copies the 32-bit build on aarch64 (ARCH). A test on another variable, + or a package that installs no core, is an error. """ - names = {re.sub(r"[-_]lr$", "", package).replace("-", "_") for package in libretro} - names.update(standalone_emulators(standalone)) - return sorted(names) + body = _INSTALL_BODY_RE.search(package_mk) + if body is None: + raise RuntimeError("package.mk has no makeinstall_target") + # config/options:36 sets TARGET_ARCH="${ARCH}". + values = {"ARCH": arch, "TARGET_ARCH": arch, "ENABLE_32BIT": "true" if with_32bit else "false"} + + def holds(condition: str) -> bool: + tests = _TEST_RE.findall(condition) + if not tests or re.sub(_TEST_RE, "", condition).strip(" ;&") not in ("", "then"): + raise RuntimeError(f"makeinstall_target tests {condition.strip()!r}") + for variable, value in tests: + if variable not in values: + raise RuntimeError(f"makeinstall_target tests ${{{variable}}}") + if values[variable] != value: + return False + return True + + found: list[str] = [] + stack: list[bool] = [] + in_case = False + for raw in body.group(1).splitlines(): + line = raw.strip() + if re.match(r"case \$\{TARGET_ARCH\} in$", line): + in_case = True + stack.append(False) + continue + if line == "esac": + in_case = False + stack.pop() + continue + if in_case and (arm := re.match(r"([\w|*]+)\)\s*(.*)$", line)): + stack[-1] = arch in arm.group(1).split("|") or arm.group(1) == "*" + line = arm.group(2) + if line.startswith("if "): + stack.append(holds(line[3:])) + continue + if line == "else": + stack[-1] = not stack[-1] + continue + if line == "fi": + stack.pop() + continue + if not all(stack): + continue + for sources, target in _COPY_RE.findall(line): + if named := _CORE_FILE_RE.search(target): + found.append(named.group(1)) + continue + for source in sources.split(): + if not source.startswith("-") and (named := _CORE_FILE_RE.search(source.rsplit("/", 1)[-1])): + found.append(named.group(1)) + if not found: + raise RuntimeError("makeinstall_target installs no libretro core") + return sorted(set(found)) + + +def package_paths(tree: list[str]) -> dict[str, str]: + """package name -> its package.mk, a ROCKNIX project package first. + + The build takes projects/ROCKNIX/packages/<...>/ over + packages/<...>/ when both exist. + """ + found: dict[str, str] = {} + for path in sorted(tree, key=lambda p: not p.startswith("projects/ROCKNIX/")): + if path.endswith("/package.mk"): + found.setdefault(path.rsplit("/", 2)[-2], path) + return found # A package that builds nothing itself and pulls other cores in. idtech-lr @@ -149,13 +224,40 @@ class Scraper(BaseTargetScraper): raise RuntimeError(f"no device in {DEVICES_DIR} at {self.tag}") return sorted(devices) - def fetch_targets(self) -> dict: - self.tag = self.pinned_revision(PLATFORM_NAME) + def _tree(self) -> list[str]: + listing = json.loads(fetch_bytes( + f"https://api.github.com/repos/ROCKNIX/distribution/git/trees/{self.tag}?recursive=1", + headers=github_headers(), + )) + if listing.get("truncated"): + raise RuntimeError(f"ROCKNIX tree at {self.tag} is truncated") + return [entry["path"] for entry in listing.get("tree", [])] + + def device_builds(self, tag: str | None = None) -> dict[str, tuple[str, list[str], list[str]]]: + """{device: (architecture, libretro cores, standalone packages)}. + + Read at `tag`, by default the revision platforms/rocknix.yml was + scraped at: metapackages resolved to the packages they pull in, and + each libretro package to the cores it installs on the device. + """ + self.tag = tag or self.pinned_revision(PLATFORM_NAME) package_mk = self._raw(EMULATORS_MK) if "LIBRETRO_CORES" not in package_mk: raise RuntimeError(f"no LIBRETRO_CORES in {EMULATORS_MK} at {self.tag}") + paths = package_paths(self._tree()) + texts: dict[str, str] = {} - targets: dict[str, dict] = {} + def cores_of(package: str, arch: str, with_32bit: bool) -> list[str]: + if package not in paths: + raise RuntimeError(f"no package.mk for {package} at {self.tag}") + if package not in texts: + texts[package] = self._raw(paths[package]) + try: + return installed_cores(texts[package], arch, with_32bit) + except RuntimeError as e: + raise RuntimeError(f"{paths[package]}: {e}") from e + + builds: dict[str, tuple[str, list[str], list[str]]] = {} for device in self._devices(): options = self._raw(f"{DEVICES_DIR}/{device}/options") arch = "x86_64" if device == "AMD64" else "aarch64" @@ -165,9 +267,19 @@ class Scraper(BaseTargetScraper): if meta in libretro: libretro.remove(meta) libretro += metapackage_cores(self._raw(path), arch) - cores = core_names(libretro, standalone) - targets[device.lower()] = {"architecture": arch, "cores": cores} - print(f" {device.lower()} ({arch}): {len(cores)} cores", file=sys.stderr) + cores = sorted({c for package in libretro for c in cores_of(package, arch, with_32bit)}) + builds[device.lower()] = (arch, cores, standalone) + return builds + + def fetch_targets(self) -> dict: + targets: dict[str, dict] = {} + for device, (arch, libretro, standalone) in self.device_builds().items(): + # A standalone package is kept under both spellings, as the + # platform scraper keeps it: vice-sa is the only name + # vice-standalone answers to, vice that of the libretro profile. + cores = sorted({*libretro, *standalone_emulators(standalone)}) + targets[device] = {"architecture": arch, "cores": cores} + print(f" {device} ({arch}): {len(cores)} cores", file=sys.stderr) return { "platform": PLATFORM_NAME, diff --git a/tests/test_launchers_rocknix.py b/tests/test_launchers_rocknix.py index 26fa9192c..aa721896b 100644 --- a/tests/test_launchers_rocknix.py +++ b/tests/test_launchers_rocknix.py @@ -619,8 +619,10 @@ class PlatformRecords(unittest.TestCase): cls.config = yaml.safe_load((REPO_ROOT / "platforms" / "rocknix.yml").read_text()) def test_every_emulator_is_one_rocknix_packages(self): + # RetroArch itself runs the libretro cores the list names. cores = set(self.config["cores"]) - self.assertEqual(sorted({r["emulator"] for r in self.config["launchers"]} - cores), []) + self.assertEqual(sorted({r["emulator"] for r in self.config["launchers"]} - cores), + ["retroarch"]) def test_every_reference_is_in_the_rocknix_project(self): for record in self.config["launchers"]: diff --git a/tests/test_scraper_contract.py b/tests/test_scraper_contract.py index e4ca795b4..a7555649d 100644 --- a/tests/test_scraper_contract.py +++ b/tests/test_scraper_contract.py @@ -480,7 +480,10 @@ class RocknixRunsItsStandaloneEmulators(unittest.TestCase): root = Path(__file__).resolve().parents[1] platform = yaml.safe_load((root / "platforms" / "rocknix.yml").read_text()) self.assertIn(f"/distribution/{platform['version']}/", platform["source"]) - self.assertIn("retroarch", platform["cores"]) + # RetroArch runs the libretro cores some device image installs, not + # every libretro profile. + self.assertNotIn("retroarch", platform["cores"]) + self.assertIn("beetle_psx", platform["cores"]) registry = yaml.safe_load((root / "platforms" / "_registry.yml").read_text()) self.assertNotIn("cores", registry["platforms"]["rocknix"]) diff --git a/tests/test_target_scrapers.py b/tests/test_target_scrapers.py index 81c389d5d..9d231ade6 100644 --- a/tests/test_target_scrapers.py +++ b/tests/test_target_scrapers.py @@ -393,35 +393,60 @@ class RocknixPerDevice(unittest.TestCase): ) def test_each_device_gets_its_own_cores(self): - from scraper.targets.rocknix_targets_scraper import ( # noqa: PLC0415 - core_names, - parse_device_cores, - ) + from scraper.targets.rocknix_targets_scraper import parse_device_cores # noqa: PLC0415 - h700 = core_names(*parse_device_cores(self.PACKAGE_MK, "H700", "aarch64", True)) - self.assertTrue({"drastic", "gpsp", "duckstation", "beetle_psx", "hatari", - "81", "neocd"} <= set(h700)) - self.assertFalse({"aethersx2", "dolphin"} & set(h700)) - no32 = core_names(*parse_device_cores(self.PACKAGE_MK, "H700", "aarch64", False)) - self.assertNotIn("gpsp", no32) - amd64 = core_names(*parse_device_cores(self.PACKAGE_MK, "AMD64", "x86_64", False)) - self.assertIn("dolphin", amd64) - self.assertNotIn("drastic", amd64) + libretro, standalone = parse_device_cores(self.PACKAGE_MK, "H700", "aarch64", True) + self.assertTrue({"gpsp-lr", "duckstation-lr", "beetle-psx-lr", "81-lr", + "neocd_lr"} <= set(libretro)) + self.assertTrue({"drastic-sa", "hatarisa"} <= set(standalone)) + self.assertFalse({"aethersx2-sa", "dolphin-sa"} & set(standalone)) + libretro, _ = parse_device_cores(self.PACKAGE_MK, "H700", "aarch64", False) + self.assertNotIn("gpsp-lr", libretro) + _, standalone = parse_device_cores(self.PACKAGE_MK, "AMD64", "x86_64", False) + self.assertIn("dolphin-sa", standalone) + self.assertNotIn("drastic-sa", standalone) + + def test_a_package_names_the_cores_it_installs(self): + """vice-lr builds ten EMUTYPEs: read as the package name, vice_x64 + and its siblings reached no ROCKNIX device.""" + from scraper.targets.rocknix_targets_scraper import installed_cores # noqa: PLC0415 + + vice = ("makeinstall_target() {\n mkdir -p ${INSTALL}/usr/lib/libretro\n" + " cp -a built/vice_x128_libretro.so ${INSTALL}/usr/lib/libretro\n" + " cp -a built/vice_x64_libretro.so ${INSTALL}/usr/lib/libretro\n}\n") + self.assertEqual(installed_cores(vice, "aarch64", True), ["vice_x128", "vice_x64"]) + renamed = ("makeinstall_target() {\n" + " cp -a mednafen_psx_libretro.so ${INSTALL}/usr/lib/libretro/beetle_psx_libretro.so\n" + " cp -a fake08_libretro.{so,info} $INSTALL/usr/lib/libretro\n}\n") + self.assertEqual(installed_cores(renamed, "aarch64", True), ["beetle_psx", "fake08"]) + pcsx = ("makeinstall_target() {\n" + " case ${TARGET_ARCH} in\n" + " aarch64|x86_64) cp -a ../pcsx_rearmed_libretro.so ${INSTALL}/usr/lib/libretro ;;\n" + " arm) cp -a ../pcsx_rearmed_libretro.so ${INSTALL}/usr/lib/libretro/pcsx_rearmed32_libretro.so ;;\n" + " esac\n" + ' if [ "${TARGET_ARCH}" = "aarch64" ] && [ "${ENABLE_32BIT}" = "true" ]; then\n' + " cp -a x/usr/lib/libretro/pcsx_rearmed32_libretro.so ${INSTALL}/usr/lib/libretro\n" + " fi\n}\n") + self.assertEqual(installed_cores(pcsx, "aarch64", True), ["pcsx_rearmed", "pcsx_rearmed32"]) + self.assertEqual(installed_cores(pcsx, "aarch64", False), ["pcsx_rearmed"]) + self.assertEqual(installed_cores(pcsx, "x86_64", True), ["pcsx_rearmed"]) + with self.assertRaises(RuntimeError): + installed_cores('makeinstall_target() {\n if [ "${DEVICE}" = "H700" ]; then\n' + " cp a_libretro.so ${INSTALL}/usr/lib/libretro\n fi\n}\n", + "aarch64", True) + with self.assertRaises(RuntimeError): + installed_cores("makeinstall_target() {\n :\n}\n", "aarch64", True) def test_a_standalone_package_keeps_both_spellings(self): """vice-standalone answers to vice-sa only, as scummvm-standalone to scummvm-sa: stripped to vice, every targeted pack left them out while the untargeted one, read from the platform scraper, kept them.""" from scraper.rocknix_scraper import standalone_emulators # noqa: PLC0415 - from scraper.targets.rocknix_targets_scraper import ( # noqa: PLC0415 - core_names, - parse_device_cores, - ) + from scraper.targets.rocknix_targets_scraper import parse_device_cores # noqa: PLC0415 - libretro, standalone = parse_device_cores(self.PACKAGE_MK, "SM8550", "aarch64", False) - names = set(core_names(libretro, standalone)) + _, standalone = parse_device_cores(self.PACKAGE_MK, "SM8550", "aarch64", False) + names = set(standalone_emulators(standalone)) self.assertTrue({"aethersx2-sa", "aethersx2", "dolphin-sa", "dolphin"} <= names) - self.assertTrue(set(standalone_emulators(standalone)) <= names) def test_a_metapackage_names_the_cores_it_installs(self): from scraper.targets.rocknix_targets_scraper import metapackage_cores # noqa: PLC0415 From 828d1527eef4c62a1a9f7db4306ed4d2d727d9cc Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Mon, 9 Nov 2026 21:53:15 +0100 Subject: [PATCH 5/6] fix: keep the cores batocera's retroarch installs Batocera builds RetroArch with its core downloader (configure defaults, qb/config.params.sh:149-150), shown in the menu since it is no Lakka build (config.def.h:743-745), and configgen sets no URL, so the compiled one applies: buildbot linux/x86_64 or linux/armhf, none on aarch64 (config.def.h:1855-1861). The targets scraper names that list per board (downloads, an armv7-A CPU read from arch/Config.in.arm), the builder and the published target lists add it, and the platform names the union as installable_cores. applewin, gong, squirreljme and vemulator come back that way. A build option that compiles the downloader out, or a shipped retroarch.cfg that blanks its URL, turns it off. --- install/targets/batocera.json | 678 ++++++++++++++++++ platforms/batocera.yml | 151 ++++ platforms/targets/batocera.yml | 10 +- schemas/platform.schema.json | 8 + scripts/common.py | 31 +- scripts/scraper/batocera_scraper.py | 23 +- .../targets/batocera_targets_scraper.py | 75 +- .../scraper/targets/knulli_targets_scraper.py | 1 + tests/test_target_scrapers.py | 76 +- 9 files changed, 1037 insertions(+), 16 deletions(-) diff --git a/install/targets/batocera.json b/install/targets/batocera.json index 755702725..e07d34af4 100644 --- a/install/targets/batocera.json +++ b/install/targets/batocera.json @@ -612,6 +612,7 @@ "zc210" ], "bcm2836": [ + "2048", "81", "a5200", "abuse", @@ -621,23 +622,38 @@ "bennugd", "bk", "bluemsx", + "bsnes_cplusplus98", + "bsnes_mercury_accuracy", + "bsnes_mercury_balanced", + "bsnes_mercury_performance", "bstone", "cannonball", "cap32", "cdogs", "cgenius", + "chailove", + "crocods", "devilutionx", "dice", + "dinothawr", + "dosbox_core", "dosbox_pure", "dosbox_staging", + "dosbox_svn", + "dosbox_svn_ce", "duckstation", "duckstation-legacy", "easyrpg", + "ecwolf", "emuscv", "ep128emu-core", "fake08", "fallout1-ce", "fallout2-ce", + "fbalpha2012", + "fbalpha2012_cps1", + "fbalpha2012_cps2", + "fbalpha2012_neogeo", "fbneo", "fceumm", "flycastvl", @@ -646,15 +662,21 @@ "freeintv", "fuse", "gambatte", + "gearboy", "gearcoleco", "gearsystem", + "genesis_plus_gx", "genesisplusgx", "genesisplusgx-expanded", + "gme", + "gpsp", "gsplus", "gw", "handy", "hatari", "hcl", + "higan_sfc", + "higan_sfc_balanced", "holani", "hurrican", "hypseus-singe", @@ -664,31 +686,46 @@ "lutro", "mame", "mame078plus", + "mame2000", + "mame2003", + "mame2003_plus", + "mednafen_gba", "mednafen_lynx", "mednafen_ngp", + "mednafen_pce", + "mednafen_pce_fast", + "mednafen_psx", + "mednafen_snes", "mednafen_supergrafx", + "mednafen_vb", "mednafen_wswan", + "mesen", "mgba", "minivmac", "moonlight", "mrboom", + "mu", "mupen64plus", "mupen64plus-next", + "nekop2", "neocd", "nestopia", "np2kai", "nxengine", "o2em", + "oberon", "openbor", "openbor4432", "openbor6412", "openbor7142", "openbor7530", "openjazz", + "opera", "pce_fast", "pcsx_rearmed", "pd777", "picodrive", + "pocketcdg", "pocketsnes", "pokemini", "potator", @@ -700,20 +737,32 @@ "px68k", "pygame", "quasi88", + "quicknes", + "reminiscence", + "remotejoy", "samcoupe", + "sameboy", "sameduck", "scummvm", "sdlpop", "smsplus", "snes9x", + "snes9x2002", + "snes9x2005", + "snes9x2005_plus", + "snes9x2010", "snes9x_next", "solarus", + "squirreljme", "stella", + "stella2014", "superbroswar", "swanstation", "taradino", + "test", "tgbdual", "theodore", + "thepowdertoy", "thextech", "tic80", "tyrian", @@ -722,15 +771,21 @@ "uzem", "vb", "vba-m", + "vba_next", + "vbam", "vecx", + "vemulator", "vice", "vice_x128", "vice_x64", "vice_x64sc", + "vice_xcbm2", + "vice_xcbm5x0", "vice_xpet", "vice_xplus4", "vice_xscpu64", "vice_xvic", + "virtualjaguar", "x1", "x128", "x16emu", @@ -1231,6 +1286,7 @@ ], "h3": { "cores": [ + "2048", "81", "a5200", "abuse", @@ -1240,20 +1296,35 @@ "bennugd", "bk", "bluemsx", + "bsnes_cplusplus98", + "bsnes_mercury_accuracy", + "bsnes_mercury_balanced", + "bsnes_mercury_performance", "bstone", "cannonball", "cap32", "cdogs", "cgenius", + "chailove", + "crocods", "devilutionx", "dice", + "dinothawr", + "dosbox_core", "dosbox_pure", + "dosbox_svn", + "dosbox_svn_ce", "easyrpg", + "ecwolf", "emuscv", "ep128emu-core", "fake08", "fallout1-ce", "fallout2-ce", + "fbalpha2012", + "fbalpha2012_cps1", + "fbalpha2012_cps2", + "fbalpha2012_neogeo", "fbneo", "fceumm", "flycastvl", @@ -1262,82 +1333,124 @@ "freeintv", "fuse", "gambatte", + "gearboy", "gearcoleco", "gearsystem", + "genesis_plus_gx", "genesisplusgx", "genesisplusgx-expanded", + "gme", + "gpsp", "gsplus", "gw", "handy", "hatari", "hcl", + "higan_sfc", + "higan_sfc_balanced", "holani", "hurrican", "hypseus-singe", "ioquake3", "lightspark", "lowresnx", + "mame", "mame078plus", + "mame2000", + "mame2003", + "mame2003_plus", + "mednafen_gba", "mednafen_lynx", "mednafen_ngp", + "mednafen_pce", + "mednafen_pce_fast", + "mednafen_psx", + "mednafen_snes", "mednafen_supergrafx", + "mednafen_vb", "mednafen_wswan", + "mesen", "mgba", "minivmac", "moonlight", "mrboom", + "mu", "mupen64plus", + "nekop2", "neocd", "nestopia", "np2kai", "nxengine", "o2em", + "oberon", "openbor", "openbor4432", "openbor6412", "openbor7142", "openbor7530", "openjazz", + "opera", "pce_fast", "pcsx_rearmed", "pd777", "picodrive", + "pocketcdg", "pocketsnes", "pokemini", "potator", "ppsspp", "prboom", "prosystem", + "puae", "px68k", "quasi88", + "quicknes", + "reminiscence", + "remotejoy", "samcoupe", + "sameboy", "sameduck", "scummvm", "sdlpop", "smsplus", "snes9x", + "snes9x2002", + "snes9x2005", + "snes9x2005_plus", + "snes9x2010", "snes9x_next", "solarus", + "squirreljme", "stella", + "stella2014", "superbroswar", "taradino", + "test", "tgbdual", "theodore", + "thepowdertoy", "thextech", "tic80", "tyrian", "tyrquake", "uae4arm", + "uzem", "vb", + "vba_next", + "vbam", "vecx", + "vemulator", "vice", "vice_x128", "vice_x64", "vice_x64sc", + "vice_xcbm2", + "vice_xcbm5x0", "vice_xpet", "vice_xplus4", "vice_xscpu64", "vice_xvic", + "virtualjaguar", "x1", "x128", "x16emu", @@ -2273,6 +2386,7 @@ "zc210" ], "odroidxu4": [ + "2048", "81", "a5200", "abuse", @@ -2282,25 +2396,40 @@ "bennugd", "bk", "bluemsx", + "bsnes_cplusplus98", + "bsnes_mercury_accuracy", + "bsnes_mercury_balanced", + "bsnes_mercury_performance", "bstone", "cannonball", "cap32", "cdogs", "cgenius", + "chailove", + "crocods", "devilutionx", "dice", + "dinothawr", "dosbox-x", + "dosbox_core", "dosbox_pure", "dosbox_staging", + "dosbox_svn", + "dosbox_svn_ce", "dosboxx", "duckstation", "duckstation-legacy", "easyrpg", + "ecwolf", "emuscv", "ep128emu-core", "fake08", "fallout1-ce", "fallout2-ce", + "fbalpha2012", + "fbalpha2012_cps1", + "fbalpha2012_cps2", + "fbalpha2012_neogeo", "fbneo", "fceumm", "flycast", @@ -2309,15 +2438,21 @@ "freeintv", "fuse", "gambatte", + "gearboy", "gearcoleco", "gearsystem", + "genesis_plus_gx", "genesisplusgx", "genesisplusgx-expanded", + "gme", + "gpsp", "gsplus", "gw", "handy", "hatari", "hcl", + "higan_sfc", + "higan_sfc_balanced", "holani", "hurrican", "hypseus-singe", @@ -2327,22 +2462,34 @@ "lutro", "mame", "mame078plus", + "mame2000", + "mame2003", + "mame2003_plus", + "mednafen_gba", "mednafen_lynx", "mednafen_ngp", + "mednafen_pce", + "mednafen_pce_fast", "mednafen_psx", + "mednafen_snes", "mednafen_supergrafx", + "mednafen_vb", "mednafen_wswan", + "mesen", "mgba", "minivmac", "moonlight", "mrboom", + "mu", "mupen64plus", "mupen64plus-next", + "nekop2", "neocd", "nestopia", "np2kai", "nxengine", "o2em", + "oberon", "openbor", "openbor4432", "openbor6412", @@ -2355,6 +2502,7 @@ "pcsx_rearmed", "pd777", "picodrive", + "pocketcdg", "pokemini", "potator", "ppsspp", @@ -2365,20 +2513,32 @@ "px68k", "pygame", "quasi88", + "quicknes", + "reminiscence", + "remotejoy", "samcoupe", + "sameboy", "sameduck", "scummvm", "sdlpop", "smsplus", "snes9x", + "snes9x2002", + "snes9x2005", + "snes9x2005_plus", + "snes9x2010", "snes9x_next", "solarus", + "squirreljme", "stella", + "stella2014", "superbroswar", "swanstation", "taradino", + "test", "tgbdual", "theodore", + "thepowdertoy", "thextech", "tic80", "tyrian", @@ -2387,15 +2547,21 @@ "uzem", "vb", "vba-m", + "vba_next", + "vbam", "vecx", + "vemulator", "vice", "vice_x128", "vice_x64", "vice_x64sc", + "vice_xcbm2", + "vice_xcbm5x0", "vice_xpet", "vice_xplus4", "vice_xscpu64", "vice_xvic", + "virtualjaguar", "x1", "x128", "x16emu", @@ -2592,6 +2758,7 @@ }, "rk3128": { "cores": [ + "2048", "81", "a5200", "abuse", @@ -2601,21 +2768,36 @@ "bennugd", "bk", "bluemsx", + "bsnes_cplusplus98", + "bsnes_mercury_accuracy", + "bsnes_mercury_balanced", + "bsnes_mercury_performance", "bstone", "cannonball", "cap32", "cdogs", "cgenius", + "chailove", + "crocods", "devilutionx", "dice", + "dinothawr", + "dosbox_core", "dosbox_pure", "dosbox_staging", + "dosbox_svn", + "dosbox_svn_ce", "easyrpg", + "ecwolf", "emuscv", "ep128emu-core", "fake08", "fallout1-ce", "fallout2-ce", + "fbalpha2012", + "fbalpha2012_cps1", + "fbalpha2012_cps2", + "fbalpha2012_neogeo", "fbneo", "fceumm", "flycast", @@ -2624,38 +2806,57 @@ "freeintv", "fuse", "gambatte", + "gearboy", "gearcoleco", "gearsystem", + "genesis_plus_gx", "genesisplusgx", "genesisplusgx-expanded", + "gme", + "gpsp", "gsplus", "gw", "handy", "hatari", "hcl", + "higan_sfc", + "higan_sfc_balanced", "holani", "hurrican", "hypseus-singe", "lightspark", "lowresnx", "lutro", + "mame", "mame078plus", + "mame2000", + "mame2003", + "mame2003_plus", + "mednafen_gba", "mednafen_lynx", "mednafen_ngp", + "mednafen_pce", + "mednafen_pce_fast", "mednafen_psx", + "mednafen_snes", "mednafen_supergrafx", + "mednafen_vb", "mednafen_wswan", + "mesen", "mgba", "minivmac", "moonlight", "mrboom", + "mu", "mupen64plus", "mupen64plus-next", + "nekop2", "neocd", "nestopia", "np2kai", "nxengine", "o2em", + "oberon", "openbor", "openbor4432", "openbor6412", @@ -2669,6 +2870,7 @@ "pcsx_rearmed", "pd777", "picodrive", + "pocketcdg", "pokemini", "potator", "ppsspp", @@ -2679,20 +2881,32 @@ "px68k", "pygame", "quasi88", + "quicknes", + "reminiscence", + "remotejoy", "samcoupe", + "sameboy", "sameduck", "scummvm", "sdlpop", "smsplus", "snes9x", + "snes9x2002", + "snes9x2005", + "snes9x2005_plus", + "snes9x2010", "snes9x_next", "solarus", + "squirreljme", "stella", + "stella2014", "superbroswar", "swanstation", "taradino", + "test", "tgbdual", "theodore", + "thepowdertoy", "thextech", "tic80", "tyrian", @@ -2701,15 +2915,21 @@ "uzem", "vb", "vba-m", + "vba_next", + "vbam", "vecx", + "vemulator", "vice", "vice_x128", "vice_x64", "vice_x64sc", + "vice_xcbm2", + "vice_xcbm5x0", "vice_xpet", "vice_xplus4", "vice_xscpu64", "vice_xvic", + "virtualjaguar", "x1", "x128", "x16emu", @@ -2732,6 +2952,7 @@ }, "rk3288": { "cores": [ + "2048", "81", "a5200", "abuse", @@ -2741,25 +2962,40 @@ "bennugd", "bk", "bluemsx", + "bsnes_cplusplus98", + "bsnes_mercury_accuracy", + "bsnes_mercury_balanced", + "bsnes_mercury_performance", "bstone", "cannonball", "cap32", "cdogs", "cgenius", + "chailove", + "crocods", "devilutionx", "dice", + "dinothawr", "dosbox-x", + "dosbox_core", "dosbox_pure", "dosbox_staging", + "dosbox_svn", + "dosbox_svn_ce", "dosboxx", "duckstation", "easyrpg", + "ecwolf", "eduke32", "emuscv", "ep128emu-core", "fake08", "fallout1-ce", "fallout2-ce", + "fbalpha2012", + "fbalpha2012_cps1", + "fbalpha2012_cps2", + "fbalpha2012_neogeo", "fbneo", "fceumm", "flycast", @@ -2769,15 +3005,21 @@ "fury", "fuse", "gambatte", + "gearboy", "gearcoleco", "gearsystem", + "genesis_plus_gx", "genesisplusgx", "genesisplusgx-expanded", + "gme", + "gpsp", "gsplus", "gw", "handy", "hatari", "hcl", + "higan_sfc", + "higan_sfc_balanced", "holani", "hurrican", "hypseus-singe", @@ -2788,23 +3030,34 @@ "lutro", "mame", "mame078plus", + "mame2000", + "mame2003", + "mame2003_plus", + "mednafen_gba", "mednafen_lynx", "mednafen_ngp", + "mednafen_pce", + "mednafen_pce_fast", "mednafen_psx", + "mednafen_snes", "mednafen_supergrafx", + "mednafen_vb", "mednafen_wswan", "mesen", "mgba", "minivmac", "moonlight", "mrboom", + "mu", "mupen64plus", "mupen64plus-next", + "nekop2", "neocd", "nestopia", "np2kai", "nxengine", "o2em", + "oberon", "openbor", "openbor4432", "openbor6412", @@ -2818,6 +3071,7 @@ "pcsx_rearmed", "pd777", "picodrive", + "pocketcdg", "pokemini", "potator", "ppsspp", @@ -2828,20 +3082,32 @@ "px68k", "pygame", "quasi88", + "quicknes", + "reminiscence", + "remotejoy", "samcoupe", + "sameboy", "sameduck", "scummvm", "sdlpop", "smsplus", "snes9x", + "snes9x2002", + "snes9x2005", + "snes9x2005_plus", + "snes9x2010", "snes9x_next", "solarus", + "squirreljme", "stella", + "stella2014", "superbroswar", "swanstation", "taradino", + "test", "tgbdual", "theodore", + "thepowdertoy", "thextech", "tic80", "tyrian", @@ -2850,15 +3116,21 @@ "uzem", "vb", "vba-m", + "vba_next", + "vbam", "vecx", + "vemulator", "vice", "vice_x128", "vice_x64", "vice_x64sc", + "vice_xcbm2", + "vice_xcbm5x0", "vice_xpet", "vice_xplus4", "vice_xscpu64", "vice_xvic", + "virtualjaguar", "vitaquake2", "x1", "x128", @@ -4244,6 +4516,7 @@ "zc210" ], "rpi2": [ + "2048", "81", "a5200", "abuse", @@ -4253,23 +4526,38 @@ "bennugd", "bk", "bluemsx", + "bsnes_cplusplus98", + "bsnes_mercury_accuracy", + "bsnes_mercury_balanced", + "bsnes_mercury_performance", "bstone", "cannonball", "cap32", "cdogs", "cgenius", + "chailove", + "crocods", "devilutionx", "dice", + "dinothawr", + "dosbox_core", "dosbox_pure", "dosbox_staging", + "dosbox_svn", + "dosbox_svn_ce", "duckstation", "duckstation-legacy", "easyrpg", + "ecwolf", "emuscv", "ep128emu-core", "fake08", "fallout1-ce", "fallout2-ce", + "fbalpha2012", + "fbalpha2012_cps1", + "fbalpha2012_cps2", + "fbalpha2012_neogeo", "fbneo", "fceumm", "flycastvl", @@ -4278,15 +4566,21 @@ "freeintv", "fuse", "gambatte", + "gearboy", "gearcoleco", "gearsystem", + "genesis_plus_gx", "genesisplusgx", "genesisplusgx-expanded", + "gme", + "gpsp", "gsplus", "gw", "handy", "hatari", "hcl", + "higan_sfc", + "higan_sfc_balanced", "holani", "hurrican", "hypseus-singe", @@ -4296,31 +4590,46 @@ "lutro", "mame", "mame078plus", + "mame2000", + "mame2003", + "mame2003_plus", + "mednafen_gba", "mednafen_lynx", "mednafen_ngp", + "mednafen_pce", + "mednafen_pce_fast", + "mednafen_psx", + "mednafen_snes", "mednafen_supergrafx", + "mednafen_vb", "mednafen_wswan", + "mesen", "mgba", "minivmac", "moonlight", "mrboom", + "mu", "mupen64plus", "mupen64plus-next", + "nekop2", "neocd", "nestopia", "np2kai", "nxengine", "o2em", + "oberon", "openbor", "openbor4432", "openbor6412", "openbor7142", "openbor7530", "openjazz", + "opera", "pce_fast", "pcsx_rearmed", "pd777", "picodrive", + "pocketcdg", "pocketsnes", "pokemini", "potator", @@ -4332,20 +4641,32 @@ "px68k", "pygame", "quasi88", + "quicknes", + "reminiscence", + "remotejoy", "samcoupe", + "sameboy", "sameduck", "scummvm", "sdlpop", "smsplus", "snes9x", + "snes9x2002", + "snes9x2005", + "snes9x2005_plus", + "snes9x2010", "snes9x_next", "solarus", + "squirreljme", "stella", + "stella2014", "superbroswar", "swanstation", "taradino", + "test", "tgbdual", "theodore", + "thepowdertoy", "thextech", "tic80", "tyrian", @@ -4354,15 +4675,21 @@ "uzem", "vb", "vba-m", + "vba_next", + "vbam", "vecx", + "vemulator", "vice", "vice_x128", "vice_x64", "vice_x64sc", + "vice_xcbm2", + "vice_xcbm5x0", "vice_xpet", "vice_xplus4", "vice_xscpu64", "vice_xvic", + "virtualjaguar", "x1", "x128", "x16emu", @@ -5209,6 +5536,7 @@ "zc210" ], "s812": [ + "2048", "81", "a5200", "abuse", @@ -5218,25 +5546,40 @@ "bennugd", "bk", "bluemsx", + "bsnes_cplusplus98", + "bsnes_mercury_accuracy", + "bsnes_mercury_balanced", + "bsnes_mercury_performance", "bstone", "cannonball", "cap32", "cdogs", "cgenius", + "chailove", + "crocods", "devilutionx", "dice", + "dinothawr", "dosbox-x", + "dosbox_core", "dosbox_pure", "dosbox_staging", + "dosbox_svn", + "dosbox_svn_ce", "dosboxx", "duckstation", "duckstation-legacy", "easyrpg", + "ecwolf", "emuscv", "ep128emu-core", "fake08", "fallout1-ce", "fallout2-ce", + "fbalpha2012", + "fbalpha2012_cps1", + "fbalpha2012_cps2", + "fbalpha2012_neogeo", "fbneo", "fceumm", "flycast", @@ -5245,15 +5588,21 @@ "freeintv", "fuse", "gambatte", + "gearboy", "gearcoleco", "gearsystem", + "genesis_plus_gx", "genesisplusgx", "genesisplusgx-expanded", + "gme", + "gpsp", "gsplus", "gw", "handy", "hatari", "hcl", + "higan_sfc", + "higan_sfc_balanced", "holani", "hurrican", "hypseus-singe", @@ -5263,22 +5612,34 @@ "lutro", "mame", "mame078plus", + "mame2000", + "mame2003", + "mame2003_plus", + "mednafen_gba", "mednafen_lynx", "mednafen_ngp", + "mednafen_pce", + "mednafen_pce_fast", "mednafen_psx", + "mednafen_snes", "mednafen_supergrafx", + "mednafen_vb", "mednafen_wswan", + "mesen", "mgba", "minivmac", "moonlight", "mrboom", + "mu", "mupen64plus", "mupen64plus-next", + "nekop2", "neocd", "nestopia", "np2kai", "nxengine", "o2em", + "oberon", "openbor", "openbor4432", "openbor6412", @@ -5291,6 +5652,7 @@ "pcsx_rearmed", "pd777", "picodrive", + "pocketcdg", "pokemini", "potator", "ppsspp", @@ -5301,20 +5663,32 @@ "px68k", "pygame", "quasi88", + "quicknes", + "reminiscence", + "remotejoy", "samcoupe", + "sameboy", "sameduck", "scummvm", "sdlpop", "smsplus", "snes9x", + "snes9x2002", + "snes9x2005", + "snes9x2005_plus", + "snes9x2010", "snes9x_next", "solarus", + "squirreljme", "stella", + "stella2014", "superbroswar", "swanstation", "taradino", + "test", "tgbdual", "theodore", + "thepowdertoy", "thextech", "tic80", "tyrian", @@ -5323,15 +5697,21 @@ "uzem", "vb", "vba-m", + "vba_next", + "vbam", "vecx", + "vemulator", "vice", "vice_x128", "vice_x64", "vice_x64sc", + "vice_xcbm2", + "vice_xcbm5x0", "vice_xpet", "vice_xplus4", "vice_xscpu64", "vice_xvic", + "virtualjaguar", "x1", "x128", "x16emu", @@ -6663,13 +7043,22 @@ ] }, "x86_64": [ + "2048", + "3dengine", "81", + "DoubleCherryGB", "a5200", "abuse", + "amiarcadia", "amiberry", + "anarch", + "applewin", + "ardens", "arduous", "atari800", "azahar", + "b2", + "bbkemu", "beetle-saturn", "bennugd", "bigpemu", @@ -6677,28 +7066,53 @@ "blastem", "bluemsx", "boom3", + "boom3_xp", "bsnes", + "bsnes-jg", + "bsnes2014_accuracy", + "bsnes2014_balanced", + "bsnes2014_performance", + "bsnes_cplusplus98", "bsnes_hd", + "bsnes_hd_beta", + "bsnes_mercury_accuracy", + "bsnes_mercury_balanced", + "bsnes_mercury_performance", "bstone", "cannonball", "cap32", "catacombgl", + "cdi2015", "cdogs", "cemu", "cgenius", + "chailove", + "citra", + "citra2018", "clk", + "clownmdemu", + "coolbaremu", "corsixth", + "craft", + "crocods", "demul", "desmume", + "desmume2015", "devilutionx", "dhewm3", "dice", + "dingooemu", + "dinothawr", + "dirksimple", "dolphin", "dosbox", "dosbox-x", + "dosbox_core", "dosbox_pure", "dosbox_staging", + "dosbox_svn", "dosboxx", + "doukutsu_rs", "duckstation", "dxx-rebirth", "easyrpg", @@ -6706,66 +7120,132 @@ "eduke32", "emuscv", "ep128emu-core", + "ep128emu_core", "etlegacy", "fake08", "fallout1-ce", "fallout2-ce", + "fbalpha", + "fbalpha2012", + "fbalpha2012_cps1", + "fbalpha2012_cps2", + "fbalpha2012_cps3", + "fbalpha2012_neogeo", "fbneo", "fceumm", + "fixgb", + "fixnes", "flycast", "fmsx", "freechaf", "freeintv", + "frodo", "fsuae", "fury", "fuse", + "galaksija", + "gam4980", "gambatte", + "gametank", + "gearboy", "gearcoleco", + "geargrafx", + "gearlynx", "gearsystem", + "geartowns", + "genesis_plus_gx", + "genesis_plus_gx_wide", "genesisplusgx", "genesisplusgx-expanded", "genesisplusgx-wide", + "geolith", + "gme", + "gong", + "gpsp", "gsplus", "gw", "gzdoom", "handy", "hatari", + "hatari2014", "hatarib", + "hbmame", "hcl", "holani", "hurrican", + "hyperscanemu", "hypseus-singe", + "hypseushw", "ikemen", "iortcw", + "irogb", + "jaxe", "jazz2-native", + "jollycv", + "jumpnbump", "kronos", "lightspark", "lindbergh-loader", + "linuxloader", "lowresnx", "lutro", + "m2000", "mame", "mame078plus", + "mame2000", + "mame2003", + "mame2003_midway", + "mame2003_plus", + "mame2010", + "mame2015", + "mame2016", + "mcsoftserve", + "mednafen_gba", "mednafen_lynx", "mednafen_ngp", + "mednafen_pce", + "mednafen_pce_fast", + "mednafen_pcfx", "mednafen_psx", + "mednafen_psx_hw", + "mednafen_saturn", + "mednafen_snes", + "mednafen_supafaust", "mednafen_supergrafx", + "mednafen_vb", "mednafen_wswan", "melonds", "melondsds", "mesen", "mesen-s", + "mesen2", + "meteor", "mgba", "minivmac", "model2emu", + "mojozork", "moonlight", "mrboom", + "mu", "mupen64plus", "mupen64plus-next", + "mupen64plus_next", + "n64emu", + "native32emu", + "nekop2", "neocd", "nestopia", + "nicaiemu", + "noods", "np2kai", + "nside_sfc_balanced", + "nuance", + "numero", "nxengine", "o2em", + "oberon", + "odin", + "omicron", "openbor", "openbor4432", "openbor6412", @@ -6774,19 +7254,29 @@ "openjazz", "openjk", "openjkdf2", + "openlara", "openmohaa", "openmsx", "opera", + "panda3ds", + "parallel_dc", "parallel_n64", "pce", "pce_fast", + "pcee2", + "pcem", "pcfx", "pcsx2", "pcsx_rearmed", "pd777", "picodrive", + "piece", "play", + "playdiaemu", + "pocketcdg", "pokemini", + "pokket", + "pokketstation", "potator", "ppsspp", "prboom", @@ -6796,62 +7286,99 @@ "px68k", "pygame", "pyxel", + "qemu", "quasi88", + "quicknes", + "race", + "radio", "raze", "redream", "reminiscence", + "remotejoy", + "retro8", + "romcleaner", "rpcs3", "ruffle", + "rust_dos", + "rustynes", "samcoupe", "same_cdi", + "sameboy", "sameduck", "scummvm", "sdlpop", "shadps4", + "skyemu", "smsplus", "snes9x", + "snes9x2002", + "snes9x2005", + "snes9x2005_plus", + "snes9x2010", "snes9x_next", "solarus", "sonic-mania", "sonic2013", "sonic3-air", "soniccd", + "spmp8000emu", + "squirreljme", "stella", + "stella2014", + "stella2023", "superbroswar", "supermodel", "swanstation", + "tamalibretro", "taradino", "tgbdual", "theforceengine", "theodore", + "thepowdertoy", "thextech", + "tia", "tic80", "tr1x", "tr2x", "tsugaru", "tyrian", "tyrquake", + "uw8", + "uzdoom", "uzem", + "vaporspec", "vb", "vba-m", + "vba_next", + "vbam", "vecx", + "vemulator", "vice", "vice_x128", "vice_x64", "vice_x64sc", + "vice_xcbm2", + "vice_xcbm5x0", "vice_xpet", "vice_xplus4", "vice_xscpu64", "vice_xvic", + "video_processor", "vircon32", "virtualjaguar", + "virtualxt", "vita3k", "vitaquake2", + "vitaquake2-rogue", + "vitaquake2-xatrix", + "vitaquake2-zaero", + "vitaquake3", "vkquake", "vkquake2", "vkquake3", "vpinball", "wasm4", + "wqxemu", "x1", "x128", "x16emu", @@ -6861,6 +7388,7 @@ "xemu", "xenia", "xenia-canary", + "xenia_edge", "xpet", "xplus4", "xrick", @@ -6868,19 +7396,29 @@ "xscpu64", "xvic", "yabasanshiro", + "yabause", "ymir", "yquake2", "zc210" ], "zen3": { "cores": [ + "2048", + "3dengine", "81", + "DoubleCherryGB", "a5200", "abuse", + "amiarcadia", "amiberry", + "anarch", + "applewin", + "ardens", "arduous", "atari800", "azahar", + "b2", + "bbkemu", "beetle-saturn", "bennugd", "bigpemu", @@ -6888,28 +7426,53 @@ "blastem", "bluemsx", "boom3", + "boom3_xp", "bsnes", + "bsnes-jg", + "bsnes2014_accuracy", + "bsnes2014_balanced", + "bsnes2014_performance", + "bsnes_cplusplus98", "bsnes_hd", + "bsnes_hd_beta", + "bsnes_mercury_accuracy", + "bsnes_mercury_balanced", + "bsnes_mercury_performance", "bstone", "cannonball", "cap32", "catacombgl", + "cdi2015", "cdogs", "cemu", "cgenius", + "chailove", + "citra", + "citra2018", "clk", + "clownmdemu", + "coolbaremu", "corsixth", + "craft", + "crocods", "demul", "desmume", + "desmume2015", "devilutionx", "dhewm3", "dice", + "dingooemu", + "dinothawr", + "dirksimple", "dolphin", "dosbox", "dosbox-x", + "dosbox_core", "dosbox_pure", "dosbox_staging", + "dosbox_svn", "dosboxx", + "doukutsu_rs", "duckstation", "dxx-rebirth", "easyrpg", @@ -6917,66 +7480,132 @@ "eduke32", "emuscv", "ep128emu-core", + "ep128emu_core", "etlegacy", "fake08", "fallout1-ce", "fallout2-ce", + "fbalpha", + "fbalpha2012", + "fbalpha2012_cps1", + "fbalpha2012_cps2", + "fbalpha2012_cps3", + "fbalpha2012_neogeo", "fbneo", "fceumm", + "fixgb", + "fixnes", "flycast", "fmsx", "freechaf", "freeintv", + "frodo", "fsuae", "fury", "fuse", + "galaksija", + "gam4980", "gambatte", + "gametank", + "gearboy", "gearcoleco", + "geargrafx", + "gearlynx", "gearsystem", + "geartowns", + "genesis_plus_gx", + "genesis_plus_gx_wide", "genesisplusgx", "genesisplusgx-expanded", "genesisplusgx-wide", + "geolith", + "gme", + "gong", + "gpsp", "gsplus", "gw", "gzdoom", "handy", "hatari", + "hatari2014", "hatarib", + "hbmame", "hcl", "holani", "hurrican", + "hyperscanemu", "hypseus-singe", + "hypseushw", "ikemen", "iortcw", + "irogb", + "jaxe", "jazz2-native", + "jollycv", + "jumpnbump", "kronos", "lightspark", "lindbergh-loader", + "linuxloader", "lowresnx", "lutro", + "m2000", "mame", "mame078plus", + "mame2000", + "mame2003", + "mame2003_midway", + "mame2003_plus", + "mame2010", + "mame2015", + "mame2016", + "mcsoftserve", + "mednafen_gba", "mednafen_lynx", "mednafen_ngp", + "mednafen_pce", + "mednafen_pce_fast", + "mednafen_pcfx", "mednafen_psx", + "mednafen_psx_hw", + "mednafen_saturn", + "mednafen_snes", + "mednafen_supafaust", "mednafen_supergrafx", + "mednafen_vb", "mednafen_wswan", "melonds", "melondsds", "mesen", "mesen-s", + "mesen2", + "meteor", "mgba", "minivmac", "model2emu", + "mojozork", "moonlight", "mrboom", + "mu", "mupen64plus", "mupen64plus-next", + "mupen64plus_next", + "n64emu", + "native32emu", + "nekop2", "neocd", "nestopia", + "nicaiemu", + "noods", "np2kai", + "nside_sfc_balanced", + "nuance", + "numero", "nxengine", "o2em", + "oberon", + "odin", + "omicron", "openbor", "openbor4432", "openbor6412", @@ -6985,19 +7614,29 @@ "openjazz", "openjk", "openjkdf2", + "openlara", "openmohaa", "openmsx", "opera", + "panda3ds", + "parallel_dc", "parallel_n64", "pce", "pce_fast", + "pcee2", + "pcem", "pcfx", "pcsx2", "pcsx_rearmed", "pd777", "picodrive", + "piece", "play", + "playdiaemu", + "pocketcdg", "pokemini", + "pokket", + "pokketstation", "potator", "ppsspp", "prboom", @@ -7007,62 +7646,99 @@ "px68k", "pygame", "pyxel", + "qemu", "quasi88", + "quicknes", + "race", + "radio", "raze", "redream", "reminiscence", + "remotejoy", + "retro8", + "romcleaner", "rpcs3", "ruffle", + "rust_dos", + "rustynes", "samcoupe", "same_cdi", + "sameboy", "sameduck", "scummvm", "sdlpop", "shadps4", + "skyemu", "smsplus", "snes9x", + "snes9x2002", + "snes9x2005", + "snes9x2005_plus", + "snes9x2010", "snes9x_next", "solarus", "sonic-mania", "sonic2013", "sonic3-air", "soniccd", + "spmp8000emu", + "squirreljme", "stella", + "stella2014", + "stella2023", "superbroswar", "supermodel", "swanstation", + "tamalibretro", "taradino", "tgbdual", "theforceengine", "theodore", + "thepowdertoy", "thextech", + "tia", "tic80", "tr1x", "tr2x", "tsugaru", "tyrian", "tyrquake", + "uw8", + "uzdoom", "uzem", + "vaporspec", "vb", "vba-m", + "vba_next", + "vbam", "vecx", + "vemulator", "vice", "vice_x128", "vice_x64", "vice_x64sc", + "vice_xcbm2", + "vice_xcbm5x0", "vice_xpet", "vice_xplus4", "vice_xscpu64", "vice_xvic", + "video_processor", "vircon32", "virtualjaguar", + "virtualxt", "vita3k", "vitaquake2", + "vitaquake2-rogue", + "vitaquake2-xatrix", + "vitaquake2-zaero", + "vitaquake3", "vkquake", "vkquake2", "vkquake3", "vpinball", "wasm4", + "wqxemu", "x1", "x128", "x16emu", @@ -7072,6 +7748,7 @@ "xemu", "xenia", "xenia-canary", + "xenia_edge", "xpet", "xplus4", "xrick", @@ -7079,6 +7756,7 @@ "xscpu64", "xvic", "yabasanshiro", + "yabause", "ymir", "yquake2", "zc210" diff --git a/platforms/batocera.yml b/platforms/batocera.yml index 66f677c5c..bde62cb95 100644 --- a/platforms/batocera.yml +++ b/platforms/batocera.yml @@ -5798,6 +5798,157 @@ dual_mode_cores: - scummvm - stella - tic80 +installable_cores: +- "2048" +- 3dengine +- DoubleCherryGB +- amiarcadia +- anarch +- applewin +- ardens +- b2 +- bbkemu +- boom3_xp +- bsnes-jg +- bsnes2014_accuracy +- bsnes2014_balanced +- bsnes2014_performance +- bsnes_cplusplus98 +- bsnes_hd_beta +- bsnes_mercury_accuracy +- bsnes_mercury_balanced +- bsnes_mercury_performance +- cdi2015 +- chailove +- citra +- citra2018 +- clownmdemu +- coolbaremu +- craft +- crocods +- desmume2015 +- dingooemu +- dinothawr +- dirksimple +- dosbox_core +- dosbox_svn +- dosbox_svn_ce +- doukutsu_rs +- ep128emu_core +- fbalpha2012 +- fbalpha2012_cps1 +- fbalpha2012_cps2 +- fbalpha2012_cps3 +- fbalpha2012_neogeo +- fixgb +- fixnes +- frodo +- galaksija +- gam4980 +- gametank +- gearboy +- geargrafx +- gearlynx +- geartowns +- genesis_plus_gx +- genesis_plus_gx_wide +- geolith +- gme +- gong +- hatari2014 +- hbmame +- higan_sfc +- higan_sfc_balanced +- hyperscanemu +- hypseushw +- irogb +- jaxe +- jollycv +- jumpnbump +- linuxloader +- m2000 +- mame2000 +- mame2003 +- mame2003_midway +- mame2003_plus +- mame2010 +- mame2015 +- mame2016 +- mcsoftserve +- mednafen_gba +- mednafen_pce +- mednafen_pce_fast +- mednafen_pcfx +- mednafen_psx_hw +- mednafen_saturn +- mednafen_snes +- mednafen_supafaust +- mednafen_vb +- mesen2 +- meteor +- mojozork +- mu +- mupen64plus_next +- n64emu +- native32emu +- nekop2 +- nicaiemu +- noods +- nside_sfc_balanced +- nuance +- numero +- oberon +- odin +- omicron +- openlara +- panda3ds +- parallel_dc +- pcee2 +- pcem +- piece +- playdiaemu +- pocketcdg +- pokket +- pokketstation +- qemu +- quicknes +- race +- radio +- remotejoy +- retro8 +- romcleaner +- rust_dos +- rustynes +- sameboy +- skyemu +- snes9x2002 +- snes9x2005 +- snes9x2005_plus +- snes9x2010 +- spmp8000emu +- squirreljme +- stella2023 +- tamalibretro +- test +- thepowdertoy +- tia +- uw8 +- uzdoom +- vaporspec +- vba_next +- vbam +- vemulator +- vice_xcbm2 +- vice_xcbm5x0 +- video_processor +- virtualxt +- vitaquake2-rogue +- vitaquake2-xatrix +- vitaquake2-zaero +- vitaquake3 +- wqxemu +- xenia_edge +- yabause libretro_systems: "81": - zx81 diff --git a/platforms/targets/batocera.yml b/platforms/targets/batocera.yml index a4fbf1d0e..11b4197bd 100644 --- a/platforms/targets/batocera.yml +++ b/platforms/targets/batocera.yml @@ -1,6 +1,6 @@ platform: batocera source: https://github.com/batocera-linux/batocera.linux/tree/batocera-43.1 -scraped_at: '2026-11-09T19:49:31Z' +scraped_at: '2026-11-09T20:38:15Z' targets: a3gen2: architecture: aarch64 @@ -790,6 +790,7 @@ targets: - xvic - yquake2 - zc210 + downloads: linux-armhf bcm2837: architecture: aarch64 cores: @@ -1075,6 +1076,7 @@ targets: - orangepi-one - orangepi-pc - orangepi-plus2e + downloads: linux-armhf h5: architecture: aarch64 cores: @@ -2181,6 +2183,7 @@ targets: - xvic - yquake2 - zc210 + downloads: linux-armhf qcs6490: architecture: aarch64 cores: @@ -2516,6 +2519,7 @@ targets: - powkiddy_a13 - ps5000 - ps7000 + downloads: linux-armhf rk3288: architecture: arm cores: @@ -2674,6 +2678,7 @@ targets: images: - miqi - tinkerboard + downloads: linux-armhf rk3326: architecture: aarch64 cores: @@ -3887,6 +3892,7 @@ targets: - xvic - yquake2 - zc210 + downloads: linux-armhf s905: architecture: aarch64 cores: @@ -5501,6 +5507,7 @@ targets: - ymir - yquake2 - zc210 + downloads: linux-x86_64 zen3: architecture: x86_64 cores: @@ -5732,3 +5739,4 @@ targets: - zc210 images: - x86-64-v3 + downloads: linux-x86_64 diff --git a/schemas/platform.schema.json b/schemas/platform.schema.json index abe4f8917..c0a972192 100644 --- a/schemas/platform.schema.json +++ b/schemas/platform.schema.json @@ -45,6 +45,14 @@ }, "uniqueItems": true }, + "installable_cores": { + "description": "Buildbot libretro cores the platform's RetroArch core downloader installs on at least one board, from the RetroArch targets entry of that board's architecture.", + "type": "array", + "items": { + "type": "string" + }, + "uniqueItems": true + }, "dual_mode_cores": { "type": "array", "items": { diff --git a/scripts/common.py b/scripts/common.py index e96c5ef63..f1404569c 100644 --- a/scripts/common.py +++ b/scripts/common.py @@ -869,11 +869,24 @@ def load_target_config( ) ) - declared = (targets[canonical] or {}).get("cores") + return _target_core_set(targets[canonical] or {}, overrides, canonical, platforms_dir) + + +def _target_core_set( + entry: dict, overrides: dict, target: str, platforms_dir: str +) -> set[str] | None: + """A target's cores: what the board builds, overrides applied, and the + buildbot cores its RetroArch core downloader installs (`downloads`, a + RetroArch targets entry), which a Batocera x86_64 or armv7 board can + fetch from its menu.""" + declared = entry.get("cores") if declared is None: return None - cores = set(str(c) for c in declared) - return apply_target_overrides(cores, overrides, canonical) + cores = apply_target_overrides({str(c) for c in declared}, overrides, target) + downloads = entry.get("downloads") + if downloads: + cores |= load_target_config("retroarch", str(downloads), platforms_dir) or set() + return cores def apply_target_overrides( @@ -917,12 +930,9 @@ def list_available_targets( result = [] for tname, tdata in sorted(data.get("targets", {}).items()): aliases = (overrides.get(tname) or {}).get("aliases", []) - declared = (tdata or {}).get("cores") # None: the target runs every core the platform declares. - count = ( - None if declared is None - else len(apply_target_overrides({str(c) for c in declared}, overrides, tname)) - ) + found = _target_core_set(tdata or {}, overrides, tname, platforms_dir) + count = None if found is None else len(found) result.append( { "name": tname, @@ -2533,6 +2543,7 @@ def group_identical_platforms( cores if isinstance(cores, str) else sorted(map(str, cores or [])), sorted(map(str, config.get("standalone_cores") or [])), sorted(map(str, config.get("dual_mode_cores") or [])), + sorted(map(str, config.get("installable_cores") or [])), # One archive cannot serve a filesystem that folds case and one # that does not: Lakka keeps bios.col beside BIOS.col. bool(config.get("case_insensitive_fs")), @@ -3202,9 +3213,11 @@ def resolve_platform_cores( # An emulator the platform launches standalone runs there even when # its own list names only the cores: Batocera's es_systems.yml files # lexaloffle and gsplus as standalone emulators and nowhere else. + # A core the platform's RetroArch installs from the buildbot runs + # there as well (installable_cores, Batocera x86_64 and armv7). core_set = {str(c) for c in cores_config} | { str(c) for c in config.get("standalone_cores") or [] - } + } | {str(c) for c in config.get("installable_cores") or []} index = upstream_profile_index(profiles) platform = str(config.get("_key") or "") diff --git a/scripts/scraper/batocera_scraper.py b/scripts/scraper/batocera_scraper.py index 84a22c2b3..36a014dd8 100644 --- a/scripts/scraper/batocera_scraper.py +++ b/scripts/scraper/batocera_scraper.py @@ -18,7 +18,7 @@ from collections.abc import Callable from pathlib import Path -from common import yaml_load +from common import load_target_config, yaml_load from hashing import cut_md5_owner from safeparse import yaml_load_upstream @@ -557,6 +557,21 @@ def boards_build(selection: BuildSelection, launchers: list[dict]) -> Callable[[ return built +def installable_cores(selection: BuildSelection, cores: list[str]) -> list[str]: + """Buildbot cores a board's RetroArch core downloader installs, beyond + the cores the image builds. + + The targets scraper names, per board, the RetroArch targets entry its + downloader reads (`downloads`): a Batocera x86_64 board installs any + core of linux-x86_64, an armv7 one any of linux-armhf. + """ + found: set[str] = set() + for entry in selection.targets.values(): + if entry.get("downloads"): + found |= load_target_config("retroarch", entry["downloads"]) or set() + return sorted(found - set(cores)) + + def records_run(launchers: list[dict], built: Callable[[str], bool]) -> list[dict]: """The launcher records of emulators the build runs. @@ -623,9 +638,11 @@ class Scraper(BaseScraper): launchers = extract_launchers( lambda path: self._fetch_text(f"{_RAW_BASE}/{self.tag}/{path}") ) - built = boards_build(BoardTargets().build_selection(self.tag), launchers) + selection = BoardTargets().build_selection(self.tag) + built = boards_build(selection, launchers) launchers = records_run(launchers, built) cores, standalone, dual = self._fetch_cores(built) + installable = installable_cores(selection, cores) libretro_systems = es_systems_libretro_systems( self._fetch_text(ES_SYSTEMS_URL.format(tag=self.tag)), _not_cores() ) @@ -645,6 +662,8 @@ class Scraper(BaseScraper): if dual: # Launched both ways: each layout gets its files. result["dual_mode_cores"] = dual + if installable: + result["installable_cores"] = installable # The systems each libretro core runs for: its save folders. result["libretro_systems"] = libretro_systems result["launchers"] = launchers diff --git a/scripts/scraper/targets/batocera_targets_scraper.py b/scripts/scraper/targets/batocera_targets_scraper.py index ce272de03..83b8e31c1 100644 --- a/scripts/scraper/targets/batocera_targets_scraper.py +++ b/scripts/scraper/targets/batocera_targets_scraper.py @@ -219,6 +219,8 @@ def _parse_meta_flags(text: str) -> list[tuple[str, str]]: # ("select BR2_PACKAGE_XROAR if BR2_PACKAGE_HAS_LIBGL") reached no board. BUILDROOT_KCONFIG = ( "arch/Config.in", + # Each armv7-A CPU selects BR2_ARM_CPU_ARMV7A (cortex_a15_a7 included). + "arch/Config.in.arm", "package/mesa3d/Config.in", "package/libglvnd/Config.in", ) @@ -330,6 +332,51 @@ def _parse_es_systems(text: str) -> dict[str, list[str]]: return package_to_emulators +# RetroArch's core downloader fetches from DEFAULT_BUILDBOT_SERVER_URL, which +# Linux sets for x86_64, i386 and armv7 hard-float alone, and leaves empty on +# aarch64 (config.def.h:1855-1864 at Batocera's 4b74434 and at v1.22.2). +def buildbot_target(symbols: set[str] | frozenset[str]) -> str | None: + """The RetroArch targets entry a board's core downloader installs from. + + `symbols` are the board's active symbols. An armv7-A CPU selects + BR2_ARM_CPU_ARMV7A (arch/Config.in.arm) and is hard-float unless the + board asks for soft-float (BR2_ARM_EABI): Buildroot picks EABIHF for a + CPU with a VFP, which every Cortex-A has. armv6 (bcm2835), aarch64 and + riscv64 get no buildbot URL. + """ + if "BR2_x86_64" in symbols: + return "linux-x86_64" + if "BR2_i386" in symbols: + return "linux-x86" + if "BR2_ARM_CPU_ARMV7A" in symbols and "BR2_ARM_EABI" not in symbols: + return "linux-armhf" + return None + + +# configure options and make variables that compile the downloader out. +_NO_DOWNLOADER = ("--disable-update-cores", "--disable-online-updater", "--disable-networking", + "HAVE_UPDATE_CORES=0", "HAVE_ONLINE_UPDATER=0", "HAVE_LAKKA=1") +_BLANK_CORES_URL = re.compile(r'^\s*core_updater_buildbot_cores_url\s*=\s*""', re.MULTILINE) + + +def downloader_disabled(retroarch_mk: str, shipped_cfgs: dict[str, str]) -> str | None: + """Why RetroArch's core downloader installs nothing, or None. + + Compiled out by its build options, or left without a URL by a shipped + retroarch.cfg. A config that hides the menu entry alone does not stop + it: without HAVE_LAKKA, Settings > User Interface offers it back + (menu_setting.c). A config that blanks the URL for some boards only is + a case this reader does not split, and an error. + """ + for option in _NO_DOWNLOADER: + if option in retroarch_mk: + return f"retroarch.mk sets {option}" + blank = sorted(path for path, text in shipped_cfgs.items() if _BLANK_CORES_URL.search(text)) + if blank: + raise RuntimeError(f"{', '.join(blank)} blank the core downloader URL: split per board") + return None + + # Buildroot's architecture choice, as a board sets it (arch/Config.in). _ARCH_SYMBOLS = { "BR2_aarch64": "aarch64", @@ -361,7 +408,8 @@ def _arch_from_flag(flag: str, board_flags: set[str] | frozenset[str] = frozense class BuildSelection: """What the boards of a build turn on, read once from its Kconfig. - `targets` is each board's entry of the targets file. `emulators` holds + `targets` is each board's entry of the targets file, `downloads` naming + the RetroArch targets entry its core downloader installs from. `emulators` holds every es_systems name some board builds and `listed` every name es_systems ties to a package, `symbols` every symbol some board ends up with, and `packages` the config symbol each package folder of the tree @@ -412,6 +460,7 @@ class Scraper(BaseTargetScraper): # Path prefix -> repository of a submodule the root Config.in sources # from: KNULLI reads Batocera's packages as batocera/package/... SUBMODULES: dict[str, str] = {} + RETROARCH_MK = "package/batocera/emulators/retroarch/retroarch/retroarch.mk" def __init__(self, url: str = "") -> None: super().__init__(url=url or f"https://github.com/{self.REPO}") @@ -505,6 +554,23 @@ class Scraper(BaseTargetScraper): print(f" sourced but absent at {self.tag}: {path}", file=sys.stderr) return files + def _shipped_retroarch_cfgs(self) -> dict[str, str]: + """Every retroarch.cfg the tree ships, its submodules included.""" + repos = [("", self.REPO, self.tag)] + for prefix, repo in self.SUBMODULES.items(): + pin = _fetch_json(self._api(prefix.rstrip("/"))) + repos.append((prefix, repo, pin["sha"])) + found: dict[str, str] = {} + for prefix, repo, ref in repos: + listing = _fetch_json(f"https://api.github.com/repos/{repo}/git/trees/{ref}?recursive=1") + if not isinstance(listing, dict) or listing.get("truncated"): + raise RuntimeError(f"{repo} tree at {ref} is unreadable or truncated") + for entry in listing.get("tree", []): + if re.search(r"(^|/)retroarch\.cfg$", entry["path"]): + raw = f"https://raw.githubusercontent.com/{repo}/{ref}/{entry['path']}" + found[prefix + entry["path"]] = _fetch(raw) + return found + def _buildroot_rules(self) -> list[tuple[str, str, str]]: """Buildroot's selects for the symbols the tree tests, at its pin.""" pin = _fetch_json(self._api("buildroot")) @@ -560,6 +626,10 @@ class Scraper(BaseTargetScraper): images_by_flag = _parse_images(config_in_text) if not images_by_flag: raise RuntimeError(f"no target image list parsed from {self.CONFIG_IN_PATH}") + disabled = downloader_disabled(_fetch(self._raw(self.RETROARCH_MK)), + self._shipped_retroarch_cfgs()) + if disabled: + print(f" core downloader off: {disabled}", file=sys.stderr) targets: dict[str, dict] = {} symbols: set[str] = set() @@ -593,6 +663,9 @@ class Scraper(BaseTargetScraper): images = sorted(set(images_by_flag.get(primary_flag, []))) if images and images != [target_key]: targets[target_key]["images"] = images + downloads = None if disabled else buildbot_target(selected_packages) + if downloads: + targets[target_key]["downloads"] = downloads print( f" {len(emulators)} emulators ({len(selected_packages)} packages selected)", file=sys.stderr, diff --git a/scripts/scraper/targets/knulli_targets_scraper.py b/scripts/scraper/targets/knulli_targets_scraper.py index 16de256e7..856de7e87 100644 --- a/scripts/scraper/targets/knulli_targets_scraper.py +++ b/scripts/scraper/targets/knulli_targets_scraper.py @@ -35,6 +35,7 @@ class Scraper(BatoceraScraper): SYSTEM_SYMBOL = "BR2_PACKAGE_KNULLI_SYSTEM" BUILDROOT_REPO = "knulli-cfw/buildroot" SUBMODULES = {"batocera/": "batocera-linux/batocera.linux"} + RETROARCH_MK = "package/retroarch/retroarch/retroarch.mk" def main() -> None: diff --git a/tests/test_target_scrapers.py b/tests/test_target_scrapers.py index 9d231ade6..23571eb7c 100644 --- a/tests/test_target_scrapers.py +++ b/tests/test_target_scrapers.py @@ -97,6 +97,10 @@ class FailedRequestsStopTheScrape(unittest.TestCase): _refuse() return _Body(b"BR2_PACKAGE_BATOCERA_TARGET_X86_64=y\n" b"BR2_PACKAGE_BATOCERA_SYSTEM=y\n") + if "/git/trees/" in url: + return _Body(b'{"tree": [], "truncated": false}') + if url.endswith("retroarch.mk"): + return _Body(b"RETROARCH_CONF_OPTS = --enable-networking\n") if url.startswith("https://raw.githubusercontent.com/") and url.count("/") == 6: return _Body(b'source "$BR2_EXTERNAL_BATOCERA_PATH/' b'package/batocera/core/batocera-system/Config.in"\n') @@ -243,6 +247,60 @@ class BatoceraDerivedFlags(unittest.TestCase): self.assertIn("BR2_PACKAGE_DUCKSTATION_LEGACY", r16) +class CoreDownloaderReach(unittest.TestCase): + """Batocera and KNULLI build RetroArch with its core downloader, shown in + the menu, and its compiled buildbot URL serves Linux x86_64, i386 and + armv7 hard-float alone (config.def.h:743-749, 1855-1864): a board of + those runs any core of the buildbot list, an aarch64 one none.""" + + def test_each_architecture_reads_its_buildbot_list(self): + from scraper.targets.batocera_targets_scraper import buildbot_target # noqa: PLC0415 + + self.assertEqual(buildbot_target({"BR2_x86_64"}), "linux-x86_64") + self.assertEqual(buildbot_target({"BR2_arm", "BR2_ARM_CPU_ARMV7A"}), "linux-armhf") + self.assertIsNone(buildbot_target({"BR2_arm", "BR2_ARM_CPU_ARMV7A", "BR2_ARM_EABI"})) + self.assertIsNone(buildbot_target({"BR2_arm", "BR2_arm1176jzf_s"})) + self.assertIsNone(buildbot_target({"BR2_aarch64"})) + + def test_a_build_or_a_blank_url_turns_it_off(self): + from scraper.targets.batocera_targets_scraper import downloader_disabled # noqa: PLC0415 + + self.assertIsNone(downloader_disabled("--enable-networking", { + "a/retroarch.cfg": 'menu_show_core_updater = "false"\n'})) + self.assertIn("HAVE_LAKKA=1", downloader_disabled("make HAVE_LAKKA=1", {})) + with self.assertRaises(RuntimeError): + downloader_disabled("", {"a/retroarch.cfg": 'core_updater_buildbot_cores_url = ""\n'}) + + def test_a_board_that_downloads_gets_the_buildbot_list(self): + from common import load_target_config # noqa: PLC0415 + + root = str(REPO_ROOT / "platforms") + x86 = set(load_target_config("retroarch", "linux-x86_64", root) or ()) + armhf = set(load_target_config("retroarch", "linux-armhf", root) or ()) + self.assertLessEqual(x86, set(load_target_config("batocera", "x86_64", root) or ())) + self.assertLessEqual(armhf, set(load_target_config("batocera", "rk3288", root) or ())) + self.assertFalse(x86 <= set(load_target_config("batocera", "bcm2711", root) or ())) + + def test_the_platform_names_what_its_boards_install(self): + import yaml # noqa: PLC0415 + + from common import load_target_config # noqa: PLC0415 + + root = str(REPO_ROOT / "platforms") + for platform in ("batocera",): + config = yaml.safe_load((REPO_ROOT / "platforms" / f"{platform}.yml").read_text()) + targets = yaml.safe_load( + (REPO_ROOT / "platforms" / "targets" / f"{platform}.yml").read_text())["targets"] + fetched = set() + for entry in targets.values(): + if entry.get("downloads"): + fetched |= load_target_config("retroarch", entry["downloads"], root) or set() + if entry["architecture"] == "aarch64": + self.assertNotIn("downloads", entry) + with self.subTest(platform=platform): + self.assertEqual(set(config["installable_cores"]), fetched - set(config["cores"])) + + class KnulliPerBoard(unittest.TestCase): """KNULLI builds per board on Batocera's model from its own tree: configs/knulli-*.board, package/system/knulli-system/Config.in and @@ -257,7 +315,7 @@ class KnulliPerBoard(unittest.TestCase): "BR2_PACKAGE_BATOCERA_TARGET_H700=y\n" ), "configs/knulli-r16.board": ( - "include knulli-board.common\n\nBR2_arm=y\n" + "include knulli-board.common\n\nBR2_arm=y\nBR2_cortex_a7=y\n" "BR2_PACKAGE_BATOCERA_TARGET_SUNXI_R16=y\n" ), "configs/knulli-h700_armhf_libs.board": ( @@ -330,11 +388,21 @@ class KnulliPerBoard(unittest.TestCase): return _Body('{"sha": "c2d7985b"}') if url == f"{api}batocera?ref={pin}": return _Body('{"sha": "487014d9"}') + if "/git/trees/" in url: + # A board config that hides the downloader leaves its URL. + cfg = "board/r16/retroarch.cfg" if "knulli-linux" in url else "x" + return _Body(json.dumps({"tree": [{"path": cfg}], "truncated": False})) + if url == f"{raw}board/r16/retroarch.cfg": + return _Body('menu_show_core_updater = "false"\n') + if url == f"{raw}package/retroarch/retroarch/retroarch.mk": + return _Body("RETROARCH_CONF_OPTS = --enable-networking\n") if url == ("https://raw.githubusercontent.com/batocera-linux/batocera.linux/" "487014d9/package/batocera/emulators/ppsspp/Config.in"): return _Body("config BR2_PACKAGE_PPSSPP\n\tselect BR2_PACKAGE_PPSSPP_ASSETS\n") if url == f"{raw}package/utils/vtree/Config.in": raise urllib.error.HTTPError(url, 404, "Not Found", {}, None) + if url.endswith("/c2d7985b/arch/Config.in.arm"): + return _Body("config BR2_cortex_a7\n\tselect BR2_ARM_CPU_ARMV7A\n") if url.startswith("https://raw.githubusercontent.com/knulli-cfw/buildroot/c2d7985b/"): return _Body("config BR2_aarch64\n\tselect BR2_ARCH_IS_64\n") path = url.removeprefix(raw) @@ -353,10 +421,12 @@ class KnulliPerBoard(unittest.TestCase): self.assertEqual(data["targets"]["h700"], {"architecture": "aarch64", "cores": ["assets", "mgba", "ppsspp"], "images": ["rg35xx-plus", "rg35xx-sp"]}) + # An armv7 hard-float board's RetroArch installs the armhf buildbot list. self.assertEqual(data["targets"]["r16"], - {"architecture": "arm", "cores": ["mgba"], "images": ["miyoo-a30"]}) + {"architecture": "arm", "cores": ["mgba"], "images": ["miyoo-a30"], + "downloads": "linux-armhf"}) self.assertTrue(all(pin in url or "buildroot/c2d7985b" in url - or "batocera.linux/487014d9" in url for url in seen), seen) + or "487014d9" in url for url in seen), seen) class RocknixPerDevice(unittest.TestCase): From 20885afe04380825b7b47573542c811e1826dcc3 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Mon, 9 Nov 2026 21:53:50 +0100 Subject: [PATCH 6/6] fix: keep the cores knulli's retroarch installs KNULLI's RetroArch is Batocera's build with its core downloader on. Its armv7 hard-float boards (atm7039, r16, rk3128) fetch from buildbot linux/armhf: atm7039's shipped retroarch.cfg hides the menu entry but leaves the URL, and Settings offers the entry back. Its aarch64 boards get no buildbot URL. ecwolf, squirreljme, stella2014 and vemulator come back as installable cores, and openmsx, which no buildbot list carries, stays out. --- install/targets/knulli.json | 164 ++++++++++++++++++++++++++++++ platforms/knulli.yml | 51 ++++++++++ platforms/targets/knulli.yml | 5 +- scripts/scraper/knulli_scraper.py | 7 +- tests/test_target_scrapers.py | 3 +- 5 files changed, 227 insertions(+), 3 deletions(-) diff --git a/install/targets/knulli.json b/install/targets/knulli.json index 533167e6b..89a0b6bda 100644 --- a/install/targets/knulli.json +++ b/install/targets/knulli.json @@ -321,26 +321,43 @@ }, "atm7039": { "cores": [ + "2048", "81", "a5200", "abuse", "arduous", "atari800", "bennugd", + "bk", "bluemsx", + "bsnes_cplusplus98", + "bsnes_mercury_accuracy", + "bsnes_mercury_balanced", + "bsnes_mercury_performance", "bstone", "cannonball", "cap32", "cdogs", "cgenius", + "chailove", + "crocods", "devilutionx", + "dinothawr", + "dosbox_core", "dosbox_pure", "dosbox_staging", + "dosbox_svn", + "dosbox_svn_ce", "easyrpg", + "ecwolf", "emuscv", "fake08", "fallout1-ce", "fallout2-ce", + "fbalpha2012", + "fbalpha2012_cps1", + "fbalpha2012_cps2", + "fbalpha2012_neogeo", "fbneo", "fceumm", "flycast", @@ -349,37 +366,56 @@ "freeintv", "fuse", "gambatte", + "gearboy", "gearsystem", + "genesis_plus_gx", "genesisplusgx", "geolith", + "gme", + "gpsp", "gsplus", "gw", "handy", "hatari", "hcl", + "higan_sfc", + "higan_sfc_balanced", "holani", "hurrican", "ioquake3", "lowresnx", "lutro", + "mame", "mame0139", "mame078plus", + "mame2000", + "mame2003", + "mame2003_plus", + "mednafen_gba", "mednafen_lynx", "mednafen_ngp", + "mednafen_pce", + "mednafen_pce_fast", "mednafen_psx", + "mednafen_snes", "mednafen_supergrafx", + "mednafen_vb", "mednafen_wswan", + "mesen", "mgba", "minivmac", "moonlight", "mrboom", + "mu", "mupen64plus", "mupen64plus-next", + "nekop2", "neocd", "nestopia", "np2kai", "nxengine", "o2em", + "oberon", "openbor", "openbor4432", "openbor6412", @@ -392,6 +428,7 @@ "pcfx", "pcsx_rearmed", "picodrive", + "pocketcdg", "pokemini", "potator", "ppsspp", @@ -402,22 +439,34 @@ "px68k", "pygame", "quasi88", + "quicknes", + "reminiscence", + "remotejoy", "samcoupe", + "sameboy", "sameduck", "scummvm", "sdlpop", "smsplus", "snes9x", + "snes9x2002", + "snes9x2005", + "snes9x2005_plus", + "snes9x2010", "snes9x_next", "solarus", "sonic2013", "soniccd", + "squirreljme", "stella", + "stella2014", "superbroswar", "swanstation", "taradino", + "test", "tgbdual", "theodore", + "thepowdertoy", "tic80", "tyrian", "tyrquake", @@ -425,15 +474,21 @@ "uzem", "vb", "vba-m", + "vba_next", + "vbam", "vecx", + "vemulator", "vice", "vice_x128", "vice_x64", "vice_x64sc", + "vice_xcbm2", + "vice_xcbm5x0", "vice_xpet", "vice_xplus4", "vice_xscpu64", "vice_xvic", + "virtualjaguar", "x1", "x128", "x16emu", @@ -620,6 +675,7 @@ }, "r16": { "cores": [ + "2048", "81", "a5200", "abuse", @@ -627,20 +683,36 @@ "arduous", "atari800", "bennugd", + "bk", "bluemsx", + "bsnes_cplusplus98", + "bsnes_mercury_accuracy", + "bsnes_mercury_balanced", + "bsnes_mercury_performance", "bstone", "cannonball", "cap32", "cdogs", "cgenius", + "chailove", + "crocods", "devilutionx", + "dinothawr", + "dosbox_core", "dosbox_pure", "dosbox_staging", + "dosbox_svn", + "dosbox_svn_ce", "easyrpg", + "ecwolf", "emuscv", "fake08", "fallout1-ce", "fallout2-ce", + "fbalpha2012", + "fbalpha2012_cps1", + "fbalpha2012_cps2", + "fbalpha2012_neogeo", "fbneo", "fceumm", "flycast", @@ -649,37 +721,56 @@ "freeintv", "fuse", "gambatte", + "gearboy", "gearsystem", + "genesis_plus_gx", "genesisplusgx", "geolith", + "gme", + "gpsp", "gsplus", "gw", "handy", "hatari", "hcl", + "higan_sfc", + "higan_sfc_balanced", "holani", "hurrican", "ioquake3", "lowresnx", "lutro", + "mame", "mame0139", "mame078plus", + "mame2000", + "mame2003", + "mame2003_plus", + "mednafen_gba", "mednafen_lynx", "mednafen_ngp", + "mednafen_pce", + "mednafen_pce_fast", "mednafen_psx", + "mednafen_snes", "mednafen_supergrafx", + "mednafen_vb", "mednafen_wswan", + "mesen", "mgba", "minivmac", "moonlight", "mrboom", + "mu", "mupen64plus", "mupen64plus-next", + "nekop2", "neocd", "nestopia", "np2kai", "nxengine", "o2em", + "oberon", "openbor", "openbor4432", "openbor6412", @@ -692,6 +783,7 @@ "pcfx", "pcsx_rearmed", "picodrive", + "pocketcdg", "pokemini", "potator", "ppsspp", @@ -702,22 +794,34 @@ "px68k", "pygame", "quasi88", + "quicknes", + "reminiscence", + "remotejoy", "samcoupe", + "sameboy", "sameduck", "scummvm", "sdlpop", "smsplus", "snes9x", + "snes9x2002", + "snes9x2005", + "snes9x2005_plus", + "snes9x2010", "snes9x_next", "solarus", "sonic2013", "soniccd", + "squirreljme", "stella", + "stella2014", "superbroswar", "swanstation", "taradino", + "test", "tgbdual", "theodore", + "thepowdertoy", "tic80", "tyrian", "tyrquake", @@ -725,15 +829,21 @@ "uzem", "vb", "vba-m", + "vba_next", + "vbam", "vecx", + "vemulator", "vice", "vice_x128", "vice_x64", "vice_x64sc", + "vice_xcbm2", + "vice_xcbm5x0", "vice_xpet", "vice_xplus4", "vice_xscpu64", "vice_xvic", + "virtualjaguar", "x1", "x128", "x16emu", @@ -752,6 +862,7 @@ }, "rk3128": { "cores": [ + "2048", "81", "a5200", "abuse", @@ -759,21 +870,37 @@ "arduous", "atari800", "bennugd", + "bk", "bluemsx", + "bsnes_cplusplus98", + "bsnes_mercury_accuracy", + "bsnes_mercury_balanced", + "bsnes_mercury_performance", "bstone", "cannonball", "cap32", "cdogs", "cgenius", + "chailove", + "crocods", "devilutionx", + "dinothawr", + "dosbox_core", "dosbox_pure", "dosbox_staging", + "dosbox_svn", + "dosbox_svn_ce", "easyrpg", + "ecwolf", "emuscv", "fake08", "fallout1-ce", "fallout2-ce", "fbalpha", + "fbalpha2012", + "fbalpha2012_cps1", + "fbalpha2012_cps2", + "fbalpha2012_neogeo", "fbneo", "fceumm", "flycast", @@ -782,37 +909,55 @@ "freeintv", "fuse", "gambatte", + "gearboy", "gearsystem", + "genesis_plus_gx", "genesisplusgx", "geolith", + "gme", "gpsp", "gsplus", "gw", "handy", "hatari", "hcl", + "higan_sfc", + "higan_sfc_balanced", "holani", "hurrican", "lightspark", "lowresnx", "lutro", + "mame", "mame0139", "mame078plus", + "mame2000", + "mame2003", + "mame2003_plus", + "mednafen_gba", "mednafen_lynx", "mednafen_ngp", + "mednafen_pce", + "mednafen_pce_fast", "mednafen_psx", + "mednafen_snes", "mednafen_supergrafx", + "mednafen_vb", "mednafen_wswan", + "mesen", "mgba", "minivmac", "mrboom", + "mu", "mupen64plus", "mupen64plus-next", + "nekop2", "neocd", "nestopia", "np2kai", "nxengine", "o2em", + "oberon", "openbor", "openbor4432", "openbor6412", @@ -825,6 +970,7 @@ "pcfx", "pcsx_rearmed", "picodrive", + "pocketcdg", "pokemini", "potator", "ppsspp", @@ -835,22 +981,34 @@ "px68k", "pygame", "quasi88", + "quicknes", + "reminiscence", + "remotejoy", "samcoupe", + "sameboy", "sameduck", "scummvm", "sdlpop", "smsplus", "snes9x", + "snes9x2002", + "snes9x2005", + "snes9x2005_plus", + "snes9x2010", "snes9x_next", "solarus", "sonic2013", "soniccd", + "squirreljme", "stella", + "stella2014", "superbroswar", "swanstation", "taradino", + "test", "tgbdual", "theodore", + "thepowdertoy", "tic80", "tyrian", "tyrquake", @@ -858,15 +1016,21 @@ "uzem", "vb", "vba-m", + "vba_next", + "vbam", "vecx", + "vemulator", "vice", "vice_x128", "vice_x64", "vice_x64sc", + "vice_xcbm2", + "vice_xcbm5x0", "vice_xpet", "vice_xplus4", "vice_xscpu64", "vice_xvic", + "virtualjaguar", "x1", "x128", "x16emu", diff --git a/platforms/knulli.yml b/platforms/knulli.yml index c6923e911..dd5b3eff2 100644 --- a/platforms/knulli.yml +++ b/platforms/knulli.yml @@ -4907,6 +4907,57 @@ dual_mode_cores: - scummvm - stella - yabasanshiro +installable_cores: +- "2048" +- bk +- bsnes_cplusplus98 +- bsnes_mercury_accuracy +- bsnes_mercury_balanced +- bsnes_mercury_performance +- chailove +- crocods +- dinothawr +- dosbox_core +- dosbox_svn +- dosbox_svn_ce +- ecwolf +- fbalpha2012 +- fbalpha2012_cps1 +- fbalpha2012_cps2 +- fbalpha2012_neogeo +- gearboy +- genesis_plus_gx +- gme +- higan_sfc +- higan_sfc_balanced +- mame2000 +- mame2003 +- mame2003_plus +- mednafen_gba +- mednafen_pce +- mednafen_pce_fast +- mednafen_snes +- mednafen_vb +- mu +- nekop2 +- oberon +- pocketcdg +- quicknes +- remotejoy +- sameboy +- snes9x2002 +- snes9x2005 +- snes9x2005_plus +- snes9x2010 +- squirreljme +- stella2014 +- test +- thepowdertoy +- vba_next +- vbam +- vemulator +- vice_xcbm2 +- vice_xcbm5x0 libretro_systems: "81": - zx81 diff --git a/platforms/targets/knulli.yml b/platforms/targets/knulli.yml index c98caaf92..d18ca9335 100644 --- a/platforms/targets/knulli.yml +++ b/platforms/targets/knulli.yml @@ -1,6 +1,6 @@ platform: knulli source: https://github.com/knulli-cfw/knulli-linux/tree/20260511 -scraped_at: '2026-11-06T02:19:00Z' +scraped_at: '2026-11-09T20:38:23Z' targets: a133: architecture: aarch64 @@ -472,6 +472,7 @@ targets: - zc210 images: - rg35xx + downloads: linux-armhf h700: architecture: aarch64 cores: @@ -787,6 +788,7 @@ targets: - zc210 images: - miyoo-a30 + downloads: linux-armhf rk3128: architecture: arm cores: @@ -930,6 +932,7 @@ targets: - powkiddy_a13 - ps5000 - ps7000 + downloads: linux-armhf rk3326: architecture: aarch64 cores: diff --git a/scripts/scraper/knulli_scraper.py b/scripts/scraper/knulli_scraper.py index f94f56704..0522ae53a 100644 --- a/scripts/scraper/knulli_scraper.py +++ b/scripts/scraper/knulli_scraper.py @@ -42,6 +42,7 @@ from .batocera_scraper import ( es_systems_by_system, es_systems_libretro_systems, extract_systems_dict, + installable_cores, platform_systems, records_run, requirements_from_systems, @@ -136,9 +137,11 @@ class Scraper(BaseScraper): # What runs is what at least one board builds, read by the targets # scraper from the Kconfig of KNULLI's tree and its batocera and # buildroot submodules (boards_build). - built = boards_build(BoardTargets().build_selection(self.tag), launchers) + selection = BoardTargets().build_selection(self.tag) + built = boards_build(selection, launchers) launchers = records_run(launchers, built) cores, standalone, dual = configgen_cores(defaults, es_systems, dropped, built) + installable = installable_cores(selection, cores) # Each system's own list (psx, es_systems.yml:640-653: libretro # pcsx_rearmed, swanstation, mednafen_psx, then duckstation and # duckstation-legacy). @@ -160,6 +163,8 @@ class Scraper(BaseScraper): result["standalone_cores"] = standalone if dual: result["dual_mode_cores"] = dual + if installable: + result["installable_cores"] = installable # configgen hands a libretro core /userdata/saves/, as # Batocera's does: the systems each core runs for are its save folders. result["libretro_systems"] = es_systems_libretro_systems(es_systems, dropped) diff --git a/tests/test_target_scrapers.py b/tests/test_target_scrapers.py index 23571eb7c..8c17df288 100644 --- a/tests/test_target_scrapers.py +++ b/tests/test_target_scrapers.py @@ -280,6 +280,7 @@ class CoreDownloaderReach(unittest.TestCase): self.assertLessEqual(x86, set(load_target_config("batocera", "x86_64", root) or ())) self.assertLessEqual(armhf, set(load_target_config("batocera", "rk3288", root) or ())) self.assertFalse(x86 <= set(load_target_config("batocera", "bcm2711", root) or ())) + self.assertLessEqual(armhf, set(load_target_config("knulli", "r16", root) or ())) def test_the_platform_names_what_its_boards_install(self): import yaml # noqa: PLC0415 @@ -287,7 +288,7 @@ class CoreDownloaderReach(unittest.TestCase): from common import load_target_config # noqa: PLC0415 root = str(REPO_ROOT / "platforms") - for platform in ("batocera",): + for platform in ("batocera", "knulli"): config = yaml.safe_load((REPO_ROOT / "platforms" / f"{platform}.yml").read_text()) targets = yaml.safe_load( (REPO_ROOT / "platforms" / "targets" / f"{platform}.yml").read_text())["targets"]