From c7f18e84c3466096394b2dfea0a293e438427007 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Mon, 19 Oct 2026 06:57:32 +0200 Subject: [PATCH] feat: read RomM cores from its player maps RomM merges a nightly core map into the stable one when netplay is on, so it launches azahar, bsnes and genesis_plus_gx_wide, which the inherited EmulatorJS list left out. The scraper now reads both maps from the tagged frontend source and fails if either is missing. The cited mednafen_pce mapping is at index.ts:481-482 in 5.4.0. --- platforms/romm.yml | 52 +++++++++++++++++++++++++++++++++ scripts/scraper/romm_scraper.py | 44 +++++++++++++++++++++++++++- tests/test_audit_regressions.py | 42 ++++++++++++++++++++++++++ 3 files changed, 137 insertions(+), 1 deletion(-) diff --git a/platforms/romm.yml b/platforms/romm.yml index d47b2ad4..1d201357 100644 --- a/platforms/romm.yml +++ b/platforms/romm.yml @@ -7,6 +7,58 @@ base_destination: bios hash_type: sha1 verification_mode: md5 hash_rule: size_and_any_hash +cores: +- a5200 +- azahar +- beetle_vb +- bsnes +- cap32 +- crocods +- desmume +- desmume2015 +- dosbox_pure +- fbalpha2012_cps1 +- fbalpha2012_cps2 +- fbneo +- fceumm +- freeintv +- fuse +- gambatte +- gearcoleco +- genesis_plus_gx +- genesis_plus_gx_wide +- handy +- mame2003 +- mame2003_plus +- mednafen_ngp +- mednafen_pce +- mednafen_pcfx +- mednafen_psx_hw +- mednafen_wswan +- melonds +- mgba +- mupen64plus_next +- nestopia +- opera +- parallel_n64 +- pcsx_rearmed +- picodrive +- ppsspp +- prboom +- prosystem +- puae +- same_cdi +- smsplus +- snes9x +- stella2014 +- vice_x128 +- vice_x64 +- vice_x64sc +- vice_xpet +- vice_xplus4 +- vice_xvic +- virtualjaguar +- yabause systems: 3do: files: diff --git a/scripts/scraper/romm_scraper.py b/scripts/scraper/romm_scraper.py index e1f555a6..0144a0fe 100644 --- a/scripts/scraper/romm_scraper.py +++ b/scripts/scraper/romm_scraper.py @@ -23,12 +23,14 @@ Slugs are IGDB-style platform identifiers. from __future__ import annotations import json +import re import sys try: from .base_scraper import ( BaseScraper, BiosRequirement, + fetch_bytes, fetch_github_latest_version, requirement_entry, ) @@ -36,6 +38,7 @@ except ImportError: from base_scraper import ( BaseScraper, BiosRequirement, + fetch_bytes, fetch_github_latest_version, requirement_entry, ) @@ -49,6 +52,42 @@ SOURCE_URL = ( "/backend/models/fixtures/known_bios_files.json" ) +# The player's core table. _EJS_CORES_MAP is what EmulatorJS 4.2.3 runs, and +# _EJS_NIGHTLY_CORES_MAP adds the nightly cores RomM merges in when netplay is +# enabled (getSupportedEJSCores). Both are cores RomM can launch, so the pack +# carries what either needs: azahar, bsnes and genesis_plus_gx_wide exist only +# in the nightly map. +CORES_URL = ( + "https://raw.githubusercontent.com/rommapp/romm/{tag}" + "/frontend/src/utils/index.ts" +) +CORE_MAPS = ("_EJS_CORES_MAP", "_EJS_NIGHTLY_CORES_MAP") + + +def parse_player_cores(source: str) -> list[str]: + """Every core named by the player's core maps, sorted. + + A map missing from the file, or one naming no core, means the source has + changed shape: that raises instead of publishing a shorter list. + """ + cores: set[str] = set() + for name in CORE_MAPS: + match = re.search( + rf"const {name}\b[^=]*=\s*\{{(.*?)^\}}", source, re.S | re.M + ) + if match is None: + raise RuntimeError(f"{name} not found in the RomM player source") + found = set(re.findall(r"\[([^\]]*)\]", match.group(1))) + named = { + core + for values in found + for core in re.findall(r'"([A-Za-z0-9_]+)"', values) + } + if not named: + raise RuntimeError(f"{name} names no core") + cores |= named + return sorted(cores) + # IGDB slug -> retrobios system ID SLUG_MAP: dict[str, str] = { "3do": "3do", @@ -112,7 +151,7 @@ SLUG_MAP: dict[str, str] = { # Slugs whose player runs the same core as another slug and reads firmware # from its own folder. RomM keys the fixture by tg16 only, but its player maps -# both tg16 and turbografx-cd to mednafen_pce (frontend/src/utils/index.ts:540-541) +# both tg16 and turbografx-cd to mednafen_pce (frontend/src/utils/index.ts:481-482) # and lists firmware per platform folder (backend/handler/filesystem/ # firmware_handler.py get_firmware_fs_structure), so a CD library without the # system cards under its own slug cannot boot a disc. @@ -237,6 +276,9 @@ class Scraper(BaseScraper): "verification_mode": "md5", # Firmware.verify_file_hashes: size, then md5, sha1 or crc. "hash_rule": "size_and_any_hash", + "cores": parse_player_cores( + fetch_bytes(CORES_URL.format(tag=self.tag)).decode("utf-8") + ), "systems": systems, } diff --git a/tests/test_audit_regressions.py b/tests/test_audit_regressions.py index 86ef1ffd..b9674ba1 100644 --- a/tests/test_audit_regressions.py +++ b/tests/test_audit_regressions.py @@ -4876,6 +4876,48 @@ class AHashListIsReadOneWay(unittest.TestCase): +class RommRunsTheCoresItsPlayerNames(unittest.TestCase): + """RomM's player merges a nightly core map into the stable one when netplay + is on. Inheriting EmulatorJS's list left azahar, bsnes and + genesis_plus_gx_wide out of the RomM pack, though RomM launches them.""" + + SOURCE = """ +const _EJS_CORES_MAP: Record = { + "3do": ["opera"], + arcade: [ + "mame2003_plus", + "fbneo", + ], +} as const; + +const _EJS_NIGHTLY_CORES_MAP: Record = { + "3ds": ["azahar"], + snes: ["snes9x", "bsnes"], +}; +""" + + def test_both_maps_are_read(self): + from scripts.scraper.romm_scraper import parse_player_cores + + self.assertEqual( + parse_player_cores(self.SOURCE), + ["azahar", "bsnes", "fbneo", "mame2003_plus", "opera", "snes9x"], + ) + + def test_a_missing_map_raises(self): + from scripts.scraper.romm_scraper import parse_player_cores + + source = self.SOURCE.replace("_EJS_NIGHTLY_CORES_MAP", "_RENAMED") + with self.assertRaises(RuntimeError): + parse_player_cores(source) + + def test_the_platform_file_carries_the_nightly_cores(self): + from common import load_platform_config + + cores = set(load_platform_config("romm", str(ROOT / "platforms"))["cores"]) + self.assertTrue({"azahar", "bsnes", "genesis_plus_gx_wide"} <= cores) + + class RommNeoGeoFoldersGetTheirBios(unittest.TestCase): """RomM runs fbneo for neogeoaes and neogeomvs as for arcade and reads firmware per platform folder, but the fixture gives neogeo.zip to arcade