diff --git a/platforms/targets/emudeck.yml b/platforms/targets/emudeck.yml index b2493971..52f24ef0 100644 --- a/platforms/targets/emudeck.yml +++ b/platforms/targets/emudeck.yml @@ -1,40 +1,461 @@ platform: emudeck source: https://github.com/dragoonDorise/EmuDeck -scraped_at: '2026-03-26T08:44:04Z' +scraped_at: '2026-03-26T09:14:02Z' targets: steamos: architecture: x86_64 cores: - - beetle_psx - - beetle_saturn + - '2048' + - 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 + - clownmdemu + - craft + - crocods - desmume + - desmume2015 + - dice + - dinothawr + - dirksimple + - dolphin + - dosbox_core + - dosbox_pure + - dosbox_svn + - doukutsu_rs - duckstation + - easyrpg + - ecwolf + - eden + - ep128emu_core + - fbalpha + - fbalpha2012 + - fbalpha2012_cps1 + - fbalpha2012_cps2 + - fbalpha2012_cps3 + - fbalpha2012_neogeo + - fbneo + - fceumm + - fixgb + - fixnes - 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 + - 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 + - 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 - pcsx_rearmed + - pd777 - 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 + - 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 - yabause + - yuzu windows: architecture: x86_64 cores: - - beetle_psx - - beetle_saturn + - '2048' + - 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 + - clownmdemu + - craft + - crocods - desmume + - desmume2015 + - dice + - dinothawr + - dirksimple + - dolphin + - dosbox_core + - dosbox_pure + - dosbox_svn + - doukutsu_rs - duckstation + - easyrpg + - ecwolf + - eden + - ep128emu_core + - fbalpha + - fbalpha2012 + - fbalpha2012_cps1 + - fbalpha2012_cps2 + - fbalpha2012_cps3 + - fbalpha2012_neogeo + - fbneo + - fceumm + - fixgb + - fixnes - 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 + - 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 + - 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 - pcsx_rearmed + - pd777 - 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 + - 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 - yabause + - yuzu diff --git a/scripts/common.py b/scripts/common.py index e9829631..769e7b0f 100644 --- a/scripts/common.py +++ b/scripts/common.py @@ -650,11 +650,16 @@ def filter_systems_by_target( systems: dict[str, dict], profiles: dict[str, dict], target_cores: set[str] | None, + platform_cores: set[str] | None = None, ) -> dict[str, dict]: """Filter platform systems to only those reachable by target cores. 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: return systems @@ -667,19 +672,26 @@ def filter_systems_by_target( upstream_to_profile[str(alias)] = name 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]] = {} for name, p in profiles.items(): if p.get("type") == "alias": continue + if platform_cores is not None and name not in platform_cores: + continue for sid in p.get("systems", []): system_to_cores.setdefault(sid, set()).add(name) filtered = {} for sys_id, sys_data in systems.items(): 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 + 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 diff --git a/scripts/generate_pack.py b/scripts/generate_pack.py index 159b7ff7..e29c686b 100644 --- a/scripts/generate_pack.py +++ b/scripts/generate_pack.py @@ -265,10 +265,13 @@ def generate_pack( validation_index = _build_validation_index(emu_profiles) # 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( config.get("systems", {}), emu_profiles or {}, target_cores, + platform_cores=plat_cores, ) with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf: diff --git a/scripts/scraper/targets/emudeck_targets_scraper.py b/scripts/scraper/targets/emudeck_targets_scraper.py index 3e703e6b..0769dab9 100644 --- a/scripts/scraper/targets/emudeck_targets_scraper.py +++ b/scripts/scraper/targets/emudeck_targets_scraper.py @@ -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", diff --git a/scripts/verify.py b/scripts/verify.py index 33eed793..871e0d50 100644 --- a/scripts/verify.py +++ b/scripts/verify.py @@ -394,8 +394,10 @@ def verify_platform( validation_index = _build_validation_index(profiles) # Filter systems by target + plat_cores = resolve_platform_cores(config, profiles) if target_cores else None verify_systems = filter_systems_by_target( config.get("systems", {}), profiles, target_cores, + platform_cores=plat_cores, ) # Per-entry results