From 9648b9fc3b0c626b03a12da0a55a97c9d9ba73ef Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Thu, 22 Oct 2026 06:28:24 +0200 Subject: [PATCH] feat: read elec ports and per-system cores EmuELEC's ports system has one emulator, `ports`, the dispatch to a script ports.yaml generates. The scraper now reads each port's engine (the libretro core handed to emuelecRunEmu.sh with -C, else port_exe), so devilutionx, sonic2013, soniccd, sonic-mania, reminiscence and rigelengine are examined. Standalone emulators keep the spelling the launch code dispatches on instead of invented lowercase forms, and media players and runtimes go to the overrides' remove_cores. Each system of the AmberELEC, EmuELEC and KNULLI lists now carries the cores its ES list offers it, in menu order, under `cores:`. --- platforms/amberelec.yml | 164 +++++++++++++--- platforms/batocera.yml | 4 - platforms/emuelec.yml | 274 +++++++++++++++++++++------ platforms/knulli.yml | 209 +++++++++++++++++++- platforms/targets/_overrides.yml | 39 +++- schemas/platform.schema.json | 6 + scripts/scraper/amberelec_scraper.py | 25 ++- scripts/scraper/batocera_scraper.py | 38 ++++ scripts/scraper/emuelec_scraper.py | 98 +++++++++- scripts/scraper/es_emulators.py | 181 ++++++++++++------ scripts/scraper/knulli_scraper.py | 18 +- tests/test_elec_scrapers.py | 104 +++++++++- 12 files changed, 981 insertions(+), 179 deletions(-) diff --git a/platforms/amberelec.yml b/platforms/amberelec.yml index 2f48adca..c6e47254 100644 --- a/platforms/amberelec.yml +++ b/platforms/amberelec.yml @@ -16,8 +16,6 @@ cores: - PPSSPPSDL - SCUMMVMSA - a5200 -- advancemame -- amiberry - arduous - atari800 - beetle_gba @@ -73,7 +71,6 @@ cores: - gzdoom - handy - hatari -- hatarisa - hypseus_singe - jaxe - lowresnx @@ -90,8 +87,6 @@ cores: - mesen-s - mgba - mojozork -- mpv -- mupen64plus - mupen64plus_next - mupen64plussa - nekop2 @@ -99,7 +94,6 @@ cores: - nestopia - np2kai - o2em -- openbor - opera - parallel_n64 - pcsx_rearmed @@ -109,7 +103,6 @@ cores: - pokemini - potator - ppsspp -- ppssppsdl - prboom - prosystem - puae @@ -123,7 +116,6 @@ cores: - sameboy - sameduck - scummvm -- scummvmsa - smsplus - snes9x - snes9x2002 @@ -150,9 +142,7 @@ cores: - virtualjaguar - wasm4 - x1 -- yabasanshiro - yabasanshiroSA -- yabasanshirosa standalone_cores: - AMIBERRY - AdvanceMame @@ -160,34 +150,21 @@ standalone_cores: - OPENBOR - PPSSPPSDL - SCUMMVMSA -- advancemame -- amiberry - drastic - ecwolf - gzdoom -- hatari -- hatarisa - hypseus_singe - lzdoom -- mpv -- mupen64plus - mupen64plussa -- openbor - pico8 - piemu -- ppsspp -- ppssppsdl - raze -- scummvm -- scummvmsa - solarus -- yabasanshiro - yabasanshiroSA -- yabasanshirosa dual_mode_cores: -- hatari -- ppsspp -- scummvm +- HATARISA +- PPSSPPSDL +- SCUMMVMSA systems: atari-5200: files: @@ -200,6 +177,9 @@ systems: native_name: Atari 5200 native_id: atari5200 name: Atari 5200 + cores: + - atari800 + - a5200 atari-400-800: files: - name: ATARIXL.ROM @@ -232,6 +212,8 @@ systems: native_name: Atari 800 native_id: atari800 name: Atari 800 + cores: + - atari800 atari-7800: files: - name: "7800 BIOS (U).rom" @@ -243,6 +225,9 @@ systems: native_name: Atari 7800 native_id: atari7800 name: Atari 7800 + cores: + - prosystem + - mame atari-st: files: - name: tos.img @@ -254,6 +239,9 @@ systems: native_name: Atari ST native_id: atarist name: Atari ST + cores: + - hatari + - HATARISA atari-lynx: files: - name: lynxboot.img @@ -265,6 +253,9 @@ systems: native_name: Lynx native_id: atarilynx name: Lynx + cores: + - handy + - beetle_lynx coleco-colecovision: files: - name: config.ini @@ -348,6 +339,10 @@ systems: native_name: Colecovision native_id: colecovision name: Colecovision + cores: + - bluemsx + - gearcoleco + - smsplus commodore-amiga: files: - name: "CDTV Extended-ROM v1.0 (1991)(Commodore)(CDTV)[!].rom" @@ -492,6 +487,11 @@ systems: native_name: Amiga/CD32 native_id: amiga name: Amiga/CD32 + cores: + - puae2021 + - puae + - uae4arm + - AMIBERRY magnavox-odyssey2: files: - name: o2rom.bin @@ -556,6 +556,8 @@ systems: native_name: Mattel Intellivision native_id: intellivision name: Mattel Intellivision + cores: + - freeintv microsoft-msx: files: - name: config.ini @@ -2438,6 +2440,9 @@ systems: native_name: MSX native_id: msx name: MSX + cores: + - bluemsx + - fmsx nec-pc-engine: files: - name: syscard3.pce @@ -2474,6 +2479,10 @@ systems: native_name: Supergrafx native_id: pcengine name: Supergrafx + cores: + - beetle_pce_fast + - beetle_pce + - beetle_supergrafx nec-pc-fx: files: - name: pcfx.rom @@ -2485,6 +2494,8 @@ systems: native_name: PC-FX native_id: pcfx name: PC-FX + cores: + - beetle_pcfx nec-pc-88: files: - name: DISK.ROM @@ -2560,6 +2571,11 @@ systems: native_name: Nintendo Family Computer Disk System native_id: fds name: Nintendo Family Computer Disk System + cores: + - fceumm + - nestopia + - quicknes + - mesen nintendo-gb: files: - name: gb_bios.bin @@ -2578,6 +2594,16 @@ systems: native_name: Game Boy native_id: gb name: Game Boy + cores: + - gambatte + - sameboy + - gearboy + - tgbdual + - DoubleCherryGB + - mgba + - vbam + - bsnes + - mesen-s nintendo-gbc: files: - name: gb_bios.bin @@ -2596,6 +2622,16 @@ systems: native_name: Game Boy Color native_id: gbc name: Game Boy Color + cores: + - gambatte + - sameboy + - gearboy + - tgbdual + - DoubleCherryGB + - mgba + - vbam + - bsnes + - mesen-s nintendo-gba: files: - name: gba_bios.bin @@ -2628,6 +2664,12 @@ systems: native_name: Game Boy Advance native_id: gba name: Game Boy Advance + cores: + - mgba + - gpsp + - vbam + - vba_next + - beetle_gba nintendo-64: files: - name: 64DD_IPL.bin @@ -2639,6 +2681,10 @@ systems: native_name: Nintendo 64 native_id: n64 name: Nintendo 64 + cores: + - parallel_n64 + - mupen64plus_next + - mupen64plussa nintendo-ds: files: - name: firmware.bin @@ -2664,6 +2710,10 @@ systems: native_name: Nintendo DS native_id: nds name: Nintendo DS + cores: + - melonds + - melondsds + - drastic nintendo-satellaview: files: - name: BS-X.bin @@ -2675,6 +2725,18 @@ systems: native_name: Satellaview native_id: satellaview name: Satellaview + cores: + - snes9x + - snes9x2010 + - snes9x2002 + - snes9x2005_plus + - beetle_snes + - bsnes + - bsnes2014_performance + - bsnes2014_balanced + - bsnes_mercury_performance + - bsnes_mercury_balanced + - mesen-s nintendo-sufami-turbo: files: - name: STBIOS.bin @@ -2686,6 +2748,8 @@ systems: native_name: Sufami native_id: sufami name: Sufami + cores: + - snes9x 3do: files: - name: panafz1.bin @@ -2725,6 +2789,8 @@ systems: native_name: 3DO native_id: 3do name: 3DO + cores: + - opera sega-dreamcast-arcade: files: - name: naomi_boot.bin @@ -2750,6 +2816,10 @@ systems: native_name: Atomiswave native_id: naomi name: Atomiswave + cores: + - flycast + - flycast2021 + - flycast2021le sega-dreamcast: files: - name: dc_boot.bin @@ -2768,6 +2838,10 @@ systems: native_name: Dreamcast native_id: dreamcast name: Dreamcast + cores: + - flycast + - flycast2021 + - flycast2021le sega-game-gear: files: - name: bios.gg @@ -2779,6 +2853,11 @@ systems: native_name: Game Gear native_id: gamegear name: Game Gear + cores: + - gearsystem + - genesis_plus_gx + - picodrive + - smsplus sega-master-system: files: - name: bios_E.sms @@ -2804,6 +2883,11 @@ systems: native_name: MasterSystem native_id: mastersystem name: MasterSystem + cores: + - gearsystem + - genesis_plus_gx + - picodrive + - smsplus sega-saturn: files: - name: saturn_bios.bin @@ -2850,6 +2934,8 @@ systems: native_name: Sega Saturn native_id: saturn name: Sega Saturn + cores: + - yabasanshiroSA sega-mega-cd: files: - name: bios_CD_E.bin @@ -2875,6 +2961,9 @@ systems: native_name: Sega CD native_id: segacd name: Sega CD + cores: + - genesis_plus_gx + - picodrive sc-3000: files: - name: config.ini @@ -2885,6 +2974,8 @@ systems: native_name: SC-3000 native_id: sc-3000 name: SC-3000 + cores: + - bluemsx sharp-x68000: files: - name: iplrom.dat @@ -2919,6 +3010,8 @@ systems: native_name: Sharp x68000 native_id: x68000 name: Sharp x68000 + cores: + - px68k sharp-x1: files: - name: IPLROM.X1 @@ -2937,6 +3030,8 @@ systems: native_name: Sharp X1 native_id: x1 name: Sharp X1 + cores: + - x1 snk-neogeo: files: - name: neogeo.zip @@ -2953,6 +3048,16 @@ systems: native_name: NeoGeo native_id: neogeo name: NeoGeo + cores: + - fbneo + - geolith + - mame2003_plus + - fbalpha2012 + - fbalpha2019 + - mame2010 + - mame2015 + - mame2016 + - mame snk-neogeo-cd: files: - name: neogeo.zip @@ -3008,6 +3113,10 @@ systems: native_name: PSX native_id: psx name: PSX + cores: + - pcsx_rearmed + - duckstation + - swanstation sinclair-zx-spectrum: files: - name: 128p-0.rom @@ -3060,3 +3169,6 @@ systems: native_name: ZX Spectrum native_id: zxspectrum name: ZX Spectrum + cores: + - fuse + - ep128emu_core diff --git a/platforms/batocera.yml b/platforms/batocera.yml index 3cab2c10..fcf06b16 100644 --- a/platforms/batocera.yml +++ b/platforms/batocera.yml @@ -114,7 +114,6 @@ cores: - model2emu - moonlight - mrboom -- mugen - mupen64plus - mupen64plus-next - neocd @@ -217,7 +216,6 @@ cores: - vox_official - vpinball - wasm4 -- wine - x1 - x128 - x16emu @@ -5737,7 +5735,6 @@ standalone_cores: - melonds - model2emu - moonlight -- mugen - mupen64plus - openbor - openbor4432 @@ -5787,7 +5784,6 @@ standalone_cores: - vkquake3 - vox_official - vpinball -- wine - x128 - x16emu - x64 diff --git a/platforms/emuelec.yml b/platforms/emuelec.yml index 78c84232..bc476e3b 100644 --- a/platforms/emuelec.yml +++ b/platforms/emuelec.yml @@ -7,9 +7,11 @@ core_destination: bios hash_type: md5 verification_mode: md5 cores: +- "2048" - "81" - AMIBERRY - AdvanceMame +- CGeniusExe - Chocolate-Doom - DOSBOXSDL2 - Duckstation @@ -20,34 +22,42 @@ cores: - HYPSEUS - LZDoom - OPENBOR +- OpenJazz - PPSSPPSDL - Pico-8 +- RigelEngine - SCUMMVMSA - STELLASA - Solarus +- VVVVVV - a5200 -- advancemame -- amiberry +- abuse - atari800 +- bgdi - bk - bluemsx +- bs +- bstone +- cannonball - cap32 -- chocolate-doom +- ccleste +- cdogs-sdl - daphne +- devilutionx +- dinothawr - dolphin - dosbox_pure - dosbox_svn -- dosboxsdl2 -- duckstation - easyrpg -- ecwolf +- eduke32 - fake08 +- fallout-ce +- fallout2-ce - fbalpha2012 - fbneo -- fbneosa - fceumm - fceumm_mod -- ffplay +- fheroes2 - flycast - flycast_32b - flycast_dojo @@ -66,17 +76,15 @@ cores: - genesis_plus_gx_cart_special - genesis_plus_gx_wide - geolith -- gmloader - gpsp - gw -- gzdoom - handy - hatari -- hatarisa -- hypseus +- hcl +- hode +- hurrican - ikemen - jzintv -- lzdoom - mame - mame2003_plus - mame2010 @@ -92,8 +100,7 @@ cores: - mesen - mesen-s - mgba -- mpv -- mupen64plus +- mrboom - mupen64plus_32b - mupen64plus_next - mupen64plus_next_alt @@ -102,40 +109,43 @@ cores: - neocd - nestopia - np2kai +- nxengine - o2em -- openbor +- opentyrian - opera - parallel_n64_32b - pcsx_rearmed - pcsx_rearmed_32b -- pico-8 - picodrive - pocketcdg - pokemini -- ports - potator -- ppsspp -- ppssppsdl - prboom +- prince - prosystem - puae - puae2021 - px68k +- reminiscence - same_cdi - sameboy - sameduck - scummvm -- scummvmsa +- smw - snes9x - snes9x2002 - snes9x2005_plus - snes9x2010 -- solarus +- sonic2013 +- soniccd +- sonicmania - stella -- stellasa +- supertux2 +- supertuxkart - swanstation - tgbdual - tic80 +- tyrquake - uae4arm_32b - uzem - vba_next @@ -148,10 +158,13 @@ cores: - vice_xvic - virtualjaguar - x1 +- xash3d +- xrick - yabasanshiro standalone_cores: - AMIBERRY - AdvanceMame +- CGeniusExe - Chocolate-Doom - DOSBOXSDL2 - Duckstation @@ -162,52 +175,50 @@ standalone_cores: - HYPSEUS - LZDoom - OPENBOR +- OpenJazz - PPSSPPSDL - Pico-8 +- RigelEngine - SCUMMVMSA - STELLASA - Solarus -- advancemame -- amiberry -- chocolate-doom +- VVVVVV +- abuse +- bgdi +- bs +- bstone +- ccleste +- cdogs-sdl +- devilutionx - dolphin -- dosboxsdl2 -- duckstation -- ecwolf -- fbneo -- fbneosa -- ffplay -- flycast +- eduke32 +- fallout-ce +- fallout2-ce +- fheroes2 - flycast_dojo - flycastsa -- gmloader -- gzdoom -- hatari -- hatarisa -- hypseus +- hcl +- hode +- hurrican - ikemen - jzintv -- lzdoom -- mpv -- mupen64plus - mupen64plussa -- openbor -- pico-8 -- ports -- ppsspp -- ppssppsdl -- scummvm -- scummvmsa -- solarus -- stella -- stellasa +- opentyrian +- prince +- smw +- sonic2013 +- soniccd +- sonicmania +- supertux2 +- supertuxkart +- xash3d - yabasanshiro dual_mode_cores: -- fbneo -- flycast -- hatari -- scummvm -- stella +- FBNEOSA +- HATARISA +- SCUMMVMSA +- STELLASA +- flycastsa - yabasanshiro systems: atari-5200: @@ -221,6 +232,9 @@ systems: native_name: Atari 5200 native_id: atari5200 name: Atari 5200 + cores: + - a5200 + - atari800 atari-400-800: files: - name: ATARIXL.ROM @@ -253,6 +267,8 @@ systems: native_name: Atari 800 native_id: atari800 name: Atari 800 + cores: + - atari800 atari-7800: files: - name: "7800 BIOS (U).rom" @@ -264,6 +280,8 @@ systems: native_name: Atari 7800 native_id: atari7800 name: Atari 7800 + cores: + - prosystem atari-st: files: - name: tos.img @@ -275,6 +293,9 @@ systems: native_name: Atari ST native_id: atarist name: Atari ST + cores: + - HATARISA + - hatari atari-lynx: files: - name: lynxboot.img @@ -286,6 +307,10 @@ systems: native_name: Lynx native_id: atarilynx name: Lynx + cores: + - handy + - mednafen_lynx + - gearlynx coleco-colecovision: files: - name: config.ini @@ -355,6 +380,9 @@ systems: native_name: Colecovision native_id: colecovision name: Colecovision + cores: + - bluemsx + - gearcoleco commodore-amiga: files: - name: "CDTV Extended-ROM v1.0 (1991)(Commodore)(CDTV)[!].rom" @@ -499,6 +527,11 @@ systems: native_name: Amiga/CD32 native_id: amiga name: Amiga/CD32 + cores: + - AMIBERRY + - puae + - puae2021 + - uae4arm_32b fmtowns: files: - name: FMT_DIC.ROM @@ -644,6 +677,8 @@ systems: native_name: Fujistu FM-Towns native_id: fmtowns name: Fujistu FM-Towns + cores: + - mame magnavox-odyssey2: files: - name: o2rom.bin @@ -708,6 +743,9 @@ systems: native_name: Mattel Intellivision native_id: intellivision name: Mattel Intellivision + cores: + - freeintv + - jzintv microsoft-msx: files: - name: config.ini @@ -2590,6 +2628,9 @@ systems: native_name: MSX native_id: msx name: MSX + cores: + - bluemsx + - fmsx nec-pc-engine: files: - name: syscard3.pce @@ -2626,6 +2667,10 @@ systems: native_name: Supergrafx native_id: pcengine name: Supergrafx + cores: + - mednafen_pce_fast + - mednafen_supergrafx + - geargrafx nec-pc-fx: files: - name: pcfx.rom @@ -2637,6 +2682,8 @@ systems: native_name: PC-FX native_id: pcfx name: PC-FX + cores: + - mednafen_pcfx nintendo-fds: files: - name: NstDatabase.xml @@ -2655,6 +2702,11 @@ systems: native_name: Nintendo Family Computer Disk System native_id: fds name: Nintendo Family Computer Disk System + cores: + - nestopia + - fceumm + - fceumm_mod + - mesen nintendo-gb: files: - name: gb_bios.bin @@ -2673,6 +2725,15 @@ systems: native_name: Game Boy native_id: gb name: Game Boy + cores: + - gambatte + - gearboy + - sameboy + - tgbdual + - mgba + - vbam + - vba_next + - mesen-s nintendo-gbc: files: - name: gb_bios.bin @@ -2691,6 +2752,15 @@ systems: native_name: Game Boy Color native_id: gbc name: Game Boy Color + cores: + - gambatte + - gearboy + - sameboy + - tgbdual + - mgba + - vbam + - vba_next + - mesen-s nintendo-gba: files: - name: gba_bios.bin @@ -2723,6 +2793,11 @@ systems: native_name: Game Boy Advance native_id: gba name: Game Boy Advance + cores: + - mgba + - gpsp + - vbam + - vba_next nintendo-64: files: - name: 64DD_IPL.bin @@ -2734,6 +2809,12 @@ systems: native_name: Nintendo 64 native_id: n64 name: Nintendo 64 + cores: + - mupen64plus_next + - mupen64plus_next_alt + - mupen64plus_32b + - parallel_n64_32b + - mupen64plussa nintendo-ds: files: - name: firmware.bin @@ -2770,6 +2851,9 @@ systems: native_name: Satellaview native_id: satellaview name: Satellaview + cores: + - snes9x + - mesen-s nintendo-sufami-turbo: files: - name: STBIOS.bin @@ -2781,6 +2865,9 @@ systems: native_name: Sufami native_id: sufami name: Sufami + cores: + - snes9x + - mesen-s 3do: files: - name: panafz1.bin @@ -2820,6 +2907,8 @@ systems: native_name: 3DO native_id: 3do name: 3DO + cores: + - opera sega-dreamcast-arcade: files: - name: naomi_boot.bin @@ -2845,6 +2934,11 @@ systems: native_name: Atomiswave native_id: naomi name: Atomiswave + cores: + - flycast + - flycast_32b + - flycastsa + - flycast_dojo sega-dreamcast: files: - name: dc_boot.bin @@ -2863,6 +2957,11 @@ systems: native_name: Dreamcast native_id: dreamcast name: Dreamcast + cores: + - flycast + - flycast_32b + - flycastsa + - flycast_dojo sega-game-gear: files: - name: bios.gg @@ -2874,6 +2973,10 @@ systems: native_name: Game Gear native_id: gamegear name: Game Gear + cores: + - gearsystem + - genesis_plus_gx + - picodrive sega-master-system: files: - name: bios_E.sms @@ -2899,6 +3002,10 @@ systems: native_name: MasterSystem native_id: mastersystem name: MasterSystem + cores: + - gearsystem + - genesis_plus_gx + - picodrive sega-saturn: files: - name: saturn_bios.bin @@ -2945,6 +3052,9 @@ systems: native_name: Sega Saturn native_id: saturn name: Sega Saturn + cores: + - yabasanshiro + - mednafen_saturn sega-mega-cd: files: - name: bios_CD_E.bin @@ -2970,6 +3080,9 @@ systems: native_name: Sega CD native_id: segacd name: Sega CD + cores: + - genesis_plus_gx + - picodrive sc-3000: files: - name: config.ini @@ -2980,6 +3093,8 @@ systems: native_name: SC-3000 native_id: sc-3000 name: SC-3000 + cores: + - bluemsx sharp-x68000: files: - name: iplrom.dat @@ -3014,6 +3129,8 @@ systems: native_name: Sharp x68000 native_id: x68000 name: Sharp x68000 + cores: + - px68k snk-neogeo: files: - name: neogeo.zip @@ -3024,6 +3141,12 @@ systems: native_name: NeoGeo native_id: neogeo name: NeoGeo + cores: + - fbneo + - mame2003_plus + - fbalpha2012 + - geolith + - FBNEOSA snk-neogeo-cd: files: - name: neogeo.zip @@ -3066,6 +3189,11 @@ systems: native_name: PSX native_id: psx name: PSX + cores: + - pcsx_rearmed_32b + - pcsx_rearmed + - swanstation + - Duckstation diablo: files: - name: diabdat.mpq @@ -3108,6 +3236,8 @@ systems: native_name: "Diablo 1: Hellfire (devilutionx) " native_id: Diablo name: "Diablo 1: Hellfire (devilutionx) " + cores: + - devilutionx dinothawr: files: - name: dinothawr.game @@ -3118,6 +3248,8 @@ systems: native_name: Dinothawr native_id: Dinothawr name: Dinothawr + cores: + - dinothawr doom: files: - name: doom1.wad @@ -3134,6 +3266,8 @@ systems: native_name: "DOOM2 (PrBoom)" native_id: DOOM1 name: "DOOM2 (PrBoom)" + cores: + - prboom tyrian: files: - name: cubetxt1.dat @@ -3666,6 +3800,8 @@ systems: native_name: OpenTyrian native_id: OpenTyrian name: OpenTyrian + cores: + - opentyrian sega-outrun: files: - name: epr-10187.88 @@ -3904,6 +4040,8 @@ systems: native_name: Cannonball native_id: Cannonball name: Cannonball + cores: + - cannonball cave-story: files: - name: Doukutsu.exe @@ -3914,6 +4052,8 @@ systems: native_name: "Cave Story (nxengine)" native_id: CaveStory name: "Cave Story (nxengine)" + cores: + - nxengine quake: files: - name: pak0.pak @@ -3924,6 +4064,8 @@ systems: native_name: "Quake (tryquake)" native_id: Quake name: "Quake (tryquake)" + cores: + - tyrquake flashback: files: - name: level1.map @@ -3934,6 +4076,8 @@ systems: native_name: "Flashback (REminiscence)" native_id: REminiscence name: "Flashback (REminiscence)" + cores: + - reminiscence xrick: files: - name: data.zip @@ -3944,6 +4088,8 @@ systems: native_name: "Rick Dangerous (xrick)" native_id: RickDangerous name: "Rick Dangerous (xrick)" + cores: + - xrick vvvvvv: files: - name: data.zip @@ -3954,6 +4100,8 @@ systems: native_name: VVVVVV native_id: VVVVVV name: VVVVVV + cores: + - VVVVVV duke3d: files: - name: duke3d.grp @@ -3964,6 +4112,8 @@ systems: native_name: Duke Nukem 3D native_id: eduke32 name: Duke Nukem 3D + cores: + - eduke32 sonic-1-2013: files: - name: Data.rsdk @@ -3974,6 +4124,8 @@ systems: native_name: Sonic 1 native_id: sonic1 name: Sonic 1 + cores: + - sonic2013 sonic-2-2013: files: - name: Data.rsdk @@ -3984,6 +4136,8 @@ systems: native_name: Sonic 2 native_id: sonic2 name: Sonic 2 + cores: + - sonic2013 soniccd: files: - name: Data.rsdk @@ -3994,6 +4148,8 @@ systems: native_name: Sonic CD native_id: soniccd name: Sonic CD + cores: + - soniccd sonic-mania: files: - name: Data.rsdk @@ -4004,7 +4160,9 @@ systems: native_name: Sonic Mania native_id: sonicmania name: Sonic Mania - RigelEngine: + cores: + - sonicmania + duke-nukem-2: files: - name: NUKEM2.CMP destination: ports/rigelengine/NUKEM2.CMP @@ -4044,6 +4202,8 @@ systems: native_name: Duke Nukem 2 native_id: RigelEngine name: Duke Nukem 2 + cores: + - RigelEngine cdi: files: - name: cdimono1.zip diff --git a/platforms/knulli.yml b/platforms/knulli.yml index 444f82c6..45e00ab0 100644 --- a/platforms/knulli.yml +++ b/platforms/knulli.yml @@ -121,7 +121,6 @@ cores: - moonlight - mpv - mrboom -- mugen - mupen64plus - mupen64plus-next - neocd @@ -203,7 +202,6 @@ cores: - uae4arm - uqm - uzem -- vaixterm - vb - vba-m - vecx @@ -226,7 +224,6 @@ cores: - vox_official - vpinball - wasm4 -- wine - x1 - x128 - x16emu @@ -277,6 +274,8 @@ systems: native_name: Atari 800 native_id: atari800 name: Atari 800 + cores: + - atari800 atari-5200: files: - name: 5200.rom @@ -288,6 +287,9 @@ systems: native_name: Atari 5200 native_id: atari5200 name: Atari 5200 + cores: + - atari800 + - a5200 atari-st: files: - name: tos.img @@ -544,6 +546,9 @@ systems: native_name: Atari ST native_id: atarist name: Atari ST + cores: + - hatari + - hatarib atari-lynx: files: - name: lynxboot.img @@ -555,6 +560,10 @@ systems: native_name: Lynx native_id: lynx name: Lynx + cores: + - mednafen_lynx + - handy + - holani magnavox-odyssey2: files: - name: o2rom.bin @@ -573,6 +582,8 @@ systems: native_name: Odyssey 2 native_id: o2em name: Odyssey 2 + cores: + - o2em philips-videopac: files: - name: g7400.bin @@ -591,6 +602,8 @@ systems: native_name: Videopac+ G7400 native_id: videopacplus name: Videopac+ G7400 + cores: + - o2em mattel-intellivision: files: - name: exec.bin @@ -609,6 +622,8 @@ systems: native_name: Mattel Intellivision native_id: intellivision name: Mattel Intellivision + cores: + - freeintv nec-pc-engine: files: - name: syscard3.pce @@ -627,6 +642,10 @@ systems: native_name: Supergrafx native_id: pcengine name: Supergrafx + cores: + - pce + - pce_fast + - mednafen_supergrafx nec-pc-fx: files: - name: pcfx.rom @@ -638,6 +657,8 @@ systems: native_name: PC-FX native_id: pcfx name: PC-FX + cores: + - pcfx snk-neogeo: files: - name: neogeo.zip @@ -649,6 +670,15 @@ systems: native_name: NeoGeo native_id: neogeo name: NeoGeo + cores: + - fbalpha + - fbneo + - geolith + - imame4all + - mame078plus + - mame0139 + - mame + - fba2x snk-neogeo-cd: files: - name: neocd_f.rom @@ -723,6 +753,8 @@ systems: native_name: NeoGeo CD native_id: neogeocd name: NeoGeo CD + cores: + - neocd sharp-x68000: files: - name: iplrom.dat @@ -741,6 +773,8 @@ systems: native_name: Sharp x68000 native_id: x68000 name: Sharp x68000 + cores: + - px68k 3do: files: - name: panafz1.bin @@ -766,6 +800,8 @@ systems: native_name: 3DO native_id: 3do name: 3DO + cores: + - opera sega-dreamcast: files: - name: dc_boot.bin @@ -784,6 +820,11 @@ systems: native_name: Dreamcast native_id: dreamcast name: Dreamcast + cores: + - flycast + - flycastvl + - flycast-xtreme + - redream sega-dreamcast-arcade: files: - name: naomi.zip @@ -976,6 +1017,10 @@ systems: native_name: Naomi 2 native_id: naomi name: Naomi 2 + cores: + - flycast + - flycastvl + - flycast-xtreme sega-mega-cd: files: - name: bios_CD_E.bin @@ -1022,6 +1067,10 @@ systems: native_name: MSU-MD native_id: segacd name: MSU-MD + cores: + - genesisplusgx + - picodrive + - genesisplusgx-wide sega-saturn: files: - name: sega_101.bin @@ -1061,6 +1110,11 @@ systems: native_name: Sega Saturn native_id: saturn name: Sega Saturn + cores: + - yabasanshiro + - beetle-saturn + - kronos + - ymir sony-playstation: files: - name: psxonpsp660.bin @@ -1114,6 +1168,12 @@ systems: native_name: PSX native_id: psx name: PSX + cores: + - pcsx_rearmed + - swanstation + - mednafen_psx + - duckstation + - duckstation-legacy sony-playstation-2: files: - name: ps2-0230a-20080220.bin @@ -1125,6 +1185,10 @@ systems: native_name: PS2 native_id: ps2 name: PS2 + cores: + - pcsx2 + - play + - aethersx2 sony-playstation-3: files: - name: PS3UPDAT.PUP @@ -1136,6 +1200,8 @@ systems: native_name: PS3 native_id: ps3 name: PS3 + cores: + - rpcs3 nintendo-fds: files: - name: disksys.rom @@ -1147,6 +1213,10 @@ systems: native_name: Nintendo Family Computer Disk System native_id: fds name: Nintendo Family Computer Disk System + cores: + - fceumm + - nestopia + - mesen nintendo-ds: files: - name: firmware.bin @@ -1197,6 +1267,12 @@ systems: native_name: Nintendo DS native_id: nds name: Nintendo DS + cores: + - desmume + - melonds + - melondsds + - drastic + - advanced_drastic nintendo-gba: files: - name: gba_bios.bin @@ -1229,6 +1305,10 @@ systems: native_name: Nintendo Gameboy Advance native_id: gba name: Nintendo Gameboy Advance + cores: + - mgba + - vba-m + - gpsp nintendo-satellaview: files: - name: BS-X.bin @@ -1240,6 +1320,10 @@ systems: native_name: Satellaview native_id: satellaview name: Satellaview + cores: + - pocketsnes + - snes9x + - mesen-s nintendo-sufami-turbo: files: - name: STBIOS.bin @@ -1251,6 +1335,8 @@ systems: native_name: Sufami native_id: sufami name: Sufami + cores: + - snes9x nintendo-sgb: files: - name: sgb_boot.bin @@ -1283,6 +1369,9 @@ systems: native_name: Super Game Boy native_id: sgb name: Super Game Boy + cores: + - mgba + - mesen-s microsoft-msx: files: - name: MSX.ROM @@ -1420,6 +1509,10 @@ systems: native_name: MSX-Turbo native_id: msx name: MSX-Turbo + cores: + - bluemsx + - fmsx + - openmsx microsoft-xbox: files: - name: mcpx_1.0.bin @@ -1438,6 +1531,8 @@ systems: native_name: Xbox native_id: xbox name: Xbox + cores: + - xemu commodore-amiga: files: - name: kick33180.A500 @@ -1624,6 +1719,12 @@ systems: native_name: Amiga CD32 native_id: amiga500 name: Amiga CD32 + cores: + - fsuae + - amiberry + - puae + - puae2021 + - uae4arm nec-pc-88: files: - name: N88.ROM @@ -1684,6 +1785,8 @@ systems: native_name: NEC PC-8800 native_id: pc88 name: NEC PC-8800 + cores: + - quasi88 nec-pc-98: files: - name: BIOS.ROM @@ -1723,6 +1826,8 @@ systems: native_name: NEC PC-9800 native_id: pc98 name: NEC PC-9800 + cores: + - np2kai fairchild-channel-f: files: - name: sl31253.bin @@ -1748,6 +1853,8 @@ systems: native_name: Fairchild ChannelF native_id: channelf name: Fairchild ChannelF + cores: + - freechaf sharp-x1: files: - name: IPLROM.X1 @@ -1766,6 +1873,8 @@ systems: native_name: Sharp X1 native_id: x1 name: Sharp X1 + cores: + - x1 fmtowns: files: - name: FMT_DIC.ROM @@ -1911,6 +2020,9 @@ systems: native_name: Fujistu FM-Towns native_id: fmtowns name: Fujistu FM-Towns + cores: + - mame + - tsugaru gamepark-gp32: files: - name: gp32.zip @@ -1969,6 +2081,8 @@ systems: native_name: GamePark GP32 native_id: gp32 name: GamePark GP32 + cores: + - mame laser310: files: - name: laser310.zip @@ -1995,6 +2109,8 @@ systems: native_name: VTech Laser 310 native_id: laser310 name: VTech Laser 310 + cores: + - mame fpinball: files: - name: scripten.exe @@ -2006,6 +2122,8 @@ systems: native_name: Future Pinball native_id: fpinball name: Future Pinball + cores: + - fpinball scv: files: - name: upd7801g.s01 @@ -2017,6 +2135,8 @@ systems: native_name: Super Cassette Vision native_id: scv name: Super Cassette Vision + cores: + - emuscv apple-iigs: files: - name: ROM1 @@ -2074,6 +2194,9 @@ systems: native_name: Apple IIgs native_id: gsplus name: Apple IIgs + cores: + - mame + - gsplus zc210: files: - name: zcdata.dat @@ -2169,6 +2292,8 @@ systems: native_name: Zelda Classic native_id: zc210 name: Zelda Classic + cores: + - zc210 apple-macintosh-ii: files: - name: MacII.ROM @@ -2527,6 +2652,9 @@ systems: native_name: Apple Macintosh native_id: macintosh name: Apple Macintosh + cores: + - minivmac + - mame coco: files: - name: coco.zip @@ -2631,6 +2759,8 @@ systems: native_name: Tandy Color Computer native_id: coco name: Tandy Color Computer + cores: + - mame tutor: files: - name: tutor.zip @@ -2657,6 +2787,8 @@ systems: native_name: Tomy Tutor native_id: tutor name: Tomy Tutor + cores: + - mame ti99: files: - name: ti99_4a.zip @@ -2729,6 +2861,8 @@ systems: native_name: Texas Instruments TI-99 native_id: ti99 name: Texas Instruments TI-99 + cores: + - mame astrocde: files: - name: astrocde.zip @@ -2747,6 +2881,8 @@ systems: native_name: Bally Astrocade native_id: astrocde name: Bally Astrocade + cores: + - mame gmaster: files: - name: gmaster.zip @@ -2765,6 +2901,8 @@ systems: native_name: Hartung Game Master native_id: gmaster name: Hartung Game Master + cores: + - mame adam: files: - name: adam.zip @@ -2943,6 +3081,8 @@ systems: native_name: Coleco Adam native_id: adam name: Coleco Adam + cores: + - mame acorn-bbc-micro: files: - name: bbcb.zip @@ -3239,6 +3379,8 @@ systems: native_name: BBC Micro native_id: bbc name: BBC Micro + cores: + - mame apfm1000: files: - name: apfm1000.zip @@ -3273,6 +3415,8 @@ systems: native_name: APF M-1000 native_id: apfm1000 name: APF M-1000 + cores: + - mame fm7: files: - name: fm7.zip @@ -3369,6 +3513,8 @@ systems: native_name: Fujitsu FM-7 native_id: fm7 name: Fujitsu FM-7 + cores: + - mame archimedes: files: - name: aa310.zip @@ -3705,6 +3851,8 @@ systems: native_name: Acorn Archimedes native_id: archimedes name: Acorn Archimedes + cores: + - mame atom: files: - name: atom.zip @@ -3739,6 +3887,8 @@ systems: native_name: Acorn Atom native_id: atom name: Acorn Atom + cores: + - mame electron: files: - name: electron.zip @@ -3839,6 +3989,8 @@ systems: native_name: Acorn Electron native_id: electron name: Acorn Electron + cores: + - mame apple-ii: files: - name: apple2e.zip @@ -4051,6 +4203,10 @@ systems: native_name: Apple II native_id: apple2 name: Apple II + cores: + - mame + - applewin + - gsplus camplynx: files: - name: lynx48k.zip @@ -4185,6 +4341,8 @@ systems: native_name: Camputers Lynx native_id: camplynx name: Camputers Lynx + cores: + - mame vgmplay: files: - name: qsound.zip @@ -4231,6 +4389,8 @@ systems: native_name: Video Game Music Player native_id: vgmplay name: Video Game Music Player + cores: + - mame gamepock: files: - name: gamepock.zip @@ -4249,6 +4409,8 @@ systems: native_name: Epoch Game Pocket Computer native_id: gamepock name: Epoch Game Pocket Computer + cores: + - mame gamecom: files: - name: gamecom.zip @@ -4275,6 +4437,8 @@ systems: native_name: Tiger Game.com native_id: gamecom name: Tiger Game.com + cores: + - mame xegs: files: - name: xegs.zip @@ -4293,6 +4457,8 @@ systems: native_name: Atari XE Game System native_id: xegs name: Atari XE Game System + cores: + - mame crvision: files: - name: crvision.zip @@ -4311,6 +4477,8 @@ systems: native_name: VTech CreatiVision native_id: crvision name: VTech CreatiVision + cores: + - mame vtech-vsmile: files: - name: vsmile.zip @@ -4345,6 +4513,8 @@ systems: native_name: VTech V.Smile native_id: vsmile name: VTech V.Smile + cores: + - mame vtech-socrates: files: - name: socrates.zip @@ -4395,6 +4565,8 @@ systems: native_name: VTech Socrates native_id: socrates name: VTech Socrates + cores: + - mame entex-advision: files: - name: advision.zip @@ -4421,6 +4593,8 @@ systems: native_name: Entex Adventure Vision native_id: advision name: Entex Adventure Vision + cores: + - mame gamate: files: - name: gamate.zip @@ -4447,6 +4621,8 @@ systems: native_name: Bitcorp Gamate native_id: gamate name: Bitcorp Gamate + cores: + - mame cdi: files: - name: cdimono1.zip @@ -4497,6 +4673,9 @@ systems: native_name: Phillips CD-i native_id: cdi name: Phillips CD-i + cores: + - same_cdi + - mame hikaru: files: - name: hikaru.zip @@ -4515,6 +4694,8 @@ systems: native_name: Hikaru native_id: hikaru name: Hikaru + cores: + - demul sony-playstation-vita: files: - name: PSP2UPDAT.PUP @@ -4533,6 +4714,8 @@ systems: native_name: PS Vita native_id: psvita name: PS Vita + cores: + - vita3k vectrex: files: - name: vectrex.zip @@ -4559,6 +4742,9 @@ systems: native_name: GCE Vectrex native_id: vectrex name: GCE Vectrex + cores: + - vecx + - mame scummvm: files: - name: MT32_PCM.ROM @@ -4577,6 +4763,8 @@ systems: native_name: ScummVM native_id: scummvm name: ScummVM + cores: + - scummvm supracan: files: - name: supracan.zip @@ -4595,6 +4783,8 @@ systems: native_name: Supr'A'can native_id: supracan name: Supr'A'can + cores: + - mame tandy-vis: files: - name: vis.zip @@ -4621,6 +4811,8 @@ systems: native_name: Tandy Video Information System native_id: vis name: Tandy Video Information System + cores: + - mame nintendo-64dd: files: - name: 64DD_IPL.bin @@ -4632,6 +4824,10 @@ systems: native_name: Nintendo 64DD native_id: n64dd name: Nintendo 64DD + cores: + - mupen64plus + - mupen64plus-next + - parallel_n64 nintendo-gamecube: files: - name: IPL.bin @@ -4657,6 +4853,8 @@ systems: native_name: Nintendo GameCube native_id: gamecube name: Nintendo GameCube + cores: + - dolphin chihiro: files: - name: mcpx_1.0.bin @@ -4675,6 +4873,8 @@ systems: native_name: Sega Chihiro native_id: chihiro name: Sega Chihiro + cores: + - xemu standalone_cores: - abuse - advanced_drastic @@ -4737,7 +4937,6 @@ standalone_cores: - model2emu - moonlight - mpv -- mugen - mupen64plus - openbor - openbor4432 @@ -4780,7 +4979,6 @@ standalone_cores: - tsugaru - tyrian - uqm -- vaixterm - vice - vita3k - vkquake @@ -4788,7 +4986,6 @@ standalone_cores: - vkquake3 - vox_official - vpinball -- wine - x128 - x16emu - x64 diff --git a/platforms/targets/_overrides.yml b/platforms/targets/_overrides.yml index 8c0f13c3..79f8443d 100644 --- a/platforms/targets/_overrides.yml +++ b/platforms/targets/_overrides.yml @@ -23,8 +23,10 @@ batocera: # fsuae/amiberry, not emulators of their own. batocera-es-system # groups them under the emulator's node in es_systems.yml. # odcommander is OpenDingux's file manager, and flatpak/steam/wine-tkg/sh - # are runtime launchers. N64 video plugins (gliden64, glide64mk2, - # rice) are backends to mupen64plus, not emulators. + # are runtime launchers. wine is the Windows system's launcher and + # mugen the MUGEN system's, both running the user's Windows program + # on wine-tkg (configgen-defaults.yml). N64 video plugins (gliden64, + # glide64mk2, rice) are backends to mupen64plus, not emulators. remove_cores: - A500 - "A500+" @@ -40,6 +42,8 @@ batocera: - sh - steam - wine-tkg + - wine + - mugen - gliden64 - glide64mk2 - rice @@ -168,6 +172,37 @@ rocknix: amd64: aliases: [x86_64, pc] +# KNULLI reads Batocera's list above, plus its own: vaixterm is a terminal, +# filed as the VaixTerm system's emulator (configgen-defaults.yml:669-671). +knulli: + targets: + _default: + remove_cores: + - vaixterm + +# EmuELEC and AmberELEC have no target file: the scrapers read these lists. +# mpv and ffplay play the Media Player system's videos (EmuELEC +# emuelecRunEmu.sh:301-303, AmberELEC runemu.py:122). gmloader runs a +# GameMaker game's own Android library (emuelecRunEmu.sh:331-334). Ports +# that start the user's own game binary name its runtime or the game as +# port_exe: mono for Celeste and TMNTSR, the game for Shovel Knight, which +# shovelknight.sh starts through box64 (ports.yaml, emuelec-ports). +emuelec: + targets: + _default: + remove_cores: + - ffplay + - gmloader + - mono + - mpv + - ShovelKnight + +amberelec: + targets: + _default: + remove_cores: + - mpv + recalbox: targets: _default: diff --git a/schemas/platform.schema.json b/schemas/platform.schema.json index 33c7f0cc..b6007472 100644 --- a/schemas/platform.schema.json +++ b/schemas/platform.schema.json @@ -302,6 +302,12 @@ "type": "string", "description": "Emulator core name" }, + "cores": { + "type": "array", + "items": {"type": "string"}, + "uniqueItems": true, + "description": "The emulators and cores the platform's system list offers this system, as the platform writes them, in its order" + }, "manufacturer": { "type": "string", "description": "Hardware manufacturer" diff --git a/scripts/scraper/amberelec_scraper.py b/scripts/scraper/amberelec_scraper.py index 3bad7865..0f729fae 100644 --- a/scripts/scraper/amberelec_scraper.py +++ b/scripts/scraper/amberelec_scraper.py @@ -37,11 +37,12 @@ from .batocera_scraper import ( SYSTEM_SLUG_MAP as BATOCERA_SLUGS, ) from .batocera_scraper import ( + _not_cores, extract_systems_dict, platform_systems, requirements_from_systems, ) -from .es_emulators import es_cores, xml_emulators +from .es_emulators import attach_system_cores, es_cores, system_cores, xml_systems PLATFORM_NAME = "amberelec" @@ -65,9 +66,14 @@ SYSTEM_SLUG_MAP = { # names. `mame` lists MAME machines and runs them on the libretro MAME core # (lines 233-261 set self.core = 'mame', line 355 builds the RetroArch command). RUNNERS: dict[str, str | None] = {"retroarch": None, "retrorun": None, "mame": "mame"} -# runemu.py:113, PPSSPPSDL runs /usr/bin/ppsspp.sh, installed by the -# ppssppsa package (packages/games/emulators/ppssppsa). -RENAMED = {"PPSSPPSDL": "ppsspp"} +# runemu.py:110-128 starts every other emulator through the script its +# standalone_emulators table names, keyed by the emulator name es_systems.cfg +# writes. Three are the standalone build of an emulator the list also offers +# as a libretro core, the package's PKG_SITE naming the same project: +# hatarisa hatari/hatari (packages/games/emulators/hatarisa), ppssppsa +# hrydgard/ppsspp (PPSSPPSDL runs its ppsspp.sh, line 116) and scummvmsa +# scummvm/scummvm. +BUILDS_OF = {"HATARISA": "hatari", "PPSSPPSDL": "ppsspp", "SCUMMVMSA": "scummvm"} _PRERELEASE_COMMIT = re.compile(r"AmberELEC/AmberELEC@([0-9a-f]{40})") @@ -128,11 +134,12 @@ class Scraper(BaseScraper): systems = platform_systems( self.fetch_requirements(), extract_systems_dict(self._fetch_raw()), SYSTEM_SLUG_MAP ) - cores, standalone, dual = es_cores( - xml_emulators(self._fetch_text(ES_SYSTEMS_TEMPLATE.format(ref=self.commit))), - RUNNERS, - RENAMED, - ) + # es_systems.cfg gives each its in menu order + # (atarist, lines 267-288: retroarch's hatari, then HATARISA). + es_systems = xml_systems(self._fetch_text(ES_SYSTEMS_TEMPLATE.format(ref=self.commit))) + not_cores = _not_cores(PLATFORM_NAME) + cores, standalone, dual = es_cores(es_systems, RUNNERS, BUILDS_OF, not_cores) + attach_system_cores(systems, system_cores(es_systems, RUNNERS, not_cores)) return { "platform": "AmberELEC", "version": self.tag, diff --git a/scripts/scraper/batocera_scraper.py b/scripts/scraper/batocera_scraper.py index cac180ea..14df84a4 100644 --- a/scripts/scraper/batocera_scraper.py +++ b/scripts/scraper/batocera_scraper.py @@ -142,6 +142,44 @@ def es_systems_libretro(text: str) -> set[str]: return found +def es_systems_by_system( + es_systems_text: str, defaults_text: str, dropped: set[str] +) -> dict[str, list[str]]: + """system -> the cores es_systems.yml offers it, in the file's order. + + Read as configgen_cores reads the whole list: a core under `libretro:` + is a libretro core, any other emulator is its own core and so is each + build it lists. The core configgen-defaults.yml starts by default comes + last when es_systems.yml does not offer it. + """ + data = yaml_load_upstream(es_systems_text) + defaults = yaml_load_upstream(defaults_text) + if not isinstance(data, dict) or not isinstance(defaults, dict): + raise ValueError("es_systems.yml or configgen-defaults.yml is not a mapping") + result: dict[str, list[str]] = {} + for system, spec in data.items(): + emulators = spec.get("emulators") if isinstance(spec, dict) else None + names: list[str] = [] + if isinstance(emulators, dict): + for emulator, entries in emulators.items(): + if not isinstance(entries, dict): + continue + if emulator != "libretro": + names.append(str(emulator)) + names.extend(str(c) for c, s in entries.items() if isinstance(s, dict)) + default = defaults.get(system) + if isinstance(default, dict): + emulator, core = default.get("emulator"), default.get("core") + if emulator and emulator != "libretro": + names.append(str(emulator)) + if core: + names.append(str(core)) + names = [n for n in dict.fromkeys(names) if n not in dropped] + if names: + result[str(system)] = names + return result + + SYSTEM_SLUG_MAP = { "advision": "entex-advision", "amiga500": "commodore-amiga", diff --git a/scripts/scraper/emuelec_scraper.py b/scripts/scraper/emuelec_scraper.py index a607c2f3..065fa4ea 100644 --- a/scripts/scraper/emuelec_scraper.py +++ b/scripts/scraper/emuelec_scraper.py @@ -27,19 +27,29 @@ Verification (batocera-systems at the release tag): from __future__ import annotations import re +import shlex import urllib.error import urllib.request +from safeparse import yaml_load_upstream + from .base_scraper import BaseScraper, BiosRequirement, fetch_github_latest_version from .batocera_scraper import ( SYSTEM_SLUG_MAP as BATOCERA_SLUGS, ) from .batocera_scraper import ( + _not_cores, extract_systems_dict, platform_systems, requirements_from_systems, ) -from .es_emulators import es_cores, json_emulators +from .es_emulators import ( + SystemEmulators, + attach_system_cores, + es_cores, + json_systems, + system_cores, +) PLATFORM_NAME = "emuelec" @@ -47,12 +57,14 @@ REPO = "EmuELEC/EmuELEC" _RAW = "https://raw.githubusercontent.com/EmuELEC/EmuELEC/{ref}/" SOURCE_TEMPLATE = _RAW + "packages/sx05re/emuelec/bin/batocera/batocera-systems" ES_SYSTEMS_TEMPLATE = _RAW + "packages/sx05re/emuelec-emulationstation/config/es_systems.json" +PORTS_TEMPLATE = _RAW + "packages/sx05re/emuelec-ports/sources/ports.yaml" # Batocera's names, plus the keys EmuELEC writes that Batocera does not: # console keys under the slug the other scrapers give them, and each port # under the system of the profile of the engine its name gives # (devilutionx, prboom, opentyrian, cannonball, nxengine, tyrquake, -# reminiscence, xrick, vvvvvv, eduke32, sonic2013, sonic-mania, dinothawr). +# reminiscence, xrick, vvvvvv, eduke32, sonic2013, sonic-mania, dinothawr, +# rigelengine). SYSTEM_SLUG_MAP = { **BATOCERA_SLUGS, "atari7800": "atari-7800", @@ -75,14 +87,77 @@ SYSTEM_SLUG_MAP = { "sonic1": "sonic-1-2013", "sonic2": "sonic-2-2013", "sonicmania": "sonic-mania", + "RigelEngine": "duke-nukem-2", } # es_systems.json names libretro cores under `libretro`, and `retrorun` -# loads the libretro core it lists. +# loads the libretro core it lists. emuelecRunEmu.sh:101-116 turns either +# into ${CORE}_libretro. Any other emulator runs the core it names +# (EMU="${CORE}", line 107) through the case at lines 164-353. RUNNERS: dict[str, str | None] = {"libretro": None, "retrorun": None} -# packages/sx05re/emulators/PPSSPPSDL builds PPSSPP's SDL front end. -RENAMED = {"PPSSPPSDL": "ppsspp"} +# A standalone emulator and the libretro core of the same emulator, from the +# packages each one builds (packages/sx05re/emulators//package.mk +# PKG_SITE): hatarisa hatari/hatari, stellasa stella-emu/stella, fbneoSA +# finalburnneo/FBNeo, flycastsa flyinghead/flycast, scummvmsa +# scummvm/scummvm, PPSSPPSDL hrydgard/ppsspp. +BUILDS_OF = { + "HATARISA": "hatari", + "STELLASA": "stella", + "FBNEOSA": "fbneo", + "flycastsa": "flycast", + "SCUMMVMSA": "scummvm", + "PPSSPPSDL": "ppsspp", +} + +# The ES system `ports` (es_systems.json:2570-2590) runs one generated +# script per entry of ports.yaml through emuelecRunApp.sh:22, and its single +# emulator `ports` is that dispatch, not a program. The scripts are rendered +# from port.sh.j2 by port_builder.py (emuelec-ports/package.mk:62-64): each +# runs its `commands`, so the engine is the libretro core a command hands +# emuelecRunEmu.sh with -C (lines 367-369 load _libretro.so), or the +# program `port_exe` names. +PORTS_SYSTEM = "ports" + + +def port_engines(text: str) -> list[tuple[str, str, str | None, bool]]: + """(port, engine, check_bios key, libretro?) for each entry of ports.yaml. + + The engine is written as the OS writes it: the libretro core a command + hands emuelecRunEmu.sh with -C, else `port_exe`. `check_bios` is the + batocera-systems key the script hands ee_check_bios on a failed start + (port.sh.j2:23-25), the key the port's files are listed under. + """ + data = yaml_load_upstream(text) + if not isinstance(data, dict) or not data: + raise ValueError("ports.yaml names no port") + engines = [] + for port, spec in data.items(): + core = None + for command in map(str, spec.get("commands") or []): + if "emuelecRunEmu.sh" in command: + core = next( + (w[2:] for w in shlex.split(command) if w.startswith("-C")), core + ) + engine = core or spec.get("port_exe") + if not engine: + raise ValueError(f"ports.yaml: {port} names neither a core nor port_exe") + engines.append((str(port), str(engine), spec.get("check_bios"), core is not None)) + return engines + + +def with_ports( + systems: SystemEmulators, engines: list[tuple[str, str, str | None, bool]] +) -> SystemEmulators: + """The ES systems, each port engine filed under `ports` and its own key.""" + def entry(engine: str, libretro: bool) -> tuple[str, list[str]]: + return ("libretro", [engine]) if libretro else (engine, [engine]) + + result = {**systems, PORTS_SYSTEM: [entry(e, lr) for _, e, _, lr in engines]} + for _, engine, key, libretro in engines: + if key: + result[key] = [*result.get(key, []), entry(engine, libretro)] + return result class Scraper(BaseScraper): """Scraper for EmuELEC's batocera-systems.""" @@ -115,11 +190,16 @@ class Scraper(BaseScraper): systems = platform_systems( self.fetch_requirements(), extract_systems_dict(self._fetch_raw()), SYSTEM_SLUG_MAP ) - cores, standalone, dual = es_cores( - json_emulators(self._fetch_text(ES_SYSTEMS_TEMPLATE.format(ref=self.tag))), - RUNNERS, - RENAMED, + # es_systems.json gives each system its emulators in menu order (psx, + # lines 3610-3660: libretro pcsx_rearmed_32b, pcsx_rearmed, + # swanstation, then Duckstation). + es_systems = with_ports( + json_systems(self._fetch_text(ES_SYSTEMS_TEMPLATE.format(ref=self.tag))), + port_engines(self._fetch_text(PORTS_TEMPLATE.format(ref=self.tag))), ) + not_cores = _not_cores(PLATFORM_NAME) + cores, standalone, dual = es_cores(es_systems, RUNNERS, BUILDS_OF, not_cores) + attach_system_cores(systems, system_cores(es_systems, RUNNERS, not_cores)) return { "platform": "EmuELEC", "version": self.tag.removeprefix("v"), diff --git a/scripts/scraper/es_emulators.py b/scripts/scraper/es_emulators.py index 998321fe..12da77bd 100644 --- a/scripts/scraper/es_emulators.py +++ b/scripts/scraper/es_emulators.py @@ -1,88 +1,161 @@ -"""The emulators an EmulationStation system list offers, by profile names. +"""The emulators an EmulationStation system list offers, by system. AmberELEC writes its list as es_systems.cfg XML, EmuELEC as an es_systems.json -it turns into that XML at build time. Both name, per system, the emulators a -user may pick and the cores under each. Some emulators are libretro front -ends (RetroArch, retrorun) whose cores are libretro core names. Every other -emulator is a standalone program named by the OS (`HATARISA`, `PPSSPPSDL`). +it turns into that XML at build time. Both name, per system and in the order +the menu shows them, the emulators a user may pick and the cores under each. +Some emulators are libretro front ends (RetroArch, retrorun) whose cores are +libretro core names. Every other emulator is a standalone program, named as +the OS's launch code dispatches on it (`HATARISA`, `PPSSPPSDL`): profiles +answer to that spelling in their `cores:`. """ from __future__ import annotations import json -import re +from collections.abc import Iterable, Mapping from safeparse import parse_untrusted_xml +# system -> [(emulator, [core, ...]), ...], both in the list's order. +SystemEmulators = dict[str, list[tuple[str, list[str]]]] + + +def xml_systems(text: str) -> SystemEmulators: + """Each system's emulators and cores, from an es_systems.cfg.""" + root = parse_untrusted_xml(text, "es_systems.cfg") + systems: SystemEmulators = {} + for system in root.iter("system"): + name = (system.findtext("name") or "").strip() + if not name: + continue + systems[name] = [ + (emulator.get("name"), + [core.text.strip() for core in emulator.iter("core") if core.text]) + for emulator in system.iter("emulator") + if emulator.get("name") + ] + if not any(systems.values()): + raise ValueError("es_systems.cfg names no emulator") + return systems + + +def json_systems(text: str) -> SystemEmulators: + """Each system's emulators and cores, from an es_systems.json.""" + systems: SystemEmulators = {} + for system in json.loads(text).get("systems", []): + systems[system["name"]] = [ + (emulator["name"], [core["name"] for core in emulator.get("cores") or []]) + for emulator in system.get("emulators") or [] + ] + if not any(systems.values()): + raise ValueError("es_systems.json names no emulator") + return systems + + +def _pairs(systems: SystemEmulators) -> dict[str, set[str]]: + pairs: dict[str, set[str]] = {} + for emulators in systems.values(): + for emulator, cores in emulators: + pairs.setdefault(emulator, set()).update(cores) + return pairs + def xml_emulators(text: str) -> dict[str, set[str]]: """Emulator -> cores, from an es_systems.cfg.""" - root = parse_untrusted_xml(text, "es_systems.cfg") - pairs: dict[str, set[str]] = {} - for emulator in root.iter("emulator"): - name = emulator.get("name") - if name: - pairs.setdefault(name, set()).update( - core.text.strip() for core in emulator.iter("core") if core.text - ) - if not pairs: - raise ValueError("es_systems.cfg names no emulator") - return pairs + return _pairs(xml_systems(text)) def json_emulators(text: str) -> dict[str, set[str]]: """Emulator -> cores, from an es_systems.json.""" - pairs: dict[str, set[str]] = {} - for system in json.loads(text).get("systems", []): - for emulator in system.get("emulators") or []: - pairs.setdefault(emulator["name"], set()).update( - core["name"] for core in emulator.get("cores") or [] - ) - if not pairs: - raise ValueError("es_systems.json names no emulator") - return pairs + return _pairs(json_systems(text)) -def standalone_names(emulator: str) -> list[str]: - """The names a profile may know a standalone emulator by. +def _names( + emulator: str, + cores: Iterable[str], + runners: Mapping[str, str | None], +) -> list[str]: + """The core names one emulator entry stands for. - The OS's own spelling, its lowercase, and the lowercase without the - `sa` suffix the ELEC family appends to a standalone build of an emulator - that also exists as a core (`HATARISA`, `scummvmsa`, `yabasanshiroSA`). + A runner's entry stands for the libretro cores it lists, or for the one + core it always loads. A standalone emulator stands for itself. """ - lower = emulator.lower() - names = [emulator, lower] - stripped = re.sub(r"-?sa$", "", lower) - if stripped: - names.append(stripped) - return list(dict.fromkeys(names)) + if emulator in runners: + fixed = runners[emulator] + return [fixed] if fixed else list(cores) + return [emulator] + + +def system_cores( + systems: SystemEmulators, + runners: Mapping[str, str | None], + not_cores: Iterable[str] = (), +) -> dict[str, list[str]]: + """system -> the cores it runs, in the list's order, as the OS writes them.""" + dropped = set(not_cores) + result: dict[str, list[str]] = {} + for system, emulators in systems.items(): + names = [ + name + for emulator, cores in emulators + for name in _names(emulator, cores, runners) + if name not in dropped + ] + if names: + result[system] = list(dict.fromkeys(names)) + return result def es_cores( - pairs: dict[str, set[str]], - runners: dict[str, str | None], - renamed: dict[str, str] | None = None, + systems: SystemEmulators, + runners: Mapping[str, str | None], + builds_of: Mapping[str, str] | None = None, + not_cores: Iterable[str] = (), ) -> tuple[list[str], list[str], list[str]]: """(cores, standalone_cores, dual_mode_cores) of an ES system list. `runners` maps each emulator that runs libretro cores to None when the cores it lists are libretro core names, or to the one core it always loads (AmberELEC's `mame` lists MAME machines and loads the MAME core). - `renamed` gives the emulator a standalone name stands for when the - suffix rule cannot (`PPSSPPSDL` is PPSSPP), each one read in the OS's - launch code. + `builds_of` maps a standalone emulator to the libretro core of the same + emulator, each pair read in the OS's packages: the standalone one is + dual-mode when the list also offers that core, as is a standalone name + the list also offers as a core. `not_cores` names what + the list files as an emulator without emulating anything (a media + player, a runtime). """ - renamed = renamed or {} + builds_of = builds_of or {} + dropped = set(not_cores) libretro: set[str] = set() - for runner, fixed in runners.items(): - if runner in pairs: - libretro.update([fixed] if fixed else pairs[runner]) standalone: set[str] = set() - for emulator in pairs: - if emulator in runners: - continue - standalone.update(standalone_names(emulator)) - if emulator in renamed: - standalone.add(renamed[emulator]) - dual = {name for name in standalone if name in libretro} + for emulators in systems.values(): + for emulator, cores in emulators: + names = {n for n in _names(emulator, cores, runners) if n not in dropped} + (libretro if emulator in runners else standalone).update(names) + dual = { + name for name in standalone + if name in libretro or builds_of.get(name) in libretro + } return sorted(libretro | standalone), sorted(standalone), sorted(dual) + + +def attach_system_cores( + platform_systems: dict[str, dict], by_native: Mapping[str, list[str]] +) -> None: + """Give each platform system the cores its native systems run. + + A slug can gather several native systems (Diablo and Hellfire under + diablo): its list is their union, in the order first met. A system the + ES list does not name gets no list, as nothing says what runs it. + """ + for entry in platform_systems.values(): + natives = [entry.get("native_id")] + [ + f.get("native_system") for f in entry.get("files") or [] + ] + cores = [ + core + for native in dict.fromkeys(n for n in natives if n) + for core in by_native.get(native, []) + ] + if cores: + entry["cores"] = list(dict.fromkeys(cores)) diff --git a/scripts/scraper/knulli_scraper.py b/scripts/scraper/knulli_scraper.py index 0a924ac8..9cda899b 100644 --- a/scripts/scraper/knulli_scraper.py +++ b/scripts/scraper/knulli_scraper.py @@ -37,10 +37,12 @@ from .batocera_scraper import ( from .batocera_scraper import ( _not_cores, configgen_cores, + es_systems_by_system, extract_systems_dict, platform_systems, requirements_from_systems, ) +from .es_emulators import attach_system_cores PLATFORM_NAME = "knulli" @@ -93,12 +95,16 @@ class Scraper(BaseScraper): self.fetch_requirements(), extract_systems_dict(self._fetch_raw()), SYSTEM_SLUG_MAP ) # es_systems.yml files machine selectors and plugins under an - # emulator as Batocera's does: Batocera's list of them applies. - cores, standalone, dual = configgen_cores( - self._fetch_text(CONFIGGEN_DEFAULTS_TEMPLATE.format(ref=self.tag)), - self._fetch_text(ES_SYSTEMS_TEMPLATE.format(ref=self.tag)), - _not_cores("batocera"), - ) + # emulator as Batocera's does: Batocera's list of them applies, with + # KNULLI's own. + defaults = self._fetch_text(CONFIGGEN_DEFAULTS_TEMPLATE.format(ref=self.tag)) + es_systems = self._fetch_text(ES_SYSTEMS_TEMPLATE.format(ref=self.tag)) + dropped = _not_cores("batocera") | _not_cores(PLATFORM_NAME) + cores, standalone, dual = configgen_cores(defaults, es_systems, dropped) + # Each system's own list (psx, es_systems.yml:640-653: libretro + # pcsx_rearmed, swanstation, mednafen_psx, then duckstation and + # duckstation-legacy). + attach_system_cores(systems, es_systems_by_system(es_systems, defaults, dropped)) result = { "platform": "KNULLI", "version": self.tag, diff --git a/tests/test_elec_scrapers.py b/tests/test_elec_scrapers.py index 7f96d84a..aeace915 100644 --- a/tests/test_elec_scrapers.py +++ b/tests/test_elec_scrapers.py @@ -17,7 +17,13 @@ REPO_ROOT = Path(__file__).resolve().parents[1] sys.path.insert(0, str(REPO_ROOT / "scripts")) from scraper import amberelec_scraper, emuelec_scraper, knulli_scraper # noqa: E402 -from scraper.es_emulators import es_cores, json_emulators, xml_emulators # noqa: E402 +from scraper.es_emulators import ( # noqa: E402 + attach_system_cores, + es_cores, + json_systems, + system_cores, + xml_systems, +) CHECKER = ''' @@ -133,24 +139,47 @@ class TheListsReadAsPythonReadsThem(unittest.TestCase): class TheCoresComeFromTheOsList(unittest.TestCase): def test_amberelec_runners_and_standalone_names(self): cores, standalone, dual = es_cores( - xml_emulators(AMBERELEC_ES), amberelec_scraper.RUNNERS, amberelec_scraper.RENAMED + xml_systems(AMBERELEC_ES), amberelec_scraper.RUNNERS, amberelec_scraper.BUILDS_OF ) # mame lists MAME machines and loads the MAME core: advision is no core. self.assertIn("mame", cores) self.assertNotIn("advision", cores) self.assertNotIn("mame", standalone) self.assertIn("flycast", cores) - self.assertTrue({"HATARISA", "hatari", "PPSSPPSDL", "ppsspp"} <= set(standalone)) - self.assertEqual(dual, ["hatari", "ppsspp"]) + # A standalone emulator keeps the name runemu.py dispatches on. + self.assertEqual(standalone, ["HATARISA", "PPSSPPSDL"]) + self.assertEqual(dual, ["HATARISA", "PPSSPPSDL"]) def test_emuelec_runners_and_standalone_names(self): cores, standalone, dual = es_cores( - json_emulators(EMUELEC_ES), emuelec_scraper.RUNNERS, emuelec_scraper.RENAMED + json_systems(EMUELEC_ES), emuelec_scraper.RUNNERS, emuelec_scraper.BUILDS_OF ) - self.assertIn("stella", standalone) + self.assertEqual(standalone, ["STELLASA", "yabasanshiro"]) self.assertNotIn("yabasanshiroSA", cores) self.assertEqual(dual, ["yabasanshiro"]) + def test_a_name_that_emulates_nothing_is_dropped(self): + cores, standalone, _ = es_cores( + json_systems(EMUELEC_ES), emuelec_scraper.RUNNERS, not_cores={"STELLASA"} + ) + self.assertNotIn("STELLASA", cores + standalone) + + def test_each_system_keeps_its_cores_in_menu_order(self): + by_system = system_cores(xml_systems(AMBERELEC_ES), amberelec_scraper.RUNNERS) + self.assertEqual(by_system["atarist"], ["hatari", "HATARISA"]) + self.assertEqual(by_system["advision"], ["mame"]) + self.assertEqual(by_system["dreamcast"], ["flycast"]) + + def test_a_slug_gathers_the_cores_of_its_native_systems(self): + systems = { + "diablo": {"native_id": "Diablo", "files": [ + {"native_system": "Diablo"}, {"native_system": "Hellfire"}]}, + "unlisted": {"native_id": "o2em", "files": []}, + } + attach_system_cores(systems, {"Diablo": ["devilutionx"], "Hellfire": ["devilutionx", "x"]}) + self.assertEqual(systems["diablo"]["cores"], ["devilutionx", "x"]) + self.assertNotIn("cores", systems["unlisted"]) + def test_the_newest_prerelease_names_its_commit(self): body = "This is a prerelease based on the commit: AmberELEC/AmberELEC@" + "a" * 40 + "." self.assertEqual(amberelec_scraper.prerelease_commit(body), "a" * 40) @@ -158,6 +187,53 @@ class TheCoresComeFromTheOsList(unittest.TestCase): amberelec_scraper.prerelease_commit("no commit here") +PORTS = """ +2048: + commands: + - /usr/bin/emuelecRunEmu.sh "" -Pports "${2}" -C2048 "-SC${0}" + +Diablo: + check_bios: Diablo + init_port: true + port_exe: "devilutionx" + commands: + - devilutionx.sh >> $EE_LOG 2>&1 + +Flashback: + check_bios: REminiscence + commands: + - /usr/bin/emuelecRunEmu.sh "/storage/roms/ports/reminiscence" -Pports "${2}" -Creminiscence "-SC${0}" +""" + + +class EmuelecPortsRunTheirEngine(unittest.TestCase): + """The ES system `ports` has one emulator, `ports`, the dispatch to each + port's script: what runs is the core or program the script names.""" + + def test_each_port_names_its_engine(self): + self.assertEqual(emuelec_scraper.port_engines(PORTS), [ + ("2048", "2048", None, True), + ("Diablo", "devilutionx", "Diablo", False), + ("Flashback", "reminiscence", "REminiscence", True), + ]) + + def test_engines_replace_the_dispatch(self): + systems = emuelec_scraper.with_ports( + {"ports": [("ports", ["ports"])]}, emuelec_scraper.port_engines(PORTS) + ) + cores, standalone, _ = es_cores(systems, emuelec_scraper.RUNNERS) + self.assertNotIn("ports", cores) + self.assertEqual(standalone, ["devilutionx"]) + self.assertTrue({"2048", "reminiscence"} <= set(cores)) + by_system = system_cores(systems, emuelec_scraper.RUNNERS) + self.assertEqual(by_system["Diablo"], ["devilutionx"]) + self.assertEqual(by_system["REminiscence"], ["reminiscence"]) + + def test_a_port_without_an_engine_is_refused(self): + with self.assertRaises(ValueError): + emuelec_scraper.port_engines("Broken:\n commands:\n - true\n") + + class CoreFilesGoUnderTheStatedFolder(unittest.TestCase): """EmuELEC's ports/ entries are a quarter of its list, so the most common root of its destinations fell under the 90% rule and core files went to @@ -204,5 +280,21 @@ class TheExportKeepsTheirShape(unittest.TestCase): self.assertEqual(Knulli.declared_path(fe), "bios/bios_CD_E.bin") +class TheCommittedListsAgree(unittest.TestCase): + """A system's cores are a part of the platform's: a name only the system + carries would escape every reader of the platform list.""" + + def test_system_cores_are_platform_cores(self): + import yaml + + for name in ("amberelec", "emuelec", "knulli"): + with self.subTest(platform=name): + config = yaml.safe_load((REPO_ROOT / "platforms" / f"{name}.yml").read_text()) + platform = {str(c) for c in config["cores"]} + listed = {str(c) for s in config["systems"].values() for c in s.get("cores", [])} + self.assertTrue(listed, "no system carries its cores") + self.assertEqual(listed - platform, set()) + + if __name__ == "__main__": unittest.main()