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,12 +1,13 @@
"""Scraper for EmuDeck emulator targets.
Sources:
SteamOS: dragoonDorise/EmuDeck — checkBIOS.sh, install scripts
Windows: EmuDeck/emudeck-we — checkBIOS.ps1
SteamOS: dragoonDorise/EmuDeck — functions/EmuScripts/*.sh
Windows: EmuDeck/emudeck-we — functions/EmuScripts/*.ps1
"""
from __future__ import annotations
import argparse
import json
import re
import sys
import urllib.error
@@ -19,38 +20,25 @@ from . import BaseTargetScraper
PLATFORM_NAME = "emudeck"
STEAMOS_CHECKBIOS_URL = (
"https://raw.githubusercontent.com/dragoonDorise/EmuDeck/"
"main/functions/checkBIOS.sh"
)
WINDOWS_CHECKBIOS_URL = (
"https://raw.githubusercontent.com/EmuDeck/emudeck-we/"
"main/functions/checkBIOS.ps1"
)
STEAMOS_API = "https://api.github.com/repos/dragoonDorise/EmuDeck/contents/functions/EmuScripts"
WINDOWS_API = "https://api.github.com/repos/EmuDeck/emudeck-we/contents/functions/EmuScripts"
# checkBIOS functions check by system, not by core. Map to actual emulators.
# Source: EmuDeck install scripts + wiki documentation.
_BIOS_SYSTEM_TO_CORES: dict[str, list[str]] = {
"ps1bios": ["beetle_psx", "pcsx_rearmed", "duckstation", "swanstation"],
"ps2bios": ["pcsx2"],
"segacdbios": ["genesisplusgx", "picodrive"],
"saturnbios": ["beetle_saturn", "kronos", "yabasanshiro", "yabause"],
"dreamcastbios": ["flycast"],
"dsbios": ["melonds", "desmume"],
"ryujinxbios": [], # standalone, not libretro
"yuzubios": [], # standalone, not libretro
"citronbios": ["citron"],
# Map EmuDeck script names to emulator profile keys
# Script naming: emuDeckDolphin.sh -> dolphin
# Some need explicit mapping when names differ
_NAME_OVERRIDES: dict[str, str] = {
"pcsx2qt": "pcsx2",
"rpcs3legacy": "rpcs3",
"cemuproton": "cemu",
"rmg": "mupen64plus_next",
"bigpemu": "bigpemu",
"eden": "eden",
"suyu": "suyu",
"ares": "ares",
}
# Patterns for BIOS check function names
_SH_EMULATOR_RE = re.compile(
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,
)
# Scripts that are not emulators (config helpers, etc.)
_SKIP = {"retroarch_maincfg", "retroarch"}
def _fetch(url: str) -> str | None:
@@ -65,19 +53,31 @@ def _fetch(url: str) -> str | None:
return None
def _extract_cores(text: str, pattern: re.Pattern[str]) -> list[str]:
"""Extract core names by parsing BIOS check functions and mapping to cores."""
seen: set[str] = set()
results: list[str] = []
for m in pattern.finditer(text):
system_name = m.group(1).lower()
# Map system BIOS check to actual core names
cores = _BIOS_SYSTEM_TO_CORES.get(system_name, [])
for core in cores:
if core not in seen:
seen.add(core)
results.append(core)
return sorted(results)
def _list_emuscripts(api_url: str) -> list[str]:
"""List emulator script filenames from GitHub API."""
raw = _fetch(api_url)
if not raw:
return []
entries = json.loads(raw)
names = []
for e in entries:
name = e.get("name", "")
if name.endswith(".sh") or name.endswith(".ps1"):
names.append(name)
return names
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):
@@ -86,25 +86,67 @@ class Scraper(BaseTargetScraper):
def __init__(self, url: str = "https://github.com/dragoonDorise/EmuDeck"):
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:
print(" fetching SteamOS checkBIOS.sh...", file=sys.stderr)
sh_text = _fetch(STEAMOS_CHECKBIOS_URL)
steamos_cores = _extract_cores(sh_text, _SH_EMULATOR_RE) if sh_text else []
steamos_cores = self._fetch_cores_for_target(STEAMOS_API, "SteamOS")
windows_cores = self._fetch_cores_for_target(WINDOWS_API, "Windows")
print(" fetching Windows checkBIOS.ps1...", file=sys.stderr)
ps1_text = _fetch(WINDOWS_CHECKBIOS_URL)
windows_cores = _extract_cores(ps1_text, _PS1_EMULATOR_RE) if ps1_text else []
targets: dict[str, dict] = {
"steamos": {
targets: dict[str, dict] = {}
if steamos_cores:
targets["steamos"] = {
"architecture": "x86_64",
"cores": steamos_cores,
},
"windows": {
}
if windows_cores:
targets["windows"] = {
"architecture": "x86_64",
"cores": windows_cores,
},
}
}
return {
"platform": "emudeck",