From d9ef6ad0ad44d1da457222feeaaa3254b1cc252e Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Sat, 17 Oct 2026 03:40:09 +0200 Subject: [PATCH] feat: read retrobat's cores from its es_systems RetroBat's platform file carried no core list, and a registry list copied from Batocera stood in with ports RetroBat never runs and none of its standalone emulators marked as such. The scraper now reads es_systems.cfg at the release tag: libretro cores, and every other emulator as standalone. --- platforms/_registry.yml | 169 ----------- platforms/retrobat.yml | 418 +++++++++++++++++++++++++++- scripts/scraper/retrobat_scraper.py | 44 +++ tests/test_scraper_contract.py | 35 +++ 4 files changed, 496 insertions(+), 170 deletions(-) diff --git a/platforms/_registry.yml b/platforms/_registry.yml index 12584541..c7adf172 100644 --- a/platforms/_registry.yml +++ b/platforms/_registry.yml @@ -232,175 +232,6 @@ platforms: # Windows: names differing only by case land on one file. case_insensitive_fs: true schedule: weekly - cores: - - '81' - - a5200 - - abuse - - arduous - - ares - - atari800 - - azahar - - bennugd - - bk - - bluemsx - - bsnes - - bstone - - cannonball - - cap32 - - catacombgl - - cdogs - - cemu - - cgenius - - citron - - clk - - corsixth - - demul - - devilutionx - - dhewm3 - - dice - - dolphin - - dosbox_pure - - dxx-rebirth - - easyrpg - - ecwolf - - eduke32 - - eka2l1 - - emuscv - - etlegacy - - fake08 - - fallout1-ce - - fallout2-ce - - fbalpha2012 - - fbalpha2012_neogeo - - fbneo - - fceumm - - flatpak - - flycast - - freechaf - - freeintv - - freej2me - - fury - - fuse - - gambatte - - geargrafx - - gearsystem - - genesisplusgx - - glide64mk2 - - gong - - gsplus - - gw - - gzdoom - - hatari - - hcl - - hurrican - - hypseus-singe - - ikemen - - ioquake3 - - iortcw - - jazz2-native - - lindbergh-loader - - lowresnx - - lutro - - mame - - mame078plus - - mamemess - - mednafen_lynx - - mednafen_ngp - - mednafen_supergrafx - - mednafen_wswan - - melonds - - mgba - - minivmac - - model2emu - - moonlight - - mrboom - - mupen64plus_next - - neocd - - np2kai - - nxengine - - o2em - - odcommander - - openbor6412 - - openjazz - - openjk - - openjkdf2 - - openmohaa - - opera - - parallel_n64 - - pce_fast - - pcfx - - pcsx2 - - pcsx_rearmed - - pd777 - - picodrive - - play - - pokemini - - potator - - ppsspp - - prboom - - prosystem - - puae - - px68k - - pygame - - pyxel - - quasi88 - - raze - - reminiscence - - rpcs3 - - ruffle - - samcoupe - - sameduck - - scummvm - - sdlpop - - sh - - shadps4 - - snes9x - - solarus - - sonic-mania - - sonic2013 - - sonic3-air - - squirreljme - - steam - - stella - - superbroswar - - supermodel - - taradino - - tgbdual - - theforceengine - - theodore - - thextech - - tic80 - - tr1x - - tr2x - - tsugaru - - tyrian - - tyrquake - - uqm - - uzem - - vb - - vecx - - vice_x64 - - vircon32 - - virtualjaguar - - vita3k - - vox_official - - vpinball - - wasm4 - - wine-tkg - - x1 - - x128 - - x16emu - - xash3d_fwgs - - xemu - - xenia-canary - - xpet - - xplus4 - - xrick - - xroar - - xvic - - yabasanshiro - - yquake2 - - zc210 target_scraper: null target_source: null install: diff --git a/platforms/retrobat.yml b/platforms/retrobat.yml index 7bc2d6ee..15b20fe7 100644 --- a/platforms/retrobat.yml +++ b/platforms/retrobat.yml @@ -1,10 +1,426 @@ platform: RetroBat -version: 8.3.0 +version: 8.3.1 homepage: "https://www.retrobat.org" source: "https://raw.githubusercontent.com/RetroBat-Official/emulatorlauncher/master/batocera-systems/Resources/batocera-systems.json" base_destination: bios hash_type: md5 verification_mode: md5 +cores: +- 2ship +- "81" +- DoubleCherryGB +- a5200 +- altirra +- amiarcadia +- amiberry +- amigaforever +- applewin +- arcadeduck +- arcadeflashweb +- ardens +- arduous +- ares +- atari800 +- azahar +- b2 +- bennugd +- bigpemu +- bizhawk +- bk +- blastem +- bluemsx +- boom3 +- boom3_xp +- bsnes +- bsnes-jg +- bsnes_hd_beta +- bstone +- bsyndrome +- cannonball +- cap32 +- capriceforever +- cdogs +- cemu +- cgenius +- chihiro +- chihiro-gun +- citra +- citron +- corsixth +- crocods +- cxbx +- daphne +- demul +- desmume +- desmume2015 +- devilutionx +- dhewm3 +- dice +- dinothawr +- dolphin +- dolphin-triforce +- dosbox +- dosbox-pure +- dosbox-staging +- dosbox_pure +- doukutsu_rs +- duckstation +- dusklight +- easyrpg +- ecwolf +- eden +- eduke32 +- eka2l1 +- emuscv +- ep128emu_core +- exodos +- fake08 +- fbalpha +- fbalpha2012 +- fbalpha2012_cps1 +- fbalpha2012_cps2 +- fbalpha2012_cps3 +- fbalpha2012_neogeo +- fbneo +- fceumm +- flycast +- fpinball +- freechaf +- freeintv +- freej2me +- frodo +- fuse +- gambatte +- gearcoleco +- geargrafx +- gearsystem +- gemrb +- genesis_plus_gx +- genesis_plus_gx_expanded +- genesis_plus_gx_wide +- geolith +- ghostship +- gopher64 +- gpsp +- groovymame +- gsplus +- gw +- gzdoom +- handy +- hatari +- hatarib +- hbmame +- holani +- hypseus +- ikemen +- imageviewer +- jgenesis +- jollycv +- jynx +- jzintv +- kega-fusion +- kronos +- linuxloader +- love +- lowresnx +- lutro +- m2000 +- m2emulator +- magicengine +- mame +- mame2000 +- mame2003 +- mame2003_midway +- mame2003_plus +- mame2010 +- mame2014 +- mame2016 +- mame64 +- mandarine +- mednafen +- mednafen_gba +- mednafen_lynx +- mednafen_ngp +- mednafen_pce +- mednafen_pce_fast +- mednafen_pcfx +- mednafen_psx_hw +- mednafen_saturn +- mednafen_snes +- mednafen_supergrafx +- mednafen_vb +- mednafen_wswan +- melonds +- melondsds +- mesen +- mesen-s +- mgba +- mugen +- mupen64 +- mupen64plus_next +- nekop2 +- neocd +- nestopia +- noods +- nosgba +- np2kai +- nxengine +- o2em +- openbor +- opengoal +- openjazz +- openlara +- openmsx +- opera +- oricutron +- parallel_n64 +- pcsx2 +- pcsx2-16 +- pcsx2x6 +- pcsx_rearmed +- pd777 +- pdark +- phoenix +- pico8 +- picodrive +- pinballfx +- play +- pocketcdg +- pokemini +- potator +- powerbomberman +- ppsspp +- prboom +- project64 +- prosystem +- psxmame +- puae +- px68k +- quasi88 +- quiverlauncher +- race +- raine +- raze +- redream +- reminiscence +- retro8 +- rpcs3 +- rtcw +- ruffle +- ryujinx +- same_cdi +- sameboy +- sameduck +- scummvm +- shadps4 +- simcoupe +- simple64 +- singe2 +- snes9x +- snes9x2005 +- soh +- solarus +- solarus2 +- sonic3air +- sonicmania +- sonicretro +- sonicretrocd +- ssf +- starship +- stella +- stella2014 +- sudachi +- superbroswar +- supermodel +- swanstation +- teknoparrot +- tgbdual +- theforceengine +- theodore +- tic80 +- tsugaru +- tyrquake +- uzdoom +- uzem +- vecx +- vice_x128 +- vice_x64 +- vice_x64sc +- vice_xpet +- vice_xplus4 +- vice_xvic +- vircon32 +- virtualjaguar +- vita3k +- vitaquake2 +- vitaquake2-rogue +- vitaquake2-xatrix +- vitaquake2-zaero +- vkquake +- vkquake2 +- vpinball +- wasm4 +- windows +- winuae +- x1 +- xash3d +- xemu +- xemu-chihiro +- xenia +- xenia-canary +- xenia-edge +- xenia-manager +- xm6pro +- xroar +- yabasanshiro +- ymir +- yuzu +- zaccariapinball +- zc255 +- zesarux +- zinc +- zquestclassic +standalone_cores: +- 2ship +- altirra +- amiberry +- amigaforever +- applewin +- arcadeduck +- arcadeflashweb +- ares +- azahar +- bigpemu +- bizhawk +- blastem +- bstone +- bsyndrome +- capriceforever +- cdogs +- cemu +- cgenius +- chihiro +- chihiro-gun +- citra +- citron +- corsixth +- cxbx +- daphne +- demul +- desmume +- devilutionx +- dhewm3 +- dolphin +- dolphin-triforce +- dosbox +- dosbox-pure +- dosbox-staging +- duckstation +- dusklight +- eden +- eduke32 +- eka2l1 +- exodos +- fbneo +- flycast +- fpinball +- gemrb +- ghostship +- gopher64 +- groovymame +- gsplus +- gzdoom +- hatari +- hbmame +- hypseus +- ikemen +- jgenesis +- jynx +- jzintv +- kega-fusion +- kronos +- linuxloader +- love +- m2emulator +- magicengine +- mame64 +- mandarine +- mednafen +- melonds +- mesen +- mgba +- mugen +- mupen64 +- nosgba +- openbor +- opengoal +- openjazz +- openmsx +- oricutron +- pcsx2 +- pcsx2-16 +- pcsx2x6 +- pdark +- phoenix +- pico8 +- pinballfx +- play +- powerbomberman +- ppsspp +- project64 +- psxmame +- quiverlauncher +- raine +- raze +- redream +- rpcs3 +- rtcw +- ruffle +- ryujinx +- scummvm +- shadps4 +- simcoupe +- simple64 +- singe2 +- snes9x +- soh +- solarus +- solarus2 +- sonic3air +- sonicmania +- sonicretro +- sonicretrocd +- ssf +- starship +- stella +- sudachi +- supermodel +- teknoparrot +- theforceengine +- tsugaru +- uzdoom +- vita3k +- vkquake +- vkquake2 +- vpinball +- windows +- winuae +- xash3d +- xemu +- xemu-chihiro +- xenia +- xenia-canary +- xenia-edge +- xenia-manager +- xm6pro +- xroar +- yabasanshiro +- ymir +- yuzu +- zaccariapinball +- zesarux +- zinc +- zquestclassic systems: 3do: files: diff --git a/scripts/scraper/retrobat_scraper.py b/scripts/scraper/retrobat_scraper.py index 0e6e5b52..43701922 100644 --- a/scripts/scraper/retrobat_scraper.py +++ b/scripts/scraper/retrobat_scraper.py @@ -9,6 +9,8 @@ Hash: MD5 primary from __future__ import annotations import json +import urllib.error +import urllib.request try: from .base_scraper import ( @@ -34,6 +36,37 @@ SOURCE_URL = ( GITHUB_REPO = "RetroBat-Official/retrobat" +ES_SYSTEMS_URL = ( + "https://raw.githubusercontent.com/RetroBat-Official/retrobat/{tag}" + "/system/templates/emulationstation/es_systems.cfg" +) + + +def es_systems_cores(text: str) -> tuple[set[str], set[str]]: + """Every core RetroBat's ES offers, and the emulators it runs standalone. + + emulatorlauncher starts what es_systems.cfg lists: a libretro core by its + name under ````, any other emulator by its own + name. batocera-systems.json names BIOS files only, so the platform read + from it alone had no core list and relied on one kept by hand. + """ + from common import parse_untrusted_xml + + root = parse_untrusted_xml(text, "es_systems.cfg") + cores: set[str] = set() + standalone: set[str] = set() + for emulator in root.iter("emulator"): + name = (emulator.get("name") or "").strip() + if not name: + continue + listed = {(core.text or "").strip() for core in emulator.iter("core")} - {""} + if name == "libretro": + cores |= listed + else: + cores.add(name) + standalone.add(name) + return cores, standalone + # Map RetroBat system keys to our normalized system IDs SYSTEM_SLUG_MAP = { "ps2": "sony-playstation-2", @@ -161,6 +194,15 @@ class Scraper(BaseScraper): systems[req.system]["files"].append(requirement_entry(req)) version = fetch_github_latest_version(GITHUB_REPO) + es_url = ES_SYSTEMS_URL.format(tag=version) + req = urllib.request.Request(es_url, headers={"User-Agent": "retrobios-scraper/1.0"}) + try: + with urllib.request.urlopen(req, timeout=30) as resp: + cores, standalone = es_systems_cores(resp.read().decode("utf-8")) + except urllib.error.URLError as e: + raise ConnectionError(f"Failed to fetch {es_url}: {e}") from e + if not cores: + raise RuntimeError(f"es_systems.cfg at {version} names no core") return { "platform": "RetroBat", @@ -170,6 +212,8 @@ class Scraper(BaseScraper): "base_destination": "bios", "hash_type": "md5", "verification_mode": "md5", + "cores": sorted(cores), + "standalone_cores": sorted(standalone), "systems": systems, } diff --git a/tests/test_scraper_contract.py b/tests/test_scraper_contract.py index 920634ed..7e12e5d7 100644 --- a/tests/test_scraper_contract.py +++ b/tests/test_scraper_contract.py @@ -260,6 +260,41 @@ core = "mame2003_plus" self.assertNotIn("cores", registry["platforms"]["recalbox"]) +class RetroBatCoresFromItsES(unittest.TestCase): + """RetroBat's YAML carried no core list: batocera-systems.json names + BIOS files only, and a registry list copied from Batocera stood in, + with abuse, clk or etlegacy that RetroBat never runs.""" + + ES_SYSTEMS = """\ + + + snes + snes9xbsnes + + + samcoupe + simcoupe + + +""" + + def test_libretro_cores_and_standalone_emulators(self): + from scraper.retrobat_scraper import es_systems_cores + + cores, standalone = es_systems_cores(self.ES_SYSTEMS) + self.assertEqual(cores, {"snes9x", "bsnes", "simcoupe"}) + self.assertEqual(standalone, {"bsnes", "simcoupe"}) + + def test_the_registry_leaves_the_list_to_the_scraper(self): + import yaml + + root = Path(__file__).resolve().parents[1] + registry = yaml.safe_load((root / "platforms" / "_registry.yml").read_text()) + self.assertNotIn("cores", registry["platforms"]["retrobat"]) + platform = yaml.safe_load((root / "platforms" / "retrobat.yml").read_text()) + self.assertIn("snes9x", platform["cores"]) + + class UnreadableReleaseStopsTheScrape(unittest.TestCase): """A failed tag lookup fell back to master, or to an empty or invented version. BizHawk, RomM, Batocera and Recalbox then wrote a YAML read from