fix: filter baseline by platform-scoped cores, include retroarch cores in emudeck targets

This commit is contained in:
Abdessamad Derraz
2026-03-26 10:20:43 +01:00
parent 6402b77374
commit 0a1880f606
5 changed files with 547 additions and 67 deletions

View File

@@ -1,40 +1,461 @@
platform: emudeck platform: emudeck
source: https://github.com/dragoonDorise/EmuDeck source: https://github.com/dragoonDorise/EmuDeck
scraped_at: '2026-03-26T08:44:04Z' scraped_at: '2026-03-26T09:14:02Z'
targets: targets:
steamos: steamos:
architecture: x86_64 architecture: x86_64
cores: cores:
- beetle_psx - '2048'
- beetle_saturn - 3dengine
- '81'
- DoubleCherryGB
- a5200
- amiarcadia
- anarch
- applewin
- ardens
- arduous
- ares
- atari800
- azahar
- b2
- bigpemu
- bk
- blastem
- bluemsx
- boom3
- boom3_xp
- bsnes
- bsnes-jg
- bsnes2014_accuracy
- bsnes2014_balanced
- bsnes2014_performance
- bsnes_cplusplus98
- bsnes_hd_beta
- bsnes_mercury_accuracy
- bsnes_mercury_balanced
- bsnes_mercury_performance
- cannonball
- cap32
- cdi2015
- cemu
- chailove
- citra
- citra2018
- citron - citron
- clownmdemu
- craft
- crocods
- desmume - desmume
- desmume2015
- dice
- dinothawr
- dirksimple
- dolphin
- dosbox_core
- dosbox_pure
- dosbox_svn
- doukutsu_rs
- duckstation - duckstation
- easyrpg
- ecwolf
- eden
- ep128emu_core
- fbalpha
- fbalpha2012
- fbalpha2012_cps1
- fbalpha2012_cps2
- fbalpha2012_cps3
- fbalpha2012_neogeo
- fbneo
- fceumm
- fixgb
- fixnes
- flycast - flycast
- genesisplusgx - fmsx
- freechaf
- freeintv
- frodo
- fuse
- galaksija
- gam4980
- gambatte
- gearboy
- gearcoleco
- geargrafx
- gearlynx
- gearsystem
- genesis_plus_gx
- genesis_plus_gx_wide
- geolith
- gme
- gong
- gpsp
- gw
- handy
- hatari
- holani
- jaxe
- jollycv
- jumpnbump
- kronos - kronos
- lowresnx
- lutro
- m2000
- mame
- mame2000
- mame2003
- mame2003_midway
- mame2003_plus
- mame2010
- 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 - melonds
- melondsds
- mesen
- mesen-s
- meteor
- mgba
- minivmac
- model2
- mojozork
- mrboom
- mu
- mupen64plus_next
- nekop2
- neocd
- nestopia
- noods
- np2kai
- numero
- nxengine
- o2em
- oberon
- openlara
- opera
- panda3ds
- parallel_n64
- pcsx2 - pcsx2
- pcsx_rearmed - pcsx_rearmed
- pd777
- picodrive - picodrive
- play
- pocketcdg
- pokemini
- potator
- ppsspp
- prboom
- primehack
- prosystem
- puae
- puae2021
- px68k
- qemu
- quasi88
- quicknes
- race
- reminiscence
- retro8
- romcleaner
- rpcs3
- ryujinx
- same_cdi
- sameboy
- sameduck
- scummvm
- shadps4
- skyemu
- smsplus
- snes9x
- snes9x2002
- snes9x2005
- snes9x2005_plus
- snes9x2010
- squirreljme
- stella
- stella2014
- stella2023
- superbroswar
- supermodel
- suyu
- swanstation - swanstation
- tamalibretro
- tgbdual
- theodore
- thepowdertoy
- tic80
- tyrquake
- uw8
- uzem
- vaporspec
- vba_next
- vbam
- vecx
- vemulator
- vice_x128
- vice_x64
- vice_x64sc
- vice_xcbm2
- vice_xcbm5x0
- vice_xpet
- vice_xplus4
- vice_xscpu64
- vice_xvic
- vircon32
- virtualjaguar
- virtualxt
- vita3k
- vitaquake2
- vitaquake2-rogue
- vitaquake2-xatrix
- vitaquake2-zaero
- vitaquake3
- wasm4
- x1
- xemu
- xenia
- xrick
- yabasanshiro - yabasanshiro
- yabause - yabause
- yuzu
windows: windows:
architecture: x86_64 architecture: x86_64
cores: cores:
- beetle_psx - '2048'
- beetle_saturn - 3dengine
- '81'
- DoubleCherryGB
- a5200
- amiarcadia
- anarch
- applewin
- ardens
- arduous
- atari800
- azahar
- b2
- bigpemu
- bk
- blastem
- bluemsx
- boom3
- boom3_xp
- bsnes
- bsnes-jg
- bsnes2014_accuracy
- bsnes2014_balanced
- bsnes2014_performance
- bsnes_cplusplus98
- bsnes_hd_beta
- bsnes_mercury_accuracy
- bsnes_mercury_balanced
- bsnes_mercury_performance
- cannonball
- cap32
- cdi2015
- cemu
- chailove
- citra
- citra2018
- citron - citron
- clownmdemu
- craft
- crocods
- desmume - desmume
- desmume2015
- dice
- dinothawr
- dirksimple
- dolphin
- dosbox_core
- dosbox_pure
- dosbox_svn
- doukutsu_rs
- duckstation - duckstation
- easyrpg
- ecwolf
- eden
- ep128emu_core
- fbalpha
- fbalpha2012
- fbalpha2012_cps1
- fbalpha2012_cps2
- fbalpha2012_cps3
- fbalpha2012_neogeo
- fbneo
- fceumm
- fixgb
- fixnes
- flycast - flycast
- genesisplusgx - fmsx
- freechaf
- freeintv
- frodo
- fuse
- galaksija
- gam4980
- gambatte
- gearboy
- gearcoleco
- geargrafx
- gearlynx
- gearsystem
- genesis_plus_gx
- genesis_plus_gx_wide
- geolith
- gme
- gong
- gpsp
- gw
- handy
- hatari
- holani
- jaxe
- jollycv
- jumpnbump
- kronos - kronos
- lowresnx
- lutro
- m2000
- mame
- mame2000
- mame2003
- mame2003_midway
- mame2003_plus
- mame2010
- 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 - melonds
- melondsds
- mesen
- mesen-s
- meteor
- mgba
- minivmac
- model2
- mojozork
- mrboom
- mu
- mupen64plus_next
- nekop2
- neocd
- nestopia
- noods
- np2kai
- numero
- nxengine
- o2em
- oberon
- openlara
- opera
- panda3ds
- parallel_n64
- pcsx2 - pcsx2
- pcsx_rearmed - pcsx_rearmed
- pd777
- picodrive - picodrive
- play
- pocketcdg
- pokemini
- potator
- ppsspp
- prboom
- primehack
- prosystem
- puae
- puae2021
- px68k
- qemu
- quasi88
- quicknes
- race
- reminiscence
- retro8
- romcleaner
- rpcs3
- ryujinx
- same_cdi
- sameboy
- sameduck
- scummvm
- shadps4
- skyemu
- smsplus
- snes9x
- snes9x2002
- snes9x2005
- snes9x2005_plus
- snes9x2010
- squirreljme
- stella
- stella2014
- stella2023
- superbroswar
- supermodel
- swanstation - swanstation
- tamalibretro
- template
- tgbdual
- theodore
- thepowdertoy
- tic80
- tyrquake
- uw8
- uzem
- vaporspec
- vba_next
- vbam
- vecx
- vemulator
- vice_x128
- vice_x64
- vice_x64sc
- vice_xcbm2
- vice_xcbm5x0
- vice_xpet
- vice_xplus4
- vice_xscpu64
- vice_xvic
- vircon32
- virtualjaguar
- virtualxt
- vita3k
- vitaquake2
- vitaquake2-rogue
- vitaquake2-xatrix
- vitaquake2-zaero
- vitaquake3
- wasm4
- x1
- xemu
- xenia
- xrick
- yabasanshiro - yabasanshiro
- yabause - yabause
- yuzu

View File

@@ -650,11 +650,16 @@ def filter_systems_by_target(
systems: dict[str, dict], systems: dict[str, dict],
profiles: dict[str, dict], profiles: dict[str, dict],
target_cores: set[str] | None, target_cores: set[str] | None,
platform_cores: set[str] | None = None,
) -> dict[str, dict]: ) -> dict[str, dict]:
"""Filter platform systems to only those reachable by target cores. """Filter platform systems to only those reachable by target cores.
A system is reachable if at least one core that emulates it is available A system is reachable if at least one core that emulates it is available
on the target. Returns the filtered systems dict (or all if no target). on the target. Only considers cores relevant to the platform (from
platform_cores). Systems whose cores are all outside the platform's
scope are kept (no information to exclude them).
Returns the filtered systems dict (or all if no target).
""" """
if target_cores is None: if target_cores is None:
return systems return systems
@@ -667,19 +672,26 @@ def filter_systems_by_target(
upstream_to_profile[str(alias)] = name upstream_to_profile[str(alias)] = name
expanded_target = {upstream_to_profile.get(c, c) for c in target_cores} expanded_target = {upstream_to_profile.get(c, c) for c in target_cores}
# Build system -> profile keys mapping # Build system -> profile keys mapping (only platform-relevant cores)
system_to_cores: dict[str, set[str]] = {} system_to_cores: dict[str, set[str]] = {}
for name, p in profiles.items(): for name, p in profiles.items():
if p.get("type") == "alias": if p.get("type") == "alias":
continue continue
if platform_cores is not None and name not in platform_cores:
continue
for sid in p.get("systems", []): for sid in p.get("systems", []):
system_to_cores.setdefault(sid, set()).add(name) system_to_cores.setdefault(sid, set()).add(name)
filtered = {} filtered = {}
for sys_id, sys_data in systems.items(): for sys_id, sys_data in systems.items():
cores_for_system = system_to_cores.get(sys_id, set()) cores_for_system = system_to_cores.get(sys_id, set())
if cores_for_system & expanded_target: if not cores_for_system:
# No platform-relevant core maps to this system — keep it
filtered[sys_id] = sys_data filtered[sys_id] = sys_data
elif cores_for_system & expanded_target:
# At least one core for this system is on the target
filtered[sys_id] = sys_data
# else: all platform cores for this system are off-target — exclude
return filtered return filtered

View File

@@ -265,10 +265,13 @@ def generate_pack(
validation_index = _build_validation_index(emu_profiles) validation_index = _build_validation_index(emu_profiles)
# Filter systems by target if specified # Filter systems by target if specified
from common import resolve_platform_cores
plat_cores = resolve_platform_cores(config, emu_profiles or {}) if target_cores else None
pack_systems = filter_systems_by_target( pack_systems = filter_systems_by_target(
config.get("systems", {}), config.get("systems", {}),
emu_profiles or {}, emu_profiles or {},
target_cores, target_cores,
platform_cores=plat_cores,
) )
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf: with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:

View File

@@ -1,12 +1,13 @@
"""Scraper for EmuDeck emulator targets. """Scraper for EmuDeck emulator targets.
Sources: Sources:
SteamOS: dragoonDorise/EmuDeck — checkBIOS.sh, install scripts SteamOS: dragoonDorise/EmuDeck — functions/EmuScripts/*.sh
Windows: EmuDeck/emudeck-we — checkBIOS.ps1 Windows: EmuDeck/emudeck-we — functions/EmuScripts/*.ps1
""" """
from __future__ import annotations from __future__ import annotations
import argparse import argparse
import json
import re import re
import sys import sys
import urllib.error import urllib.error
@@ -19,38 +20,25 @@ from . import BaseTargetScraper
PLATFORM_NAME = "emudeck" PLATFORM_NAME = "emudeck"
STEAMOS_CHECKBIOS_URL = ( STEAMOS_API = "https://api.github.com/repos/dragoonDorise/EmuDeck/contents/functions/EmuScripts"
"https://raw.githubusercontent.com/dragoonDorise/EmuDeck/" WINDOWS_API = "https://api.github.com/repos/EmuDeck/emudeck-we/contents/functions/EmuScripts"
"main/functions/checkBIOS.sh"
)
WINDOWS_CHECKBIOS_URL = (
"https://raw.githubusercontent.com/EmuDeck/emudeck-we/"
"main/functions/checkBIOS.ps1"
)
# checkBIOS functions check by system, not by core. Map to actual emulators. # Map EmuDeck script names to emulator profile keys
# Source: EmuDeck install scripts + wiki documentation. # Script naming: emuDeckDolphin.sh -> dolphin
_BIOS_SYSTEM_TO_CORES: dict[str, list[str]] = { # Some need explicit mapping when names differ
"ps1bios": ["beetle_psx", "pcsx_rearmed", "duckstation", "swanstation"], _NAME_OVERRIDES: dict[str, str] = {
"ps2bios": ["pcsx2"], "pcsx2qt": "pcsx2",
"segacdbios": ["genesisplusgx", "picodrive"], "rpcs3legacy": "rpcs3",
"saturnbios": ["beetle_saturn", "kronos", "yabasanshiro", "yabause"], "cemuproton": "cemu",
"dreamcastbios": ["flycast"], "rmg": "mupen64plus_next",
"dsbios": ["melonds", "desmume"], "bigpemu": "bigpemu",
"ryujinxbios": [], # standalone, not libretro "eden": "eden",
"yuzubios": [], # standalone, not libretro "suyu": "suyu",
"citronbios": ["citron"], "ares": "ares",
} }
# Patterns for BIOS check function names # Scripts that are not emulators (config helpers, etc.)
_SH_EMULATOR_RE = re.compile( _SKIP = {"retroarch_maincfg", "retroarch"}
r'(?:function\s+|^)(?:check|install|setup)([A-Za-z0-9_]+)\s*\(',
re.MULTILINE,
)
_PS1_EMULATOR_RE = re.compile(
r'function\s+(?:check|install|setup)([A-Za-z0-9_]+)\s*(?:\(\))?\s*\{',
re.MULTILINE | re.IGNORECASE,
)
def _fetch(url: str) -> str | None: def _fetch(url: str) -> str | None:
@@ -65,19 +53,31 @@ def _fetch(url: str) -> str | None:
return None return None
def _extract_cores(text: str, pattern: re.Pattern[str]) -> list[str]: def _list_emuscripts(api_url: str) -> list[str]:
"""Extract core names by parsing BIOS check functions and mapping to cores.""" """List emulator script filenames from GitHub API."""
seen: set[str] = set() raw = _fetch(api_url)
results: list[str] = [] if not raw:
for m in pattern.finditer(text): return []
system_name = m.group(1).lower() entries = json.loads(raw)
# Map system BIOS check to actual core names names = []
cores = _BIOS_SYSTEM_TO_CORES.get(system_name, []) for e in entries:
for core in cores: name = e.get("name", "")
if core not in seen: if name.endswith(".sh") or name.endswith(".ps1"):
seen.add(core) names.append(name)
results.append(core) return names
return sorted(results)
def _script_to_core(filename: str) -> str | None:
"""Convert EmuScripts filename to core profile key."""
# Strip extension and emuDeck prefix
name = re.sub(r'\.(sh|ps1)$', '', filename, flags=re.IGNORECASE)
name = re.sub(r'^emuDeck', '', name, flags=re.IGNORECASE)
if not name:
return None
key = name.lower()
if key in _SKIP:
return None
return _NAME_OVERRIDES.get(key, key)
class Scraper(BaseTargetScraper): class Scraper(BaseTargetScraper):
@@ -86,25 +86,67 @@ class Scraper(BaseTargetScraper):
def __init__(self, url: str = "https://github.com/dragoonDorise/EmuDeck"): def __init__(self, url: str = "https://github.com/dragoonDorise/EmuDeck"):
super().__init__(url=url) super().__init__(url=url)
def _fetch_cores_for_target(self, api_url: str, label: str,
arch: str = "x86_64") -> list[str]:
print(f" fetching {label} EmuScripts...", file=sys.stderr)
scripts = _list_emuscripts(api_url)
cores: list[str] = []
seen: set[str] = set()
has_retroarch = False
for script in scripts:
core = _script_to_core(script)
if core and core not in seen:
seen.add(core)
cores.append(core)
# Detect RetroArch presence (provides all libretro cores)
name = re.sub(r'\.(sh|ps1)$', '', script, flags=re.IGNORECASE)
if name.lower() in ("emudeckretroarch", "retroarch_maincfg"):
has_retroarch = True
standalone_count = len(cores)
# EmuDeck ships RetroArch = all its libretro cores are available
if has_retroarch:
ra_cores = self._load_retroarch_cores(arch)
for c in ra_cores:
if c not in seen:
seen.add(c)
cores.append(c)
print(f" {label}: {standalone_count} standalone + "
f"{len(cores) - standalone_count} via RetroArch = {len(cores)} total",
file=sys.stderr)
return sorted(cores)
@staticmethod
def _load_retroarch_cores(arch: str) -> list[str]:
"""Load RetroArch target cores for given architecture."""
import os
target_path = os.path.join("platforms", "targets", "retroarch.yml")
if not os.path.exists(target_path):
return []
with open(target_path) as f:
data = yaml.safe_load(f) or {}
# Find a target matching the architecture
for tname, tinfo in data.get("targets", {}).items():
if tinfo.get("architecture") == arch:
return tinfo.get("cores", [])
return []
def fetch_targets(self) -> dict: def fetch_targets(self) -> dict:
print(" fetching SteamOS checkBIOS.sh...", file=sys.stderr) steamos_cores = self._fetch_cores_for_target(STEAMOS_API, "SteamOS")
sh_text = _fetch(STEAMOS_CHECKBIOS_URL) windows_cores = self._fetch_cores_for_target(WINDOWS_API, "Windows")
steamos_cores = _extract_cores(sh_text, _SH_EMULATOR_RE) if sh_text else []
print(" fetching Windows checkBIOS.ps1...", file=sys.stderr) targets: dict[str, dict] = {}
ps1_text = _fetch(WINDOWS_CHECKBIOS_URL) if steamos_cores:
windows_cores = _extract_cores(ps1_text, _PS1_EMULATOR_RE) if ps1_text else [] targets["steamos"] = {
targets: dict[str, dict] = {
"steamos": {
"architecture": "x86_64", "architecture": "x86_64",
"cores": steamos_cores, "cores": steamos_cores,
}, }
"windows": { if windows_cores:
targets["windows"] = {
"architecture": "x86_64", "architecture": "x86_64",
"cores": windows_cores, "cores": windows_cores,
}, }
}
return { return {
"platform": "emudeck", "platform": "emudeck",

View File

@@ -394,8 +394,10 @@ def verify_platform(
validation_index = _build_validation_index(profiles) validation_index = _build_validation_index(profiles)
# Filter systems by target # Filter systems by target
plat_cores = resolve_platform_cores(config, profiles) if target_cores else None
verify_systems = filter_systems_by_target( verify_systems = filter_systems_by_target(
config.get("systems", {}), profiles, target_cores, config.get("systems", {}), profiles, target_cores,
platform_cores=plat_cores,
) )
# Per-entry results # Per-entry results