feat: standalone emulator support for batocera and multi-platform name mapping

resolve_platform_cores() builds reverse index from profile cores: field,
fixing 17 name mismatches across Batocera, RetroBat, and Recalbox
(genesisplusgx, pce_fast, pcfx, vb, mame078plus, vice cores, etc.).

standalone_path field on file entries + standalone_cores on platform
YAMLs enable mode-aware pack generation. find_undeclared_files() uses
standalone_path for cores the platform runs standalone, filters by
mode: libretro/standalone per file.

batocera.yml gains standalone_cores (92 entries from configgen-defaults).
generate_readme.py dynamically lists platforms from registry.
3 profiles updated for standalone type/path (mame, hatari, mupen64plus_next).
78 E2E tests pass, pipeline verified.
This commit is contained in:
Abdessamad Derraz
2026-03-26 00:44:21 +01:00
parent 44dc946217
commit 3f676b75e8
31 changed files with 1492 additions and 40 deletions

View File

@@ -1,8 +1,8 @@
# RetroBIOS
Complete BIOS and firmware packs for RetroArch, Batocera, Recalbox, Lakka, RetroPie, EmuDeck, RetroBat, RetroDECK, and RomM.
Complete BIOS and firmware packs for Batocera, EmuDeck, Lakka, Recalbox, RetroArch, RetroBat, RetroDECK, RetroPie, and RomM.
**6,748** verified files across **294** systems, ready to extract into your emulator's BIOS directory.
**6,748** verified files across **305** systems, ready to extract into your emulator's BIOS directory.
## Download BIOS packs
@@ -26,14 +26,14 @@ BIOS, firmware, and system files for consoles from Atari to PlayStation 3.
Each file is checked against the emulator's source code to match what the code actually loads at runtime.
- **9 platforms** supported with platform-specific verification
- **306 emulators** profiled from source (RetroArch cores + standalone)
- **294 systems** covered (NES, SNES, PlayStation, Saturn, Dreamcast, ...)
- **307 emulators** profiled from source (RetroArch cores + standalone)
- **305 systems** covered (NES, SNES, PlayStation, Saturn, Dreamcast, ...)
- **6,748 files** verified with MD5, SHA1, CRC32 checksums
- **5251 MB** total collection size
## Supported systems
NES, SNES, Nintendo 64, GameCube, Wii, Game Boy, Game Boy Advance, Nintendo DS, Nintendo 3DS, Switch, PlayStation, PlayStation 2, PlayStation 3, PSP, PS Vita, Mega Drive, Saturn, Dreamcast, Game Gear, Master System, Neo Geo, Atari 2600, Atari 7800, Atari Lynx, Atari ST, MSX, PC Engine, TurboGrafx-16, ColecoVision, Intellivision, Commodore 64, Amiga, ZX Spectrum, Arcade (MAME), and 260+ more.
NES, SNES, Nintendo 64, GameCube, Wii, Game Boy, Game Boy Advance, Nintendo DS, Nintendo 3DS, Switch, PlayStation, PlayStation 2, PlayStation 3, PSP, PS Vita, Mega Drive, Saturn, Dreamcast, Game Gear, Master System, Neo Geo, Atari 2600, Atari 7800, Atari Lynx, Atari ST, MSX, PC Engine, TurboGrafx-16, ColecoVision, Intellivision, Commodore 64, Amiga, ZX Spectrum, Arcade (MAME), and 271+ more.
Full list with per-file details: **[https://abdess.github.io/retrobios/](https://abdess.github.io/retrobios/)**
@@ -79,4 +79,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
This repository provides BIOS files for personal backup and archival purposes.
*Auto-generated on 2026-03-25T22:12:06Z*
*Auto-generated on 2026-03-25T23:43:15Z*

View File

@@ -1,5 +1,5 @@
{
"generated_at": "2026-03-25T22:06:53Z",
"generated_at": "2026-03-25T23:42:57Z",
"total_files": 6748,
"total_size": 5505760050,
"files": {

View File

@@ -1,7 +1,7 @@
emulator: Beetle PC-FX (Mednafen)
type: libretro
core_classification: community_fork
cores: [mednafen_pcfx]
cores: [mednafen_pcfx, pcfx]
source: "https://github.com/libretro/beetle-pcfx-libretro"
upstream: "https://mednafen.github.io/"
profiled_date: "2026-03-24"

View File

@@ -6,7 +6,7 @@ upstream: "https://mednafen.github.io/"
profiled_date: "2026-03-24"
core_version: "v0.9.36.1"
display_name: "Nintendo - Virtual Boy (Beetle VB)"
cores: [mednafen_vb]
cores: [mednafen_vb, vb]
systems: [nintendo-virtualboy]
notes: |

View File

@@ -6,7 +6,7 @@ upstream: "https://github.com/bsnes-emu/bsnes"
profiled_date: "2026-03-23"
core_version: "v10.6"
display_name: "Nintendo - SNES / SFC (bsnes-hd beta)"
cores: [bsnes_hd_beta]
cores: [bsnes_hd_beta, bsneshd]
systems:
- nintendo-snes
- nintendo-sgb

1248
emulators/clk.yml Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -8,6 +8,7 @@ core_version: "v1.7.4"
display_name: "Sega - MS/GG/MD/CD (Genesis Plus GX)"
cores:
- genesis_plus_gx
- genesisplusgx
systems:
- sega-megadrive
- sega-megacd

View File

@@ -8,6 +8,7 @@ core_version: "v1.7.4"
display_name: "Sega - MS/GG/MD/CD (Genesis Plus GX Wide)"
cores:
- genesis_plus_gx_wide
- genesisplusgxwide
systems:
- sega-megadrive
- sega-megacd

View File

@@ -1,5 +1,5 @@
emulator: Hatari
type: libretro
type: standalone + libretro
core_classification: frozen_snapshot
source: "https://github.com/libretro/hatari"
upstream: "https://github.com/hatari/hatari"

View File

@@ -1,5 +1,5 @@
emulator: MAME
type: libretro
type: standalone + libretro
core_classification: official_port
source: "https://github.com/libretro/mame"
upstream: "https://github.com/mamedev/mame"

View File

@@ -11,6 +11,7 @@ mame_version: "0.78 (plus backports)"
cores:
- mame2003_plus
- mame078plus
systems:
- snk-neogeo-mvs

View File

@@ -6,7 +6,7 @@ upstream: "https://mednafen.github.io/"
profiled_date: "2026-03-24"
core_version: "v1.31.0.0"
display_name: "NEC - PC Engine / CD (Beetle PCE FAST)"
cores: [mednafen_pce_fast]
cores: [mednafen_pce_fast, pce_fast]
systems: [nec-pc-engine]
verification: existence
notes: >

View File

@@ -9,7 +9,7 @@ source: "https://github.com/libretro/Mesen-S"
upstream: "https://github.com/SourMesen/Mesen-S"
profiled_date: "2026-03-24"
core_version: "0.4.0"
cores: [mesen-s]
cores: [mesen-s, mesen_s]
systems: [nintendo-snes, nintendo-gb, nintendo-gbc, nintendo-super-game-boy, nintendo-satellaview]
notes: >

View File

@@ -12,10 +12,11 @@ cores: [mupen64plus_next, mupen64plus_next_develop, mupen64plus_next_gles3, mupe
files:
- name: "IPL.n64"
path: "Mupen64plus/IPL.n64"
standalone_path: "64DD_IPL.bin"
aliases: ["64DD_IPL.bin"]
size: 4194304
required: false
description: "64DD IPL ROM"
note: "Only needed for N64 Disk Drive games (.ndd) via subsystem API. Accepts Z64, N64, and V64 byte-swap formats."
source_ref: "mupen64plus-core/src/main/main.c:940-1024"
- name: "font.ttf"

View File

@@ -6,7 +6,7 @@ upstream: "https://github.com/opentyrian/opentyrian"
profiled_date: "2026-03-24"
core_version: "1.0.0.6"
display_name: "Tyrian (OpenTyrian)"
cores: [opentyrian]
cores: [opentyrian, tyrian]
systems: [tyrian]
verification: existence

View File

@@ -6,7 +6,7 @@ upstream: "https://sourceforge.net/projects/vice-emu/"
profiled_date: "2026-03-25"
core_version: "3.10"
display_name: "Commodore - C128 (VICE x128)"
cores: [vice_x128]
cores: [vice_x128, x128]
systems: [commodore-c128]
notes: >
System ROMs embedded in binary: C128 kernal (318020-05), chargen (390059-01),

View File

@@ -6,7 +6,7 @@ upstream: "https://sourceforge.net/projects/vice-emu/"
profiled_date: "2026-03-25"
core_version: "3.10"
display_name: "Commodore - C64 (VICE x64, fast)"
cores: [vice_x64]
cores: [vice_x64, x64]
systems: [commodore-c64]
notes: >
System ROMs embedded in binary: C64 BASIC (901226-01), chargen (901225-01),

View File

@@ -6,7 +6,7 @@ upstream: "https://sourceforge.net/projects/vice-emu/"
profiled_date: "2026-03-25"
core_version: "3.10"
display_name: "Commodore - CBM-II 6x0/7x0 (VICE xcbm2)"
cores: [vice_xcbm2]
cores: [vice_xcbm2, xcbm2]
systems: [commodore-cbm-ii]
notes: >
System ROMs embedded in binary: chargen 600 (901237-01), chargen 700 (901232-01),

View File

@@ -6,7 +6,7 @@ upstream: "https://sourceforge.net/projects/vice-emu/"
profiled_date: "2026-03-25"
core_version: "3.10"
display_name: "Commodore - CBM-II 5x0 (VICE xcbm5x0)"
cores: [vice_xcbm5x0]
cores: [vice_xcbm5x0, xcbm5x0]
systems: [commodore-cbm-ii]
notes: >
System ROMs embedded in binary: chargen 500 (901225-01), kernal 500

View File

@@ -6,7 +6,7 @@ upstream: "https://sourceforge.net/projects/vice-emu/"
profiled_date: "2026-03-25"
core_version: "3.10"
display_name: "Commodore - PET (VICE xpet)"
cores: [vice_xpet]
cores: [vice_xpet, xpet]
systems: [commodore-pet]
notes: >
System ROMs embedded in binary: kernal 1 (901439-04-07), kernal 2 (901465-03),

View File

@@ -6,7 +6,7 @@ upstream: "https://sourceforge.net/projects/vice-emu/"
profiled_date: "2026-03-25"
core_version: "3.10"
display_name: "Commodore - PLUS/4 (VICE xplus4)"
cores: [vice_xplus4]
cores: [vice_xplus4, xplus4]
systems: [commodore-plus4]
notes: >
All system ROMs embedded in binary: BASIC (318006-01), kernal PAL (318004-05),

View File

@@ -6,7 +6,7 @@ upstream: "https://sourceforge.net/projects/vice-emu/"
profiled_date: "2026-03-25"
core_version: "3.10"
display_name: "Commodore - C64 SuperCPU (VICE xscpu64)"
cores: [vice_xscpu64]
cores: [vice_xscpu64, xscpu64]
systems: [commodore-c64-supercpu]
notes: >
Embedded SCPU64 ROM V0.07 (free replacement by Soci/Singular, 64 KB), chargen

View File

@@ -6,7 +6,7 @@ upstream: "https://sourceforge.net/projects/vice-emu/"
profiled_date: "2026-03-25"
core_version: "3.10"
display_name: "Commodore - VIC-20 (VICE xvic)"
cores: [vice_xvic]
cores: [vice_xvic, xvic]
systems: [commodore-vic20]
notes: >
System ROMs embedded in binary: BASIC (901486-01), chargen (901460-03),

View File

@@ -399,9 +399,10 @@ nav:
- PCSX-ReARMed: emulators/pcsx_rearmed.md
- Launchers (1):
- Dolphin Launcher: emulators/dolphin_launcher.md
- Other (10):
- Other (11):
- Beetle GBA (Mednafen): emulators/beetle_gba.md
- Cemu: emulators/cemu.md
- Clock Signal (CLK): emulators/clk.md
- ep128emu-core: emulators/ep128emu.md
- PCSX2: emulators/pcsx2.md
- Redream: emulators/redream.md

View File

@@ -5,6 +5,87 @@ source: "https://raw.githubusercontent.com/batocera-linux/batocera.linux/master/
base_destination: bios
hash_type: md5
verification_mode: md5
standalone_cores:
- abuse
- azahar
- bstone
- cannonball
- catacombgl
- cdogs
- cemu
- cgenius
- citron
- clk
- corsixth
- demul
- devilutionx
- dhewm3
- dolphin
- dxx-rebirth
- easyrpg
- ecwolf
- eduke32
- eka2l1
- etlegacy
- fallout1-ce
- fallout2-ce
- flatpak
- fury
- gsplus
- gzdoom
- hcl
- hurrican
- hypseus-singe
- ikemen
- ioquake3
- iortcw
- jazz2-native
- lexaloffle
- lindbergh-loader
- mame
- model2emu
- moonlight
- mupen64plus
- odcommander
- openbor
- openjazz
- openjk
- openjkdf2
- openmohaa
- pcsx2
- play
- ppsspp
- pygame
- pyxel
- raze
- rpcs3
- ruffle
- samcoupe
- scummvm
- sdlpop
- shadps4
- solarus
- sonic-mania
- sonic2013
- sonic3-air
- steam
- supermodel
- taradino
- theforceengine
- thextech
- tr1x
- tr2x
- tsugaru
- tyrian
- uqm
- vice
- vita3k
- vpinball
- x16emu
- xash3d_fwgs
- xemu
- xenia-canary
- yquake2
cores:
- 81
- a5200

View File

@@ -508,11 +508,20 @@ def resolve_platform_cores(
}
if isinstance(cores_config, list):
core_set = set(cores_config)
core_set = {str(c) for c in cores_config}
# Build reverse index: platform core name -> profile name
# Uses profile filename (dict key) + all names in cores: field
core_to_profile: dict[str, str] = {}
for name, p in profiles.items():
if p.get("type") == "alias":
continue
core_to_profile[name] = name
for core_name in p.get("cores", []):
core_to_profile[str(core_name)] = name
return {
name for name in profiles
if name in core_set
and profiles[name].get("type") != "alias"
core_to_profile[c]
for c in core_set
if c in core_to_profile
}
# Fallback: system ID intersection

View File

@@ -204,7 +204,7 @@ def _collect_emulator_extras(
if not u["in_repo"]:
continue
name = u["name"]
dest = name
dest = u.get("path") or name
full_dest = f"{base_dest}/{dest}" if base_dest else dest
if full_dest in seen:
continue

View File

@@ -109,8 +109,9 @@ def generate_readme(db: dict, platforms_dir: str) -> str:
lines = [
"# RetroBIOS",
"",
f"Complete BIOS and firmware packs for RetroArch, Batocera, Recalbox, Lakka,"
f" RetroPie, EmuDeck, RetroBat, and RetroDECK.",
f"Complete BIOS and firmware packs for "
f"{', '.join(c['platform'] for c in sorted(coverages.values(), key=lambda x: x['platform'])[:-1])}"
f", and {sorted(coverages.values(), key=lambda x: x['platform'])[-1]['platform']}.",
"",
f"**{total_files:,}** verified files across **{len(system_ids)}** systems,"
f" ready to extract into your emulator's BIOS directory.",

View File

@@ -127,8 +127,12 @@ class Scraper(BaseScraper):
def __init__(self, url: str = SOURCE_URL):
super().__init__(url=url)
def _fetch_cores(self) -> list[str]:
"""Extract core names from Batocera configgen-defaults.yml."""
def _fetch_cores(self) -> tuple[list[str], list[str]]:
"""Extract core names and standalone cores from configgen-defaults.yml.
Returns (all_cores, standalone_cores) where standalone_cores are
those with emulator != "libretro".
"""
try:
req = urllib.request.Request(
CONFIGGEN_DEFAULTS_URL,
@@ -142,13 +146,19 @@ class Scraper(BaseScraper):
) from e
data = yaml.safe_load(raw)
cores: set[str] = set()
standalone: set[str] = set()
for system, cfg in data.items():
if system == "default" or not isinstance(cfg, dict):
continue
core = cfg.get("core")
emulator = cfg.get("emulator", "")
core = cfg.get("core", "")
if core:
cores.add(core)
return sorted(cores)
if emulator and emulator != "libretro":
standalone.add(emulator)
if core and core != emulator:
standalone.add(core)
return sorted(cores), sorted(standalone)
def _extract_systems_dict(self, raw: str) -> dict:
"""Extract and parse the 'systems' dict from the Python source via ast.literal_eval."""
@@ -295,7 +305,8 @@ class Scraper(BaseScraper):
if num.isdigit():
batocera_version = num
return {
cores, standalone = self._fetch_cores()
result = {
"platform": "Batocera",
"version": batocera_version or "",
"homepage": "https://batocera.org",
@@ -303,9 +314,12 @@ class Scraper(BaseScraper):
"base_destination": "bios",
"hash_type": "md5",
"verification_mode": "md5",
"cores": self._fetch_cores(),
"cores": cores,
"systems": systems,
}
if standalone:
result["standalone_cores"] = standalone
return result
def main():

View File

@@ -227,6 +227,7 @@ def find_undeclared_files(
profiles = emu_profiles if emu_profiles is not None else load_emulator_profiles(emulators_dir)
relevant = resolve_platform_cores(config, profiles)
standalone_set = set(str(c) for c in config.get("standalone_cores", []))
undeclared = []
seen = set()
for emu_name, profile in sorted(profiles.items()):
@@ -235,21 +236,36 @@ def find_undeclared_files(
if emu_name not in relevant:
continue
# Check if this profile is standalone: match profile name or any cores: alias
is_standalone = emu_name in standalone_set or bool(
standalone_set & {str(c) for c in profile.get("cores", [])}
)
for f in profile.get("files", []):
fname = f.get("name", "")
if not fname or fname in seen:
continue
# Skip standalone-only files for libretro platforms
if f.get("mode") == "standalone":
# Mode filtering: skip files incompatible with platform's usage
file_mode = f.get("mode")
if file_mode == "standalone" and not is_standalone:
continue
if file_mode == "libretro" and is_standalone:
continue
if fname in declared_names:
continue
# Determine destination path based on mode
if is_standalone:
dest = f.get("standalone_path") or f.get("path") or fname
else:
dest = f.get("path") or fname
in_repo = fname in by_name or fname.rsplit("/", 1)[-1] in by_name
seen.add(fname)
undeclared.append({
"emulator": profile.get("emulator", emu_name),
"name": fname,
"path": dest,
"required": f.get("required", False),
"hle_fallback": f.get("hle_fallback", False),
"category": f.get("category", "bios"),

View File

@@ -1098,5 +1098,83 @@ class TestE2E(unittest.TestCase):
self.assertIn("d.bin", names)
def test_108_standalone_path_in_undeclared(self):
"""Undeclared files use standalone_path when core is in standalone_cores."""
# Create a platform with standalone_cores
config = {
"platform": "TestStandalone",
"verification_mode": "existence",
"cores": ["test_emu"],
"standalone_cores": ["test_emu"],
"systems": {
"console-a": {
"files": [
{"name": "present_req.bin", "destination": "present_req.bin",
"required": True},
],
},
},
}
with open(os.path.join(self.platforms_dir, "test_standalone.yml"), "w") as fh:
yaml.dump(config, fh)
# Create emulator with standalone_path divergence
emu = {
"emulator": "TestStandaloneEmu",
"type": "standalone + libretro",
"cores": ["test_emu"],
"systems": ["console-a"],
"files": [
{"name": "libretro_file.bin", "path": "subdir/libretro_file.bin",
"standalone_path": "flat_file.bin", "required": True},
{"name": "standalone_only.bin", "mode": "standalone", "required": False},
{"name": "libretro_only.bin", "mode": "libretro", "required": False},
],
}
with open(os.path.join(self.emulators_dir, "test_standalone_emu.yml"), "w") as fh:
yaml.dump(emu, fh)
config = load_platform_config("test_standalone", self.platforms_dir)
profiles = load_emulator_profiles(self.emulators_dir)
undeclared = find_undeclared_files(config, self.emulators_dir, self.db, profiles)
by_name = {u["name"]: u for u in undeclared}
# standalone_path used for undeclared file (core is standalone)
self.assertIn("libretro_file.bin", by_name)
self.assertEqual(by_name["libretro_file.bin"]["path"], "flat_file.bin")
# standalone-only file IS included (core is standalone)
self.assertIn("standalone_only.bin", by_name)
# libretro-only file is EXCLUDED (core is standalone)
self.assertNotIn("libretro_only.bin", by_name)
def test_109_no_standalone_cores_uses_libretro_path(self):
"""Without standalone_cores, undeclared files use path: (libretro)."""
config = load_platform_config("test_existence", self.platforms_dir)
profiles = load_emulator_profiles(self.emulators_dir)
undeclared = find_undeclared_files(config, self.emulators_dir, self.db, profiles)
# standalone_only.bin should be excluded (platform has no standalone_cores)
names = {u["name"] for u in undeclared}
self.assertNotIn("standalone_only.bin", names)
def test_110_cores_alias_reverse_index(self):
"""resolve_platform_cores matches via cores: field aliases."""
emu = {
"emulator": "TestAliasCore",
"type": "libretro",
"cores": ["test_alias_core", "shortname"],
"systems": ["console-a"],
"files": [],
}
with open(os.path.join(self.emulators_dir, "test_alias_core.yml"), "w") as fh:
yaml.dump(emu, fh)
config = {"cores": ["shortname"]}
profiles = load_emulator_profiles(self.emulators_dir)
resolved = resolve_platform_cores(config, profiles)
self.assertIn("test_alias_core", resolved)
if __name__ == "__main__":
unittest.main()