From a8bfadd4c1e93ba2c2ecd0ba187b9927915722e2 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Sat, 17 Oct 2026 15:51:39 +0200 Subject: [PATCH] feat: list recalbox cores per board Recalbox builds seven board images with different emulators. The new target scraper reads each board's defconfig against the package every system.ini core names. The names that reached no profile are mapped to the projects Recalbox builds or profiled from their source, and the mupen64plus video plugins are set aside. --- emulators/dolphin.yml | 1 + emulators/eightyone.yml | 56 + emulators/exel-hence.yml | 34 + emulators/flycast.yml | 1 + emulators/frotz.yml | 33 + emulators/genesis_plus_gx.yml | 1 + emulators/hypseus-singe.yml | 1 + emulators/julius.yml | 108 ++ emulators/lexaloffle.yml | 1 + emulators/lrps2.yml | 2 +- emulators/mednafen_stv.yml | 124 ++ emulators/rb5000.yml | 41 + emulators/reicast.yml | 63 + emulators/sugarbox2.yml | 36 + emulators/vice_x64sc.yml | 2 +- emulators/vvvvvv.yml | 50 + platforms/_registry.yml | 4 +- platforms/targets/_overrides.yml | 24 + platforms/targets/recalbox.yml | 1204 +++++++++++++++++ scripts/scraper/recalbox_scraper.py | 75 +- .../targets/recalbox_targets_scraper.py | 126 ++ tests/test_target_scrapers.py | 34 + 22 files changed, 1997 insertions(+), 24 deletions(-) create mode 100644 emulators/eightyone.yml create mode 100644 emulators/exel-hence.yml create mode 100644 emulators/frotz.yml create mode 100644 emulators/julius.yml create mode 100644 emulators/mednafen_stv.yml create mode 100644 emulators/rb5000.yml create mode 100644 emulators/reicast.yml create mode 100644 emulators/sugarbox2.yml create mode 100644 emulators/vvvvvv.yml create mode 100644 platforms/targets/recalbox.yml create mode 100644 scripts/scraper/targets/recalbox_targets_scraper.py diff --git a/emulators/dolphin.yml b/emulators/dolphin.yml index ff58f6d0..1ba1d8c7 100644 --- a/emulators/dolphin.yml +++ b/emulators/dolphin.yml @@ -10,6 +10,7 @@ core_version: "Git" display_name: "Nintendo - GameCube / Wii (Dolphin)" cores: - dolphin + - "dolphin-gui" systems: [nintendo-gamecube, nintendo-wii, sega-triforce] # path: is where the libretro core reads a file, standalone_path: where the diff --git a/emulators/eightyone.yml b/emulators/eightyone.yml new file mode 100644 index 00000000..ae543c87 --- /dev/null +++ b/emulators/eightyone.yml @@ -0,0 +1,56 @@ +emulator: EightyOne (Recalbox) +type: libretro +core_classification: community_fork +source: "https://gitlab.com/recalbox/packages/recalbox-cores/libretro-eightyone" +upstream: "https://github.com/charlierobson/EightyOne" +profiled_date: "2026-10-17" +source_commit: "39ea0cc26453f4026458cfff62f10223a6bb1724" +core_version: "v1.2" +display_name: "Sinclair - ZX81 / ZX Spectrum (EightyOne, Recalbox)" +cores: ["eightyone"] +systems: + - sinclair-zx81 + - sinclair-zx80 + - sinclair-zx-spectrum + - jupiter-ace + - timex-ts1500 + - timex-tc2048 + - timex-ts2068 + - lambda-8300 + - ringo-r470 + - microdigital-tk85 + +notes: | + Recalbox port of EightyOne, vendored under 81/ and built with CMake. + Machines: ZX80, ZX81, ZX97LE, TS1000, TS1500, TK85, Lambda 8300, + Ringo R470, Spectrum 16K/48K/+/128K/+2/+2A/+3, TC2048, TC2068, TS2068, + Jupiter ACE. + ref: libretro/libretro_core.cpp:876-1074 + + The ROM directory is /eightyone/ with ROMs/, FDCs/ and IDEs/ + subfolders. + ref: libretro/libretro_core.cpp:2620-2631 + + 86 machine, interface, IDE, graphics and speech ROMs are compiled in as + zlib-compressed arrays and looked up by basename. + ref: libretro/eo_roms.cpp:16-103, libretro/eo_roms.cpp:106-128 + + memory_load, memoryLoadToAddress and font_load query the embedded table + before opening any path, and every ROM name the core requests is in that + table, so no ROM is read from the system directory. + ref: 81/Source/zx81/snap.cpp:1189-1199, 81/Source/zx81/snap.cpp:1285-1299, 81/Source/zx81/snap.cpp:1317-1328, 81/Source/zx81config.cpp:161-210, 81/Source/zx81config.cpp:259-284 + + Didaktik D40/D80 tests for eightyone/FDCs/didaktik.gama.rom and stores its + full path in CurRom when present; memory_load reduces the path to its + basename and serves the embedded copy. + ref: libretro/libretro_core.cpp:2953-2968, 81/Source/Spectrum/spec48.cpp:361-362 + + ZXpand writes zxpand-eeprom.bin (256 bytes of 0xFF) under the ROM + directory when it is absent. + ref: 81/Source/zxpand/smbsd.cpp:75-98 + + Digitalker is a stub and its SSR ROM loader is not compiled; CapsImg is + linked statically. + ref: libretro/eo_stubs.cpp:67-68, CMakeLists.txt:267-291 + +files: [] diff --git a/emulators/exel-hence.yml b/emulators/exel-hence.yml new file mode 100644 index 00000000..85b3e385 --- /dev/null +++ b/emulators/exel-hence.yml @@ -0,0 +1,34 @@ +emulator: Exel-Hence +type: libretro +core_classification: pure_libretro +source: "https://gitlab.com/recalbox/packages/recalbox-cores/libretro-exel-hence" +upstream: "https://gitlab.com/recalbox/packages/recalbox-cores/libretro-exel-hence" +profiled_date: "2026-10-17" +source_commit: "213328332cbfd8121e018493443e223a7a780961" +core_version: "v0.9" +display_name: "Exelvision - EXL 100 (Exel-Hence)" +cores: + - "exel-hence" +systems: + - "exelvision-exl100" + +notes: | + Exelvision EXL 100 emulator written as a libretro core by Recalbox. + + retro_load_game() asks for the system directory and hands it to + Exl100LoadRoms() (ref: src/libretro.c:710-734), which ignores it: the + main and sub CPU BIOS images are unpacked from arrays compiled into the + core (ref: src/exl100.c:1163-1181). The images are raw DEFLATE streams + XOR'd against a rotating key and inflated at load time + (ref: src/exl100_bios.c:56-101). Both arrays are present in the + shipped header (ref: src/exl100_bios.h:6703-6704). Exelmemoria, the + ExelBASIC and ExelBASIC+ cartridges and the ExelDisk DOS come from the same embedded + tables (ref: src/libretro.c:508, 533, 562). + + The only file opened by name is a sidecar next to a .cram content file, + cram.txt or .txt, scanned for a CALL EXEC or RUN + line to auto-start the program (ref: src/libretro.c:614-686). It + belongs to the content. + +exclusion_note: "BIOS, BASIC cartridges and ExelDisk DOS compiled into the core; the system directory is never read" +files: [] diff --git a/emulators/flycast.yml b/emulators/flycast.yml index 831c6a13..8ef285db 100644 --- a/emulators/flycast.yml +++ b/emulators/flycast.yml @@ -12,6 +12,7 @@ cores: - flycast - flycast_dev - flycastvl + - "flycast-next" systems: - sega-dreamcast - sega-naomi diff --git a/emulators/frotz.yml b/emulators/frotz.yml new file mode 100644 index 00000000..62623cad --- /dev/null +++ b/emulators/frotz.yml @@ -0,0 +1,33 @@ +emulator: Frotz +type: standalone +core_classification: game_engine +source: "https://gitlab.com/DavidGriffith/frotz" +upstream: "https://gitlab.com/DavidGriffith/frotz" +profiled_date: "2026-10-17" +source_commit: "2f17d64f23f3d6e68cd676d35c9d1789e17bc9fc" +core_version: "2.55pre" +display_name: "Infocom Z-Machine (SDL Frotz)" +cores: + - "frotz" +systems: + - "zmachine" + +notes: | + Z-machine interpreter for Infocom and Inform story files (versions 1 + to 8). Recalbox builds the SDL front end, sfrotz, and launches it with + a generated config file and the story file. + + sf_initfonts() builds the text and fixed-width fonts from the bitmap + compiled into sf_deffont.c (ref: src/sdl/sf_deffont.c:22) and the + character graphics font from SF_font3 (ref: src/sdl/sf_fonts.c:979-989). + TrueType fonts are loaded only when the config file names them under + [Fonts] (ref: src/sdl/sf_resource.c:402-413), and a font that fails to + load is reported and skipped (ref: src/sdl/sf_fonts.c:959-973). No + default font path exists. + + Pictures and sounds come from the story's Blorb data or from loose + resource files under the [Resources] Dir setting, "./" by default + (ref: src/sdl/sf_resource.c:75, 415). Both belong to the game. + +exclusion_note: "fonts compiled into the binary; external TrueType fonts only when the user config names them" +files: [] diff --git a/emulators/genesis_plus_gx.yml b/emulators/genesis_plus_gx.yml index 41b5395e..68c9949c 100644 --- a/emulators/genesis_plus_gx.yml +++ b/emulators/genesis_plus_gx.yml @@ -12,6 +12,7 @@ cores: - genesisplusgx - genesisplusgx-expanded - genplus + - "genesisplusgx_ex" systems: - sega-megadrive - sega-megacd diff --git a/emulators/hypseus-singe.yml b/emulators/hypseus-singe.yml index 14da2f43..0b40c89d 100644 --- a/emulators/hypseus-singe.yml +++ b/emulators/hypseus-singe.yml @@ -10,6 +10,7 @@ display_name: "Arcade (Hypseus Singe)" cores: - hypseus-singe - hypseus + - "hypseus_singe" systems: - arcade-laserdisc mode: standalone diff --git a/emulators/julius.yml b/emulators/julius.yml new file mode 100644 index 00000000..5fdb2c20 --- /dev/null +++ b/emulators/julius.yml @@ -0,0 +1,108 @@ +emulator: Julius +type: standalone +core_classification: game_engine +source: "https://github.com/bvschaik/julius" +upstream: "https://github.com/bvschaik/julius" +profiled_date: "2026-10-17" +source_commit: "33c777019b2640b847b73815c3b4344cf7bd9576" +core_version: "1.7.0" +display_name: "Caesar III (Julius)" +cores: + - "julius" +systems: + - "caesar3" + +notes: | + Open-source reimplementation of Impressions Games' Caesar III engine. + It reads every graphic, text, sound and scenario from an installed copy + of the game; nothing is bundled. + + The data directory is the command-line argument. pre_init() makes it + the base path and runs game_pre_init(); a missing directory shows + "Julius requires the original files from Caesar 3" and exits + (ref: src/platform/julius.c:488-501, 580-586). File lookups are + case-corrected inside that directory. + + Startup loads, in order, the text and message files c3.eng and + c3_mm.eng (c3.rus and c3_mm.rus as the alternative pair) + (ref: src/game/game.c:48-63, src/core/lang.c:141-165), the central + climate graphics c3.sg2 and c3.555, the barbarian enemy graphics + goths.sg2 and goths.555, and c3_model.txt (ref: src/game/game.c:79-104). + Any of them missing aborts init. The_empire.555 is loaded with the + climate graphics; a wrong size is logged and the game continues + (ref: src/core/image.c:283-293, 323). The other climates, enemies, + sounds, videos and scenario maps are opened from the same directory on + demand. + + Recalbox lists Caesar III under roms/ports/Caesar3 with c3.exe as the + entry and patches config and save paths into /recalbox/share. + +files: + - name: "caesar3/" + type: directory + path: "caesar3/" + system: "caesar3" + category: game_data + required: true + description: "Caesar III install directory" + source_ref: "src/platform/julius.c:488-501" + note: "Given on the command line; every game file is opened by name inside it." + - name: "c3.eng" + path: "caesar3/c3.eng" + system: "caesar3" + category: game_data + required: true + description: "Caesar III interface text" + source_ref: "src/core/lang.c:23, 74-80, 141-165" + note: "Rejected when outside MIN_TEXT_SIZE..MAX_TEXT_SIZE. c3.rus replaces it on a Russian install." + - name: "c3_mm.eng" + path: "caesar3/c3_mm.eng" + system: "caesar3" + category: game_data + required: true + description: "Caesar III message text" + source_ref: "src/core/lang.c:24, 129-150, 160-165" + note: "Loaded as a pair with c3.eng; c3_mm.rus replaces it on a Russian install." + - name: "c3.sg2" + path: "caesar3/c3.sg2" + system: "caesar3" + category: game_data + required: true + description: "Main graphics index, central climate" + source_ref: "src/core/image.c:48-52, 295-306, src/game/game.c:83-86" + - name: "c3.555" + path: "caesar3/c3.555" + system: "caesar3" + category: game_data + required: true + description: "Main graphics bitmaps, central climate" + source_ref: "src/core/image.c:53-57, 314-317, src/game/game.c:83-86" + - name: "goths.sg2" + path: "caesar3/goths.sg2" + system: "caesar3" + category: game_data + required: true + description: "Barbarian enemy graphics index" + source_ref: "src/core/image.c:79, 667-675, src/game/game.c:87-90" + - name: "goths.555" + path: "caesar3/goths.555" + system: "caesar3" + category: game_data + required: true + description: "Barbarian enemy graphics bitmaps" + source_ref: "src/core/image.c:101, 681-684, src/game/game.c:87-90" + - name: "c3_model.txt" + path: "caesar3/c3_model.txt" + system: "caesar3" + category: game_data + required: true + description: "Building cost and desirability model" + source_ref: "src/building/model.c:83-88, src/game/game.c:101-104" + - name: "The_empire.555" + path: "caesar3/The_empire.555" + system: "caesar3" + category: game_data + required: false + description: "Empire map bitmap" + source_ref: "src/core/image.c:68, 283-293" + note: "A short read is logged and the empire map stays blank." diff --git a/emulators/lexaloffle.yml b/emulators/lexaloffle.yml index 8ad178fa..1b05a8f2 100644 --- a/emulators/lexaloffle.yml +++ b/emulators/lexaloffle.yml @@ -7,6 +7,7 @@ cores: - lexaloffle - "pico-8" - vox_official + - "pico8" systems: - pico8 - voxatron diff --git a/emulators/lrps2.yml b/emulators/lrps2.yml index f2f2f8b3..baee9ab5 100644 --- a/emulators/lrps2.yml +++ b/emulators/lrps2.yml @@ -12,7 +12,7 @@ profiled_date: "2026-03-25" source_commit: "6eabf2242d451ee2e6d52b1e3f10efb77143ed05" core_version: "Git" display_name: "Sony - PlayStation 2 (LRPS2)" -cores: [lrps2, pcsx2] +cores: [lrps2, pcsx2, "ps2"] systems: [sony-playstation-2] bios_directory: "pcsx2/bios/" diff --git a/emulators/mednafen_stv.yml b/emulators/mednafen_stv.yml new file mode 100644 index 00000000..788b2110 --- /dev/null +++ b/emulators/mednafen_stv.yml @@ -0,0 +1,124 @@ +emulator: Mednafen STV +type: libretro +core_classification: community_fork +source: "https://github.com/beudbeud/libretro-stv" +upstream: "https://mednafen.github.io/" +profiled_date: "2026-10-17" +source_commit: "2e42ef0c5d8238c170bcf182e68951c214fc6867" +core_version: "v1.2.1" +display_name: "Sega - ST-V (Mednafen STV)" +cores: + - "mednafen_stv" +systems: + - "sega-stv" + - "sega-saturn" + +notes: | + Libretro core by beudbeud built on the Saturn/ST-V module of Mednafen + 1.32.1, with an Asia ST-V BIOS setting and a stvbios.zip fallback added + to ss.cpp. Recalbox runs it for ST-V; the loader also accepts Saturn + disc images (cue, toc, ccd, chd, m3u). + + retro_init() points filesys.path_firmware at the frontend system + directory and names the BIOS of every region + (ref: libretro/libretro.cpp:615-641). The ST-V BIOS is picked by SMPC + area: Japan, Asia NTSC, Europe PAL, and North America for every other + area (ref: mednafen/ss/ss.cpp:1436-1446, mednafen/ss/smpc.h:32-40). + A Saturn disc takes sega_101.bin for Japan and Asia NTSC and + mpr-17933.bin otherwise (ref: mednafen/ss/ss.cpp:1447-1453). + + The loose file is tried first and must be exactly 524288 bytes; when it + is missing or the wrong size, the same name is looked up at the root of + stvbios.zip in the system directory, then without the leading slash + (ref: mednafen/ss/ss.cpp:1455-1491). With ss.bios_sanity on, the + default, a file named like one of the five Saturn BIOS images must + match its SHA-256; the ST-V images are not in that table and only the + size check applies (ref: mednafen/ss/ss.cpp:1493-1540, 2549). + + Saturn games that need the KoF 95 or Ultraman ROM cartridge, and the + Action Replay 4M Plus cart type, open their ROM from the firmware + directory under the default names below + (ref: mednafen/ss/ss.cpp:1348-1350, 1404-1412, 2542-2544). + +files: + - name: "epr-20091.ic8" + system: "sega-stv" + region: [japan] + required: true + size: 524288 + validation: [size] + description: "ST-V BIOS (Japan)" + source_ref: "libretro/libretro.cpp:636, mednafen/ss/ss.cpp:1438-1439, 1462-1468" + - name: "epr-19854.ic8" + system: "sega-stv" + region: [asia-ntsc] + required: true + size: 524288 + validation: [size] + description: "ST-V BIOS (Asia/Taiwan)" + source_ref: "libretro/libretro.cpp:637, mednafen/ss/ss.cpp:1440-1441, 1462-1468" + - name: "epr-17952a.ic8" + system: "sega-stv" + region: [north-america, latin-america, brazil, south-korea, asia-pal] + required: true + size: 524288 + validation: [size] + description: "ST-V BIOS (North America)" + source_ref: "libretro/libretro.cpp:638, mednafen/ss/ss.cpp:1444-1445, 1462-1468" + note: "Used for every SMPC area other than Japan, Asia NTSC and Europe PAL." + - name: "epr-17954a.ic8" + system: "sega-stv" + region: [europe] + required: true + size: 524288 + validation: [size] + description: "ST-V BIOS (Europe)" + source_ref: "libretro/libretro.cpp:639, mednafen/ss/ss.cpp:1442-1443, 1462-1468" + - name: "stvbios.zip" + system: "sega-stv" + category: bios_zip + required: false + description: "ST-V BIOS archive" + source_ref: "mednafen/ss/ss.cpp:1472-1491" + note: "Read only when the loose BIOS file is missing or not 524288 bytes. The member is found by the BIOS file name; its size is not checked." + contents: + - name: "epr-20091.ic8" + - name: "epr-19854.ic8" + - name: "epr-17952a.ic8" + - name: "epr-17954a.ic8" + - name: "sega_101.bin" + system: "sega-saturn" + region: [japan, asia-ntsc] + required: true + size: 524288 + sha256: "dcfef4b99605f872b6c3b6d05c045385cdea3d1b702906a0ed930df7bcb7deac" + validation: [size, sha256] + description: "Saturn BIOS (Japan)" + source_ref: "libretro/libretro.cpp:640, mednafen/ss/ss.cpp:1449-1450, 1507, 1520-1521" + - name: "mpr-17933.bin" + system: "sega-saturn" + region: [north-america, latin-america, brazil, south-korea, asia-pal, europe] + required: true + size: 524288 + sha256: "96e106f740ab448cf89f0dd49dfbac7fe5391cb6bd6e14ad5e3061c13330266f" + validation: [size, sha256] + description: "Saturn BIOS (North America/Europe)" + source_ref: "libretro/libretro.cpp:641, mednafen/ss/ss.cpp:1451-1452, 1509, 1520-1521" + - name: "mpr-18811-mx.ic1" + system: "sega-saturn" + required: false + description: "The King of Fighters '95 ROM cartridge" + source_ref: "mednafen/ss/ss.cpp:1348, 1404-1412, 2542" + note: "Opened only for the game that needs the cart; that game does not start without it." + - name: "mpr-19367-mx.ic1" + system: "sega-saturn" + required: false + description: "Ultraman ROM cartridge" + source_ref: "mednafen/ss/ss.cpp:1349, 1404-1412, 2543" + note: "Opened only for the game that needs the cart; that game does not start without it." + - name: "satar4mp.bin" + system: "sega-saturn" + required: false + description: "Action Replay 4M Plus firmware" + source_ref: "mednafen/ss/ss.cpp:1350, 1404-1412, 2544" + note: "Opened only when the Action Replay 4M Plus cart type is selected." diff --git a/emulators/rb5000.yml b/emulators/rb5000.yml new file mode 100644 index 00000000..d564d285 --- /dev/null +++ b/emulators/rb5000.yml @@ -0,0 +1,41 @@ +emulator: RB5000 +type: standalone + libretro +core_classification: official_port +source: + standalone: "https://gitlab.com/recalbox/packages/standalone/rb5000" + libretro: "https://gitlab.com/recalbox/packages/recalbox-cores/libretro-rb5000" +upstream: "https://gitlab.com/recalbox/packages/standalone/rb5000" +profiled_date: "2026-10-17" +source_commit: + standalone: "8c75a4b48a5222b0332d0508600f8ddc7ac1191c" + libretro: "7464996aa0cb1be77abf495dc901117e1af36e02" +core_version: + standalone: "v1.1" + libretro: "v0.9" +display_name: "Philips - VG 5000 (RB5000)" +cores: + - "rb5000" +systems: + - "philips-vg5k" + +notes: | + Philips VG 5000 emulator by Recalbox, a C++ fork of Daniel Coulom's + DCVG5K v2. The standalone build and the libretro core live in + two repositories and share the same Engine. + + The 16 KB system ROM is not a file. It is encoded in the + Video::sPixels image table compiled into the binary, one ROM bit per + entry as bit 0 XOR bit 5 (ref: source/Data.cpp:2). The Engine + constructor copies that table into sMirror (ref: source/Engine.cpp:54) + and Z80ReadW() rebuilds each ROM byte from eight table entries the + first time the CPU reads it, caching it in mMemory + (ref: source/Engine.cpp:379-389). The libretro core carries the same + table and decoder. + + The libretro core can map a user cartridge file over 0x0000-0x3FFF + when one is configured (ref: source/Engine.cpp:302-316); that file is + content. The on-screen display font is an Ubuntu TTF from the host + system fonts directory (ref: source/Video.cpp:90-92). + +exclusion_note: "VG 5000 system ROM is encoded in a compiled-in image table and decoded on first access; no file is read" +files: [] diff --git a/emulators/reicast.yml b/emulators/reicast.yml new file mode 100644 index 00000000..ee4826ed --- /dev/null +++ b/emulators/reicast.yml @@ -0,0 +1,63 @@ +emulator: Reicast +type: standalone +core_classification: community_fork +source: "https://github.com/reicast/reicast-emulator" +upstream: "https://github.com/reicast/reicast-emulator" +profiled_date: "2026-10-17" +source_commit: "ad61f95dd6b4c6218846a25de6194550311a5d28" +core_version: "Git" +display_name: "Sega - Dreamcast (Reicast)" +cores: + - "reicast" +systems: + - "sega-dreamcast" + +notes: | + Dreamcast emulator descended from nullDC. The Linux build is compiled + for one platform; the default is dreamcast, which sets the ROM prefix + dc_, a 2 MB boot ROM, a 128 KB flash and NVR_OPTIONAL 0 + (ref: core/types.h:209-217). + + LoadRomFiles() tries dc_boot.bin, dc_boot.bin.bin, dc_bios.bin and + dc_bios.bin.bin for the boot ROM, then dc_nvmem.bin, dc_flash_wb.bin, + dc_flash.bin and dc_flash.bin.bin for the flash + (ref: core/hw/holly/sb_mem.cpp:30-51). Each candidate is accepted + when a full chip's worth of bytes can be read from it + (ref: core/hw/flashrom/flashrom.h:39-49, 61-99). No hash is checked. + With NVR_OPTIONAL 0 a missing flash fails LoadRomFiles() the same way + a missing boot ROM does. + + When LoadRomFiles() fails, or the reios setting is on, the core starts + its HLE BIOS instead and loads only the flash, if present + (ref: core/nullDC.cpp:183-200, core/hw/holly/sb_mem.cpp:58-64). + + Upstream resolves the directory as /data/, the data dir + being $XDG_DATA_HOME/reicast or ~/.local/share/reicast, then each + $XDG_DATA_DIRS entry plus /reicast (ref: core/stdclass.cpp:83-100, + core/linux-dist/main.cpp:287-326, 355-379). Recalbox patches + nullDC.cpp to read and write both files in /recalbox/share/bios/dc/, + which is the path given below. + +files: + - name: "dc_boot.bin" + path: "dc/dc_boot.bin" + aliases: ["dc_boot.bin.bin", "dc_bios.bin", "dc_bios.bin.bin"] + system: "sega-dreamcast" + required: false + hle_fallback: true + min_size: 2097152 + validation: [size] + description: "Dreamcast boot ROM" + source_ref: "core/hw/holly/sb_mem.cpp:20, 30-36, core/types.h:211, 215" + note: "First of the four names that reads 2097152 bytes wins. Missing, the HLE BIOS runs." + - name: "dc_nvmem.bin" + path: "dc/dc_nvmem.bin" + aliases: ["dc_flash_wb.bin", "dc_flash.bin", "dc_flash.bin.bin"] + system: "sega-dreamcast" + required: false + hle_fallback: true + min_size: 131072 + validation: [size] + description: "Dreamcast flash memory" + source_ref: "core/hw/holly/sb_mem.cpp:22-23, 37-48, 58-64, core/types.h:212, 217" + note: "Written back as dc_nvmem.bin on exit. Missing, LoadRomFiles() fails even with a boot ROM present and the HLE BIOS runs." diff --git a/emulators/sugarbox2.yml b/emulators/sugarbox2.yml new file mode 100644 index 00000000..8d584c42 --- /dev/null +++ b/emulators/sugarbox2.yml @@ -0,0 +1,36 @@ +emulator: Sugarbox2 +type: libretro +core_classification: community_fork +source: "https://gitlab.com/recalbox/packages/recalbox-cores/libretro-sugarbox2" +upstream: "https://github.com/Tom1975/SugarboxV2" +profiled_date: "2026-10-17" +source_commit: "6b565a43a25e4dfb69867f7e927a707e15ea460f" +core_version: "v0.9" +display_name: "Amstrad - CPC / GX4000 (Sugarbox2)" +cores: + - "sugarbox2" +systems: + - "amstrad-cpc" + +notes: | + Libretro core by Recalbox wrapping Tom1975's SugarboxV2 + (CPC 464, 664, 6128, 464+, 6128+ and GX-4000). Recalbox 10.1.1 builds + tag v1.5 from a token-gated copy of this repository; the public + repository carries v0.9 only, which is the revision read here. + + Every ROM is compiled into the core as a zlib stream: OS and BASIC for + each model and language, AMSDOS, the Plus cartridge plus_en.cpr, the + CP/M Plus, FutureOS, SymbOS and utility expansion ROMs, 57 blobs in all + (ref: libretro/RomData.h:1-15). + rom_uncompress() inflates them when the machine is built + (ref: libretro/LibretroCore.cpp:1098-1107) and they are handed to the + memory map directly, never through a path + (ref: libretro/LibretroCore.cpp:1173-1174). + + The keyboard map files the engine reads are written from embedded data + into the save directory, only when missing: CONF/KeyboardMaps.ini and + Keyboards/101_keyboard_linux + (ref: libretro/LibretroCore.cpp:1477-1502, 2169-2173). + +exclusion_note: "all CPC, Plus and GX-4000 ROMs compiled into the core as zlib blobs; keyboard maps written by the core into the save directory" +files: [] diff --git a/emulators/vice_x64sc.yml b/emulators/vice_x64sc.yml index 9d96f1bb..a45aafd2 100644 --- a/emulators/vice_x64sc.yml +++ b/emulators/vice_x64sc.yml @@ -7,7 +7,7 @@ profiled_date: "2026-03-25" source_commit: "f63b56688f3133a2bb17499db9eebb7ab81df5f5" core_version: "3.10" display_name: "Commodore - C64 (VICE x64sc, accurate)" -cores: [vice_x64sc] +cores: [vice_x64sc, "x64sx"] systems: [commodore-c64] notes: > Cycle-accurate C64 emulator using the viciisc VIC-II implementation. diff --git a/emulators/vvvvvv.yml b/emulators/vvvvvv.yml new file mode 100644 index 00000000..26cbf29b --- /dev/null +++ b/emulators/vvvvvv.yml @@ -0,0 +1,50 @@ +emulator: VVVVVV +type: standalone +core_classification: game_engine +source: "https://github.com/TerryCavanagh/VVVVVV" +upstream: "https://github.com/TerryCavanagh/VVVVVV" +profiled_date: "2026-10-17" +source_commit: "a0bd2f3da42da2f5249e2a4892e2b48b51b2fd86" +core_version: "v2.5" +display_name: "VVVVVV" +cores: + - "vvvvvv" +systems: + - "vvvvvv" + +notes: | + Terry Cavanagh's VVVVVV game, desktop build. The source release holds + the engine, the level data, the language files and the fonts; graphics, + music and sounds are in data.zip, which is not in the repository. + + FILESYSTEM_init() mounts data.zip from the -assets argument, or from + the binary's directory when no argument is given, and exits with a + "data.zip missing!" message box when the mount fails + (ref: desktop_version/src/FileSystemUtils.cpp:318-346). Recalbox + passes -assets /recalbox/share/roms/ports/vvvvvv/data.zip. The game + accepts the data.zip of the retail release or of the free Make and + Play Edition (ref: desktop_version/src/FileSystemUtils.cpp:332-335). + + The lang/ and fonts/ directories are mounted ahead of data.zip from + the binary's directory (ref: desktop_version/src/FileSystemUtils.cpp:81-177, + 312-315); Recalbox installs them from the source tree beside the + binary. gamecontrollerdb.txt is read from the binary's directory when + present (ref: desktop_version/src/FileSystemUtils.cpp:349-353). + +files: + - name: "data.zip" + system: "vvvvvv" + category: game_data + path: "data.zip" + required: true + unsourceable: "game data the collection does not hold; a same-named archive of another game must not stand in" + description: "VVVVVV game assets (graphics, music, sounds)" + source_ref: "desktop_version/src/FileSystemUtils.cpp:318-346, desktop_version/src/main.cpp:433-438" + note: "Path given by -assets, otherwise data.zip next to the binary. Retail and Make and Play Edition archives are both accepted; the code checks neither size nor hash." + - name: "gamecontrollerdb.txt" + system: "vvvvvv" + path: "gamecontrollerdb.txt" + required: false + description: "SDL game controller mappings" + source_ref: "desktop_version/src/FileSystemUtils.cpp:349-353" + note: "Read from the binary's directory; a missing file is logged and ignored." diff --git a/platforms/_registry.yml b/platforms/_registry.yml index c1def9da..c859ba68 100644 --- a/platforms/_registry.yml +++ b/platforms/_registry.yml @@ -211,8 +211,8 @@ platforms: # Bios.cpp:64-68 hashes a .zip with Zip::Md5Composite, never its bytes zip_digest: composite schedule: monthly - target_scraper: null - target_source: null + target_scraper: recalbox_targets + target_source: https://gitlab.com/recalbox/recalbox/-/tree/master/configs install: detect: - os: linux diff --git a/platforms/targets/_overrides.yml b/platforms/targets/_overrides.yml index dec39866..43968547 100644 --- a/platforms/targets/_overrides.yml +++ b/platforms/targets/_overrides.yml @@ -128,3 +128,27 @@ rocknix: - wine amd64: aliases: [x86_64, pc] + +recalbox: + targets: + _default: + # system.ini names a mupen64plus video plugin as the core of the + # mupen64plus emulator; mupenGenerator.py loads it with --gfx + # mupen64plus-video-.so. Plugins, not emulators. + remove_cores: + - glide64mk2 # mupen64plus-video-glide64mk2 + - gliden64 # GLideN64 video plugin + - n64_gles2 # mupen64plus-video-gles2n64 + - rice # mupen64plus-video-rice + - rice_gles2 # mupen64plus-video-gles2rice + # Recalbox names its boards by defconfig (configs/recalbox-_defconfig). + rpi3: + aliases: [rpi3b] + rpi4_64: + aliases: [rpi4, rpi400] + rpi5_64: + aliases: [rpi5] + rpizero2: + aliases: [rpi-zero2] + x86_64: + aliases: [pc] diff --git a/platforms/targets/recalbox.yml b/platforms/targets/recalbox.yml new file mode 100644 index 00000000..29785233 --- /dev/null +++ b/platforms/targets/recalbox.yml @@ -0,0 +1,1204 @@ +platform: recalbox +source: https://gitlab.com/recalbox/recalbox/-/tree/10.1.1/configs +scraped_at: '2026-10-17T13:18:19Z' +targets: + odroidgo2: + architecture: aarch64 + cores: + - '2048' + - '81' + - a5200 + - advancemame + - amiberry + - applewin + - arduous + - atari800 + - b2 + - beebem + - bk + - bluemsx + - cannonball + - cap32 + - crocods + - daphne + - dice + - dinothawr + - dirksimple + - dosbox + - dosbox_pure + - duckstation + - easyrpg + - ecwolf + - eightyone + - emuscv + - exel-hence + - fake08 + - fbneo + - fceumm + - flycast + - fmsx + - freechaf + - freeintv + - frotz + - fuse + - gambatte + - gearcoleco + - geargrafx + - gearsystem + - genesisplusgx + - genesisplusgx_ex + - genesisplusgxwide + - geolith + - glide64mk2 + - gong + - gpsp + - gsplus + - gw + - handy + - hatari + - hatarib + - holani + - hypseus_singe + - imageviewer + - julius + - lowresnx + - lutro + - m2000 + - mame2003 + - mame2003_plus + - mame2010 + - mame2015 + - mednafen_lynx + - mednafen_ngp + - mednafen_pce_fast + - mednafen_supergrafx + - mednafen_vb + - mednafen_wswan + - mesen + - mesen_s + - meteor + - mgba + - minivmac + - mojozork + - mrboom + - mu + - mupen64plus + - mupen64plus_next + - neocd + - nestopia + - np2kai + - nxengine + - o2em + - openbor + - opera + - oricutron + - parallel_n64 + - pcsx_rearmed + - pd777 + - pico8 + - picodrive + - pokemini + - potator + - ppsspp + - prboom + - prosystem + - puae + - puae2021 + - px68k + - quasi88 + - quicknes + - race + - rb5000 + - reminiscence + - retro8 + - rice + - sameboy + - sameduck + - scummvm + - sdlpop + - simcoupe + - snes9x + - snes9x2002 + - snes9x2005 + - snes9x2010 + - solarus + - stella + - stella2014 + - sugarbox2 + - swanstation + - tamalibretro + - tgbdual + - theodore + - thepowdertoy + - ti99sim + - tic80 + - tyrquake + - uae4arm + - uzem + - vecx + - vice + - vice_x128 + - vice_x64 + - vice_x64sc + - vice_xcbm2 + - vice_xcbm5x0 + - vice_xpet + - vice_xplus4 + - vice_xscpu64 + - vice_xvic + - vircon32 + - vitaquake2 + - vvvvvv + - wasm4 + - x1 + - x128 + - x64 + - x64sx + - xcbm2 + - xcbm5x0 + - xpet + - xplus4 + - xrick + - xroar + - xscpu64 + - xvic + - yabasanshiro + rg353x: + architecture: aarch64 + cores: + - '2048' + - '81' + - a5200 + - advancemame + - amiberry + - applewin + - arduous + - atari800 + - b2 + - beebem + - bk + - bluemsx + - bsnes + - bsneshd + - cannonball + - cap32 + - cdi2015 + - crocods + - daphne + - dice + - dinothawr + - dirksimple + - dosbox + - dosbox_pure + - duckstation + - easyrpg + - ecwolf + - eightyone + - emuscv + - exel-hence + - fake08 + - fbneo + - fceumm + - flycast + - fmsx + - freechaf + - freeintv + - frotz + - fuse + - gambatte + - gearcoleco + - geargrafx + - gearsystem + - genesisplusgx + - genesisplusgx_ex + - genesisplusgxwide + - geolith + - glide64mk2 + - gong + - gpsp + - gsplus + - gw + - handy + - hatari + - hatarib + - holani + - hypseus_singe + - imageviewer + - julius + - lowresnx + - lutro + - m2000 + - mame2003 + - mame2003_plus + - mame2010 + - mame2015 + - mednafen_lynx + - mednafen_ngp + - mednafen_pce_fast + - mednafen_pcfx + - mednafen_psx + - mednafen_supafaust + - mednafen_supergrafx + - mednafen_vb + - mednafen_wswan + - mesen + - mesen_s + - meteor + - mgba + - minivmac + - mojozork + - mrboom + - mu + - mupen64plus + - mupen64plus_next + - neocd + - nestopia + - np2kai + - nxengine + - o2em + - openbor + - opera + - oricutron + - parallel_n64 + - pcsx_rearmed + - pd777 + - pico8 + - picodrive + - pokemini + - potator + - ppsspp + - prboom + - prosystem + - puae + - puae2021 + - px68k + - quasi88 + - quicknes + - race + - rb5000 + - reminiscence + - retro8 + - rice + - sameboy + - sameduck + - scummvm + - sdlpop + - simcoupe + - snes9x + - snes9x2002 + - snes9x2005 + - snes9x2010 + - solarus + - stella + - stella2014 + - sugarbox2 + - swanstation + - tamalibretro + - tgbdual + - theodore + - thepowdertoy + - ti99sim + - tic80 + - tyrquake + - uae4arm + - uzem + - vecx + - vice + - vice_x128 + - vice_x64 + - vice_x64sc + - vice_xcbm2 + - vice_xcbm5x0 + - vice_xpet + - vice_xplus4 + - vice_xscpu64 + - vice_xvic + - vircon32 + - virtualjaguar + - vitaquake2 + - vvvvvv + - wasm4 + - x1 + - x128 + - x64 + - x64sx + - xcbm2 + - xcbm5x0 + - xpet + - xplus4 + - xrick + - xroar + - xscpu64 + - xvic + - yabasanshiro + - ymir + rpi3: + architecture: armhf + cores: + - '2048' + - '81' + - a5200 + - advancemame + - amiberry + - applewin + - arduous + - atari800 + - b2 + - beebem + - bk + - bluemsx + - cannonball + - cap32 + - crocods + - daphne + - dice + - dinothawr + - dirksimple + - dosbox + - dosbox_pure + - duckstation + - easyrpg + - ecwolf + - eightyone + - emuscv + - exel-hence + - fake08 + - fbneo + - fceumm + - flycast + - flycast-next + - fmsx + - freechaf + - freeintv + - frotz + - fuse + - gambatte + - gearcoleco + - geargrafx + - gearsystem + - genesisplusgx + - genesisplusgxwide + - geolith + - glide64mk2 + - gliden64 + - gong + - gpsp + - gsplus + - gw + - handy + - hatari + - hatarib + - holani + - hypseus_singe + - imageviewer + - julius + - lowresnx + - lutro + - m2000 + - mame2000 + - mame2003 + - mame2003_plus + - mame2010 + - mame2015 + - mednafen_lynx + - mednafen_ngp + - mednafen_pce_fast + - mednafen_supafaust + - mednafen_supergrafx + - mednafen_vb + - mednafen_wswan + - meteor + - mgba + - minivmac + - mojozork + - moonlight + - mrboom + - mu + - mupen64plus + - mupen64plus_next + - n64_gles2 + - neocd + - nestopia + - np2kai + - nxengine + - o2em + - openbor + - opera + - oricutron + - parallel_n64 + - pcsx_rearmed + - pd777 + - pico8 + - picodrive + - pokemini + - potator + - ppsspp + - prboom + - prosystem + - puae + - puae2021 + - px68k + - quasi88 + - quicknes + - race + - rb5000 + - reicast + - reminiscence + - retro8 + - rice + - rice_gles2 + - sameboy + - sameduck + - scummvm + - sdlpop + - simcoupe + - snes9x + - snes9x2005 + - snes9x2010 + - solarus + - stella + - stella2014 + - sugarbox2 + - swanstation + - tamalibretro + - tgbdual + - theodore + - thepowdertoy + - ti99sim + - tic80 + - tyrquake + - uae4arm + - uzem + - vecx + - vice + - vice_x128 + - vice_x64 + - vice_x64sc + - vice_xcbm2 + - vice_xcbm5x0 + - vice_xpet + - vice_xplus4 + - vice_xscpu64 + - vice_xvic + - vircon32 + - vvvvvv + - wasm4 + - x1 + - x128 + - x64 + - x64sx + - xcbm2 + - xcbm5x0 + - xpet + - xplus4 + - xrick + - xroar + - xscpu64 + - xvic + rpi4_64: + architecture: aarch64 + cores: + - '2048' + - '81' + - a5200 + - advancemame + - amiberry + - applewin + - arduous + - atari800 + - b2 + - beebem + - bk + - bluemsx + - bsnes + - bsneshd + - cannonball + - cap32 + - cdi2015 + - corsixth + - crocods + - daphne + - dice + - dinothawr + - dirksimple + - dosbox + - dosbox_pure + - duckstation + - easyrpg + - ecwolf + - eightyone + - emuscv + - exel-hence + - fake08 + - fbneo + - fceumm + - flycast + - flycast-next + - fmsx + - freechaf + - freeintv + - frotz + - fuse + - gambatte + - gearcoleco + - geargrafx + - gearsystem + - genesisplusgx + - genesisplusgx_ex + - genesisplusgxwide + - geolith + - glide64mk2 + - gong + - gpsp + - gsplus + - gw + - handy + - hatari + - hatarib + - holani + - hypseus_singe + - imageviewer + - julius + - lowresnx + - lutro + - m2000 + - mame0258 + - mame0278 + - mame2003 + - mame2003_plus + - mame2010 + - mame2015 + - mednafen_lynx + - mednafen_ngp + - mednafen_pce_fast + - mednafen_pcfx + - mednafen_psx + - mednafen_saturn + - mednafen_supafaust + - mednafen_supergrafx + - mednafen_vb + - mednafen_wswan + - melonds + - mesen + - mesen_s + - meteor + - mgba + - minivmac + - mojozork + - moonlight + - mrboom + - mu + - mupen64plus + - mupen64plus_next + - neocd + - nestopia + - np2kai + - nxengine + - o2em + - openbor + - opera + - oricutron + - parallel_n64 + - pcsx_rearmed + - pd777 + - pico8 + - picodrive + - pokemini + - potator + - ppsspp + - prboom + - prosystem + - puae + - puae2021 + - px68k + - quasi88 + - quicknes + - race + - rb5000 + - reminiscence + - retro8 + - rice + - sameboy + - sameduck + - scummvm + - sdlpop + - simcoupe + - snes9x + - snes9x2002 + - snes9x2005 + - snes9x2010 + - solarus + - stella + - stella2014 + - sugarbox2 + - swanstation + - tamalibretro + - tgbdual + - theodore + - thepowdertoy + - ti99sim + - tic80 + - tyrquake + - uae4arm + - uzem + - vecx + - vice + - vice_x128 + - vice_x64 + - vice_x64sc + - vice_xcbm2 + - vice_xcbm5x0 + - vice_xpet + - vice_xplus4 + - vice_xscpu64 + - vice_xvic + - vircon32 + - virtualjaguar + - vitaquake2 + - vvvvvv + - wasm4 + - x1 + - x128 + - x64 + - x64sx + - xcbm2 + - xcbm5x0 + - xpet + - xplus4 + - xrick + - xroar + - xscpu64 + - xvic + - yabasanshiro + - ymir + rpi5_64: + architecture: aarch64 + cores: + - '2048' + - '81' + - a5200 + - amiberry + - applewin + - arduous + - atari800 + - b2 + - beebem + - bk + - bluemsx + - bsnes + - bsneshd + - cannonball + - cap32 + - cdi2015 + - corsixth + - craft + - crocods + - daphne + - dice + - dinothawr + - dirksimple + - dolphin + - dolphin-gui + - dosbox + - dosbox_pure + - duckstation + - easyrpg + - ecwolf + - eightyone + - emuscv + - exel-hence + - fake08 + - fbneo + - fceumm + - flycast + - flycast-next + - fmsx + - freechaf + - freeintv + - frotz + - fuse + - gambatte + - gearcoleco + - geargrafx + - gearsystem + - genesisplusgx + - genesisplusgx_ex + - genesisplusgxwide + - geolith + - glide64mk2 + - gong + - gpsp + - gsplus + - gw + - handy + - hatari + - hatarib + - holani + - hypseus_singe + - imageviewer + - julius + - lowresnx + - lutro + - m2000 + - mame0258 + - mame0278 + - mame2003_plus + - mednafen_lynx + - mednafen_ngp + - mednafen_pce_fast + - mednafen_pcfx + - mednafen_psx + - mednafen_saturn + - mednafen_stv + - mednafen_supafaust + - mednafen_supergrafx + - mednafen_vb + - mednafen_wswan + - melonds + - mesen + - mesen_s + - meteor + - mgba + - minivmac + - mojozork + - moonlight + - mrboom + - mu + - mupen64plus + - mupen64plus_next + - neocd + - nestopia + - np2kai + - nxengine + - o2em + - openbor + - openlara + - opera + - oricutron + - parallel_n64 + - pcsx_rearmed + - pd777 + - pico8 + - picodrive + - pokemini + - potator + - ppsspp + - prboom + - prosystem + - puae + - px68k + - quasi88 + - quicknes + - race + - rb5000 + - reminiscence + - retro8 + - rice + - sameboy + - sameduck + - scummvm + - sdlpop + - simcoupe + - snes9x + - snes9x2002 + - snes9x2005 + - snes9x2010 + - solarus + - stella + - stella2014 + - sugarbox2 + - supermodel + - swanstation + - tamalibretro + - tgbdual + - theodore + - thepowdertoy + - ti99sim + - tic80 + - tyrquake + - uae4arm + - uzem + - vecx + - vice + - vice_x128 + - vice_x64 + - vice_x64sc + - vice_xcbm2 + - vice_xcbm5x0 + - vice_xpet + - vice_xplus4 + - vice_xscpu64 + - vice_xvic + - vircon32 + - virtualjaguar + - vitaquake2 + - vpinball + - vvvvvv + - wasm4 + - x1 + - x128 + - x64 + - x64sx + - xcbm2 + - xcbm5x0 + - xpet + - xplus4 + - xrick + - xroar + - xscpu64 + - xvic + - yabasanshiro + - ymir + rpizero2: + architecture: armhf + cores: + - '2048' + - '81' + - a5200 + - advancemame + - amiberry + - applewin + - arduous + - atari800 + - b2 + - beebem + - bk + - bluemsx + - cannonball + - cap32 + - crocods + - daphne + - dice + - dinothawr + - dirksimple + - dosbox + - dosbox_pure + - duckstation + - easyrpg + - ecwolf + - eightyone + - emuscv + - exel-hence + - fake08 + - fbneo + - fceumm + - flycast + - flycast-next + - fmsx + - freechaf + - freeintv + - frotz + - fuse + - gambatte + - gearcoleco + - geargrafx + - gearsystem + - genesisplusgx + - genesisplusgxwide + - geolith + - glide64mk2 + - gliden64 + - gong + - gpsp + - gsplus + - gw + - handy + - hatari + - hatarib + - holani + - hypseus_singe + - imageviewer + - julius + - lowresnx + - lutro + - m2000 + - mame2000 + - mame2003 + - mame2003_plus + - mame2010 + - mame2015 + - mednafen_lynx + - mednafen_ngp + - mednafen_pce_fast + - mednafen_supafaust + - mednafen_supergrafx + - mednafen_vb + - mednafen_wswan + - meteor + - mgba + - mojozork + - moonlight + - mrboom + - mu + - mupen64plus + - mupen64plus_next + - n64_gles2 + - neocd + - nestopia + - np2kai + - nxengine + - o2em + - openbor + - opera + - oricutron + - parallel_n64 + - pcsx_rearmed + - pd777 + - pico8 + - picodrive + - pokemini + - potator + - ppsspp + - prboom + - prosystem + - puae + - puae2021 + - px68k + - quasi88 + - quicknes + - race + - rb5000 + - reicast + - reminiscence + - retro8 + - rice + - rice_gles2 + - sameboy + - sameduck + - scummvm + - sdlpop + - simcoupe + - snes9x + - snes9x2005 + - snes9x2010 + - solarus + - stella + - stella2014 + - sugarbox2 + - swanstation + - tamalibretro + - tgbdual + - theodore + - thepowdertoy + - ti99sim + - tic80 + - tyrquake + - uae4arm + - uzem + - vecx + - vice + - vice_x128 + - vice_x64 + - vice_x64sc + - vice_xcbm2 + - vice_xcbm5x0 + - vice_xpet + - vice_xplus4 + - vice_xscpu64 + - vice_xvic + - vircon32 + - vvvvvv + - wasm4 + - x1 + - x128 + - x64 + - x64sx + - xcbm2 + - xcbm5x0 + - xpet + - xplus4 + - xrick + - xroar + - xscpu64 + - xvic + x86_64: + architecture: x86_64 + cores: + - '2048' + - '81' + - a5200 + - amiberry + - applewin + - arduous + - atari800 + - b2 + - beebem + - bk + - bluemsx + - boom3 + - bsnes + - bsneshd + - cannonball + - cap32 + - cdi2015 + - corsixth + - craft + - crocods + - daphne + - desmume + - dice + - dinothawr + - dirksimple + - dolphin + - dolphin-gui + - dosbox + - dosbox_pure + - duckstation + - easyrpg + - ecwolf + - eightyone + - emuscv + - exel-hence + - fake08 + - fbneo + - fceumm + - flycast + - flycast-next + - fmsx + - freechaf + - freeintv + - frotz + - fuse + - gambatte + - gearcoleco + - geargrafx + - gearsystem + - genesisplusgx + - genesisplusgx_ex + - genesisplusgxwide + - geolith + - glide64mk2 + - gong + - gpsp + - gsplus + - gw + - handy + - hatari + - hatarib + - holani + - hypseus_singe + - imageviewer + - julius + - kronos + - lowresnx + - lutro + - m2000 + - mame0258 + - mame0278 + - mame2003 + - mame2003_plus + - mame2010 + - mame2015 + - mednafen_lynx + - mednafen_ngp + - mednafen_pce_fast + - mednafen_pcfx + - mednafen_psx + - mednafen_psx_hw + - mednafen_saturn + - mednafen_stv + - mednafen_supafaust + - mednafen_supergrafx + - mednafen_vb + - mednafen_wswan + - melonds + - mesen + - mesen_s + - meteor + - mgba + - minivmac + - mojozork + - moonlight + - mrboom + - mu + - mupen64plus + - mupen64plus_next + - neocd + - nestopia + - np2kai + - nxengine + - o2em + - openbor + - openlara + - opera + - oricutron + - parallel_n64 + - pcsx2 + - pcsx_rearmed + - pd777 + - pico8 + - picodrive + - pokemini + - potator + - ppsspp + - prboom + - prosystem + - ps2 + - puae + - px68k + - quasi88 + - quicknes + - race + - rb5000 + - reminiscence + - retro8 + - rice + - same_cdi + - sameboy + - sameduck + - scummvm + - sdlpop + - simcoupe + - snes9x + - snes9x2002 + - snes9x2005 + - snes9x2010 + - solarus + - stella + - stella2014 + - sugarbox2 + - supermodel + - swanstation + - tamalibretro + - tgbdual + - theodore + - thepowdertoy + - ti99sim + - tic80 + - tyrquake + - uzem + - vecx + - vice + - vice_x128 + - vice_x64 + - vice_x64sc + - vice_xcbm2 + - vice_xcbm5x0 + - vice_xpet + - vice_xplus4 + - vice_xscpu64 + - vice_xvic + - vircon32 + - virtualjaguar + - vitaquake2 + - vitaquake3 + - vitavoyager + - vpinball + - vvvvvv + - wasm4 + - x1 + - x128 + - x64 + - x64sx + - xcbm2 + - xcbm5x0 + - xemu + - xpet + - xplus4 + - xrick + - xroar + - xscpu64 + - xvic + - yabasanshiro + - yabause + - ymir diff --git a/scripts/scraper/recalbox_scraper.py b/scripts/scraper/recalbox_scraper.py index 4d7e21b0..e7a3fa26 100644 --- a/scripts/scraper/recalbox_scraper.py +++ b/scripts/scraper/recalbox_scraper.py @@ -117,18 +117,14 @@ BLOBS_QUERY = ( BLOBS_PER_QUERY = 50 -def system_ini_cores(text: str) -> tuple[set[str], set[str]]: - """The cores one Recalbox system.ini offers, and those run standalone. +def _ini_sections(text: str) -> dict[str, dict[str, str]]: + """Sections and their key = value lines of one Recalbox system.ini. - Each ``[core.N]`` section (or its board override ``[core.N.]``) - names an ``emulator`` and a ``core``: ``libretro`` with the core's name, - or a standalone emulator. es_bios.xml only names the cores that need a - BIOS, so read alone it left most of Recalbox's cores outside the pack. + Recalbox's .ini files are not strict INI (a value can run on a bare + line), so only sections and key = value lines are read. """ import re - # Recalbox's .ini files are not strict INI (a value can run on a bare - # line), so only sections and key = value lines are read. sections: dict[str, dict[str, str]] = {} current: dict[str, str] | None = None for raw in text.splitlines(): @@ -142,25 +138,64 @@ def system_ini_cores(text: str) -> tuple[set[str], set[str]]: if current is not None and "=" in line: key, value = line.split("=", 1) current[key.strip()] = value.strip().strip('"') + return sections + + +def _core_names(values: dict[str, str]) -> tuple[set[str], set[str]]: + """(cores, standalone) one [core.N] section names.""" + emulator = values.get("emulator", "") + core = values.get("core", "") + if emulator == "libretro": + return ({core} if core else set()), set() + if not emulator: + return set(), set() + names = {emulator} | ({core} if core and core != emulator else set()) + return names, set(names) + + +def system_ini_cores(text: str) -> tuple[set[str], set[str]]: + """The cores one Recalbox system.ini offers, and those run standalone. + + Each ``[core.N]`` section (or its board override ``[core.N.]``) + names an ``emulator`` and a ``core``: ``libretro`` with the core's name, + or a standalone emulator. es_bios.xml only names the cores that need a + BIOS, so read alone it left most of Recalbox's cores outside the pack. + """ + import re + cores: set[str] = set() standalone: set[str] = set() - for section, values in sections.items(): + for section, values in _ini_sections(text).items(): if not re.fullmatch(r"core\.\d+(\.[\w-]+)?", section): continue - emulator = values.get("emulator", "") - core = values.get("core", "") - if emulator == "libretro": - if core: - cores.add(core) - elif emulator: - cores.add(emulator) - standalone.add(emulator) - if core and core != emulator: - cores.add(core) - standalone.add(core) + found, alone = _core_names(values) + cores |= found + standalone |= alone return cores, standalone +def board_cores(text: str, board: str, enabled: set[str]) -> set[str]: + """The cores of one system.ini a board's image builds. + + A core is built when the Buildroot package its section names is enabled + in the board's defconfig. ``[core.N.]`` replaces the section on + that board and ``package.`` its package. + """ + import re + + sections = _ini_sections(text) + cores: set[str] = set() + for section, values in sections.items(): + match = re.fullmatch(r"core\.(\d+)", section) + if not match: + continue + values = sections.get(f"{section}.{board}", values) + package = values.get(f"package.{board}", values.get("package", "")) + if package in enabled: + cores |= _core_names(values)[0] + return cores + + class Scraper(BaseScraper): """Scraper for Recalbox es_bios.xml.""" diff --git a/scripts/scraper/targets/recalbox_targets_scraper.py b/scripts/scraper/targets/recalbox_targets_scraper.py new file mode 100644 index 00000000..5382848a --- /dev/null +++ b/scripts/scraper/targets/recalbox_targets_scraper.py @@ -0,0 +1,126 @@ +"""Scraper for Recalbox per-board emulator availability. + +Sources (recalbox/recalbox on GitLab, at the tag platforms/recalbox.yml names): + - configs/recalbox-_defconfig -- the Buildroot packages each image + enables, one `BR2_PACKAGE_=y` line per emulator package + - package/recalbox-romfs2/systems//system.ini -- the cores of each + system, each `[core.N]` naming the package that builds it, with board + overrides (`package.`, `[core.N.]`) + +Recalbox runs on Raspberry Pi 3 to 5, two handhelds and x86_64: without a +target list, a Pi 3 pack carried the firmware of emulators only x86_64 builds. +""" + +from __future__ import annotations + +import argparse +import json +import re +import sys +from datetime import datetime, timezone + +import yaml + +from . import BaseTargetScraper + +PLATFORM_NAME = "recalbox" + +CONFIGS_TREE_URL = ( + "https://gitlab.com/api/v4/projects/recalbox%2Frecalbox/repository/tree" + "?path=configs&ref={tag}&per_page=100" +) +DEFCONFIG_URL = "https://gitlab.com/recalbox/recalbox/-/raw/{tag}/configs/{name}" +_DEFCONFIG_RE = re.compile(r"^recalbox-(\w+)_defconfig$") +_ENABLED_RE = re.compile(r"^(BR2_PACKAGE_\w+)=y\s*$", re.MULTILINE) + +try: + from ..base_scraper import fetch_bytes + from ..recalbox_scraper import SYSTEMS_TREE_URL, Scraper as PlatformScraper, board_cores +except ImportError: + from scraper.base_scraper import fetch_bytes + from scraper.recalbox_scraper import SYSTEMS_TREE_URL, Scraper as PlatformScraper, board_cores + + +def enabled_packages(defconfig: str) -> set[str]: + """The Buildroot packages a defconfig turns on.""" + return set(_ENABLED_RE.findall(defconfig)) + + +def defconfig_arch(defconfig: str) -> str: + """The architecture a defconfig builds for, from its Buildroot arch symbol.""" + for symbol, arch in (("BR2_x86_64", "x86_64"), ("BR2_aarch64", "aarch64"), + ("BR2_arm", "armhf")): + if re.search(rf"^{symbol}=y\s*$", defconfig, re.MULTILINE): + return arch + raise RuntimeError("defconfig names no BR2_x86_64, BR2_aarch64 or BR2_arm") + + +class Scraper(BaseTargetScraper): + """Per-board core lists from Recalbox's defconfigs and system.ini files.""" + + def __init__(self, url: str = "https://gitlab.com/recalbox/recalbox"): + super().__init__(url=url) + + def fetch_targets(self) -> dict: + platform = PlatformScraper() + tag = platform.tag + listing = json.loads(fetch_bytes(CONFIGS_TREE_URL.format(tag=tag))) + boards = { + match.group(1): item["name"] + for item in listing + if (match := _DEFCONFIG_RE.match(item.get("name", ""))) + } + if not boards: + raise RuntimeError(f"no recalbox-*_defconfig in configs/ at {tag}") + + systems: list[str] = [] + page = 1 + while True: + entries = json.loads(fetch_bytes(SYSTEMS_TREE_URL.format(tag=tag, page=page))) + systems += [e["name"] for e in entries + if e.get("type") == "tree" and not e["name"].startswith(".")] + if len(entries) < 100: + break + page += 1 + inis = platform._system_inis(systems) + + targets: dict[str, dict] = {} + for board, name in sorted(boards.items()): + defconfig = fetch_bytes(DEFCONFIG_URL.format(tag=tag, name=name)).decode("utf-8") + enabled = enabled_packages(defconfig) + cores: set[str] = set() + for text in inis: + cores |= board_cores(text, board, enabled) + arch = defconfig_arch(defconfig) + targets[board] = {"architecture": arch, "cores": sorted(cores)} + print(f" {board} ({arch}): {len(cores)} cores", file=sys.stderr) + + return { + "platform": PLATFORM_NAME, + "source": f"{self.url}/-/tree/{tag}/configs", + "scraped_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"), + "targets": targets, + } + + +def main() -> None: + parser = argparse.ArgumentParser(description="Scrape Recalbox per-board cores") + parser.add_argument("--dry-run", action="store_true", help="Show target summary") + parser.add_argument("--output", "-o", help="Output YAML file") + args = parser.parse_args() + + scraper = Scraper() + data = scraper.fetch_targets() + if args.dry_run: + for name, info in data["targets"].items(): + print(f" {name} ({info['architecture']}): {len(info['cores'])} cores") + return + if args.output: + scraper.write_output(data, args.output) + print(f"Written to {args.output}") + return + print(yaml.dump(data, default_flow_style=False, sort_keys=False)) + + +if __name__ == "__main__": + main() diff --git a/tests/test_target_scrapers.py b/tests/test_target_scrapers.py index 15a0bbe8..5ec4c0a6 100644 --- a/tests/test_target_scrapers.py +++ b/tests/test_target_scrapers.py @@ -233,6 +233,40 @@ class RocknixPerDevice(unittest.TestCase): ["ecwolf-lr", "prboom-lr", "vitaquake3-lr"]) +class RecalboxPerBoard(unittest.TestCase): + """Recalbox had no target list: a Raspberry Pi 3 pack carried the + firmware of emulators only its x86_64 image builds.""" + + SYSTEM_INI = ( + "[core.0]\npackage = BR2_PACKAGE_PCSX2\nemulator = \"pcsx2\"\ncore = \"pcsx2\"\n" + "[core.1]\npackage = BR2_PACKAGE_LIBRETRO_PCSX_REARMED\nemulator = \"libretro\"\n" + "core = \"pcsx_rearmed\"\npackage.rpi3 = BR2_PACKAGE_LIBRETRO_PCSX_REARMED_NEON\n" + "[core.2]\npackage = BR2_PACKAGE_LIBRETRO_SWANSTATION\nemulator = \"libretro\"\n" + "core = \"swanstation\"\n" + "[core.2.rpi3]\npackage = BR2_PACKAGE_LIBRETRO_DUCKSTATION\nemulator = \"libretro\"\n" + "core = \"duckstation\"\n" + ) + + def test_a_board_gets_the_cores_its_defconfig_builds(self): + from scraper.recalbox_scraper import board_cores # noqa: PLC0415 + from scraper.targets.recalbox_targets_scraper import ( # noqa: PLC0415 + defconfig_arch, + enabled_packages, + ) + + x86 = enabled_packages("BR2_x86_64=y\nBR2_PACKAGE_PCSX2=y\n" + "BR2_PACKAGE_LIBRETRO_PCSX_REARMED=y\n" + "BR2_PACKAGE_LIBRETRO_SWANSTATION=y\n# BR2_PACKAGE_X is not set\n") + self.assertEqual(board_cores(self.SYSTEM_INI, "x86_64", x86), + {"pcsx2", "pcsx_rearmed", "swanstation"}) + rpi3 = enabled_packages("BR2_arm=y\nBR2_PACKAGE_LIBRETRO_PCSX_REARMED_NEON=y\n" + "BR2_PACKAGE_LIBRETRO_DUCKSTATION=y\n") + self.assertEqual(board_cores(self.SYSTEM_INI, "rpi3", rpi3), + {"pcsx_rearmed", "duckstation"}) + self.assertEqual(defconfig_arch("BR2_arm=y\n"), "armhf") + self.assertEqual(defconfig_arch("BR2_aarch64=y\n"), "aarch64") + + class RetroPieModuleFlags(unittest.TestCase): """The RetroPie target list read flags as requirements and libretro alone.