mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-04-15 21:32:32 -05:00
Compare commits
24 Commits
8ac64d6143
...
74f48b8881
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74f48b8881 | ||
|
|
0a1880f606 | ||
|
|
6402b77374 | ||
|
|
a3de47dd88 | ||
|
|
7e8491fdf7 | ||
|
|
bccea60f8d | ||
|
|
16b4dcc270 | ||
|
|
15e8c0eccb | ||
|
|
14eaff73f7 | ||
|
|
dfb7d9a25a | ||
|
|
03a9fa3276 | ||
|
|
2980100fba | ||
|
|
6b86c543af | ||
|
|
8e91552b16 | ||
|
|
c6ab8e9c3a | ||
|
|
ac66f0b73b | ||
|
|
de58f3f28e | ||
|
|
89054084b7 | ||
|
|
ea9cd93e83 | ||
|
|
5ac14079d6 | ||
|
|
1e939f1470 | ||
|
|
1c34790737 | ||
|
|
e17d771710 | ||
|
|
0549b8945e |
12
README.md
12
README.md
@@ -2,7 +2,7 @@
|
||||
|
||||
Complete BIOS and firmware packs for Batocera, EmuDeck, Lakka, Recalbox, RetroArch, RetroBat, RetroDECK, RetroPie, and RomM.
|
||||
|
||||
**6,748** verified files across **322** systems, ready to extract into your emulator's BIOS directory.
|
||||
**6,748** verified files across **352** systems, ready to extract into your emulator's BIOS directory.
|
||||
|
||||
## Download BIOS packs
|
||||
|
||||
@@ -26,14 +26,14 @@ BIOS, firmware, and system files for consoles from Atari to PlayStation 3.
|
||||
Each file is checked against the emulator's source code to match what the code actually loads at runtime.
|
||||
|
||||
- **9 platforms** supported with platform-specific verification
|
||||
- **319 emulators** profiled from source (RetroArch cores + standalone)
|
||||
- **322 systems** covered (NES, SNES, PlayStation, Saturn, Dreamcast, ...)
|
||||
- **328 emulators** profiled from source (RetroArch cores + standalone)
|
||||
- **352 systems** covered (NES, SNES, PlayStation, Saturn, Dreamcast, ...)
|
||||
- **6,748 files** verified with MD5, SHA1, CRC32 checksums
|
||||
- **5251 MB** total collection size
|
||||
|
||||
## Supported systems
|
||||
|
||||
NES, SNES, Nintendo 64, GameCube, Wii, Game Boy, Game Boy Advance, Nintendo DS, Nintendo 3DS, Switch, PlayStation, PlayStation 2, PlayStation 3, PSP, PS Vita, Mega Drive, Saturn, Dreamcast, Game Gear, Master System, Neo Geo, Atari 2600, Atari 7800, Atari Lynx, Atari ST, MSX, PC Engine, TurboGrafx-16, ColecoVision, Intellivision, Commodore 64, Amiga, ZX Spectrum, Arcade (MAME), and 288+ more.
|
||||
NES, SNES, Nintendo 64, GameCube, Wii, Game Boy, Game Boy Advance, Nintendo DS, Nintendo 3DS, Switch, PlayStation, PlayStation 2, PlayStation 3, PSP, PS Vita, Mega Drive, Saturn, Dreamcast, Game Gear, Master System, Neo Geo, Atari 2600, Atari 7800, Atari Lynx, Atari ST, MSX, PC Engine, TurboGrafx-16, ColecoVision, Intellivision, Commodore 64, Amiga, ZX Spectrum, Arcade (MAME), and 318+ more.
|
||||
|
||||
Full list with per-file details: **[https://abdess.github.io/retrobios/](https://abdess.github.io/retrobios/)**
|
||||
|
||||
@@ -84,7 +84,7 @@ The [documentation site](https://abdess.github.io/retrobios/) provides:
|
||||
- **Per-emulator profiles** with source code references for every file
|
||||
- **Per-system pages** showing which emulators and platforms cover each console
|
||||
- **Gap analysis** identifying missing files and undeclared core requirements
|
||||
- **Cross-reference** mapping files across 9 platforms and 319 emulators
|
||||
- **Cross-reference** mapping files across 9 platforms and 328 emulators
|
||||
|
||||
## How it works
|
||||
|
||||
@@ -110,4 +110,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
||||
|
||||
This repository provides BIOS files for personal backup and archival purposes.
|
||||
|
||||
*Auto-generated on 2026-03-26T06:14:11Z*
|
||||
*Auto-generated on 2026-03-26T12:17:35Z*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"generated_at": "2026-03-26T06:09:06Z",
|
||||
"generated_at": "2026-03-26T12:17:17Z",
|
||||
"total_files": 6748,
|
||||
"total_size": 5505760050,
|
||||
"files": {
|
||||
|
||||
441
emulators/ares.yml
Normal file
441
emulators/ares.yml
Normal file
@@ -0,0 +1,441 @@
|
||||
emulator: ares
|
||||
type: standalone
|
||||
source: "https://github.com/ares-emulator/ares"
|
||||
upstream: "https://github.com/ares-emulator/ares"
|
||||
profiled_date: "2026-03-26"
|
||||
core_version: "v147"
|
||||
display_name: "ares"
|
||||
cores: [ares]
|
||||
mode: standalone
|
||||
systems:
|
||||
- arcade
|
||||
- atari-2600
|
||||
- colecovision
|
||||
- myvision
|
||||
- famicom
|
||||
- famicom-disk-system
|
||||
- game-boy
|
||||
- game-boy-color
|
||||
- game-boy-advance
|
||||
- master-system
|
||||
- game-gear
|
||||
- sg-1000
|
||||
- sc-3000
|
||||
- mega-drive
|
||||
- sega-32x
|
||||
- mega-cd
|
||||
- mega-cd-32x
|
||||
- mega-ld
|
||||
- msx
|
||||
- msx2
|
||||
- neo-geo
|
||||
- neo-geo-pocket
|
||||
- neo-geo-pocket-color
|
||||
- nintendo-64
|
||||
- nintendo-64dd
|
||||
- pc-engine
|
||||
- pc-engine-cd
|
||||
- pc-engine-ld
|
||||
- supergrafx
|
||||
- supergrafx-cd
|
||||
- sony-playstation
|
||||
- saturn
|
||||
- super-famicom
|
||||
- wonderswan
|
||||
- wonderswan-color
|
||||
- pocket-challenge-v2
|
||||
- zx-spectrum
|
||||
- zx-spectrum-128
|
||||
|
||||
notes: |
|
||||
Multi-system emulator by Near (byuu), successor to higan/bsnes. Focuses on
|
||||
accuracy and preservation. 38 systems emulated.
|
||||
|
||||
Firmware identification uses SHA256 hashes. Filenames are user-defined; ares
|
||||
scans a configured directory and matches by hash. EmuDeck installs ares as
|
||||
Flatpak (dev.ares.ares), configures biosPath in settings.bml.
|
||||
ref: desktop-ui/settings/firmware.cpp:116-136
|
||||
|
||||
Firmware path resolution: settings.paths.firmware (user override) or
|
||||
{userData}/ares/Firmware/ (default). Per-system firmware assigned in
|
||||
Settings > Firmware dialog or via scan-by-hash.
|
||||
ref: desktop-ui/settings/settings.hpp:90-102, desktop-ui/desktop-ui.cpp:68-75
|
||||
|
||||
Systems with fully embedded firmware (no user files needed):
|
||||
Game Boy (boot.rom), Game Boy Color (boot.rom), Mega Drive (TMSS),
|
||||
Sega 32X (TMSS + vector + SH2 boot M/S), Nintendo 64 (PIF NTSC/PAL/SM5 +
|
||||
CIC 6101/6102/6105/7101), Super Famicom (IPL + all coprocessor firmware:
|
||||
DSP1-4, CX4, ST010, ST011, ST018, SGB1/2 boot ROMs), WonderSwan (boot.rom),
|
||||
WonderSwan Color (boot.rom), Pocket Challenge V2 (boot.rom),
|
||||
ZX Spectrum (BIOS), ZX Spectrum 128 (BIOS + sub ROM).
|
||||
ref: mia/resource/resource.bml, mia/Firmware/
|
||||
|
||||
SFC coprocessor firmware auto-injected if missing from ROM image.
|
||||
ref: mia/medium/super-famicom.cpp:42-62
|
||||
|
||||
Neo Geo BIOS: accepts ZIP archive (neogeo.zip) or direct binary. AES looks
|
||||
for "neo-epo.bin" inside ZIP, MVS looks for "sp-45.sp1". Both apply byte-swap
|
||||
after loading.
|
||||
ref: mia/system/neo-geo-aes.cpp:12-30, mia/system/neo-geo-mvs.cpp:12-30
|
||||
|
||||
SFC subsystem ROMs (SuFami Turbo, Satellaview, Super Game Boy): loaded as
|
||||
game cartridges via CLI args, not through the firmware system. EmuDeck creates
|
||||
launcher shortcuts that pass these alongside game ROMs.
|
||||
ref: emuDeckares.sh (EmuDeck repo)
|
||||
|
||||
Systems with no firmware: Arcade, Atari 2600, Famicom, MyVision, PC Engine,
|
||||
SuperGrafx, SG-1000, SC-3000.
|
||||
|
||||
files:
|
||||
# --- ColecoVision ---
|
||||
- name: colecovision.rom
|
||||
system: colecovision
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "990bf1956f10207d8781b619eb74f89b00d921c8d45c95c334c16c8cceca09ad"
|
||||
validation: [sha256]
|
||||
description: "ColecoVision BIOS"
|
||||
source_ref: "desktop-ui/emulator/colecovision.cpp:12"
|
||||
|
||||
# --- Famicom Disk System ---
|
||||
- name: disksys.rom
|
||||
system: famicom-disk-system
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "fdc1a76e654feea993fcb38366e05ee5f4eb641f86fe6bebaeefd412e112dd72"
|
||||
validation: [sha256]
|
||||
description: "Famicom Disk System BIOS"
|
||||
source_ref: "desktop-ui/emulator/famicom-disk-system.cpp:19"
|
||||
|
||||
# --- Game Boy Advance ---
|
||||
- name: gba_bios.bin
|
||||
system: game-boy-advance
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570"
|
||||
validation: [sha256]
|
||||
description: "GBA BIOS"
|
||||
source_ref: "desktop-ui/emulator/game-boy-advance.cpp:14"
|
||||
|
||||
# --- Game Gear ---
|
||||
- name: bios.gg
|
||||
system: game-gear
|
||||
required: false
|
||||
mode: standalone
|
||||
sha256: "8c8a21335038285cfa03dc076100c1f0bfadf3e4ff70796f11f3dfaaab60eee2"
|
||||
validation: [sha256]
|
||||
description: "Game Gear BIOS"
|
||||
source_ref: "desktop-ui/emulator/game-gear.cpp:12, mia/system/game-gear.cpp:8 (//optional)"
|
||||
|
||||
# --- Master System ---
|
||||
- name: bios_U.sms
|
||||
system: master-system
|
||||
required: false
|
||||
mode: standalone
|
||||
sha256: "477617917a12a30f9f43844909dc2de6e6a617430f5c9a36306c86414a670d50"
|
||||
validation: [sha256]
|
||||
description: "Master System BIOS (US)"
|
||||
source_ref: "desktop-ui/emulator/master-system.cpp:14, mia/system/master-system.cpp:8 (//optional)"
|
||||
|
||||
- name: bios_J.sms
|
||||
system: master-system
|
||||
required: false
|
||||
mode: standalone
|
||||
sha256: "67846e26764bd862f19179294347f7353a4166b62ac4198a5ec32933b7da486e"
|
||||
validation: [sha256]
|
||||
description: "Master System BIOS (Japan)"
|
||||
source_ref: "desktop-ui/emulator/master-system.cpp:15"
|
||||
|
||||
- name: bios_E.sms
|
||||
system: master-system
|
||||
required: false
|
||||
mode: standalone
|
||||
sha256: "477617917a12a30f9f43844909dc2de6e6a617430f5c9a36306c86414a670d50"
|
||||
validation: [sha256]
|
||||
description: "Master System BIOS (Europe), same binary as US"
|
||||
source_ref: "desktop-ui/emulator/master-system.cpp:16"
|
||||
|
||||
# --- Mega CD ---
|
||||
# Also used by Mega CD 32X (desktop-ui/emulator/mega-cd-32x.cpp)
|
||||
- name: bios_CD_U.bin
|
||||
system: mega-cd
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "fb477cdbf94c84424c2feca4fe40656d85393fe7b7b401911b45ad2eb991258c"
|
||||
validation: [sha256]
|
||||
description: "Mega CD / Sega CD BIOS (US)"
|
||||
source_ref: "desktop-ui/emulator/mega-cd.cpp:17"
|
||||
|
||||
- name: bios_CD_J.bin
|
||||
system: mega-cd
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "7133fc2dd2fe5b7d0acd53a5f10f3d00b5d31270239ad20d74ef32393e24af88"
|
||||
validation: [sha256]
|
||||
description: "Mega CD BIOS (Japan)"
|
||||
source_ref: "desktop-ui/emulator/mega-cd.cpp:18"
|
||||
|
||||
- name: bios_CD_E.bin
|
||||
system: mega-cd
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "fe608a2a07676a23ab5fd5eee2f53c9e2526d69a28aa16ccd85c0ec42e6933cb"
|
||||
validation: [sha256]
|
||||
description: "Mega CD BIOS (Europe)"
|
||||
source_ref: "desktop-ui/emulator/mega-cd.cpp:19"
|
||||
|
||||
# --- Mega LD (LaserActive SEGA PAC) ---
|
||||
- name: mega_ld_bios_U.bin
|
||||
system: mega-ld
|
||||
required: true
|
||||
mode: standalone
|
||||
description: "LaserActive SEGA PAC BIOS (US)"
|
||||
source_ref: "desktop-ui/emulator/mega-ld.cpp:18"
|
||||
|
||||
- name: mega_ld_bios_J.bin
|
||||
system: mega-ld
|
||||
required: true
|
||||
mode: standalone
|
||||
description: "LaserActive SEGA PAC BIOS (Japan)"
|
||||
source_ref: "desktop-ui/emulator/mega-ld.cpp:19"
|
||||
|
||||
# --- MSX ---
|
||||
- name: MSX.ROM
|
||||
system: msx
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "413a2b601a94b3792e054be2439cc77a1819cceadbfa9542f88d51c7480f2ef0"
|
||||
validation: [sha256]
|
||||
description: "MSX BIOS ROM (Japan)"
|
||||
source_ref: "desktop-ui/emulator/msx.cpp:15"
|
||||
|
||||
# --- MSX2 ---
|
||||
- name: MSX2.ROM
|
||||
system: msx2
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "0c672d86ead61a97f49a583b88b7c1905da120645cd44f0c9f2baf4f4631e0b1"
|
||||
validation: [sha256]
|
||||
description: "MSX2 main BIOS ROM (Japan)"
|
||||
source_ref: "desktop-ui/emulator/msx2.cpp:15"
|
||||
|
||||
- name: MSX2EXT.ROM
|
||||
system: msx2
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "6c6f421a10c428d960b7ecc990f99af1c638147f747bddca7b0bf0e2ab738300"
|
||||
validation: [sha256]
|
||||
description: "MSX2 sub ROM (Japan)"
|
||||
source_ref: "desktop-ui/emulator/msx2.cpp:16"
|
||||
|
||||
# --- Neo Geo AES ---
|
||||
- name: neo-epo.bin
|
||||
system: neo-geo
|
||||
required: true
|
||||
mode: standalone
|
||||
description: "Neo Geo AES BIOS. Accepts neogeo.zip (extracts neo-epo.bin) or direct file. Byte-swapped on load."
|
||||
source_ref: "desktop-ui/emulator/neo-geo-aes.cpp:14, mia/system/neo-geo-aes.cpp:16"
|
||||
|
||||
# --- Neo Geo MVS ---
|
||||
- name: sp-45.sp1
|
||||
system: neo-geo
|
||||
required: true
|
||||
mode: standalone
|
||||
description: "Neo Geo MVS BIOS. Accepts neogeo.zip (extracts sp-45.sp1) or direct file. Byte-swapped on load."
|
||||
source_ref: "desktop-ui/emulator/neo-geo-mvs.cpp:15, mia/system/neo-geo-mvs.cpp:16"
|
||||
|
||||
# --- Neo Geo Pocket ---
|
||||
- name: ngp_bios.rom
|
||||
system: neo-geo-pocket
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "0293555b21c4fac516d25199df7809b26beeae150e1d4504a050db32264a6ad7"
|
||||
validation: [sha256]
|
||||
description: "Neo Geo Pocket BIOS"
|
||||
source_ref: "desktop-ui/emulator/neo-geo-pocket.cpp:12"
|
||||
|
||||
# --- Neo Geo Pocket Color ---
|
||||
- name: ngpc_bios.rom
|
||||
system: neo-geo-pocket-color
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "8fb845a2f71514cec20728e2f0fecfade69444f8d50898b92c2259f1ba63e10d"
|
||||
validation: [sha256]
|
||||
description: "Neo Geo Pocket Color BIOS"
|
||||
source_ref: "desktop-ui/emulator/neo-geo-pocket-color.cpp:12"
|
||||
|
||||
# --- Nintendo 64DD ---
|
||||
- name: 64dd_ipl_J.bin
|
||||
system: nintendo-64dd
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "806400ec0df94b0755de6c5b8249d6b6a9866124c5ddbdac198bde22499bfb8b"
|
||||
validation: [sha256]
|
||||
description: "Nintendo 64DD IPL ROM (Japan retail)"
|
||||
source_ref: "desktop-ui/emulator/nintendo-64dd.cpp:17"
|
||||
|
||||
- name: 64dd_ipl_U.bin
|
||||
system: nintendo-64dd
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "e9fec87a45fba02399e88064b9e2f8cf0f2106e351c58279a87f05da5bc984ad"
|
||||
validation: [sha256]
|
||||
description: "Nintendo 64DD IPL ROM (US dev kit)"
|
||||
source_ref: "desktop-ui/emulator/nintendo-64dd.cpp:18"
|
||||
|
||||
- name: 64dd_ipl_DEV.bin
|
||||
system: nintendo-64dd
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "9c2962a8b994a29e4cd04b3a6e4ed730a751414655ab6a9799ebf5fc08b79d44"
|
||||
validation: [sha256]
|
||||
description: "Nintendo 64DD IPL ROM (development)"
|
||||
source_ref: "desktop-ui/emulator/nintendo-64dd.cpp:19"
|
||||
|
||||
# --- PC Engine CD ---
|
||||
- name: syscard1.pce
|
||||
system: pc-engine-cd
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "afe9f27f91ac918348555b86298b4f984643eafa2773196f2c5441ea84f0c3bb"
|
||||
validation: [sha256]
|
||||
description: "PC Engine CD-ROM2 System Card v1.0 (Japan). Also used by PC Engine LD."
|
||||
source_ref: "desktop-ui/emulator/pc-engine-cd.cpp:15"
|
||||
|
||||
- name: syscard3.pce
|
||||
system: pc-engine-cd
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "e11527b3b96ce112a037138988ca72fd117a6b0779c2480d9e03eaebece3d9ce"
|
||||
validation: [sha256]
|
||||
description: "Super CD-ROM2 System Card v3.0 (Japan). Also used by SuperGrafx CD as Arcade Card."
|
||||
source_ref: "desktop-ui/emulator/pc-engine-cd.cpp:16, desktop-ui/emulator/supergrafx-cd.cpp:15"
|
||||
|
||||
- name: syscard3u.pce
|
||||
system: pc-engine-cd
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "cadac2725711b3c442bcf237b02f5a5210c96f17625c35fa58f009e0ed39e4db"
|
||||
validation: [sha256]
|
||||
description: "TurboGrafx-CD Super System Card v3.0 (US)"
|
||||
source_ref: "desktop-ui/emulator/pc-engine-cd.cpp:17"
|
||||
|
||||
- name: games_express.pce
|
||||
system: pc-engine-cd
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "4b86bb96a48a4ca8375fc0109631d0b1d64f255a03b01de70594d40788ba6c3d"
|
||||
validation: [sha256]
|
||||
description: "Games Express CD Card (Japan). Also used by PC Engine LD."
|
||||
source_ref: "desktop-ui/emulator/pc-engine-cd.cpp:18"
|
||||
|
||||
# --- PC Engine LD (LaserActive NEC PAC) ---
|
||||
- name: pac-n10.pce
|
||||
system: pc-engine-ld
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "0e87a3385a27b3a4cac51934819b7eefa5b3d690768d2495633838488cd0e2e4"
|
||||
validation: [sha256]
|
||||
description: "NEC PAC-N10 LaserActive module (US)"
|
||||
source_ref: "desktop-ui/emulator/pc-engine-ld.cpp:20"
|
||||
|
||||
- name: pac-n1.pce
|
||||
system: pc-engine-ld
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "459325690a458baebd77495c91e37c4dddfdd542ba13a821ce954e5bb245627f"
|
||||
validation: [sha256]
|
||||
description: "NEC PAC-N1 LaserActive module (Japan)"
|
||||
source_ref: "desktop-ui/emulator/pc-engine-ld.cpp:21"
|
||||
|
||||
- name: pce-lp1.pce
|
||||
system: pc-engine-ld
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "3f43b3b577117d84002e99cb0baeb97b0d65b1d70b4adadc68817185c6a687f0"
|
||||
validation: [sha256]
|
||||
description: "NEC PCE-LP1 LaserActive module (Japan)"
|
||||
source_ref: "desktop-ui/emulator/pc-engine-ld.cpp:22"
|
||||
|
||||
# --- PlayStation ---
|
||||
- name: scph5501.bin
|
||||
system: sony-playstation
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "11052b6499e466bbf0a709b1f9cb6834a9418e66680387912451e971cf8a1fef"
|
||||
validation: [sha256]
|
||||
description: "PlayStation BIOS (US)"
|
||||
source_ref: "desktop-ui/emulator/playstation.cpp:18"
|
||||
|
||||
- name: scph5500.bin
|
||||
system: sony-playstation
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "9c0421858e217805f4abe18698afea8d5aa36ff0727eb8484944e00eb5e7eadb"
|
||||
validation: [sha256]
|
||||
description: "PlayStation BIOS (Japan)"
|
||||
source_ref: "desktop-ui/emulator/playstation.cpp:19"
|
||||
|
||||
- name: scph5502.bin
|
||||
system: sony-playstation
|
||||
required: true
|
||||
mode: standalone
|
||||
sha256: "1faaa18fa820a0225e488d9f086296b8e6c46df739666093987ff7d8fd352c09"
|
||||
validation: [sha256]
|
||||
description: "PlayStation BIOS (Europe)"
|
||||
source_ref: "desktop-ui/emulator/playstation.cpp:20"
|
||||
|
||||
# --- Saturn ---
|
||||
- name: saturn_bios_U.bin
|
||||
system: saturn
|
||||
required: true
|
||||
mode: standalone
|
||||
description: "Sega Saturn BIOS (US)"
|
||||
source_ref: "desktop-ui/emulator/saturn.cpp:14"
|
||||
|
||||
- name: saturn_bios_J.bin
|
||||
system: saturn
|
||||
required: true
|
||||
mode: standalone
|
||||
description: "Sega Saturn BIOS (Japan)"
|
||||
source_ref: "desktop-ui/emulator/saturn.cpp:15"
|
||||
|
||||
- name: saturn_bios_E.bin
|
||||
system: saturn
|
||||
required: true
|
||||
mode: standalone
|
||||
description: "Sega Saturn BIOS (Europe)"
|
||||
source_ref: "desktop-ui/emulator/saturn.cpp:16"
|
||||
|
||||
# --- SFC subsystem ROMs (loaded as game cartridges, not firmware) ---
|
||||
- name: SGB1.sfc
|
||||
system: super-famicom
|
||||
required: false
|
||||
mode: standalone
|
||||
category: game_data
|
||||
description: "Super Game Boy cartridge ROM. Loaded via CLI as SFC cartridge alongside GB game."
|
||||
source_ref: "EmuDeck emuDeckares.sh (launcher)"
|
||||
|
||||
- name: SGB2.sfc
|
||||
system: super-famicom
|
||||
required: false
|
||||
mode: standalone
|
||||
category: game_data
|
||||
description: "Super Game Boy 2 cartridge ROM. Loaded via CLI as SFC cartridge alongside GB game."
|
||||
source_ref: "EmuDeck emuDeckares.sh (launcher)"
|
||||
|
||||
- name: STBIOS.bin
|
||||
system: super-famicom
|
||||
required: false
|
||||
mode: standalone
|
||||
category: game_data
|
||||
description: "SuFami Turbo adapter BIOS. Loaded via CLI as SFC cartridge alongside SuFami games."
|
||||
source_ref: "EmuDeck emuDeckares.sh (launcher)"
|
||||
|
||||
- name: BS-X.bin
|
||||
system: super-famicom
|
||||
required: false
|
||||
mode: standalone
|
||||
category: game_data
|
||||
description: "Satellaview BS-X Town cartridge ROM. Loaded via CLI as SFC cartridge for BS games."
|
||||
source_ref: "EmuDeck emuDeckares.sh (launcher)"
|
||||
23
emulators/bigpemu.yml
Normal file
23
emulators/bigpemu.yml
Normal file
@@ -0,0 +1,23 @@
|
||||
emulator: BigPEmu
|
||||
type: standalone
|
||||
source: "https://www.richwhitehouse.com/jaguar/"
|
||||
upstream: "https://www.richwhitehouse.com/jaguar/"
|
||||
profiled_date: "2026-03-26"
|
||||
core_version: "1.12"
|
||||
display_name: "BigPEmu (Atari Jaguar)"
|
||||
cores:
|
||||
- bigpemu
|
||||
systems:
|
||||
- atari-jaguar
|
||||
- atari-jaguarcd
|
||||
mode: standalone
|
||||
|
||||
notes: |
|
||||
Closed-source Atari Jaguar and Jaguar CD emulator by Rich Whitehouse.
|
||||
Built-in boot ROM emulation for both cartridge and CD. No external
|
||||
BIOS or firmware files required. The emulator supports optional
|
||||
"custom boot ROM images" via the UI (loading mode for specific
|
||||
homebrew demos), but no fixed file path or file name is expected.
|
||||
EmuDeck confirms: "No BIOS are required to play BigPEmu."
|
||||
|
||||
files: []
|
||||
55
emulators/eden.yml
Normal file
55
emulators/eden.yml
Normal file
@@ -0,0 +1,55 @@
|
||||
emulator: Eden
|
||||
type: standalone
|
||||
core_classification: community_fork
|
||||
source: "https://git.eden-emu.dev/eden-emu/eden"
|
||||
upstream: "https://git.eden-emu.dev/eden-emu/eden"
|
||||
profiled_date: "2026-03-26"
|
||||
core_version: "0.1.1"
|
||||
display_name: "Eden (Nintendo Switch)"
|
||||
systems: [nintendo-switch]
|
||||
analysis_date: "2026-03-26"
|
||||
analysis_commit: "9347202 (depth=1)"
|
||||
mode: standalone
|
||||
|
||||
# Eden is a community fork of yuzu, started by Camille LaVey.
|
||||
# Key files loaded from <data_dir>/keys/ (fs_paths.h:20, path_util.cpp:158).
|
||||
# On Linux: $XDG_DATA_HOME/eden/keys/
|
||||
# On Windows: %APPDATA%/eden/keys/
|
||||
# Firmware NCAs are installed via UI (Tools > Install Firmware), not placed as files.
|
||||
# HLE fallbacks exist for fonts, timezone, system version, mii model, ng word lists.
|
||||
|
||||
files:
|
||||
- name: "prod.keys"
|
||||
required: true
|
||||
path: "switch/"
|
||||
mode: standalone
|
||||
note: "Production keys for NCA decryption (master, key area, header, titlekek)"
|
||||
source_ref: "src/core/crypto/key_manager.cpp:652-653"
|
||||
|
||||
- name: "title.keys"
|
||||
required: false
|
||||
path: "switch/"
|
||||
mode: standalone
|
||||
note: "Per-title encryption keys (rights_id to titlekey mappings)"
|
||||
source_ref: "src/core/crypto/key_manager.cpp:655-656"
|
||||
|
||||
- name: "console.keys"
|
||||
required: false
|
||||
path: "switch/"
|
||||
mode: standalone
|
||||
note: "Console-specific keys (BIS, SD seed)"
|
||||
source_ref: "src/core/crypto/key_manager.cpp:657-658"
|
||||
|
||||
- name: "key_retail.bin"
|
||||
required: false
|
||||
path: "switch/"
|
||||
size: 160
|
||||
mode: standalone
|
||||
note: "Amiibo decryption keys (two InternalKey structs, 0x50 bytes each)"
|
||||
source_ref: "src/core/hle/service/nfc/common/amiibo_crypto.cpp:281-302"
|
||||
|
||||
notes: |
|
||||
Eden is a standalone Nintendo Switch emulator, community fork of yuzu by Camille LaVey.
|
||||
dev.keys can be used instead of prod.keys when use_dev_keys is enabled (for Switch dev units).
|
||||
Firmware (system NCAs) must be installed through Eden's UI from a firmware ZIP or NCA folder.
|
||||
Required for commercial games. Homebrew (.nro, .nso) can run without keys or firmware.
|
||||
37
emulators/model2.yml
Normal file
37
emulators/model2.yml
Normal file
@@ -0,0 +1,37 @@
|
||||
emulator: Model 2 Emulator
|
||||
type: standalone
|
||||
source: closed-source
|
||||
upstream: closed-source
|
||||
profiled_date: "2026-03-26"
|
||||
core_version: "1.1a"
|
||||
display_name: "Model 2 Emulator (Sega Model 2)"
|
||||
cores:
|
||||
- model2
|
||||
systems:
|
||||
- sega-model2
|
||||
mode: standalone
|
||||
|
||||
notes: |
|
||||
Closed-source Sega Model 2 arcade emulator by ElSemi, formerly Nebula Model 2.
|
||||
Emulates Model 2, 2A, 2B, and 2C hardware. Windows-only, runs via Proton on
|
||||
Linux (EmuDeck setup). Last official release 1.1a (2014-01-02).
|
||||
|
||||
Uses MAME-format merged ROM sets. Each game ZIP contains all required ROMs
|
||||
(program, geometry, texture, sound, protection chips). No shared parent BIOS
|
||||
set exists for Sega Model 2 hardware; each game board has its own ROM chips.
|
||||
|
||||
EmuDeck packages version 1.1c from SeongGino/edc-repo0004. Installs to
|
||||
romsPath/model2/ with emulator_multicpu.exe as main binary. Ships with
|
||||
Lua scripts for game-specific patches (widescreen, fixes). No checkBIOS
|
||||
entry in EmuDeck; EMULATOR.INI references only a roms/ subdirectory.
|
||||
|
||||
Source code never published. Original distribution via nebula.emulatronia.com
|
||||
(archived). PhilrocWP/Model2 GitHub repo no longer exists.
|
||||
|
||||
files: []
|
||||
|
||||
exclusion_note: >
|
||||
Uses MAME-format merged ROM sets where all required ROMs are contained within
|
||||
each game's ZIP file. No standalone BIOS or firmware files are loaded by the
|
||||
emulator. The Sega Model 2 hardware has no shared system BIOS; each game board
|
||||
has its own set of program, geometry, texture, and sound ROMs.
|
||||
329
emulators/primehack.yml
Normal file
329
emulators/primehack.yml
Normal file
@@ -0,0 +1,329 @@
|
||||
emulator: PrimeHack
|
||||
type: standalone
|
||||
core_classification: enhanced_fork
|
||||
source: "https://github.com/shiiion/dolphin"
|
||||
upstream: "https://github.com/dolphin-emu/dolphin"
|
||||
profiled_date: "2026-03-26"
|
||||
core_version: "1.0.8"
|
||||
display_name: "PrimeHack (Dolphin fork for Metroid Prime)"
|
||||
systems: [nintendo-gamecube, nintendo-wii]
|
||||
analysis_date: "2026-03-26"
|
||||
analysis_commit: "b10f147 (depth=1)"
|
||||
mode: standalone
|
||||
|
||||
# PrimeHack is a Dolphin fork by shiiion adding mouselook controls for Metroid Prime Trilogy.
|
||||
# BIOS loading code is identical to upstream Dolphin.
|
||||
# File paths relative to Dolphin User directory:
|
||||
# Standalone: User/GC/ and User/Wii/
|
||||
# Sys/ is checked as fallback when not found in User/.
|
||||
|
||||
pack_structure:
|
||||
standalone: ""
|
||||
|
||||
data_directories:
|
||||
- ref: dolphin-sys
|
||||
destination: "Sys"
|
||||
source_ref: "Source/Core/Common/CommonPaths.h:128-141"
|
||||
|
||||
files:
|
||||
# -- GameCube IPL (Boot ROM) --
|
||||
# Region-specific, placed in GC/<region>/IPL.bin
|
||||
# Checked in User/GC/<region>/ then Sys/GC/<region>/
|
||||
- name: "IPL.bin"
|
||||
path: "GC/USA/IPL.bin"
|
||||
size: 2097152
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
note: "GameCube NTSC-U boot ROM. HLE available but real IPL needed for GC menu boot and accurate fonts"
|
||||
source_ref: "Source/Core/Common/CommonPaths.h:138, Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp:109-110"
|
||||
|
||||
- name: "IPL.bin"
|
||||
path: "GC/EUR/IPL.bin"
|
||||
size: 2097152
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
note: "GameCube PAL boot ROM"
|
||||
source_ref: "Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp:184"
|
||||
|
||||
- name: "IPL.bin"
|
||||
path: "GC/JAP/IPL.bin"
|
||||
size: 2097152
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
note: "GameCube NTSC-J boot ROM. JAP is the legacy directory name"
|
||||
source_ref: "Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp:186"
|
||||
|
||||
# -- DSP ROMs --
|
||||
# Used by DSP LLE for accurate audio.
|
||||
# PrimeHack ships free replacement ROMs, but real dumps improve accuracy.
|
||||
# Searched in: User/GC/ then Sys/GC/
|
||||
- name: "dsp_rom.bin"
|
||||
path: "GC/dsp_rom.bin"
|
||||
size: 8192
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
validation: [size]
|
||||
known_hash_adler32: "0x66f334fe"
|
||||
note: "DSP instruction ROM for LLE audio. Free replacement (v0.4) included"
|
||||
source_ref: "Source/Core/Common/CommonPaths.h:135, Source/Core/Core/HW/DSPLLE/DSPLLE.cpp:87-117"
|
||||
|
||||
- name: "dsp_coef.bin"
|
||||
path: "GC/dsp_coef.bin"
|
||||
size: 4096
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
validation: [size]
|
||||
known_hash_adler32: "0xf3b93527"
|
||||
note: "DSP coefficient ROM for LLE audio. Free replacement included"
|
||||
source_ref: "Source/Core/Common/CommonPaths.h:136, Source/Core/Core/DSP/DSPCore.cpp:32-38"
|
||||
|
||||
# -- GameCube Fonts --
|
||||
# Bundled free alternatives exist but have padding differences causing misplaced text.
|
||||
# If IPL dump is present, fonts are extracted from it instead (preferred).
|
||||
# Searched in: Sys/GC/
|
||||
- name: "font_western.bin"
|
||||
path: "GC/font_western.bin"
|
||||
size: 9589
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
note: "Windows-1252 font for GC/Wii text rendering. Free alternative bundled, real one from IPL dump preferred"
|
||||
source_ref: "Source/Core/Common/CommonPaths.h:132, Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp:131,203-218"
|
||||
|
||||
- name: "font_japanese.bin"
|
||||
path: "GC/font_japanese.bin"
|
||||
size: 303693
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
note: "Shift-JIS font for Japanese text. Free alternative bundled, real one from IPL dump preferred"
|
||||
source_ref: "Source/Core/Common/CommonPaths.h:133, Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp:130"
|
||||
|
||||
# -- GBA BIOS (for GC-GBA link) --
|
||||
# Used by integrated mGBA core for GameCube-GBA connectivity.
|
||||
# Loaded from User/GBA/gba_bios.bin.
|
||||
- name: "gba_bios.bin"
|
||||
path: "GBA/gba_bios.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
note: "GBA BIOS for GC-GBA link feature (uses integrated mGBA). Path configurable in settings"
|
||||
source_ref: "Source/Core/Common/CommonPaths.h:144, Source/Core/Core/HW/GBACore.cpp:344-361"
|
||||
|
||||
# -- Wii System Files --
|
||||
- name: "SYSCONF"
|
||||
path: "Wii/shared2/sys/SYSCONF"
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
note: "Wii system configuration. Auto-generated by PrimeHack, can be imported from NAND backup"
|
||||
source_ref: "Source/Core/Common/CommonPaths.h:117, Source/Core/Core/WiiRoot.cpp:266"
|
||||
|
||||
- name: "setting.txt"
|
||||
path: "Wii/title/00000001/00000002/data/setting.txt"
|
||||
size: 256
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
note: "Wii region/language settings. Auto-generated during Wii boot emulation"
|
||||
source_ref: "Source/Core/Common/CommonPaths.h:152, Source/Core/Core/Boot/Boot_BS2Emu.cpp:377-454"
|
||||
|
||||
# -- Wii NAND Backup (BootMii) --
|
||||
- name: "nand.bin"
|
||||
path: null
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
note: "BootMii NAND backup. Can be imported to populate Wii NAND with channels, saves, system menu"
|
||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:26-89"
|
||||
|
||||
- name: "keys.bin"
|
||||
path: null
|
||||
size: 1024
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
note: "OTP/SEEPROM dump (Wii encryption keys). Needed if not appended to nand.bin for NAND import"
|
||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:19,76-88"
|
||||
|
||||
# -- Wii SD Card Image --
|
||||
- name: "WiiSD.raw"
|
||||
path: "Load/WiiSD.raw"
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
note: "Virtual SD card image for Wii homebrew. Auto-created, supports SD/SDHC up to 4GB"
|
||||
source_ref: "Source/Core/Common/CommonPaths.h:149"
|
||||
|
||||
# -- Gecko Code Handler --
|
||||
- name: "codehandler.bin"
|
||||
path: null
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
note: "Gecko/Ocarina cheat code handler. Shipped with PrimeHack in Sys/"
|
||||
source_ref: "Source/Core/Common/CommonPaths.h:154, Source/Core/Core/GeckoCode.cpp:121"
|
||||
|
||||
# -- Wii System Menu (WAD) --
|
||||
- name: "Wii System Menu"
|
||||
path: null
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
note: "Wii System Menu WAD. Installed to NAND via Tools > Install WAD, needed for Wii Menu boot"
|
||||
source_ref: "Source/Core/DolphinQt/MenuBar.cpp:323,1219-1227"
|
||||
|
||||
# -- NAND Certificates (auto-extracted) --
|
||||
- name: "clientca.pem"
|
||||
path: "Wii/clientca.pem"
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
note: "SSL client certificate. Auto-extracted from IOS13 content during NAND import"
|
||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:201-285"
|
||||
|
||||
- name: "clientcakey.pem"
|
||||
path: "Wii/clientcakey.pem"
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
note: "SSL client private key. Auto-extracted from IOS13 content during NAND import"
|
||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:237"
|
||||
|
||||
- name: "rootca.pem"
|
||||
path: "Wii/rootca.pem"
|
||||
required: false
|
||||
mode: standalone
|
||||
hle_fallback: true
|
||||
note: "SSL root CA certificate. Auto-extracted from IOS13 content during NAND import"
|
||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:238"
|
||||
|
||||
# -- Realtek Bluetooth firmware (Wiimote passthrough) --
|
||||
# Required for real Wiimote connectivity via USB Bluetooth adapters with Realtek chipsets.
|
||||
# PrimeHack can auto-download these from gitlab.com/kernel-firmware/linux-firmware.
|
||||
# Placed in Load/Firmware/rtl_bt/.
|
||||
# ref: Source/Core/Core/IOS/USB/Bluetooth/RealtekFirmwareLoader.cpp:373-416
|
||||
|
||||
- name: "rtl8723a_fw.bin"
|
||||
path: "Load/Firmware/rtl_bt/rtl8723a_fw.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
note: "Realtek 8723A BT firmware for Wiimote passthrough"
|
||||
source_ref: "RealtekFirmwareLoader.cpp:379"
|
||||
|
||||
- name: "rtl8723b_fw.bin"
|
||||
path: "Load/Firmware/rtl_bt/rtl8723b_fw.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
note: "Realtek 8723B BT firmware for Wiimote passthrough"
|
||||
source_ref: "RealtekFirmwareLoader.cpp:381"
|
||||
|
||||
- name: "rtl8723d_fw.bin"
|
||||
path: "Load/Firmware/rtl_bt/rtl8723d_fw.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
note: "Realtek 8723D BT firmware for Wiimote passthrough"
|
||||
source_ref: "RealtekFirmwareLoader.cpp:383"
|
||||
|
||||
- name: "rtl8761a_fw.bin"
|
||||
path: "Load/Firmware/rtl_bt/rtl8761a_fw.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
note: "Realtek 8761A BT firmware for Wiimote passthrough"
|
||||
source_ref: "RealtekFirmwareLoader.cpp:385"
|
||||
|
||||
- name: "rtl8761bu_fw.bin"
|
||||
path: "Load/Firmware/rtl_bt/rtl8761bu_fw.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
note: "Realtek 8761BU BT firmware for Wiimote passthrough"
|
||||
source_ref: "RealtekFirmwareLoader.cpp:387"
|
||||
|
||||
- name: "rtl8821a_fw.bin"
|
||||
path: "Load/Firmware/rtl_bt/rtl8821a_fw.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
note: "Realtek 8821A BT firmware for Wiimote passthrough"
|
||||
source_ref: "RealtekFirmwareLoader.cpp:389"
|
||||
|
||||
- name: "rtl8821c_fw.bin"
|
||||
path: "Load/Firmware/rtl_bt/rtl8821c_fw.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
note: "Realtek 8821C BT firmware for Wiimote passthrough"
|
||||
source_ref: "RealtekFirmwareLoader.cpp:391"
|
||||
|
||||
- name: "rtl8822b_fw.bin"
|
||||
path: "Load/Firmware/rtl_bt/rtl8822b_fw.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
note: "Realtek 8822B BT firmware for Wiimote passthrough"
|
||||
source_ref: "RealtekFirmwareLoader.cpp:395"
|
||||
|
||||
- name: "rtl8822cu_fw.bin"
|
||||
path: "Load/Firmware/rtl_bt/rtl8822cu_fw.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
note: "Realtek 8822CU BT firmware for Wiimote passthrough"
|
||||
source_ref: "RealtekFirmwareLoader.cpp:393"
|
||||
|
||||
- name: "rtl8851bu_fw.bin"
|
||||
path: "Load/Firmware/rtl_bt/rtl8851bu_fw.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
note: "Realtek 8851BU BT firmware for Wiimote passthrough"
|
||||
source_ref: "RealtekFirmwareLoader.cpp:397"
|
||||
|
||||
- name: "rtl8852au_fw.bin"
|
||||
path: "Load/Firmware/rtl_bt/rtl8852au_fw.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
note: "Realtek 8852AU BT firmware for Wiimote passthrough"
|
||||
source_ref: "RealtekFirmwareLoader.cpp:399"
|
||||
|
||||
- name: "rtl8852bu_fw.bin"
|
||||
path: "Load/Firmware/rtl_bt/rtl8852bu_fw.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
note: "Realtek 8852BU BT firmware for Wiimote passthrough"
|
||||
source_ref: "RealtekFirmwareLoader.cpp:401"
|
||||
|
||||
- name: "rtl8852cu_fw.bin"
|
||||
path: "Load/Firmware/rtl_bt/rtl8852cu_fw.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
note: "Realtek 8852CU BT firmware for Wiimote passthrough"
|
||||
source_ref: "RealtekFirmwareLoader.cpp:403"
|
||||
|
||||
- name: "rtl8852btu_fw.bin"
|
||||
path: "Load/Firmware/rtl_bt/rtl8852btu_fw.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
note: "Realtek 8852BT/8852BE-VT BT firmware for Wiimote passthrough"
|
||||
source_ref: "RealtekFirmwareLoader.cpp:405"
|
||||
|
||||
- name: "rtl8922au_fw.bin"
|
||||
path: "Load/Firmware/rtl_bt/rtl8922au_fw.bin"
|
||||
required: false
|
||||
mode: standalone
|
||||
note: "Realtek 8922AU BT firmware for Wiimote passthrough"
|
||||
source_ref: "RealtekFirmwareLoader.cpp:407"
|
||||
|
||||
notes:
|
||||
hle_available: true
|
||||
hle_note: >
|
||||
PrimeHack provides HLE for GameCube IPL (boot ROM), DSP, and Wii system functions
|
||||
(same as Dolphin). No BIOS files are strictly required for most games.
|
||||
ipl_regions: ["USA", "EUR", "JAP", "JPN", "DEV"]
|
||||
dsp_rom_note: >
|
||||
DSP ROMs are verified at load time via Adler32 hash.
|
||||
Official Nintendo hashes: irom=0x66f334fe, drom=0xf3b93527.
|
||||
bt_passthrough_note: >
|
||||
Bluetooth passthrough allows connecting real Wiimotes via USB Bluetooth adapters.
|
||||
Realtek chipsets require firmware files in Load/Firmware/rtl_bt/.
|
||||
PrimeHack can auto-download these from kernel-firmware/linux-firmware on GitLab.
|
||||
36
emulators/supermodel.yml
Normal file
36
emulators/supermodel.yml
Normal file
@@ -0,0 +1,36 @@
|
||||
emulator: Supermodel
|
||||
type: standalone
|
||||
source: https://github.com/trzy/Supermodel
|
||||
upstream: https://github.com/trzy/Supermodel
|
||||
profiled_date: "2026-03-26"
|
||||
core_version: "0.3a"
|
||||
display_name: "Supermodel (Sega Model 3)"
|
||||
cores:
|
||||
- supermodel
|
||||
systems:
|
||||
- sega-model3
|
||||
mode: standalone
|
||||
|
||||
notes: |
|
||||
Open-source Sega Model 3 arcade emulator by Bart Trzynadlowski (trzy) and
|
||||
contributors, under GPL-3.0. Emulates all three Model 3 steppings (1.0, 1.5,
|
||||
2.0/2.1) including Real3D graphics, SCSP sound, DSB1/DSB2 MPEG music boards,
|
||||
drive boards, and net board. Cross-platform (Windows, Linux, macOS).
|
||||
|
||||
Uses MAME-compatible ROM sets loaded from ZIP archives. GameLoader identifies
|
||||
games by CRC32 checksums defined in Config/Games.xml. Parent/child ROM set
|
||||
relationships exist between regional variants of the same game, not as shared
|
||||
BIOS sets. No standalone BIOS or firmware files are loaded outside of game ZIPs.
|
||||
|
||||
EmuDeck installs the Flatpak package (com.supermodel3.Supermodel). Config
|
||||
stored in ~/.supermodel/Config/. No checkBIOS entry in EmuDeck.
|
||||
|
||||
files: []
|
||||
|
||||
exclusion_note: >
|
||||
The Sega Model 3 arcade hardware has no shared system BIOS. Each game board
|
||||
contains its own program ROMs (CROM), video ROMs (VROM), sound program, sound
|
||||
samples, and optional DSB/drive board ROMs, all contained within per-game
|
||||
MAME-format ZIP archives. Supermodel loads all required data from these game
|
||||
ZIPs via CRC32-based identification defined in Games.xml. No standalone BIOS
|
||||
or firmware files are referenced by the emulator source code.
|
||||
57
emulators/suyu.yml
Normal file
57
emulators/suyu.yml
Normal file
@@ -0,0 +1,57 @@
|
||||
emulator: Suyu
|
||||
type: standalone
|
||||
core_classification: community_fork
|
||||
source: "https://github.com/suyu-emu/suyu"
|
||||
upstream: "https://github.com/suyu-emu/suyu"
|
||||
profiled_date: "2026-03-26"
|
||||
core_version: "Yuzu EA 4176 based"
|
||||
display_name: "Suyu (Nintendo Switch)"
|
||||
systems: [nintendo-switch]
|
||||
analysis_date: "2026-03-26"
|
||||
analysis_commit: "c096d16 (Hengle/suyu-emu mirror, depth=1)"
|
||||
mode: standalone
|
||||
|
||||
# Suyu is a community fork of yuzu, started after yuzu's legal takedown.
|
||||
# Key files loaded from <data_dir>/keys/ (fs_paths.h:20, path_util.cpp:124).
|
||||
# On Linux: $XDG_DATA_HOME/suyu/keys/
|
||||
# On Windows: %APPDATA%/suyu/keys/
|
||||
# Firmware NCAs are installed via UI, not placed as files.
|
||||
# HLE fallbacks exist for fonts, timezone, system version, mii model, ng word lists.
|
||||
# Original repos (GitHub + GitLab) are DMCA'd. Analysis from community mirror.
|
||||
|
||||
files:
|
||||
- name: "prod.keys"
|
||||
required: true
|
||||
path: "switch/"
|
||||
mode: standalone
|
||||
note: "Production keys for NCA decryption (master, key area, header, titlekek)"
|
||||
source_ref: "src/core/crypto/key_manager.cpp:654"
|
||||
|
||||
- name: "title.keys"
|
||||
required: false
|
||||
path: "switch/"
|
||||
mode: standalone
|
||||
note: "Per-title encryption keys (rights_id to titlekey mappings)"
|
||||
source_ref: "src/core/crypto/key_manager.cpp:657"
|
||||
|
||||
- name: "console.keys"
|
||||
required: false
|
||||
path: "switch/"
|
||||
mode: standalone
|
||||
note: "Console-specific keys (BIS, SD seed)"
|
||||
source_ref: "src/core/crypto/key_manager.cpp:658"
|
||||
|
||||
- name: "key_retail.bin"
|
||||
required: false
|
||||
path: "switch/"
|
||||
size: 160
|
||||
mode: standalone
|
||||
note: "Amiibo decryption keys (two InternalKey structs, 0x50 bytes each)"
|
||||
source_ref: "src/core/hle/service/nfc/common/amiibo_crypto.cpp:273-294"
|
||||
|
||||
notes: |
|
||||
Suyu is a standalone Nintendo Switch emulator, community fork of yuzu after legal takedown.
|
||||
dev.keys can be used instead of prod.keys when use_dev_keys is enabled (for Switch dev units).
|
||||
Firmware (system NCAs) must be installed through Suyu's UI from a firmware ZIP or NCA folder.
|
||||
Required for commercial games. Homebrew (.nro, .nso) can run without keys or firmware.
|
||||
Original repos DMCA'd by Nintendo. Analysis performed from community mirror (Hengle/suyu-emu).
|
||||
38
emulators/xenia.yml
Normal file
38
emulators/xenia.yml
Normal file
@@ -0,0 +1,38 @@
|
||||
emulator: Xenia Canary
|
||||
type: standalone
|
||||
core_classification: community_fork
|
||||
source: "https://github.com/xenia-canary/xenia-canary"
|
||||
upstream: "https://github.com/xenia-canary/xenia-canary"
|
||||
profiled_date: "2026-03-26"
|
||||
core_version: "canary"
|
||||
display_name: "Xenia Canary (Xbox 360)"
|
||||
cores:
|
||||
- xenia
|
||||
systems:
|
||||
- microsoft-xbox-360
|
||||
analysis_date: "2026-03-26"
|
||||
analysis_commit: "3efc88a (depth=1)"
|
||||
mode: standalone
|
||||
|
||||
notes: |
|
||||
Xenia Canary is an experimental open-source Xbox 360 emulator (BSD license)
|
||||
originally by Ben Vanik, now maintained by the xenia-canary community. Pure
|
||||
HLE: the Xbox 360 kernel (xboxkrnl), XAM, and XBDM modules are fully
|
||||
reimplemented in C++. XConfig system settings are hardcoded with user-
|
||||
configurable overrides (language, region, audio, video). No BIOS, firmware,
|
||||
NAND dump, flash dump, or keyvault file is loaded at any point.
|
||||
|
||||
EmuDeck installs xenia_canary.exe into the xbox360 ROMs folder. Config via
|
||||
xenia-canary.config.toml. Game patches downloaded separately into patches/.
|
||||
|
||||
files: []
|
||||
|
||||
exclusion_note: >
|
||||
Xenia is a pure high-level emulation (HLE) Xbox 360 emulator. The entire
|
||||
Xbox 360 operating system (kernel, XAM dashboard services, XBDM debug
|
||||
manager) is reimplemented in C++ as HLE modules loaded at startup
|
||||
(emulator.cc:316-318). System configuration (XConfig) returns hardcoded
|
||||
values from xboxkrnl_xconfig.cc. Memory, CPU (JIT), GPU, APU, and HID
|
||||
subsystems are all software-emulated. No external BIOS, firmware, NAND
|
||||
flash dump, or system files are loaded from disk. The wiki confirms:
|
||||
"Xenia doesn't require any Xbox 360 system files."
|
||||
59
emulators/yuzu.yml
Normal file
59
emulators/yuzu.yml
Normal file
@@ -0,0 +1,59 @@
|
||||
emulator: Yuzu
|
||||
type: standalone
|
||||
core_classification: official_port
|
||||
source: "https://github.com/yuzu-emu-mirror/yuzu"
|
||||
upstream: "https://github.com/yuzu-emu/yuzu"
|
||||
profiled_date: "2026-03-26"
|
||||
core_version: "EA 4176"
|
||||
display_name: "Yuzu (Nintendo Switch)"
|
||||
systems: [nintendo-switch]
|
||||
analysis_date: "2026-03-26"
|
||||
analysis_commit: "15e6e48 (yuzu-emu-mirror, depth=1)"
|
||||
mode: standalone
|
||||
|
||||
# Yuzu is the original Nintendo Switch emulator by bunnei and the yuzu team.
|
||||
# Legally shut down by Nintendo in March 2024. Citron, Suyu, Eden are community forks.
|
||||
# Key files loaded from <data_dir>/keys/ (fs_paths.h:18, path_util.cpp:124).
|
||||
# On Linux: $XDG_DATA_HOME/yuzu/keys/
|
||||
# On Windows: %APPDATA%/yuzu/keys/
|
||||
# Firmware NCAs are installed via UI (main.cpp:4160), not placed as files.
|
||||
# HLE fallbacks exist for fonts, timezone, system version, mii model, ng word lists.
|
||||
# Original repo DMCA'd by Nintendo. Analysis from community mirror (yuzu-emu-mirror).
|
||||
|
||||
files:
|
||||
- name: "prod.keys"
|
||||
required: true
|
||||
path: "switch/"
|
||||
mode: standalone
|
||||
note: "Production keys for NCA decryption (master, key area, header, titlekek)"
|
||||
source_ref: "src/core/crypto/key_manager.cpp:655-656"
|
||||
|
||||
- name: "title.keys"
|
||||
required: false
|
||||
path: "switch/"
|
||||
mode: standalone
|
||||
note: "Per-title encryption keys (rights_id to titlekey mappings)"
|
||||
source_ref: "src/core/crypto/key_manager.cpp:659-660"
|
||||
|
||||
- name: "console.keys"
|
||||
required: false
|
||||
path: "switch/"
|
||||
mode: standalone
|
||||
note: "Console-specific keys (BIS, SD seed)"
|
||||
source_ref: "src/core/crypto/key_manager.cpp:661-662"
|
||||
|
||||
- name: "key_retail.bin"
|
||||
required: false
|
||||
path: "switch/"
|
||||
size: 160
|
||||
mode: standalone
|
||||
note: "Amiibo decryption keys (two InternalKey structs, 0x50 bytes each)"
|
||||
source_ref: "src/core/hle/service/nfc/common/amiibo_crypto.cpp:273-294"
|
||||
|
||||
notes: |
|
||||
Yuzu is the original standalone Nintendo Switch emulator, created by bunnei (author of Citra).
|
||||
Legally shut down by Nintendo in March 2024 via lawsuit settlement. Permanently frozen.
|
||||
dev.keys can be used instead of prod.keys when use_dev_keys is enabled (for Switch dev units).
|
||||
Firmware (system NCAs) must be installed through Yuzu's UI from a firmware directory.
|
||||
Required for commercial games. Homebrew (.nro, .nso) can run without keys or firmware.
|
||||
Community forks: Citron, Suyu, Eden. Original GitHub repos DMCA'd.
|
||||
17
mkdocs.yml
17
mkdocs.yml
@@ -128,7 +128,7 @@ nav:
|
||||
- xrick: systems/xrick.md
|
||||
- Emulators:
|
||||
- Overview: emulators/index.md
|
||||
- Official ports (60):
|
||||
- Official ports (61):
|
||||
- amiarcadia: emulators/amiarcadia.md
|
||||
- Amiberry: emulators/amiberry.md
|
||||
- Ardens: emulators/ardens.md
|
||||
@@ -189,7 +189,8 @@ nav:
|
||||
- Vircon32: emulators/vircon32.md
|
||||
- vitaQuakeII: emulators/vitaquake2.md
|
||||
- yabasanshiro: emulators/yabasanshiro.md
|
||||
- Community forks (105):
|
||||
- Yuzu: emulators/yuzu.md
|
||||
- Community forks (108):
|
||||
- EightyOne: emulators/81.md
|
||||
- a5200: emulators/a5200.md
|
||||
- Anarch: emulators/anarch.md
|
||||
@@ -220,6 +221,7 @@ nav:
|
||||
- DOSBox-core: emulators/dosbox_core.md
|
||||
- DOSBox-SVN: emulators/dosbox_svn.md
|
||||
- DOSBox-SVN CE: emulators/dosbox_svn_ce.md
|
||||
- Eden: emulators/eden.md
|
||||
- EmuSCV: emulators/emuscv.md
|
||||
- ep128emu_core: emulators/ep128emu_core.md
|
||||
- FCEUmm: emulators/fceumm.md
|
||||
@@ -269,6 +271,7 @@ nav:
|
||||
- RetroDream: emulators/retrodream.md
|
||||
- SAME CDi: emulators/same_cdi.md
|
||||
- SimCoupe: emulators/simcp.md
|
||||
- Suyu: emulators/suyu.md
|
||||
- swanstation: emulators/swanstation.md
|
||||
- Syobon Action: emulators/syobonaction.md
|
||||
- TamaLIBretro: emulators/tamalibretro.md
|
||||
@@ -293,6 +296,7 @@ nav:
|
||||
- vitavoyager: emulators/vitavoyager.md
|
||||
- X Millennium: emulators/x1.md
|
||||
- x64sdl: emulators/x64sdl.md
|
||||
- Xenia Canary: emulators/xenia.md
|
||||
- yabause: emulators/yabause.md
|
||||
- ymir: emulators/ymir.md
|
||||
- Pure libretro (29):
|
||||
@@ -351,7 +355,7 @@ nav:
|
||||
- WASM-4: emulators/wasm4.md
|
||||
- XRick: emulators/xrick.md
|
||||
- Zelda Classic v2.10: emulators/zc210.md
|
||||
- Enhanced forks (12):
|
||||
- Enhanced forks (13):
|
||||
- bsnes-hd beta: emulators/bsnes_hd_beta.md
|
||||
- bsnes-mercury: emulators/bsnes_mercury.md
|
||||
- DOSBox Pure: emulators/dosbox_pure.md
|
||||
@@ -363,6 +367,7 @@ nav:
|
||||
- MAME 2003-Plus: emulators/mame2003_plus.md
|
||||
- Mupen64Plus-Next: emulators/mupen64plus_next.md
|
||||
- NP2kai: emulators/np2kai.md
|
||||
- PrimeHack: emulators/primehack.md
|
||||
- SMS Plus GX: emulators/smsplus.md
|
||||
- Frozen snapshots (32):
|
||||
- bnes: emulators/bnes.md
|
||||
@@ -401,8 +406,10 @@ nav:
|
||||
- PCSX-ReARMed: emulators/pcsx_rearmed.md
|
||||
- Launchers (1):
|
||||
- Dolphin Launcher: emulators/dolphin_launcher.md
|
||||
- Other (21):
|
||||
- Other (25):
|
||||
- ares: emulators/ares.md
|
||||
- Beetle GBA (Mednafen): emulators/beetle_gba.md
|
||||
- BigPEmu: emulators/bigpemu.md
|
||||
- Cemu: emulators/cemu.md
|
||||
- Clock Signal (CLK): emulators/clk.md
|
||||
- Demul: emulators/demul.md
|
||||
@@ -410,12 +417,14 @@ nav:
|
||||
- ep128emu-core: emulators/ep128emu.md
|
||||
- GSplus: emulators/gsplus.md
|
||||
- Lexaloffle: emulators/lexaloffle.md
|
||||
- Model 2 Emulator: emulators/model2.md
|
||||
- openMSX: emulators/openmsx.md
|
||||
- PCSX2: emulators/pcsx2.md
|
||||
- Redream: emulators/redream.md
|
||||
- RPCS3: emulators/rpcs3.md
|
||||
- Ryujinx: emulators/ryujinx.md
|
||||
- shadps4: emulators/shadps4.md
|
||||
- Supermodel: emulators/supermodel.md
|
||||
- tsugaru: emulators/tsugaru.md
|
||||
- VBA-M: emulators/vba_m.md
|
||||
- VICE: emulators/vice.md
|
||||
|
||||
@@ -17,6 +17,8 @@ platforms:
|
||||
hash_type: sha1
|
||||
schedule: weekly
|
||||
cores: all_libretro
|
||||
target_scraper: retroarch_targets
|
||||
target_source: "https://buildbot.libretro.com/nightly/"
|
||||
|
||||
batocera:
|
||||
config: batocera.yml
|
||||
@@ -28,6 +30,8 @@ platforms:
|
||||
hash_type: md5
|
||||
schedule: weekly
|
||||
cores: [81, a5200, abuse, arduous, atari800, azahar, bennugd, bk, bluemsx, bsnes, bstone, cannonball, cap32, catacombgl, cdogs, cemu, cgenius, citron, clk, corsixth, demul, devilutionx, dhewm3, dice, dolphin, dosbox_pure, dxx-rebirth, easyrpg, ecwolf, eduke32, eka2l1, emuscv, etlegacy, fake08, fallout1-ce, fallout2-ce, fbneo, fceumm, flatpak, flycast, freechaf, freeintv, fury, fuse, gambatte, gearsystem, genesisplusgx, glide64mk2, gong, gsplus, gw, gzdoom, hatari, hcl, hurrican, hypseus-singe, ikemen, ioquake3, iortcw, jazz2-native, lindbergh-loader, lowresnx, lutro, mame, mame078plus, mednafen_lynx, mednafen_ngp, mednafen_supergrafx, mednafen_wswan, melonds, mgba, minivmac, model2emu, moonlight, mrboom, neocd, np2kai, nxengine, o2em, odcommander, openbor6412, openjazz, openjk, openjkdf2, openmohaa, opera, pce_fast, pcfx, pcsx2, pcsx_rearmed, pd777, picodrive, play, pokemini, potator, ppsspp, prboom, prosystem, puae, px68k, pygame, pyxel, quasi88, raze, reminiscence, rpcs3, ruffle, samcoupe, sameduck, scummvm, sdlpop, sh, shadps4, snes9x, solarus, sonic2013, sonic3-air, sonic-mania, steam, stella, superbroswar, supermodel, taradino, tgbdual, theforceengine, theodore, thextech, tic80, tr1x, tr2x, tsugaru, tyrian, tyrquake, uqm, uzem, vb, vecx, vice_x64, vircon32, virtualjaguar, vita3k, vox_official, vpinball, wasm4, wine-tkg, x1, x128, x16emu, xash3d_fwgs, xemu, xenia-canary, xpet, xplus4, xrick, xvic, yabasanshiro, yquake2, zc210]
|
||||
target_scraper: batocera_targets
|
||||
target_source: "https://github.com/batocera-linux/batocera.linux"
|
||||
|
||||
recalbox:
|
||||
config: recalbox.yml
|
||||
@@ -38,6 +42,8 @@ platforms:
|
||||
source_format: xml
|
||||
hash_type: md5
|
||||
schedule: monthly
|
||||
target_scraper: null
|
||||
target_source: null
|
||||
cores: ["2048", 81, a5200, advancemame, 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, emuscv, fake08, fba2x, fbneo, fceumm, flycast, flycast-next, fmsx, freechaf, freeintv, frotz, fuse, gambatte, gearcoleco, geargrafx, gearsystem, genesisplusgx, genesisplusgx_ex, genesisplusgxwide, geolith, glide64mk2, gliden64, gliden64_20, gong, gpsp, gsplus, gw, handy, hatari, hatarib, holani, imageviewer, julius, kronos, lowresnx, lutro, mame0258, mame0278, mame2000, mame2003, mame2003_plus, mame2010, mame2015, mame2016, mednafen_lynx, mednafen_ngp, mednafen_pce_fast, mednafen_pcfx, mednafen_psx, mednafen_psx_hw, mednafen_saturn, mednafen_supafaust, mednafen_supergrafx, mednafen_vb, mednafen_wswan, melonds, mesen, mesen_s, meteor, mgba, minivmac, mojozork, moonlight, mrboom, mu, mupen64plus, mupen64plus_next, n64_gles2, neocd, nestopia, np2kai, nxengine, o2em, openbor, openlara, opera, oricutron, parallel_n64, pcsx2, pcsx_rearmed, pico8, picodrive, pisnes, pokemini, potator, ppsspp, prboom, prosystem, ps2, puae, px68k, quasi88, quicknes, race, rb5000, reicast, reminiscence, retro8, retrodream, rice, rice_gles2, sameboy, same_cdi, sameduck, scummvm, sdlpop, simcoupe, snes9x, snes9x2002, snes9x2005, snes9x2010, solarus, stella, stella2014, stonesoup, supermodel, swanstation, tamalibretro, tgbdual, theodore, thepowdertoy, ti99sim, tic80, tyrquake, uae4all, uae4arm, uzem, vecx, vice_x128, vice_x64, vice_x64sc, vice_xcbm2, vice_xcbm5x0, vice_xpet, vice_xplus4, vice_xscpu64, vice_xvic, virtualjaguar, vitaquake2, vitaquake3, vitavoyager, vpinball, vvvvvv, wasm4, x1, x128, x64, x64sx, xcbm2, xcbm5x0, xemu, xpet, xplus4, xrick, xroar, xscpu64, xvic, yabasanshiro, yabause]
|
||||
|
||||
retrobat:
|
||||
@@ -50,6 +56,8 @@ platforms:
|
||||
hash_type: md5
|
||||
schedule: weekly
|
||||
cores: [81, a5200, abuse, arduous, atari800, azahar, bennugd, bk, bluemsx, bsnes, bstone, cannonball, cap32, catacombgl, cdogs, cemu, cgenius, citron, clk, corsixth, demul, devilutionx, dhewm3, dice, dolphin, dosbox_pure, dxx-rebirth, easyrpg, ecwolf, eduke32, eka2l1, emuscv, etlegacy, fake08, fallout1-ce, fallout2-ce, fbneo, fceumm, flatpak, flycast, freechaf, freeintv, fury, fuse, gambatte, gearsystem, genesisplusgx, glide64mk2, gong, gsplus, gw, gzdoom, hatari, hcl, hurrican, hypseus-singe, ikemen, ioquake3, iortcw, jazz2-native, lindbergh-loader, lowresnx, lutro, mame, mame078plus, mednafen_lynx, mednafen_ngp, mednafen_supergrafx, mednafen_wswan, melonds, mgba, minivmac, model2emu, moonlight, mrboom, neocd, np2kai, nxengine, o2em, odcommander, openbor6412, openjazz, openjk, openjkdf2, openmohaa, opera, pce_fast, pcfx, pcsx2, pcsx_rearmed, pd777, picodrive, play, pokemini, potator, ppsspp, prboom, prosystem, puae, px68k, pygame, pyxel, quasi88, raze, reminiscence, rpcs3, ruffle, samcoupe, sameduck, scummvm, sdlpop, sh, shadps4, snes9x, solarus, sonic2013, sonic3-air, sonic-mania, steam, stella, superbroswar, supermodel, taradino, tgbdual, theforceengine, theodore, thextech, tic80, tr1x, tr2x, tsugaru, tyrian, tyrquake, uqm, uzem, vb, vecx, vice_x64, vircon32, virtualjaguar, vita3k, vox_official, vpinball, wasm4, wine-tkg, x1, x128, x16emu, xash3d_fwgs, xemu, xenia-canary, xpet, xplus4, xrick, xvic, yabasanshiro, yquake2, zc210]
|
||||
target_scraper: null
|
||||
target_source: null
|
||||
|
||||
emudeck:
|
||||
config: emudeck.yml
|
||||
@@ -61,6 +69,8 @@ platforms:
|
||||
source_format: bash_script+csv
|
||||
hash_type: md5
|
||||
schedule: weekly
|
||||
target_scraper: emudeck_targets
|
||||
target_source: "https://github.com/dragoonDorise/EmuDeck"
|
||||
# dragoonDorise/EmuDeck = official repo (creator's account, 3.4k stars)
|
||||
# EmuDeck/emudeck.github.io = official wiki (org account)
|
||||
|
||||
@@ -72,6 +82,8 @@ platforms:
|
||||
inherits_from: retroarch
|
||||
cores: all_libretro
|
||||
schedule: weekly
|
||||
target_scraper: lakka_targets
|
||||
target_source: "https://buildbot.libretro.com/nightly/"
|
||||
|
||||
retrodeck:
|
||||
config: retrodeck.yml
|
||||
@@ -83,6 +95,8 @@ platforms:
|
||||
hash_type: md5
|
||||
schedule: monthly
|
||||
cores: [azahar, cemu, dolphin, duckstation, gzdoom, mame, melonds, openbor, pcsx2, pico-8, ppsspp, primehack, retroarch, rpcs3, ruffle, solarus, vita3k, xemu, xroar]
|
||||
target_scraper: null
|
||||
target_source: null
|
||||
# Each component/<name>/component_manifest.json declares BIOS requirements
|
||||
# Scraper enumerates top-level dirs via GitHub API, fetches each manifest directly
|
||||
|
||||
@@ -96,6 +110,8 @@ platforms:
|
||||
hash_type: sha1
|
||||
schedule: monthly
|
||||
inherits_from: emulatorjs # cores inherited from emulatorjs.yml
|
||||
target_scraper: null
|
||||
target_source: null
|
||||
|
||||
retropie:
|
||||
config: retropie.yml
|
||||
@@ -104,3 +120,5 @@ platforms:
|
||||
scraper: null
|
||||
cores: all_libretro
|
||||
schedule: null
|
||||
target_scraper: retropie_targets
|
||||
target_source: "https://retropie.org.uk/stats/pkgflags/"
|
||||
|
||||
22
platforms/targets/_overrides.yml
Normal file
22
platforms/targets/_overrides.yml
Normal file
@@ -0,0 +1,22 @@
|
||||
# Manual target overrides.
|
||||
# This file is NEVER overwritten by scrapers.
|
||||
# Use it to add aliases, add/remove cores per target.
|
||||
#
|
||||
# Format:
|
||||
# platform_name:
|
||||
# targets:
|
||||
# target-name:
|
||||
# aliases: [alias1, alias2]
|
||||
# add_cores: [core_to_add]
|
||||
# remove_cores: [core_to_remove]
|
||||
|
||||
retroarch:
|
||||
targets:
|
||||
nintendo-switch:
|
||||
aliases: [switch, nx]
|
||||
linux-x86_64:
|
||||
aliases: [pc, linux, x86_64, desktop]
|
||||
playstation-ps2:
|
||||
aliases: [ps2]
|
||||
playstation-psp:
|
||||
aliases: [psp]
|
||||
5959
platforms/targets/batocera.yml
Normal file
5959
platforms/targets/batocera.yml
Normal file
File diff suppressed because it is too large
Load Diff
461
platforms/targets/emudeck.yml
Normal file
461
platforms/targets/emudeck.yml
Normal file
@@ -0,0 +1,461 @@
|
||||
platform: emudeck
|
||||
source: https://github.com/dragoonDorise/EmuDeck
|
||||
scraped_at: '2026-03-26T09:14:02Z'
|
||||
targets:
|
||||
steamos:
|
||||
architecture: x86_64
|
||||
cores:
|
||||
- '2048'
|
||||
- 3dengine
|
||||
- '81'
|
||||
- DoubleCherryGB
|
||||
- a5200
|
||||
- amiarcadia
|
||||
- anarch
|
||||
- applewin
|
||||
- ardens
|
||||
- arduous
|
||||
- ares
|
||||
- atari800
|
||||
- azahar
|
||||
- b2
|
||||
- bigpemu
|
||||
- bk
|
||||
- blastem
|
||||
- bluemsx
|
||||
- boom3
|
||||
- boom3_xp
|
||||
- bsnes
|
||||
- bsnes-jg
|
||||
- bsnes2014_accuracy
|
||||
- bsnes2014_balanced
|
||||
- bsnes2014_performance
|
||||
- bsnes_cplusplus98
|
||||
- bsnes_hd_beta
|
||||
- bsnes_mercury_accuracy
|
||||
- bsnes_mercury_balanced
|
||||
- bsnes_mercury_performance
|
||||
- cannonball
|
||||
- cap32
|
||||
- cdi2015
|
||||
- cemu
|
||||
- chailove
|
||||
- citra
|
||||
- citra2018
|
||||
- citron
|
||||
- clownmdemu
|
||||
- craft
|
||||
- crocods
|
||||
- desmume
|
||||
- desmume2015
|
||||
- dice
|
||||
- dinothawr
|
||||
- dirksimple
|
||||
- dolphin
|
||||
- dosbox_core
|
||||
- dosbox_pure
|
||||
- dosbox_svn
|
||||
- doukutsu_rs
|
||||
- duckstation
|
||||
- easyrpg
|
||||
- ecwolf
|
||||
- eden
|
||||
- ep128emu_core
|
||||
- fbalpha
|
||||
- fbalpha2012
|
||||
- fbalpha2012_cps1
|
||||
- fbalpha2012_cps2
|
||||
- fbalpha2012_cps3
|
||||
- fbalpha2012_neogeo
|
||||
- fbneo
|
||||
- fceumm
|
||||
- fixgb
|
||||
- fixnes
|
||||
- flycast
|
||||
- fmsx
|
||||
- freechaf
|
||||
- freeintv
|
||||
- frodo
|
||||
- fuse
|
||||
- galaksija
|
||||
- gam4980
|
||||
- gambatte
|
||||
- gearboy
|
||||
- gearcoleco
|
||||
- geargrafx
|
||||
- gearlynx
|
||||
- gearsystem
|
||||
- genesis_plus_gx
|
||||
- genesis_plus_gx_wide
|
||||
- geolith
|
||||
- gme
|
||||
- gong
|
||||
- gpsp
|
||||
- gw
|
||||
- handy
|
||||
- hatari
|
||||
- holani
|
||||
- jaxe
|
||||
- jollycv
|
||||
- jumpnbump
|
||||
- kronos
|
||||
- lowresnx
|
||||
- lutro
|
||||
- m2000
|
||||
- mame
|
||||
- mame2000
|
||||
- mame2003
|
||||
- mame2003_midway
|
||||
- mame2003_plus
|
||||
- mame2010
|
||||
- mcsoftserve
|
||||
- mednafen_gba
|
||||
- mednafen_lynx
|
||||
- mednafen_ngp
|
||||
- mednafen_pce
|
||||
- mednafen_pce_fast
|
||||
- mednafen_pcfx
|
||||
- mednafen_psx
|
||||
- mednafen_psx_hw
|
||||
- mednafen_saturn
|
||||
- mednafen_snes
|
||||
- mednafen_supafaust
|
||||
- mednafen_supergrafx
|
||||
- mednafen_vb
|
||||
- mednafen_wswan
|
||||
- melonds
|
||||
- melondsds
|
||||
- mesen
|
||||
- mesen-s
|
||||
- meteor
|
||||
- mgba
|
||||
- minivmac
|
||||
- model2
|
||||
- mojozork
|
||||
- mrboom
|
||||
- mu
|
||||
- mupen64plus_next
|
||||
- nekop2
|
||||
- neocd
|
||||
- nestopia
|
||||
- noods
|
||||
- np2kai
|
||||
- numero
|
||||
- nxengine
|
||||
- o2em
|
||||
- oberon
|
||||
- openlara
|
||||
- opera
|
||||
- panda3ds
|
||||
- parallel_n64
|
||||
- pcsx2
|
||||
- pcsx_rearmed
|
||||
- pd777
|
||||
- picodrive
|
||||
- play
|
||||
- pocketcdg
|
||||
- pokemini
|
||||
- potator
|
||||
- ppsspp
|
||||
- prboom
|
||||
- primehack
|
||||
- prosystem
|
||||
- puae
|
||||
- puae2021
|
||||
- px68k
|
||||
- qemu
|
||||
- quasi88
|
||||
- quicknes
|
||||
- race
|
||||
- reminiscence
|
||||
- retro8
|
||||
- romcleaner
|
||||
- rpcs3
|
||||
- ryujinx
|
||||
- same_cdi
|
||||
- sameboy
|
||||
- sameduck
|
||||
- scummvm
|
||||
- shadps4
|
||||
- skyemu
|
||||
- smsplus
|
||||
- snes9x
|
||||
- snes9x2002
|
||||
- snes9x2005
|
||||
- snes9x2005_plus
|
||||
- snes9x2010
|
||||
- squirreljme
|
||||
- stella
|
||||
- stella2014
|
||||
- stella2023
|
||||
- superbroswar
|
||||
- supermodel
|
||||
- suyu
|
||||
- swanstation
|
||||
- tamalibretro
|
||||
- tgbdual
|
||||
- theodore
|
||||
- thepowdertoy
|
||||
- tic80
|
||||
- tyrquake
|
||||
- uw8
|
||||
- uzem
|
||||
- vaporspec
|
||||
- vba_next
|
||||
- vbam
|
||||
- vecx
|
||||
- vemulator
|
||||
- vice_x128
|
||||
- vice_x64
|
||||
- vice_x64sc
|
||||
- vice_xcbm2
|
||||
- vice_xcbm5x0
|
||||
- vice_xpet
|
||||
- vice_xplus4
|
||||
- vice_xscpu64
|
||||
- vice_xvic
|
||||
- vircon32
|
||||
- virtualjaguar
|
||||
- virtualxt
|
||||
- vita3k
|
||||
- vitaquake2
|
||||
- vitaquake2-rogue
|
||||
- vitaquake2-xatrix
|
||||
- vitaquake2-zaero
|
||||
- vitaquake3
|
||||
- wasm4
|
||||
- x1
|
||||
- xemu
|
||||
- xenia
|
||||
- xrick
|
||||
- yabasanshiro
|
||||
- yabause
|
||||
- yuzu
|
||||
windows:
|
||||
architecture: x86_64
|
||||
cores:
|
||||
- '2048'
|
||||
- 3dengine
|
||||
- '81'
|
||||
- DoubleCherryGB
|
||||
- a5200
|
||||
- amiarcadia
|
||||
- anarch
|
||||
- applewin
|
||||
- ardens
|
||||
- arduous
|
||||
- atari800
|
||||
- azahar
|
||||
- b2
|
||||
- bigpemu
|
||||
- bk
|
||||
- blastem
|
||||
- bluemsx
|
||||
- boom3
|
||||
- boom3_xp
|
||||
- bsnes
|
||||
- bsnes-jg
|
||||
- bsnes2014_accuracy
|
||||
- bsnes2014_balanced
|
||||
- bsnes2014_performance
|
||||
- bsnes_cplusplus98
|
||||
- bsnes_hd_beta
|
||||
- bsnes_mercury_accuracy
|
||||
- bsnes_mercury_balanced
|
||||
- bsnes_mercury_performance
|
||||
- cannonball
|
||||
- cap32
|
||||
- cdi2015
|
||||
- cemu
|
||||
- chailove
|
||||
- citra
|
||||
- citra2018
|
||||
- citron
|
||||
- clownmdemu
|
||||
- craft
|
||||
- crocods
|
||||
- desmume
|
||||
- desmume2015
|
||||
- dice
|
||||
- dinothawr
|
||||
- dirksimple
|
||||
- dolphin
|
||||
- dosbox_core
|
||||
- dosbox_pure
|
||||
- dosbox_svn
|
||||
- doukutsu_rs
|
||||
- duckstation
|
||||
- easyrpg
|
||||
- ecwolf
|
||||
- eden
|
||||
- ep128emu_core
|
||||
- fbalpha
|
||||
- fbalpha2012
|
||||
- fbalpha2012_cps1
|
||||
- fbalpha2012_cps2
|
||||
- fbalpha2012_cps3
|
||||
- fbalpha2012_neogeo
|
||||
- fbneo
|
||||
- fceumm
|
||||
- fixgb
|
||||
- fixnes
|
||||
- flycast
|
||||
- fmsx
|
||||
- freechaf
|
||||
- freeintv
|
||||
- frodo
|
||||
- fuse
|
||||
- galaksija
|
||||
- gam4980
|
||||
- gambatte
|
||||
- gearboy
|
||||
- gearcoleco
|
||||
- geargrafx
|
||||
- gearlynx
|
||||
- gearsystem
|
||||
- genesis_plus_gx
|
||||
- genesis_plus_gx_wide
|
||||
- geolith
|
||||
- gme
|
||||
- gong
|
||||
- gpsp
|
||||
- gw
|
||||
- handy
|
||||
- hatari
|
||||
- holani
|
||||
- jaxe
|
||||
- jollycv
|
||||
- jumpnbump
|
||||
- kronos
|
||||
- lowresnx
|
||||
- lutro
|
||||
- m2000
|
||||
- mame
|
||||
- mame2000
|
||||
- mame2003
|
||||
- mame2003_midway
|
||||
- mame2003_plus
|
||||
- mame2010
|
||||
- mcsoftserve
|
||||
- mednafen_gba
|
||||
- mednafen_lynx
|
||||
- mednafen_ngp
|
||||
- mednafen_pce
|
||||
- mednafen_pce_fast
|
||||
- mednafen_pcfx
|
||||
- mednafen_psx
|
||||
- mednafen_psx_hw
|
||||
- mednafen_saturn
|
||||
- mednafen_snes
|
||||
- mednafen_supafaust
|
||||
- mednafen_supergrafx
|
||||
- mednafen_vb
|
||||
- mednafen_wswan
|
||||
- melonds
|
||||
- melondsds
|
||||
- mesen
|
||||
- mesen-s
|
||||
- meteor
|
||||
- mgba
|
||||
- minivmac
|
||||
- model2
|
||||
- mojozork
|
||||
- mrboom
|
||||
- mu
|
||||
- mupen64plus_next
|
||||
- nekop2
|
||||
- neocd
|
||||
- nestopia
|
||||
- noods
|
||||
- np2kai
|
||||
- numero
|
||||
- nxengine
|
||||
- o2em
|
||||
- oberon
|
||||
- openlara
|
||||
- opera
|
||||
- panda3ds
|
||||
- parallel_n64
|
||||
- pcsx2
|
||||
- pcsx_rearmed
|
||||
- pd777
|
||||
- picodrive
|
||||
- play
|
||||
- pocketcdg
|
||||
- pokemini
|
||||
- potator
|
||||
- ppsspp
|
||||
- prboom
|
||||
- primehack
|
||||
- prosystem
|
||||
- puae
|
||||
- puae2021
|
||||
- px68k
|
||||
- qemu
|
||||
- quasi88
|
||||
- quicknes
|
||||
- race
|
||||
- reminiscence
|
||||
- retro8
|
||||
- romcleaner
|
||||
- rpcs3
|
||||
- ryujinx
|
||||
- same_cdi
|
||||
- sameboy
|
||||
- sameduck
|
||||
- scummvm
|
||||
- shadps4
|
||||
- skyemu
|
||||
- smsplus
|
||||
- snes9x
|
||||
- snes9x2002
|
||||
- snes9x2005
|
||||
- snes9x2005_plus
|
||||
- snes9x2010
|
||||
- squirreljme
|
||||
- stella
|
||||
- stella2014
|
||||
- stella2023
|
||||
- superbroswar
|
||||
- supermodel
|
||||
- swanstation
|
||||
- tamalibretro
|
||||
- template
|
||||
- tgbdual
|
||||
- theodore
|
||||
- thepowdertoy
|
||||
- tic80
|
||||
- tyrquake
|
||||
- uw8
|
||||
- uzem
|
||||
- vaporspec
|
||||
- vba_next
|
||||
- vbam
|
||||
- vecx
|
||||
- vemulator
|
||||
- vice_x128
|
||||
- vice_x64
|
||||
- vice_x64sc
|
||||
- vice_xcbm2
|
||||
- vice_xcbm5x0
|
||||
- vice_xpet
|
||||
- vice_xplus4
|
||||
- vice_xscpu64
|
||||
- vice_xvic
|
||||
- vircon32
|
||||
- virtualjaguar
|
||||
- virtualxt
|
||||
- vita3k
|
||||
- vitaquake2
|
||||
- vitaquake2-rogue
|
||||
- vitaquake2-xatrix
|
||||
- vitaquake2-zaero
|
||||
- vitaquake3
|
||||
- wasm4
|
||||
- x1
|
||||
- xemu
|
||||
- xenia
|
||||
- xrick
|
||||
- yabasanshiro
|
||||
- yabause
|
||||
- yuzu
|
||||
3241
platforms/targets/retroarch.yml
Normal file
3241
platforms/targets/retroarch.yml
Normal file
File diff suppressed because it is too large
Load Diff
25
platforms/targets/retrobat.yml
Normal file
25
platforms/targets/retrobat.yml
Normal file
@@ -0,0 +1,25 @@
|
||||
platform: retrobat
|
||||
source: static
|
||||
scraped_at: "2026-03-26T00:00:00Z"
|
||||
targets:
|
||||
windows:
|
||||
architecture: x86_64
|
||||
cores: [81, a5200, abuse, arduous, atari800, azahar, bennugd, bk, bluemsx, bsnes,
|
||||
bstone, cannonball, cap32, catacombgl, cdogs, cemu, cgenius, citron, clk, corsixth,
|
||||
demul, devilutionx, dhewm3, dice, dolphin, dosbox_pure, dxx-rebirth, easyrpg,
|
||||
ecwolf, eduke32, eka2l1, emuscv, etlegacy, fake08, fallout1-ce, fallout2-ce,
|
||||
fbneo, fceumm, flatpak, flycast, freechaf, freeintv, fury, fuse, gambatte,
|
||||
gearsystem, genesisplusgx, glide64mk2, gong, gsplus, gw, gzdoom, hatari, hcl,
|
||||
hurrican, hypseus-singe, ikemen, ioquake3, iortcw, jazz2-native, lindbergh-loader,
|
||||
lowresnx, lutro, mame, mame078plus, mednafen_lynx, mednafen_ngp, mednafen_supergrafx,
|
||||
mednafen_wswan, melonds, mgba, minivmac, model2emu, moonlight, mrboom, neocd,
|
||||
np2kai, nxengine, o2em, odcommander, openbor6412, openjazz, openjk, openjkdf2,
|
||||
openmohaa, opera, pce_fast, pcfx, pcsx2, pcsx_rearmed, pd777, picodrive, play,
|
||||
pokemini, potator, ppsspp, prboom, prosystem, puae, px68k, pygame, pyxel, quasi88,
|
||||
raze, reminiscence, rpcs3, ruffle, samcoupe, sameduck, scummvm, sdlpop, sh,
|
||||
shadps4, snes9x, solarus, sonic2013, sonic3-air, sonic-mania, steam, stella,
|
||||
superbroswar, supermodel, taradino, tgbdual, theforceengine, theodore, thextech,
|
||||
tic80, tr1x, tr2x, tsugaru, tyrian, tyrquake, uqm, uzem, vb, vecx, vice_x64,
|
||||
vircon32, virtualjaguar, vita3k, vox_official, vpinball, wasm4, wine-tkg, x1,
|
||||
x128, x16emu, xash3d_fwgs, xemu, xenia-canary, xpet, xplus4, xrick, xvic,
|
||||
yabasanshiro, yquake2, zc210]
|
||||
9
platforms/targets/retrodeck.yml
Normal file
9
platforms/targets/retrodeck.yml
Normal file
@@ -0,0 +1,9 @@
|
||||
platform: retrodeck
|
||||
source: static
|
||||
scraped_at: "2026-03-26T00:00:00Z"
|
||||
targets:
|
||||
x86_64-linux:
|
||||
architecture: x86_64
|
||||
cores: [azahar, cemu, dolphin, duckstation, gzdoom, mame, melonds, openbor, pcsx2,
|
||||
pico-8, ppsspp, primehack, retroarch, rpcs3, ruffle, solarus, vita3k, xemu,
|
||||
xroar]
|
||||
662
platforms/targets/retropie.yml
Normal file
662
platforms/targets/retropie.yml
Normal file
@@ -0,0 +1,662 @@
|
||||
platform: retropie
|
||||
source: https://api.github.com/repos/RetroPie/RetroPie-Setup/contents/scriptmodules/libretrocores
|
||||
scraped_at: '2026-03-26T08:43:13Z'
|
||||
targets:
|
||||
rpi1:
|
||||
architecture: armv6
|
||||
cores:
|
||||
- '81'
|
||||
- atari800
|
||||
- beetle_lynx
|
||||
- beetle_ngp
|
||||
- beetle_pce_fast
|
||||
- beetle_pcfx
|
||||
- beetle_supergrafx
|
||||
- beetle_vb
|
||||
- beetle_wswan
|
||||
- bennugd
|
||||
- bluemsx
|
||||
- caprice32
|
||||
- desmume
|
||||
- desmume2015
|
||||
- dinothawr
|
||||
- dirksimple
|
||||
- dosbox
|
||||
- dosbox_pure
|
||||
- ep128emu
|
||||
- fbalpha2012
|
||||
- fbneo
|
||||
- fceumm
|
||||
- fmsx
|
||||
- freechaf
|
||||
- freeintv
|
||||
- fuse
|
||||
- gambatte
|
||||
- geargrafx
|
||||
- gearsystem
|
||||
- genesis_plus_gx
|
||||
- gpsp
|
||||
- gw
|
||||
- handy
|
||||
- hatari
|
||||
- mame
|
||||
- mame2000
|
||||
- mame2003
|
||||
- mame2003_plus
|
||||
- mame2010
|
||||
- mame2015
|
||||
- mame2016
|
||||
- mess
|
||||
- mess2016
|
||||
- mgba
|
||||
- mrboom
|
||||
- mupen64plus
|
||||
- mupen64plus_next
|
||||
- neocd
|
||||
- nestopia
|
||||
- np2kai
|
||||
- nxengine
|
||||
- o2em
|
||||
- opera
|
||||
- parallel_n64
|
||||
- pcsx_rearmed
|
||||
- picodrive
|
||||
- pokemini
|
||||
- ppsspp
|
||||
- prboom
|
||||
- prosystem
|
||||
- puae
|
||||
- puae2021
|
||||
- px68k
|
||||
- quasi88
|
||||
- quicknes
|
||||
- retro8
|
||||
- scummvm
|
||||
- smsplus_gx
|
||||
- snes9x
|
||||
- snes9x2002
|
||||
- snes9x2005
|
||||
- snes9x2010
|
||||
- stella
|
||||
- stella2014
|
||||
- superflappybirds
|
||||
- tgbdual
|
||||
- theodore
|
||||
- tic80
|
||||
- tyrquake
|
||||
- vecx
|
||||
- vice
|
||||
- x1
|
||||
- xrick
|
||||
rpi2:
|
||||
architecture: armv7
|
||||
cores:
|
||||
- '81'
|
||||
- atari800
|
||||
- beetle_lynx
|
||||
- beetle_ngp
|
||||
- beetle_pce
|
||||
- beetle_pce_fast
|
||||
- beetle_pcfx
|
||||
- beetle_saturn
|
||||
- beetle_supergrafx
|
||||
- beetle_vb
|
||||
- beetle_wswan
|
||||
- bennugd
|
||||
- bluemsx
|
||||
- bsnes
|
||||
- caprice32
|
||||
- desmume
|
||||
- desmume2015
|
||||
- dinothawr
|
||||
- dirksimple
|
||||
- dosbox
|
||||
- dosbox_pure
|
||||
- ep128emu
|
||||
- fbalpha2012
|
||||
- fbneo
|
||||
- fceumm
|
||||
- flycast
|
||||
- flycast_dev
|
||||
- fmsx
|
||||
- freechaf
|
||||
- freeintv
|
||||
- fuse
|
||||
- gambatte
|
||||
- geargrafx
|
||||
- gearsystem
|
||||
- genesis_plus_gx
|
||||
- gpsp
|
||||
- gw
|
||||
- handy
|
||||
- hatari
|
||||
- mame
|
||||
- mame2000
|
||||
- mame2003
|
||||
- mame2003_plus
|
||||
- mame2010
|
||||
- mame2015
|
||||
- mame2016
|
||||
- mesen
|
||||
- mess
|
||||
- mess2016
|
||||
- mgba
|
||||
- mrboom
|
||||
- mupen64plus
|
||||
- mupen64plus_next
|
||||
- neocd
|
||||
- nestopia
|
||||
- np2kai
|
||||
- nxengine
|
||||
- o2em
|
||||
- opera
|
||||
- parallel_n64
|
||||
- pcsx_rearmed
|
||||
- picodrive
|
||||
- pokemini
|
||||
- ppsspp
|
||||
- prboom
|
||||
- prosystem
|
||||
- puae
|
||||
- puae2021
|
||||
- px68k
|
||||
- quasi88
|
||||
- quicknes
|
||||
- retro8
|
||||
- scummvm
|
||||
- smsplus_gx
|
||||
- snes9x
|
||||
- snes9x2002
|
||||
- snes9x2005
|
||||
- snes9x2010
|
||||
- stella
|
||||
- stella2014
|
||||
- superflappybirds
|
||||
- tgbdual
|
||||
- theodore
|
||||
- tic80
|
||||
- tyrquake
|
||||
- vba_next
|
||||
- vecx
|
||||
- vice
|
||||
- virtualjaguar
|
||||
- x1
|
||||
- xrick
|
||||
- yabause
|
||||
rpi3:
|
||||
architecture: armv7
|
||||
cores:
|
||||
- '81'
|
||||
- atari800
|
||||
- beetle_lynx
|
||||
- beetle_ngp
|
||||
- beetle_pce
|
||||
- beetle_pce_fast
|
||||
- beetle_pcfx
|
||||
- beetle_saturn
|
||||
- beetle_supergrafx
|
||||
- beetle_vb
|
||||
- beetle_wswan
|
||||
- bennugd
|
||||
- bluemsx
|
||||
- bsnes
|
||||
- caprice32
|
||||
- desmume
|
||||
- desmume2015
|
||||
- dinothawr
|
||||
- dirksimple
|
||||
- dosbox
|
||||
- dosbox_pure
|
||||
- ep128emu
|
||||
- fbalpha2012
|
||||
- fbneo
|
||||
- fceumm
|
||||
- flycast
|
||||
- flycast_dev
|
||||
- fmsx
|
||||
- freechaf
|
||||
- freeintv
|
||||
- fuse
|
||||
- gambatte
|
||||
- geargrafx
|
||||
- gearsystem
|
||||
- genesis_plus_gx
|
||||
- gpsp
|
||||
- gw
|
||||
- handy
|
||||
- hatari
|
||||
- mame
|
||||
- mame2000
|
||||
- mame2003
|
||||
- mame2003_plus
|
||||
- mame2010
|
||||
- mame2015
|
||||
- mame2016
|
||||
- mesen
|
||||
- mess
|
||||
- mess2016
|
||||
- mgba
|
||||
- mrboom
|
||||
- mupen64plus
|
||||
- mupen64plus_next
|
||||
- neocd
|
||||
- nestopia
|
||||
- np2kai
|
||||
- nxengine
|
||||
- o2em
|
||||
- opera
|
||||
- parallel_n64
|
||||
- pcsx_rearmed
|
||||
- picodrive
|
||||
- pokemini
|
||||
- ppsspp
|
||||
- prboom
|
||||
- prosystem
|
||||
- puae
|
||||
- puae2021
|
||||
- px68k
|
||||
- quasi88
|
||||
- quicknes
|
||||
- retro8
|
||||
- scummvm
|
||||
- smsplus_gx
|
||||
- snes9x
|
||||
- snes9x2002
|
||||
- snes9x2005
|
||||
- snes9x2010
|
||||
- stella
|
||||
- stella2014
|
||||
- superflappybirds
|
||||
- tgbdual
|
||||
- theodore
|
||||
- tic80
|
||||
- tyrquake
|
||||
- vba_next
|
||||
- vecx
|
||||
- vice
|
||||
- virtualjaguar
|
||||
- x1
|
||||
- xrick
|
||||
- yabause
|
||||
rpi4:
|
||||
architecture: aarch64
|
||||
cores:
|
||||
- '81'
|
||||
- atari800
|
||||
- beetle_lynx
|
||||
- beetle_ngp
|
||||
- beetle_pce
|
||||
- beetle_pce_fast
|
||||
- beetle_pcfx
|
||||
- beetle_saturn
|
||||
- beetle_supergrafx
|
||||
- beetle_vb
|
||||
- beetle_wswan
|
||||
- bennugd
|
||||
- bluemsx
|
||||
- bsnes
|
||||
- caprice32
|
||||
- desmume
|
||||
- desmume2015
|
||||
- dinothawr
|
||||
- dirksimple
|
||||
- dosbox
|
||||
- dosbox_pure
|
||||
- ep128emu
|
||||
- fbalpha2012
|
||||
- fbneo
|
||||
- fceumm
|
||||
- flycast
|
||||
- flycast_dev
|
||||
- fmsx
|
||||
- freechaf
|
||||
- freeintv
|
||||
- fuse
|
||||
- gambatte
|
||||
- geargrafx
|
||||
- gearsystem
|
||||
- genesis_plus_gx
|
||||
- gpsp
|
||||
- gw
|
||||
- handy
|
||||
- hatari
|
||||
- mame
|
||||
- mame2000
|
||||
- mame2003
|
||||
- mame2003_plus
|
||||
- mame2010
|
||||
- mame2015
|
||||
- mame2016
|
||||
- mesen
|
||||
- mess
|
||||
- mess2016
|
||||
- mgba
|
||||
- mrboom
|
||||
- mupen64plus
|
||||
- mupen64plus_next
|
||||
- neocd
|
||||
- nestopia
|
||||
- np2kai
|
||||
- nxengine
|
||||
- o2em
|
||||
- opera
|
||||
- parallel_n64
|
||||
- pcsx_rearmed
|
||||
- picodrive
|
||||
- pokemini
|
||||
- ppsspp
|
||||
- prboom
|
||||
- prosystem
|
||||
- puae
|
||||
- puae2021
|
||||
- px68k
|
||||
- quasi88
|
||||
- quicknes
|
||||
- retro8
|
||||
- scummvm
|
||||
- smsplus_gx
|
||||
- snes9x
|
||||
- snes9x2002
|
||||
- snes9x2005
|
||||
- snes9x2010
|
||||
- stella
|
||||
- stella2014
|
||||
- superflappybirds
|
||||
- tgbdual
|
||||
- theodore
|
||||
- tic80
|
||||
- tyrquake
|
||||
- vba_next
|
||||
- vecx
|
||||
- vice
|
||||
- virtualjaguar
|
||||
- x1
|
||||
- xrick
|
||||
- yabause
|
||||
rpi5:
|
||||
architecture: aarch64
|
||||
cores:
|
||||
- '81'
|
||||
- atari800
|
||||
- beetle_lynx
|
||||
- beetle_ngp
|
||||
- beetle_pce
|
||||
- beetle_pce_fast
|
||||
- beetle_pcfx
|
||||
- beetle_saturn
|
||||
- beetle_supergrafx
|
||||
- beetle_vb
|
||||
- beetle_wswan
|
||||
- bennugd
|
||||
- bluemsx
|
||||
- bsnes
|
||||
- caprice32
|
||||
- desmume
|
||||
- desmume2015
|
||||
- dinothawr
|
||||
- dirksimple
|
||||
- dosbox
|
||||
- dosbox_pure
|
||||
- ep128emu
|
||||
- fbalpha2012
|
||||
- fbneo
|
||||
- fceumm
|
||||
- flycast
|
||||
- flycast_dev
|
||||
- fmsx
|
||||
- freechaf
|
||||
- freeintv
|
||||
- fuse
|
||||
- gambatte
|
||||
- geargrafx
|
||||
- gearsystem
|
||||
- genesis_plus_gx
|
||||
- gpsp
|
||||
- gw
|
||||
- handy
|
||||
- hatari
|
||||
- mame
|
||||
- mame2000
|
||||
- mame2003
|
||||
- mame2003_plus
|
||||
- mame2010
|
||||
- mame2015
|
||||
- mame2016
|
||||
- mesen
|
||||
- mess
|
||||
- mess2016
|
||||
- mgba
|
||||
- mrboom
|
||||
- mupen64plus
|
||||
- mupen64plus_next
|
||||
- neocd
|
||||
- nestopia
|
||||
- np2kai
|
||||
- nxengine
|
||||
- o2em
|
||||
- opera
|
||||
- parallel_n64
|
||||
- pcsx_rearmed
|
||||
- picodrive
|
||||
- pokemini
|
||||
- ppsspp
|
||||
- prboom
|
||||
- prosystem
|
||||
- puae
|
||||
- puae2021
|
||||
- px68k
|
||||
- quasi88
|
||||
- quicknes
|
||||
- retro8
|
||||
- scummvm
|
||||
- smsplus_gx
|
||||
- snes9x
|
||||
- snes9x2002
|
||||
- snes9x2005
|
||||
- snes9x2010
|
||||
- stella
|
||||
- stella2014
|
||||
- superflappybirds
|
||||
- tgbdual
|
||||
- theodore
|
||||
- tic80
|
||||
- tyrquake
|
||||
- vba_next
|
||||
- vecx
|
||||
- vice
|
||||
- virtualjaguar
|
||||
- x1
|
||||
- xrick
|
||||
- yabause
|
||||
x86:
|
||||
architecture: x86
|
||||
cores:
|
||||
- '81'
|
||||
- atari800
|
||||
- beetle_lynx
|
||||
- beetle_ngp
|
||||
- beetle_pce
|
||||
- beetle_pce_fast
|
||||
- beetle_pcfx
|
||||
- beetle_psx
|
||||
- beetle_saturn
|
||||
- beetle_supergrafx
|
||||
- beetle_vb
|
||||
- beetle_wswan
|
||||
- bennugd
|
||||
- bluemsx
|
||||
- bsnes
|
||||
- caprice32
|
||||
- desmume
|
||||
- desmume2015
|
||||
- dinothawr
|
||||
- dirksimple
|
||||
- dosbox
|
||||
- dosbox_pure
|
||||
- ep128emu
|
||||
- fbalpha2012
|
||||
- fbneo
|
||||
- fceumm
|
||||
- flycast
|
||||
- flycast_dev
|
||||
- fmsx
|
||||
- freechaf
|
||||
- freeintv
|
||||
- fuse
|
||||
- gambatte
|
||||
- geargrafx
|
||||
- gearsystem
|
||||
- genesis_plus_gx
|
||||
- gpsp
|
||||
- gw
|
||||
- handy
|
||||
- hatari
|
||||
- kronos
|
||||
- mame
|
||||
- mame2000
|
||||
- mame2003
|
||||
- mame2003_plus
|
||||
- mame2010
|
||||
- mame2015
|
||||
- mame2016
|
||||
- mesen
|
||||
- mess
|
||||
- mess2016
|
||||
- mgba
|
||||
- mrboom
|
||||
- mupen64plus
|
||||
- mupen64plus_next
|
||||
- neocd
|
||||
- nestopia
|
||||
- np2kai
|
||||
- nxengine
|
||||
- o2em
|
||||
- opera
|
||||
- parallel_n64
|
||||
- pcsx_rearmed
|
||||
- picodrive
|
||||
- pokemini
|
||||
- ppsspp
|
||||
- prboom
|
||||
- prosystem
|
||||
- puae
|
||||
- puae2021
|
||||
- px68k
|
||||
- quasi88
|
||||
- quicknes
|
||||
- retro8
|
||||
- scummvm
|
||||
- smsplus_gx
|
||||
- snes9x
|
||||
- snes9x2005
|
||||
- snes9x2010
|
||||
- stella
|
||||
- stella2014
|
||||
- superflappybirds
|
||||
- tgbdual
|
||||
- theodore
|
||||
- tic80
|
||||
- tyrquake
|
||||
- vba_next
|
||||
- vecx
|
||||
- vice
|
||||
- virtualjaguar
|
||||
- x1
|
||||
- xrick
|
||||
- yabause
|
||||
x86_64:
|
||||
architecture: x86_64
|
||||
cores:
|
||||
- '81'
|
||||
- atari800
|
||||
- beetle_lynx
|
||||
- beetle_ngp
|
||||
- beetle_pce
|
||||
- beetle_pce_fast
|
||||
- beetle_pcfx
|
||||
- beetle_psx
|
||||
- beetle_saturn
|
||||
- beetle_supergrafx
|
||||
- beetle_vb
|
||||
- beetle_wswan
|
||||
- bennugd
|
||||
- bluemsx
|
||||
- bsnes
|
||||
- caprice32
|
||||
- desmume
|
||||
- desmume2015
|
||||
- dinothawr
|
||||
- dirksimple
|
||||
- dosbox
|
||||
- dosbox_pure
|
||||
- ep128emu
|
||||
- fbalpha2012
|
||||
- fbneo
|
||||
- fceumm
|
||||
- flycast
|
||||
- flycast_dev
|
||||
- fmsx
|
||||
- freechaf
|
||||
- freeintv
|
||||
- fuse
|
||||
- gambatte
|
||||
- geargrafx
|
||||
- gearsystem
|
||||
- genesis_plus_gx
|
||||
- gpsp
|
||||
- gw
|
||||
- handy
|
||||
- hatari
|
||||
- kronos
|
||||
- mame
|
||||
- mame2000
|
||||
- mame2003
|
||||
- mame2003_plus
|
||||
- mame2010
|
||||
- mame2015
|
||||
- mame2016
|
||||
- mesen
|
||||
- mess
|
||||
- mess2016
|
||||
- mgba
|
||||
- mrboom
|
||||
- mupen64plus
|
||||
- mupen64plus_next
|
||||
- neocd
|
||||
- nestopia
|
||||
- np2kai
|
||||
- nxengine
|
||||
- o2em
|
||||
- opera
|
||||
- parallel_n64
|
||||
- pcsx_rearmed
|
||||
- picodrive
|
||||
- pokemini
|
||||
- ppsspp
|
||||
- prboom
|
||||
- prosystem
|
||||
- puae
|
||||
- puae2021
|
||||
- px68k
|
||||
- quasi88
|
||||
- quicknes
|
||||
- retro8
|
||||
- scummvm
|
||||
- smsplus_gx
|
||||
- snes9x
|
||||
- snes9x2005
|
||||
- snes9x2010
|
||||
- stella
|
||||
- stella2014
|
||||
- superflappybirds
|
||||
- tgbdual
|
||||
- theodore
|
||||
- tic80
|
||||
- tyrquake
|
||||
- vba_next
|
||||
- vecx
|
||||
- vice
|
||||
- virtualjaguar
|
||||
- x1
|
||||
- xrick
|
||||
- yabause
|
||||
7
platforms/targets/romm.yml
Normal file
7
platforms/targets/romm.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
platform: romm
|
||||
source: static
|
||||
scraped_at: "2026-03-26T00:00:00Z"
|
||||
targets:
|
||||
browser:
|
||||
architecture: wasm
|
||||
cores: []
|
||||
@@ -192,6 +192,100 @@ def list_registered_platforms(
|
||||
return platforms
|
||||
|
||||
|
||||
def load_target_config(
|
||||
platform_name: str,
|
||||
target: str,
|
||||
platforms_dir: str = "platforms",
|
||||
) -> set[str]:
|
||||
"""Load target config and return the set of core names for the given target.
|
||||
|
||||
Resolves aliases from _overrides.yml, applies add_cores/remove_cores.
|
||||
Raises ValueError if target is unknown (with list of available targets).
|
||||
Raises FileNotFoundError if no target file exists for the platform.
|
||||
"""
|
||||
targets_dir = os.path.join(platforms_dir, "targets")
|
||||
target_file = os.path.join(targets_dir, f"{platform_name}.yml")
|
||||
if not os.path.exists(target_file):
|
||||
raise FileNotFoundError(
|
||||
f"No target config for platform '{platform_name}': {target_file}"
|
||||
)
|
||||
with open(target_file) as f:
|
||||
data = yaml.safe_load(f) or {}
|
||||
|
||||
targets = data.get("targets", {})
|
||||
|
||||
overrides_file = os.path.join(targets_dir, "_overrides.yml")
|
||||
overrides = {}
|
||||
if os.path.exists(overrides_file):
|
||||
with open(overrides_file) as f:
|
||||
all_overrides = yaml.safe_load(f) or {}
|
||||
overrides = all_overrides.get(platform_name, {}).get("targets", {})
|
||||
|
||||
alias_index: dict[str, str] = {}
|
||||
for tname in targets:
|
||||
alias_index[tname] = tname
|
||||
for alias in overrides.get(tname, {}).get("aliases", []):
|
||||
alias_index[alias] = tname
|
||||
|
||||
canonical = alias_index.get(target)
|
||||
if canonical is None:
|
||||
available = sorted(targets.keys())
|
||||
aliases = []
|
||||
for tname, ovr in overrides.items():
|
||||
for a in ovr.get("aliases", []):
|
||||
aliases.append(f"{a} -> {tname}")
|
||||
msg = f"Unknown target '{target}' for platform '{platform_name}'.\n"
|
||||
msg += f"Available targets: {', '.join(available)}"
|
||||
if aliases:
|
||||
msg += f"\nAliases: {', '.join(sorted(aliases))}"
|
||||
raise ValueError(msg)
|
||||
|
||||
cores = set(str(c) for c in targets[canonical].get("cores", []))
|
||||
|
||||
ovr = overrides.get(canonical, {})
|
||||
for c in ovr.get("add_cores", []):
|
||||
cores.add(str(c))
|
||||
for c in ovr.get("remove_cores", []):
|
||||
cores.discard(str(c))
|
||||
|
||||
return cores
|
||||
|
||||
|
||||
def list_available_targets(
|
||||
platform_name: str,
|
||||
platforms_dir: str = "platforms",
|
||||
) -> list[dict]:
|
||||
"""List available targets for a platform with their aliases.
|
||||
|
||||
Returns list of dicts with keys: name, architecture, core_count, aliases.
|
||||
Returns empty list if no target file exists.
|
||||
"""
|
||||
targets_dir = os.path.join(platforms_dir, "targets")
|
||||
target_file = os.path.join(targets_dir, f"{platform_name}.yml")
|
||||
if not os.path.exists(target_file):
|
||||
return []
|
||||
with open(target_file) as f:
|
||||
data = yaml.safe_load(f) or {}
|
||||
|
||||
overrides_file = os.path.join(targets_dir, "_overrides.yml")
|
||||
overrides = {}
|
||||
if os.path.exists(overrides_file):
|
||||
with open(overrides_file) as f:
|
||||
all_overrides = yaml.safe_load(f) or {}
|
||||
overrides = all_overrides.get(platform_name, {}).get("targets", {})
|
||||
|
||||
result = []
|
||||
for tname, tdata in sorted(data.get("targets", {}).items()):
|
||||
aliases = overrides.get(tname, {}).get("aliases", [])
|
||||
result.append({
|
||||
"name": tname,
|
||||
"architecture": tdata.get("architecture", ""),
|
||||
"core_count": len(tdata.get("cores", [])),
|
||||
"aliases": aliases,
|
||||
})
|
||||
return result
|
||||
|
||||
|
||||
def resolve_local_file(
|
||||
file_entry: dict,
|
||||
db: dict,
|
||||
@@ -439,6 +533,7 @@ def load_emulator_profiles(
|
||||
|
||||
def group_identical_platforms(
|
||||
platforms: list[str], platforms_dir: str,
|
||||
target_cores_cache: dict[str, set[str] | None] | None = None,
|
||||
) -> list[tuple[list[str], str]]:
|
||||
"""Group platforms that produce identical packs (same files + base_destination).
|
||||
|
||||
@@ -473,6 +568,11 @@ def group_identical_platforms(
|
||||
entries.append(f"{full_dest}|{sha1}|{md5}")
|
||||
|
||||
fp = hashlib.sha1("|".join(sorted(entries)).encode()).hexdigest()
|
||||
if target_cores_cache:
|
||||
tc = target_cores_cache.get(platform)
|
||||
if tc is not None:
|
||||
tc_str = "|".join(sorted(tc))
|
||||
fp = hashlib.sha1(f"{fp}|{tc_str}".encode()).hexdigest()
|
||||
fingerprints.setdefault(fp, []).append(platform)
|
||||
# Prefer the root platform (no inherits) as representative
|
||||
if fp not in representatives or (not inherits[platform] and inherits.get(representatives[fp], False)):
|
||||
@@ -488,29 +588,28 @@ def group_identical_platforms(
|
||||
|
||||
def resolve_platform_cores(
|
||||
config: dict, profiles: dict[str, dict],
|
||||
target_cores: set[str] | None = None,
|
||||
) -> set[str]:
|
||||
"""Resolve which emulator profiles are relevant for a platform.
|
||||
|
||||
Resolution strategies (by priority):
|
||||
1. cores: "all_libretro" — all profiles with libretro in type
|
||||
2. cores: [list] — profiles whose dict key matches a core name
|
||||
3. cores: absent — fallback to systems intersection
|
||||
1. cores: "all_libretro" -- all profiles with libretro in type
|
||||
2. cores: [list] -- profiles whose dict key matches a core name
|
||||
3. cores: absent -- fallback to systems intersection
|
||||
|
||||
Alias profiles are always excluded (they point to another profile).
|
||||
If target_cores is provided, result is intersected with it.
|
||||
"""
|
||||
cores_config = config.get("cores")
|
||||
|
||||
if cores_config == "all_libretro":
|
||||
return {
|
||||
result = {
|
||||
name for name, p in profiles.items()
|
||||
if "libretro" in p.get("type", "")
|
||||
and p.get("type") != "alias"
|
||||
}
|
||||
|
||||
if isinstance(cores_config, list):
|
||||
elif isinstance(cores_config, list):
|
||||
core_set = {str(c) for c in cores_config}
|
||||
# Build reverse index: platform core name -> profile name
|
||||
# Uses profile filename (dict key) + all names in cores: field
|
||||
core_to_profile: dict[str, str] = {}
|
||||
for name, p in profiles.items():
|
||||
if p.get("type") == "alias":
|
||||
@@ -518,19 +617,82 @@ def resolve_platform_cores(
|
||||
core_to_profile[name] = name
|
||||
for core_name in p.get("cores", []):
|
||||
core_to_profile[str(core_name)] = name
|
||||
return {
|
||||
result = {
|
||||
core_to_profile[c]
|
||||
for c in core_set
|
||||
if c in core_to_profile
|
||||
}
|
||||
else:
|
||||
platform_systems = set(config.get("systems", {}).keys())
|
||||
result = {
|
||||
name for name, p in profiles.items()
|
||||
if set(p.get("systems", [])) & platform_systems
|
||||
and p.get("type") != "alias"
|
||||
}
|
||||
|
||||
# Fallback: system ID intersection
|
||||
platform_systems = set(config.get("systems", {}).keys())
|
||||
return {
|
||||
name for name, p in profiles.items()
|
||||
if set(p.get("systems", [])) & platform_systems
|
||||
and p.get("type") != "alias"
|
||||
}
|
||||
if target_cores is not None:
|
||||
# Build reverse index: upstream name -> profile key
|
||||
# Upstream sources (buildbot, es_systems) may use different names
|
||||
# than our profile keys (e.g., mednafen_psx vs beetle_psx).
|
||||
# The profiles' cores: field lists these alternate names.
|
||||
upstream_to_profile: dict[str, str] = {}
|
||||
for name, p in profiles.items():
|
||||
upstream_to_profile[name] = name
|
||||
for alias in p.get("cores", []):
|
||||
upstream_to_profile[str(alias)] = name
|
||||
# Expand target_cores to profile keys
|
||||
expanded = {upstream_to_profile.get(c, c) for c in target_cores}
|
||||
result = result & expanded
|
||||
return result
|
||||
|
||||
|
||||
def filter_systems_by_target(
|
||||
systems: dict[str, dict],
|
||||
profiles: dict[str, dict],
|
||||
target_cores: set[str] | None,
|
||||
platform_cores: set[str] | None = None,
|
||||
) -> dict[str, dict]:
|
||||
"""Filter platform systems to only those reachable by target cores.
|
||||
|
||||
A system is reachable if at least one core that emulates it is available
|
||||
on the target. Only considers cores relevant to the platform (from
|
||||
platform_cores). Systems whose cores are all outside the platform's
|
||||
scope are kept (no information to exclude them).
|
||||
|
||||
Returns the filtered systems dict (or all if no target).
|
||||
"""
|
||||
if target_cores is None:
|
||||
return systems
|
||||
|
||||
# Build reverse index for target core name resolution
|
||||
upstream_to_profile: dict[str, str] = {}
|
||||
for name, p in profiles.items():
|
||||
upstream_to_profile[name] = name
|
||||
for alias in p.get("cores", []):
|
||||
upstream_to_profile[str(alias)] = name
|
||||
expanded_target = {upstream_to_profile.get(c, c) for c in target_cores}
|
||||
|
||||
# Build system -> profile keys mapping (only platform-relevant cores)
|
||||
system_to_cores: dict[str, set[str]] = {}
|
||||
for name, p in profiles.items():
|
||||
if p.get("type") == "alias":
|
||||
continue
|
||||
if platform_cores is not None and name not in platform_cores:
|
||||
continue
|
||||
for sid in p.get("systems", []):
|
||||
system_to_cores.setdefault(sid, set()).add(name)
|
||||
|
||||
filtered = {}
|
||||
for sys_id, sys_data in systems.items():
|
||||
cores_for_system = system_to_cores.get(sys_id, set())
|
||||
if not cores_for_system:
|
||||
# No platform-relevant core maps to this system — keep it
|
||||
filtered[sys_id] = sys_data
|
||||
elif cores_for_system & expanded_target:
|
||||
# At least one core for this system is on the target
|
||||
filtered[sys_id] = sys_data
|
||||
# else: all platform cores for this system are off-target — exclude
|
||||
return filtered
|
||||
|
||||
|
||||
def _parse_validation(validation: list | dict | None) -> list[str]:
|
||||
|
||||
@@ -181,6 +181,8 @@ def main():
|
||||
parser.add_argument("--platforms-dir", default=DEFAULT_PLATFORMS_DIR)
|
||||
parser.add_argument("--db", default=DEFAULT_DB)
|
||||
parser.add_argument("--emulator", "-e", help="Analyze single emulator")
|
||||
parser.add_argument("--platform", "-p", help="Platform name (required for --target)")
|
||||
parser.add_argument("--target", "-t", help="Hardware target (e.g., switch, rpi4)")
|
||||
parser.add_argument("--json", action="store_true", help="JSON output")
|
||||
args = parser.parse_args()
|
||||
|
||||
@@ -188,6 +190,15 @@ def main():
|
||||
if args.emulator:
|
||||
profiles = {k: v for k, v in profiles.items() if k == args.emulator}
|
||||
|
||||
if args.target:
|
||||
if not args.platform:
|
||||
parser.error("--target requires --platform")
|
||||
from common import load_target_config, resolve_platform_cores
|
||||
target_cores = load_target_config(args.platform, args.target, args.platforms_dir)
|
||||
config = load_platform_config(args.platform, args.platforms_dir)
|
||||
relevant = resolve_platform_cores(config, profiles, target_cores=target_cores)
|
||||
profiles = {k: v for k, v in profiles.items() if k in relevant}
|
||||
|
||||
if not profiles:
|
||||
print("No emulator profiles found.", file=sys.stderr)
|
||||
return
|
||||
|
||||
@@ -28,9 +28,9 @@ from common import (
|
||||
_build_validation_index, build_zip_contents_index, check_file_validation,
|
||||
check_inside_zip, compute_hashes, fetch_large_file, filter_files_by_mode,
|
||||
group_identical_platforms, list_emulator_profiles, list_registered_platforms,
|
||||
list_system_ids, load_database, load_data_dir_registry,
|
||||
load_emulator_profiles, load_platform_config, md5_composite,
|
||||
resolve_local_file,
|
||||
filter_systems_by_target, list_system_ids, load_database,
|
||||
load_data_dir_registry, load_emulator_profiles, load_platform_config,
|
||||
md5_composite, resolve_local_file,
|
||||
)
|
||||
from deterministic_zip import rebuild_zip_deterministic
|
||||
|
||||
@@ -185,6 +185,7 @@ def _collect_emulator_extras(
|
||||
seen: set,
|
||||
base_dest: str,
|
||||
emu_profiles: dict | None = None,
|
||||
target_cores: set[str] | None = None,
|
||||
) -> list[dict]:
|
||||
"""Collect core requirement files from emulator profiles not in the platform pack.
|
||||
|
||||
@@ -198,7 +199,7 @@ def _collect_emulator_extras(
|
||||
"""
|
||||
from verify import find_undeclared_files
|
||||
|
||||
undeclared = find_undeclared_files(config, emulators_dir, db, emu_profiles)
|
||||
undeclared = find_undeclared_files(config, emulators_dir, db, emu_profiles, target_cores=target_cores)
|
||||
extras = []
|
||||
for u in undeclared:
|
||||
if not u["in_repo"]:
|
||||
@@ -229,6 +230,7 @@ def generate_pack(
|
||||
zip_contents: dict | None = None,
|
||||
data_registry: dict | None = None,
|
||||
emu_profiles: dict | None = None,
|
||||
target_cores: set[str] | None = None,
|
||||
) -> str | None:
|
||||
"""Generate a ZIP pack for a platform.
|
||||
|
||||
@@ -262,8 +264,18 @@ def generate_pack(
|
||||
if emu_profiles:
|
||||
validation_index = _build_validation_index(emu_profiles)
|
||||
|
||||
# Filter systems by target if specified
|
||||
from common import resolve_platform_cores
|
||||
plat_cores = resolve_platform_cores(config, emu_profiles or {}) if target_cores else None
|
||||
pack_systems = filter_systems_by_target(
|
||||
config.get("systems", {}),
|
||||
emu_profiles or {},
|
||||
target_cores,
|
||||
platform_cores=plat_cores,
|
||||
)
|
||||
|
||||
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
|
||||
for sys_id, system in sorted(config.get("systems", {}).items()):
|
||||
for sys_id, system in sorted(pack_systems.items()):
|
||||
for file_entry in system.get("files", []):
|
||||
dest = _sanitize_path(file_entry.get("destination", file_entry["name"]))
|
||||
if not dest:
|
||||
@@ -405,7 +417,7 @@ def generate_pack(
|
||||
emu_profiles = load_emulator_profiles(emulators_dir)
|
||||
core_files = _collect_emulator_extras(
|
||||
config, emulators_dir, db,
|
||||
seen_destinations, base_dest, emu_profiles,
|
||||
seen_destinations, base_dest, emu_profiles, target_cores=target_cores,
|
||||
)
|
||||
core_count = 0
|
||||
for fe in core_files:
|
||||
@@ -442,7 +454,7 @@ def generate_pack(
|
||||
total_files += 1
|
||||
|
||||
# Data directories from _data_dirs.yml
|
||||
for sys_id, system in sorted(config.get("systems", {}).items()):
|
||||
for sys_id, system in sorted(pack_systems.items()):
|
||||
for dd in system.get("data_directories", []):
|
||||
ref_key = dd.get("ref", "")
|
||||
if not ref_key or not data_registry or ref_key not in data_registry:
|
||||
@@ -847,6 +859,8 @@ def main():
|
||||
parser.add_argument("--refresh-data", action="store_true",
|
||||
help="Force re-download all data directories")
|
||||
parser.add_argument("--list", action="store_true", help="List available platforms")
|
||||
parser.add_argument("--target", "-t", help="Hardware target (e.g., switch, rpi4)")
|
||||
parser.add_argument("--list-targets", action="store_true", help="List available targets for the platform")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.list:
|
||||
@@ -860,6 +874,18 @@ def main():
|
||||
if args.list_systems:
|
||||
list_system_ids(args.emulators_dir)
|
||||
return
|
||||
if args.list_targets:
|
||||
if not args.platform:
|
||||
parser.error("--list-targets requires --platform")
|
||||
from common import list_available_targets
|
||||
targets = list_available_targets(args.platform, args.platforms_dir)
|
||||
if not targets:
|
||||
print(f"No targets configured for platform '{args.platform}'")
|
||||
return
|
||||
for t in targets:
|
||||
aliases = f" (aliases: {', '.join(t['aliases'])})" if t['aliases'] else ""
|
||||
print(f" {t['name']:30s} {t['architecture']:10s} {t['core_count']:>4d} cores{aliases}")
|
||||
return
|
||||
|
||||
# Mutual exclusion
|
||||
modes = sum(1 for x in (args.platform, args.all, args.emulator, args.system) if x)
|
||||
@@ -869,6 +895,10 @@ def main():
|
||||
parser.error("--platform, --all, --emulator, and --system are mutually exclusive")
|
||||
if args.standalone and not (args.emulator or args.system):
|
||||
parser.error("--standalone requires --emulator or --system")
|
||||
if args.target and not (args.platform or args.all):
|
||||
parser.error("--target requires --platform or --all")
|
||||
if args.target and (args.emulator or args.system):
|
||||
parser.error("--target is incompatible with --emulator and --system")
|
||||
|
||||
db = load_database(args.db)
|
||||
zip_contents = build_zip_contents_index(db)
|
||||
@@ -916,7 +946,31 @@ def main():
|
||||
print(f"Refreshed {updated} data director{'ies' if updated > 1 else 'y'}")
|
||||
|
||||
emu_profiles = load_emulator_profiles(args.emulators_dir)
|
||||
groups = group_identical_platforms(platforms, args.platforms_dir)
|
||||
|
||||
target_cores_cache: dict[str, set[str] | None] = {}
|
||||
if args.target:
|
||||
from common import load_target_config
|
||||
skip = []
|
||||
for p in platforms:
|
||||
try:
|
||||
target_cores_cache[p] = load_target_config(p, args.target, args.platforms_dir)
|
||||
except FileNotFoundError:
|
||||
if args.all:
|
||||
target_cores_cache[p] = None
|
||||
else:
|
||||
print(f"ERROR: No target config for platform '{p}'", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
except ValueError as e:
|
||||
if args.all:
|
||||
print(f"INFO: Skipping {p}: {e}")
|
||||
skip.append(p)
|
||||
else:
|
||||
print(f"ERROR: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
platforms = [p for p in platforms if p not in skip]
|
||||
|
||||
groups = group_identical_platforms(platforms, args.platforms_dir,
|
||||
target_cores_cache if args.target else None)
|
||||
|
||||
for group_platforms, representative in groups:
|
||||
variants = [p for p in group_platforms if p != representative]
|
||||
@@ -928,11 +982,12 @@ def main():
|
||||
print(f"\nGenerating pack for {representative}...")
|
||||
|
||||
try:
|
||||
tc = target_cores_cache.get(representative) if args.target else None
|
||||
zip_path = generate_pack(
|
||||
representative, args.platforms_dir, db, args.bios_dir, args.output_dir,
|
||||
include_extras=args.include_extras, emulators_dir=args.emulators_dir,
|
||||
zip_contents=zip_contents, data_registry=data_registry,
|
||||
emu_profiles=emu_profiles,
|
||||
emu_profiles=emu_profiles, target_cores=tc,
|
||||
)
|
||||
if zip_path and variants:
|
||||
rep_cfg = load_platform_config(representative, args.platforms_dir)
|
||||
|
||||
@@ -140,6 +140,7 @@ def main():
|
||||
# --include-extras is now a no-op: core requirements are always included
|
||||
parser.add_argument("--include-extras", action="store_true",
|
||||
help="(no-op) Core requirements are always included")
|
||||
parser.add_argument("--target", "-t", help="Hardware target (e.g., switch, rpi4)")
|
||||
args = parser.parse_args()
|
||||
|
||||
results = {}
|
||||
@@ -172,6 +173,8 @@ def main():
|
||||
verify_cmd = [sys.executable, "scripts/verify.py", "--all"]
|
||||
if args.include_archived:
|
||||
verify_cmd.append("--include-archived")
|
||||
if args.target:
|
||||
verify_cmd.extend(["--target", args.target])
|
||||
ok, verify_output = run(verify_cmd, "3/7 verify all platforms")
|
||||
results["verify"] = ok
|
||||
all_ok = all_ok and ok
|
||||
@@ -189,6 +192,8 @@ def main():
|
||||
pack_cmd.append("--offline")
|
||||
if args.include_extras:
|
||||
pack_cmd.append("--include-extras")
|
||||
if args.target:
|
||||
pack_cmd.extend(["--target", args.target])
|
||||
ok, pack_output = run(pack_cmd, "4/7 generate packs")
|
||||
results["generate_packs"] = ok
|
||||
all_ok = all_ok and ok
|
||||
|
||||
48
scripts/scraper/targets/__init__.py
Normal file
48
scripts/scraper/targets/__init__.py
Normal file
@@ -0,0 +1,48 @@
|
||||
"""Target scraper plugin discovery module.
|
||||
|
||||
Auto-detects *_targets_scraper.py files and exposes their scrapers.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
import pkgutil
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class BaseTargetScraper:
|
||||
"""Base class for target scrapers."""
|
||||
|
||||
def __init__(self, url: str = ""):
|
||||
self.url = url
|
||||
|
||||
def fetch_targets(self) -> dict:
|
||||
"""Fetch targets and their core lists. Returns dict matching target YAML format."""
|
||||
raise NotImplementedError
|
||||
|
||||
def write_output(self, data: dict, output_path: str) -> None:
|
||||
"""Write target data to YAML file."""
|
||||
try:
|
||||
import yaml
|
||||
except ImportError:
|
||||
raise ImportError("PyYAML required: pip install pyyaml")
|
||||
with open(output_path, "w") as f:
|
||||
yaml.dump(data, f, default_flow_style=False, sort_keys=False)
|
||||
|
||||
|
||||
_scrapers: dict[str, type] = {}
|
||||
|
||||
|
||||
def discover_target_scrapers() -> dict[str, type]:
|
||||
"""Auto-discover all *_targets_scraper.py modules."""
|
||||
if _scrapers:
|
||||
return _scrapers
|
||||
package_dir = Path(__file__).parent
|
||||
for finder, name, ispkg in pkgutil.iter_modules([str(package_dir)]):
|
||||
if not name.endswith("_targets_scraper"):
|
||||
continue
|
||||
module = importlib.import_module(f".{name}", package=__package__)
|
||||
platform_name = getattr(module, "PLATFORM_NAME", None)
|
||||
scraper_class = getattr(module, "Scraper", None)
|
||||
if platform_name and scraper_class:
|
||||
_scrapers[platform_name] = scraper_class
|
||||
return _scrapers
|
||||
370
scripts/scraper/targets/batocera_targets_scraper.py
Normal file
370
scripts/scraper/targets/batocera_targets_scraper.py
Normal file
@@ -0,0 +1,370 @@
|
||||
"""Scraper for Batocera per-board emulator availability.
|
||||
|
||||
Sources (batocera-linux/batocera.linux):
|
||||
- configs/batocera-*.board -- board definitions, each sets BR2_PACKAGE_BATOCERA_TARGET_*
|
||||
- package/batocera/core/batocera-system/Config.in -- select PACKAGE if CONDITION lines
|
||||
- package/batocera/emulationstation/batocera-es-system/es_systems.yml
|
||||
-- emulator requireAnyOf flag mapping
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
from datetime import datetime, timezone
|
||||
|
||||
import yaml
|
||||
|
||||
from . import BaseTargetScraper
|
||||
|
||||
PLATFORM_NAME = "batocera"
|
||||
|
||||
GITHUB_API = "https://api.github.com/repos/batocera-linux/batocera.linux/contents"
|
||||
RAW_BASE = "https://raw.githubusercontent.com/batocera-linux/batocera.linux/master"
|
||||
|
||||
CONFIG_IN_URL = f"{RAW_BASE}/package/batocera/core/batocera-system/Config.in"
|
||||
ES_SYSTEMS_URL = (
|
||||
f"{RAW_BASE}/package/batocera/emulationstation/batocera-es-system/es_systems.yml"
|
||||
)
|
||||
|
||||
_HEADERS = {
|
||||
"User-Agent": "retrobios-scraper/1.0",
|
||||
"Accept": "application/vnd.github.v3+json",
|
||||
}
|
||||
|
||||
_TARGET_FLAG_RE = re.compile(r'^(BR2_PACKAGE_BATOCERA_TARGET_\w+)=y', re.MULTILINE)
|
||||
|
||||
# Matches: select BR2_PACKAGE_FOO (optional: if CONDITION)
|
||||
# Condition may span multiple lines (backslash continuation)
|
||||
_SELECT_RE = re.compile(
|
||||
r'^\s+select\s+(BR2_PACKAGE_\w+)' # package being selected
|
||||
r'(?:\s+if\s+((?:[^\n]|\\\n)+?))?' # optional "if CONDITION" (may continue with \)
|
||||
r'(?:\s*#[^\n]*)?$', # optional trailing comment
|
||||
re.MULTILINE,
|
||||
)
|
||||
|
||||
# Meta-flag definition: "if COND\n\tconfig DERIVED_FLAG\n\t...\nendif"
|
||||
_META_BLOCK_RE = re.compile(
|
||||
r'^if\s+((?:[^\n]|\\\n)+?)\n' # condition (may span lines via \)
|
||||
r'(?:.*?\n)*?' # optional lines before the config
|
||||
r'\s+config\s+(BR2_PACKAGE_\w+)' # derived flag name
|
||||
r'.*?^endif', # end of block
|
||||
re.MULTILINE | re.DOTALL,
|
||||
)
|
||||
|
||||
|
||||
def _fetch(url: str, headers: dict | None = None) -> str | None:
|
||||
h = headers or {"User-Agent": "retrobios-scraper/1.0"}
|
||||
try:
|
||||
req = urllib.request.Request(url, headers=h)
|
||||
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||
return resp.read().decode("utf-8")
|
||||
except urllib.error.URLError as e:
|
||||
print(f" skip {url}: {e}", file=sys.stderr)
|
||||
return None
|
||||
|
||||
|
||||
def _fetch_json(url: str) -> list | dict | None:
|
||||
text = _fetch(url, headers=_HEADERS)
|
||||
if text is None:
|
||||
return None
|
||||
try:
|
||||
return json.loads(text)
|
||||
except json.JSONDecodeError as e:
|
||||
print(f" json parse error {url}: {e}", file=sys.stderr)
|
||||
return None
|
||||
|
||||
|
||||
def _normalise_condition(raw: str) -> str:
|
||||
"""Strip backslash-continuations and collapse whitespace."""
|
||||
return re.sub(r'\\\n\s*', ' ', raw).strip()
|
||||
|
||||
|
||||
def _tokenise(condition: str) -> list[str]:
|
||||
"""Split a Kconfig condition into tokens: flags, !, &&, ||, (, )."""
|
||||
token_re = re.compile(r'&&|\|\||!|\(|\)|BR2_\w+|"[^"]*"')
|
||||
return token_re.findall(condition)
|
||||
|
||||
|
||||
def _check_condition(tokens: list[str], pos: int, active: frozenset[str]) -> tuple[bool, int]:
|
||||
"""Recursive descent check of a Kconfig boolean expression."""
|
||||
return _check_or(tokens, pos, active)
|
||||
|
||||
|
||||
def _check_or(tokens: list[str], pos: int, active: frozenset[str]) -> tuple[bool, int]:
|
||||
left, pos = _check_and(tokens, pos, active)
|
||||
while pos < len(tokens) and tokens[pos] == '||':
|
||||
pos += 1
|
||||
right, pos = _check_and(tokens, pos, active)
|
||||
left = left or right
|
||||
return left, pos
|
||||
|
||||
|
||||
def _check_and(tokens: list[str], pos: int, active: frozenset[str]) -> tuple[bool, int]:
|
||||
left, pos = _check_not(tokens, pos, active)
|
||||
while pos < len(tokens) and tokens[pos] == '&&':
|
||||
pos += 1
|
||||
right, pos = _check_not(tokens, pos, active)
|
||||
left = left and right
|
||||
return left, pos
|
||||
|
||||
|
||||
def _check_not(tokens: list[str], pos: int, active: frozenset[str]) -> tuple[bool, int]:
|
||||
if pos < len(tokens) and tokens[pos] == '!':
|
||||
pos += 1
|
||||
val, pos = _check_atom(tokens, pos, active)
|
||||
return not val, pos
|
||||
return _check_atom(tokens, pos, active)
|
||||
|
||||
|
||||
def _check_atom(tokens: list[str], pos: int, active: frozenset[str]) -> tuple[bool, int]:
|
||||
if pos >= len(tokens):
|
||||
return True, pos
|
||||
tok = tokens[pos]
|
||||
if tok == '(':
|
||||
pos += 1
|
||||
val, pos = _check_or(tokens, pos, active)
|
||||
if pos < len(tokens) and tokens[pos] == ')':
|
||||
pos += 1
|
||||
return val, pos
|
||||
if tok.startswith('BR2_'):
|
||||
pos += 1
|
||||
return tok in active, pos
|
||||
if tok.startswith('"'):
|
||||
pos += 1
|
||||
return True, pos
|
||||
# Unknown token — treat as true to avoid false negatives
|
||||
pos += 1
|
||||
return True, pos
|
||||
|
||||
|
||||
def _condition_holds(condition: str, active: frozenset[str]) -> bool:
|
||||
"""Return True if a Kconfig boolean condition holds for the given active flags."""
|
||||
if not condition:
|
||||
return True
|
||||
norm = _normalise_condition(condition)
|
||||
tokens = _tokenise(norm)
|
||||
if not tokens:
|
||||
return True
|
||||
try:
|
||||
result, _ = _check_condition(tokens, 0, active)
|
||||
return result
|
||||
except Exception:
|
||||
return True # conservative: include on parse failure
|
||||
|
||||
|
||||
def _parse_meta_flags(text: str) -> list[tuple[str, str]]:
|
||||
"""Return [(derived_flag, condition_str)] from top-level if/endif blocks.
|
||||
|
||||
These define derived flags like BR2_PACKAGE_BATOCERA_TARGET_X86_64_ANY,
|
||||
BR2_PACKAGE_BATOCERA_GLES3, etc.
|
||||
"""
|
||||
results: list[tuple[str, str]] = []
|
||||
for m in _META_BLOCK_RE.finditer(text):
|
||||
cond = _normalise_condition(m.group(1))
|
||||
flag = m.group(2)
|
||||
results.append((flag, cond))
|
||||
return results
|
||||
|
||||
|
||||
def _expand_flags(primary_flag: str, meta_rules: list[tuple[str, str]]) -> frozenset[str]:
|
||||
"""Given a board's primary flag, expand to all active derived flags.
|
||||
|
||||
Iterates until stable (handles chained derivations like X86_64_ANY -> X86_ANY).
|
||||
"""
|
||||
active: set[str] = {primary_flag}
|
||||
changed = True
|
||||
while changed:
|
||||
changed = False
|
||||
for derived, cond in meta_rules:
|
||||
if derived not in active and _condition_holds(cond, frozenset(active)):
|
||||
active.add(derived)
|
||||
changed = True
|
||||
return frozenset(active)
|
||||
|
||||
|
||||
def _parse_selects(text: str) -> list[tuple[str, str]]:
|
||||
"""Parse all 'select PACKAGE [if CONDITION]' lines from Config.in.
|
||||
|
||||
Returns [(package, condition)] where condition is '' if unconditional.
|
||||
"""
|
||||
results: list[tuple[str, str]] = []
|
||||
for m in _SELECT_RE.finditer(text):
|
||||
pkg = m.group(1)
|
||||
cond = _normalise_condition(m.group(2) or '')
|
||||
results.append((pkg, cond))
|
||||
return results
|
||||
|
||||
|
||||
def _parse_es_systems(text: str) -> dict[str, list[str]]:
|
||||
"""Parse es_systems.yml: map BR2_PACKAGE_* flag -> list of emulator names.
|
||||
|
||||
The file is a dict keyed by system name. Each system has:
|
||||
emulators:
|
||||
<emulator_group>:
|
||||
<core_name>: {requireAnyOf: [BR2_PACKAGE_FOO]}
|
||||
"""
|
||||
try:
|
||||
data = yaml.safe_load(text)
|
||||
except yaml.YAMLError:
|
||||
return {}
|
||||
|
||||
if not isinstance(data, dict):
|
||||
return {}
|
||||
|
||||
package_to_emulators: dict[str, list[str]] = {}
|
||||
|
||||
for _system_name, system_data in data.items():
|
||||
if not isinstance(system_data, dict):
|
||||
continue
|
||||
emulators = system_data.get("emulators")
|
||||
if not isinstance(emulators, dict):
|
||||
continue
|
||||
for _group_name, group_data in emulators.items():
|
||||
if not isinstance(group_data, dict):
|
||||
continue
|
||||
for core_name, core_data in group_data.items():
|
||||
if not isinstance(core_data, dict):
|
||||
continue
|
||||
require = core_data.get("requireAnyOf", [])
|
||||
if not isinstance(require, list):
|
||||
continue
|
||||
for pkg_flag in require:
|
||||
if isinstance(pkg_flag, str):
|
||||
package_to_emulators.setdefault(pkg_flag, []).append(core_name)
|
||||
|
||||
return package_to_emulators
|
||||
|
||||
|
||||
def _arch_from_flag(flag: str) -> str:
|
||||
"""Guess architecture from board flag name."""
|
||||
low = flag.lower()
|
||||
if "x86_64" in low or "zen3" in low:
|
||||
return "x86_64"
|
||||
if "x86" in low:
|
||||
return "x86"
|
||||
return "aarch64"
|
||||
|
||||
|
||||
class Scraper(BaseTargetScraper):
|
||||
"""Cross-references Batocera boards, Config.in, and es_systems to build target lists."""
|
||||
|
||||
def __init__(self, url: str = "https://github.com/batocera-linux/batocera.linux"):
|
||||
super().__init__(url=url)
|
||||
|
||||
def _list_boards(self) -> list[str]:
|
||||
"""List batocera-*.board files from configs/ via GitHub API."""
|
||||
data = _fetch_json(f"{GITHUB_API}/configs")
|
||||
if not data or not isinstance(data, list):
|
||||
return []
|
||||
return [
|
||||
item["name"] for item in data
|
||||
if isinstance(item, dict)
|
||||
and item.get("name", "").startswith("batocera-")
|
||||
and item.get("name", "").endswith(".board")
|
||||
]
|
||||
|
||||
def _fetch_board_flag(self, board_name: str) -> str | None:
|
||||
"""Fetch a board file and extract its BR2_PACKAGE_BATOCERA_TARGET_* flag."""
|
||||
url = f"{RAW_BASE}/configs/{board_name}"
|
||||
text = _fetch(url)
|
||||
if text is None:
|
||||
return None
|
||||
m = _TARGET_FLAG_RE.search(text)
|
||||
return m.group(1) if m else None
|
||||
|
||||
def fetch_targets(self) -> dict:
|
||||
"""Build per-board emulator availability map."""
|
||||
print(" fetching board list...", file=sys.stderr)
|
||||
boards = self._list_boards()
|
||||
if not boards:
|
||||
print(" warning: no boards found", file=sys.stderr)
|
||||
|
||||
print(" fetching Config.in...", file=sys.stderr)
|
||||
config_in_text = _fetch(CONFIG_IN_URL) or ""
|
||||
|
||||
meta_rules = _parse_meta_flags(config_in_text)
|
||||
selects = _parse_selects(config_in_text)
|
||||
print(
|
||||
f" parsed {len(meta_rules)} meta-flag rules, {len(selects)} select lines",
|
||||
file=sys.stderr,
|
||||
)
|
||||
|
||||
print(" fetching es_systems.yml...", file=sys.stderr)
|
||||
es_text = _fetch(ES_SYSTEMS_URL) or ""
|
||||
package_to_emulators = _parse_es_systems(es_text)
|
||||
print(
|
||||
f" parsed {len(package_to_emulators)} package->emulator mappings",
|
||||
file=sys.stderr,
|
||||
)
|
||||
|
||||
targets: dict[str, dict] = {}
|
||||
for board_name in sorted(boards):
|
||||
target_key = board_name.removeprefix("batocera-").removesuffix(".board")
|
||||
print(f" processing {target_key}...", file=sys.stderr)
|
||||
primary_flag = self._fetch_board_flag(board_name)
|
||||
if primary_flag is None:
|
||||
print(f" no target flag found in {board_name}", file=sys.stderr)
|
||||
continue
|
||||
|
||||
active = _expand_flags(primary_flag, meta_rules)
|
||||
|
||||
# Determine which packages are selected for this board
|
||||
selected_packages: set[str] = set()
|
||||
for pkg, cond in selects:
|
||||
if _condition_holds(cond, active):
|
||||
selected_packages.add(pkg)
|
||||
|
||||
# Map selected packages to emulator names via es_systems.yml
|
||||
emulators: set[str] = set()
|
||||
for pkg in selected_packages:
|
||||
for emu in package_to_emulators.get(pkg, []):
|
||||
emulators.add(emu)
|
||||
|
||||
arch = _arch_from_flag(primary_flag)
|
||||
targets[target_key] = {
|
||||
"architecture": arch,
|
||||
"cores": sorted(emulators),
|
||||
}
|
||||
print(
|
||||
f" {len(emulators)} emulators ({len(selected_packages)} packages selected)",
|
||||
file=sys.stderr,
|
||||
)
|
||||
|
||||
return {
|
||||
"platform": "batocera",
|
||||
"source": self.url,
|
||||
"scraped_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
||||
"targets": targets,
|
||||
}
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Scrape Batocera per-board emulator targets"
|
||||
)
|
||||
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'])} emulators")
|
||||
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()
|
||||
184
scripts/scraper/targets/emudeck_targets_scraper.py
Normal file
184
scripts/scraper/targets/emudeck_targets_scraper.py
Normal file
@@ -0,0 +1,184 @@
|
||||
"""Scraper for EmuDeck emulator targets.
|
||||
|
||||
Sources:
|
||||
SteamOS: dragoonDorise/EmuDeck — functions/EmuScripts/*.sh
|
||||
Windows: EmuDeck/emudeck-we — functions/EmuScripts/*.ps1
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
from datetime import datetime, timezone
|
||||
|
||||
import yaml
|
||||
|
||||
from . import BaseTargetScraper
|
||||
|
||||
PLATFORM_NAME = "emudeck"
|
||||
|
||||
STEAMOS_API = "https://api.github.com/repos/dragoonDorise/EmuDeck/contents/functions/EmuScripts"
|
||||
WINDOWS_API = "https://api.github.com/repos/EmuDeck/emudeck-we/contents/functions/EmuScripts"
|
||||
|
||||
# Map EmuDeck script names to emulator profile keys
|
||||
# Script naming: emuDeckDolphin.sh -> dolphin
|
||||
# Some need explicit mapping when names differ
|
||||
_NAME_OVERRIDES: dict[str, str] = {
|
||||
"pcsx2qt": "pcsx2",
|
||||
"rpcs3legacy": "rpcs3",
|
||||
"cemuproton": "cemu",
|
||||
"rmg": "mupen64plus_next",
|
||||
"bigpemu": "bigpemu",
|
||||
"eden": "eden",
|
||||
"suyu": "suyu",
|
||||
"ares": "ares",
|
||||
}
|
||||
|
||||
# Scripts that are not emulators (config helpers, etc.)
|
||||
_SKIP = {"retroarch_maincfg", "retroarch"}
|
||||
|
||||
|
||||
def _fetch(url: str) -> str | None:
|
||||
try:
|
||||
req = urllib.request.Request(
|
||||
url, headers={"User-Agent": "retrobios-scraper/1.0"}
|
||||
)
|
||||
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||
return resp.read().decode("utf-8")
|
||||
except urllib.error.URLError as e:
|
||||
print(f" skip {url}: {e}", file=sys.stderr)
|
||||
return None
|
||||
|
||||
|
||||
def _list_emuscripts(api_url: str) -> list[str]:
|
||||
"""List emulator script filenames from GitHub API."""
|
||||
raw = _fetch(api_url)
|
||||
if not raw:
|
||||
return []
|
||||
entries = json.loads(raw)
|
||||
names = []
|
||||
for e in entries:
|
||||
name = e.get("name", "")
|
||||
if name.endswith(".sh") or name.endswith(".ps1"):
|
||||
names.append(name)
|
||||
return names
|
||||
|
||||
|
||||
def _script_to_core(filename: str) -> str | None:
|
||||
"""Convert EmuScripts filename to core profile key."""
|
||||
# Strip extension and emuDeck prefix
|
||||
name = re.sub(r'\.(sh|ps1)$', '', filename, flags=re.IGNORECASE)
|
||||
name = re.sub(r'^emuDeck', '', name, flags=re.IGNORECASE)
|
||||
if not name:
|
||||
return None
|
||||
key = name.lower()
|
||||
if key in _SKIP:
|
||||
return None
|
||||
return _NAME_OVERRIDES.get(key, key)
|
||||
|
||||
|
||||
class Scraper(BaseTargetScraper):
|
||||
"""Fetches emulator lists for EmuDeck SteamOS and Windows targets."""
|
||||
|
||||
def __init__(self, url: str = "https://github.com/dragoonDorise/EmuDeck"):
|
||||
super().__init__(url=url)
|
||||
|
||||
def _fetch_cores_for_target(self, api_url: str, label: str,
|
||||
arch: str = "x86_64") -> list[str]:
|
||||
print(f" fetching {label} EmuScripts...", file=sys.stderr)
|
||||
scripts = _list_emuscripts(api_url)
|
||||
cores: list[str] = []
|
||||
seen: set[str] = set()
|
||||
has_retroarch = False
|
||||
for script in scripts:
|
||||
core = _script_to_core(script)
|
||||
if core and core not in seen:
|
||||
seen.add(core)
|
||||
cores.append(core)
|
||||
# Detect RetroArch presence (provides all libretro cores)
|
||||
name = re.sub(r'\.(sh|ps1)$', '', script, flags=re.IGNORECASE)
|
||||
if name.lower() in ("emudeckretroarch", "retroarch_maincfg"):
|
||||
has_retroarch = True
|
||||
|
||||
standalone_count = len(cores)
|
||||
# EmuDeck ships RetroArch = all its libretro cores are available
|
||||
if has_retroarch:
|
||||
ra_cores = self._load_retroarch_cores(arch)
|
||||
for c in ra_cores:
|
||||
if c not in seen:
|
||||
seen.add(c)
|
||||
cores.append(c)
|
||||
|
||||
print(f" {label}: {standalone_count} standalone + "
|
||||
f"{len(cores) - standalone_count} via RetroArch = {len(cores)} total",
|
||||
file=sys.stderr)
|
||||
return sorted(cores)
|
||||
|
||||
@staticmethod
|
||||
def _load_retroarch_cores(arch: str) -> list[str]:
|
||||
"""Load RetroArch target cores for given architecture."""
|
||||
import os
|
||||
target_path = os.path.join("platforms", "targets", "retroarch.yml")
|
||||
if not os.path.exists(target_path):
|
||||
return []
|
||||
with open(target_path) as f:
|
||||
data = yaml.safe_load(f) or {}
|
||||
# Find a target matching the architecture
|
||||
for tname, tinfo in data.get("targets", {}).items():
|
||||
if tinfo.get("architecture") == arch:
|
||||
return tinfo.get("cores", [])
|
||||
return []
|
||||
|
||||
def fetch_targets(self) -> dict:
|
||||
steamos_cores = self._fetch_cores_for_target(STEAMOS_API, "SteamOS")
|
||||
windows_cores = self._fetch_cores_for_target(WINDOWS_API, "Windows")
|
||||
|
||||
targets: dict[str, dict] = {}
|
||||
if steamos_cores:
|
||||
targets["steamos"] = {
|
||||
"architecture": "x86_64",
|
||||
"cores": steamos_cores,
|
||||
}
|
||||
if windows_cores:
|
||||
targets["windows"] = {
|
||||
"architecture": "x86_64",
|
||||
"cores": windows_cores,
|
||||
}
|
||||
|
||||
return {
|
||||
"platform": "emudeck",
|
||||
"source": self.url,
|
||||
"scraped_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
||||
"targets": targets,
|
||||
}
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Scrape EmuDeck emulator targets"
|
||||
)
|
||||
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'])} emulators")
|
||||
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()
|
||||
200
scripts/scraper/targets/retroarch_targets_scraper.py
Normal file
200
scripts/scraper/targets/retroarch_targets_scraper.py
Normal file
@@ -0,0 +1,200 @@
|
||||
"""Scraper for RetroArch buildbot nightly targets.
|
||||
|
||||
Source: https://buildbot.libretro.com/nightly/
|
||||
Fetches directory listings per target to determine available cores.
|
||||
|
||||
Buildbot structure varies by platform:
|
||||
- linux: {path}/latest/ -> *_libretro.so.zip
|
||||
- windows: {path}/latest/ -> *_libretro.dll.zip
|
||||
- apple/osx: {path}/latest/ -> *_libretro.dylib.zip
|
||||
- android: android/latest/{arch}/ -> *_libretro_android.so.zip
|
||||
- switch: nintendo/switch/libnx/latest/ -> *_libretro_libnx.nro.zip
|
||||
- 3ds: nintendo/3ds/latest/3dsx/ -> *_libretro.3dsx.zip
|
||||
- wii/ngc: {path}/latest/ -> *_libretro_{plat}.dol.zip
|
||||
- wiiu: nintendo/wiiu/latest/ -> *_libretro.rpx.zip
|
||||
- psp: playstation/psp/latest/ -> *_libretro_psp.PBP.zip
|
||||
- ps2: playstation/ps2/latest/ -> *_libretro_ps2.elf.zip
|
||||
- vita: bundles only (VPK) - no individual cores
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import re
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
from datetime import datetime, timezone
|
||||
|
||||
import yaml
|
||||
|
||||
from . import BaseTargetScraper
|
||||
|
||||
PLATFORM_NAME = "retroarch"
|
||||
|
||||
BUILDBOT_URL = "https://buildbot.libretro.com/nightly/"
|
||||
|
||||
# (url_path_under_nightly, target_name, architecture)
|
||||
# url_path must end at the directory containing core files
|
||||
TARGETS: list[tuple[str, str, str]] = [
|
||||
("linux/x86_64/latest", "linux-x86_64", "x86_64"),
|
||||
("linux/armhf/latest", "linux-armhf", "armhf"),
|
||||
("linux/armv7-neon-hf/latest", "linux-armv7-neon-hf", "armv7"),
|
||||
("windows/x86_64/latest", "windows-x86_64", "x86_64"),
|
||||
("windows/x86/latest", "windows-x86", "x86"),
|
||||
("android/latest/arm64-v8a", "android-arm64-v8a", "aarch64"),
|
||||
("android/latest/armeabi-v7a", "android-armeabi-v7a", "armv7"),
|
||||
("android/latest/x86_64", "android-x86_64", "x86_64"),
|
||||
("android/latest/x86", "android-x86", "x86"),
|
||||
("apple/osx/x86_64/latest", "osx-x86_64", "x86_64"),
|
||||
("apple/osx/arm64/latest", "osx-arm64", "aarch64"),
|
||||
("apple/ios-arm64/latest", "ios-arm64", "aarch64"),
|
||||
("apple/tvos-arm64/latest", "tvos-arm64", "aarch64"),
|
||||
("nintendo/switch/libnx/latest", "nintendo-switch", "aarch64"),
|
||||
("nintendo/3ds/latest/3dsx", "nintendo-3ds", "arm"),
|
||||
("nintendo/ngc/latest", "nintendo-gamecube", "ppc"),
|
||||
("nintendo/wii/latest", "nintendo-wii", "ppc"),
|
||||
("nintendo/wiiu/latest", "nintendo-wiiu", "ppc"),
|
||||
("playstation/ps2/latest", "playstation-ps2", "mips"),
|
||||
("playstation/psp/latest", "playstation-psp", "mips"),
|
||||
# vita: only VPK bundles on buildbot — cores listed via libretro-super recipes
|
||||
]
|
||||
|
||||
# Recipe-based targets: (recipe_path_under_RECIPE_BASE_URL, target_name, architecture)
|
||||
RECIPE_TARGETS: list[tuple[str, str, str]] = [
|
||||
("playstation/vita", "playstation-vita", "armv7"),
|
||||
]
|
||||
|
||||
RECIPE_BASE_URL = "https://raw.githubusercontent.com/libretro/libretro-super/master/recipes/"
|
||||
|
||||
# Match any href containing _libretro followed by a platform-specific extension
|
||||
# Covers: .so.zip, .dll.zip, .dylib.zip, .nro.zip, .dol.zip, .rpx.zip,
|
||||
# .3dsx.zip, .PBP.zip, .elf.zip, _android.so.zip
|
||||
_HREF_RE = re.compile(
|
||||
r'href="([^"]*?(\w+)_libretro[^"]*?\.zip)"',
|
||||
re.IGNORECASE,
|
||||
)
|
||||
|
||||
# Extract core name: everything before _libretro
|
||||
_CORE_NAME_RE = re.compile(r'^(.+?)_libretro')
|
||||
|
||||
|
||||
class Scraper(BaseTargetScraper):
|
||||
"""Fetches core lists per target from RetroArch buildbot nightly."""
|
||||
|
||||
def __init__(self, url: str = BUILDBOT_URL):
|
||||
super().__init__(url=url)
|
||||
|
||||
def _fetch_url(self, url: str) -> str | None:
|
||||
try:
|
||||
req = urllib.request.Request(
|
||||
url, headers={"User-Agent": "retrobios-scraper/1.0"}
|
||||
)
|
||||
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||
return resp.read().decode("utf-8")
|
||||
except urllib.error.URLError as e:
|
||||
print(f" skip {url}: {e}", file=sys.stderr)
|
||||
return None
|
||||
|
||||
def _fetch_cores_for_target(self, path: str) -> list[str]:
|
||||
url = f"{self.url}{path}/"
|
||||
html = self._fetch_url(url)
|
||||
if html is None:
|
||||
return []
|
||||
cores: list[str] = []
|
||||
seen: set[str] = set()
|
||||
for match in _HREF_RE.finditer(html):
|
||||
href = match.group(1)
|
||||
filename = href.split("/")[-1]
|
||||
m = _CORE_NAME_RE.match(filename)
|
||||
if m:
|
||||
core = m.group(1)
|
||||
if core not in seen:
|
||||
seen.add(core)
|
||||
cores.append(core)
|
||||
return sorted(cores)
|
||||
|
||||
def _parse_recipe_cores(self, text: str) -> list[str]:
|
||||
cores: list[str] = []
|
||||
seen: set[str] = set()
|
||||
for line in text.splitlines():
|
||||
line = line.strip()
|
||||
if not line or line.startswith("#"):
|
||||
continue
|
||||
parts = line.split()
|
||||
if not parts:
|
||||
continue
|
||||
core = parts[0]
|
||||
if core not in seen:
|
||||
seen.add(core)
|
||||
cores.append(core)
|
||||
return sorted(cores)
|
||||
|
||||
def _fetch_cores_for_recipe(self, recipe_path: str) -> list[str]:
|
||||
url = f"{RECIPE_BASE_URL}{recipe_path}"
|
||||
text = self._fetch_url(url)
|
||||
if text is None:
|
||||
return []
|
||||
return self._parse_recipe_cores(text)
|
||||
|
||||
def fetch_targets(self) -> dict:
|
||||
targets: dict[str, dict] = {}
|
||||
for path, target_name, arch in TARGETS:
|
||||
print(f" fetching {target_name}...", file=sys.stderr)
|
||||
cores = self._fetch_cores_for_target(path)
|
||||
if not cores:
|
||||
print(f" warning: no cores found for {target_name}", file=sys.stderr)
|
||||
continue
|
||||
targets[target_name] = {
|
||||
"architecture": arch,
|
||||
"cores": cores,
|
||||
}
|
||||
print(f" {target_name}: {len(cores)} cores", file=sys.stderr)
|
||||
for recipe_path, target_name, arch in RECIPE_TARGETS:
|
||||
print(f" fetching {target_name} (recipe)...", file=sys.stderr)
|
||||
cores = self._fetch_cores_for_recipe(recipe_path)
|
||||
if not cores:
|
||||
print(f" warning: no cores found for {target_name}", file=sys.stderr)
|
||||
continue
|
||||
targets[target_name] = {
|
||||
"architecture": arch,
|
||||
"cores": cores,
|
||||
}
|
||||
print(f" {target_name}: {len(cores)} cores", file=sys.stderr)
|
||||
return {
|
||||
"platform": "retroarch",
|
||||
"source": self.url,
|
||||
"scraped_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
||||
"targets": targets,
|
||||
}
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Scrape RetroArch buildbot nightly targets"
|
||||
)
|
||||
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()
|
||||
|
||||
total_cores = sum(len(t["cores"]) for t in data["targets"].values())
|
||||
print(f"\n{len(data['targets'])} targets, {total_cores} total core entries",
|
||||
file=sys.stderr)
|
||||
|
||||
if args.dry_run:
|
||||
for name, info in sorted(data["targets"].items()):
|
||||
print(f" {name:30s} {info['architecture']:10s} {len(info['cores']):>4d} 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()
|
||||
194
scripts/scraper/targets/retropie_targets_scraper.py
Normal file
194
scripts/scraper/targets/retropie_targets_scraper.py
Normal file
@@ -0,0 +1,194 @@
|
||||
"""Scraper for RetroPie libretro core availability per platform.
|
||||
|
||||
Source: https://github.com/RetroPie/RetroPie-Setup/tree/master/scriptmodules/libretrocores
|
||||
Parses rp_module_id and rp_module_flags from each scriptmodule to determine
|
||||
which platforms each core supports.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
from datetime import datetime, timezone
|
||||
|
||||
import yaml
|
||||
|
||||
from . import BaseTargetScraper
|
||||
|
||||
PLATFORM_NAME = "retropie"
|
||||
|
||||
GITHUB_API_URL = (
|
||||
"https://api.github.com/repos/RetroPie/RetroPie-Setup/contents"
|
||||
"/scriptmodules/libretrocores"
|
||||
)
|
||||
RAW_BASE_URL = (
|
||||
"https://raw.githubusercontent.com/RetroPie/RetroPie-Setup/master"
|
||||
"/scriptmodules/libretrocores/"
|
||||
)
|
||||
|
||||
# Platform flag sets: flags that the platform possesses
|
||||
PLATFORM_FLAGS: dict[str, set[str]] = {
|
||||
"rpi1": {"arm", "armv6", "rpi", "gles"},
|
||||
"rpi2": {"arm", "armv7", "neon", "rpi", "gles"},
|
||||
"rpi3": {"arm", "armv8", "neon", "rpi", "gles"},
|
||||
"rpi4": {"arm", "armv8", "neon", "rpi", "gles", "gles3", "gles31"},
|
||||
"rpi5": {"arm", "armv8", "neon", "rpi", "gles", "gles3", "gles31"},
|
||||
"x86": {"x86"},
|
||||
"x86_64": {"x86"},
|
||||
}
|
||||
|
||||
ARCH_MAP: dict[str, str] = {
|
||||
"rpi1": "armv6",
|
||||
"rpi2": "armv7",
|
||||
"rpi3": "armv7",
|
||||
"rpi4": "aarch64",
|
||||
"rpi5": "aarch64",
|
||||
"x86": "x86",
|
||||
"x86_64": "x86_64",
|
||||
}
|
||||
|
||||
# Flags that are build directives, not platform restrictions
|
||||
_BUILD_FLAGS = {"nodistcc"}
|
||||
|
||||
_MODULE_ID_RE = re.compile(r'rp_module_id\s*=\s*["\']([^"\']+)["\']')
|
||||
_MODULE_FLAGS_RE = re.compile(r'rp_module_flags\s*=\s*["\']([^"\']*)["\']')
|
||||
|
||||
|
||||
def _fetch(url: str, accept: str = "text/plain") -> str | None:
|
||||
try:
|
||||
req = urllib.request.Request(
|
||||
url,
|
||||
headers={"User-Agent": "retrobios-scraper/1.0", "Accept": accept},
|
||||
)
|
||||
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||
return resp.read().decode("utf-8")
|
||||
except urllib.error.URLError as e:
|
||||
print(f" skip {url}: {e}", file=sys.stderr)
|
||||
return None
|
||||
|
||||
|
||||
def _is_available(flags_str: str, platform: str) -> bool:
|
||||
"""Return True if the core is available on the given platform."""
|
||||
platform_has = PLATFORM_FLAGS.get(platform, set())
|
||||
tokens = flags_str.split() if flags_str.strip() else []
|
||||
|
||||
for token in tokens:
|
||||
if token in _BUILD_FLAGS:
|
||||
continue
|
||||
if token.startswith("!"):
|
||||
# Exclusion: if platform has this flag, core is excluded
|
||||
flag = token[1:]
|
||||
if flag in platform_has:
|
||||
return False
|
||||
else:
|
||||
# Requirement: platform must have this flag
|
||||
if token not in platform_has:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def _parse_module(content: str) -> tuple[str | None, str]:
|
||||
"""Return (module_id, flags_string) from a scriptmodule file."""
|
||||
id_match = _MODULE_ID_RE.search(content)
|
||||
flags_match = _MODULE_FLAGS_RE.search(content)
|
||||
module_id = id_match.group(1) if id_match else None
|
||||
flags = flags_match.group(1) if flags_match else ""
|
||||
return module_id, flags
|
||||
|
||||
|
||||
class Scraper(BaseTargetScraper):
|
||||
"""Fetches RetroPie libretro core availability by parsing scriptmodules."""
|
||||
|
||||
def __init__(self, url: str = GITHUB_API_URL):
|
||||
super().__init__(url=url)
|
||||
|
||||
def _list_scriptmodules(self) -> list[str]:
|
||||
"""Return list of .sh filenames from the libretrocores directory."""
|
||||
raw = _fetch(self.url, accept="application/vnd.github+json")
|
||||
if raw is None:
|
||||
return []
|
||||
try:
|
||||
entries = json.loads(raw)
|
||||
except json.JSONDecodeError as e:
|
||||
print(f" JSON parse error: {e}", file=sys.stderr)
|
||||
return []
|
||||
return [e["name"] for e in entries if e.get("name", "").endswith(".sh")]
|
||||
|
||||
def _fetch_module(self, filename: str) -> str | None:
|
||||
return _fetch(f"{RAW_BASE_URL}{filename}")
|
||||
|
||||
def fetch_targets(self) -> dict:
|
||||
print(" listing RetroPie scriptmodules...", file=sys.stderr)
|
||||
filenames = self._list_scriptmodules()
|
||||
if not filenames:
|
||||
print(" warning: no scriptmodules found", file=sys.stderr)
|
||||
|
||||
# {platform: [core_id, ...]}
|
||||
platform_cores: dict[str, list[str]] = {p: [] for p in PLATFORM_FLAGS}
|
||||
|
||||
for filename in filenames:
|
||||
content = self._fetch_module(filename)
|
||||
if content is None:
|
||||
continue
|
||||
module_id, flags = _parse_module(content)
|
||||
if not module_id:
|
||||
print(f" warning: no rp_module_id in {filename}", file=sys.stderr)
|
||||
continue
|
||||
# Normalize: strip lr- prefix and convert hyphens to underscores
|
||||
# to match emulator profile keys (lr-beetle-psx -> beetle_psx)
|
||||
core_name = module_id
|
||||
if core_name.startswith("lr-"):
|
||||
core_name = core_name[3:]
|
||||
core_name = core_name.replace("-", "_")
|
||||
for platform in PLATFORM_FLAGS:
|
||||
if _is_available(flags, platform):
|
||||
platform_cores[platform].append(core_name)
|
||||
|
||||
print(f" parsed {len(filenames)} scriptmodules", file=sys.stderr)
|
||||
|
||||
targets: dict[str, dict] = {}
|
||||
for platform, arch in ARCH_MAP.items():
|
||||
cores = sorted(platform_cores.get(platform, []))
|
||||
targets[platform] = {
|
||||
"architecture": arch,
|
||||
"cores": cores,
|
||||
}
|
||||
|
||||
return {
|
||||
"platform": "retropie",
|
||||
"source": self.url,
|
||||
"scraped_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
||||
"targets": targets,
|
||||
}
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Scrape RetroPie libretro core targets from scriptmodules"
|
||||
)
|
||||
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()
|
||||
@@ -37,9 +37,10 @@ sys.path.insert(0, os.path.dirname(__file__))
|
||||
from common import (
|
||||
_build_validation_index, build_zip_contents_index, check_file_validation,
|
||||
check_inside_zip, compute_hashes, filter_files_by_mode,
|
||||
group_identical_platforms, list_emulator_profiles, list_system_ids,
|
||||
load_data_dir_registry, load_emulator_profiles, load_platform_config,
|
||||
md5sum, md5_composite, resolve_local_file, resolve_platform_cores,
|
||||
filter_systems_by_target, group_identical_platforms, list_emulator_profiles,
|
||||
list_system_ids, load_data_dir_registry, load_emulator_profiles,
|
||||
load_platform_config, md5sum, md5_composite, resolve_local_file,
|
||||
resolve_platform_cores,
|
||||
)
|
||||
DEFAULT_DB = "database.json"
|
||||
DEFAULT_PLATFORMS_DIR = "platforms"
|
||||
@@ -205,6 +206,7 @@ def find_undeclared_files(
|
||||
emulators_dir: str,
|
||||
db: dict,
|
||||
emu_profiles: dict | None = None,
|
||||
target_cores: set[str] | None = None,
|
||||
) -> list[dict]:
|
||||
"""Find files needed by cores but not declared in platform config."""
|
||||
# Collect all filenames declared by this platform
|
||||
@@ -226,7 +228,7 @@ def find_undeclared_files(
|
||||
by_name = db.get("indexes", {}).get("by_name", {})
|
||||
profiles = emu_profiles if emu_profiles is not None else load_emulator_profiles(emulators_dir)
|
||||
|
||||
relevant = resolve_platform_cores(config, profiles)
|
||||
relevant = resolve_platform_cores(config, profiles, target_cores=target_cores)
|
||||
standalone_set = set(str(c) for c in config.get("standalone_cores", []))
|
||||
undeclared = []
|
||||
seen = set()
|
||||
@@ -278,6 +280,7 @@ def find_undeclared_files(
|
||||
|
||||
def find_exclusion_notes(
|
||||
config: dict, emulators_dir: str, emu_profiles: dict | None = None,
|
||||
target_cores: set[str] | None = None,
|
||||
) -> list[dict]:
|
||||
"""Document why certain emulator files are intentionally excluded.
|
||||
|
||||
@@ -292,7 +295,7 @@ def find_exclusion_notes(
|
||||
for sys_id in config.get("systems", {}):
|
||||
platform_systems.add(sys_id)
|
||||
|
||||
relevant = resolve_platform_cores(config, profiles)
|
||||
relevant = resolve_platform_cores(config, profiles, target_cores=target_cores)
|
||||
notes = []
|
||||
for emu_name, profile in sorted(profiles.items()):
|
||||
emu_systems = set(profile.get("systems", []))
|
||||
@@ -368,6 +371,7 @@ def verify_platform(
|
||||
config: dict, db: dict,
|
||||
emulators_dir: str = DEFAULT_EMULATORS_DIR,
|
||||
emu_profiles: dict | None = None,
|
||||
target_cores: set[str] | None = None,
|
||||
) -> dict:
|
||||
"""Verify all BIOS files for a platform, including cross-reference gaps."""
|
||||
mode = config.get("verification_mode", "existence")
|
||||
@@ -389,6 +393,13 @@ def verify_platform(
|
||||
hle_index[f.get("name", "")] = True
|
||||
validation_index = _build_validation_index(profiles)
|
||||
|
||||
# Filter systems by target
|
||||
plat_cores = resolve_platform_cores(config, profiles) if target_cores else None
|
||||
verify_systems = filter_systems_by_target(
|
||||
config.get("systems", {}), profiles, target_cores,
|
||||
platform_cores=plat_cores,
|
||||
)
|
||||
|
||||
# Per-entry results
|
||||
details = []
|
||||
# Per-destination aggregation
|
||||
@@ -396,7 +407,7 @@ def verify_platform(
|
||||
file_required: dict[str, bool] = {}
|
||||
file_severity: dict[str, str] = {}
|
||||
|
||||
for sys_id, system in config.get("systems", {}).items():
|
||||
for sys_id, system in verify_systems.items():
|
||||
for file_entry in system.get("files", []):
|
||||
local_path, resolve_status = resolve_local_file(
|
||||
file_entry, db, zip_contents,
|
||||
@@ -452,8 +463,8 @@ def verify_platform(
|
||||
status_counts[s] = status_counts.get(s, 0) + 1
|
||||
|
||||
# Cross-reference undeclared files
|
||||
undeclared = find_undeclared_files(config, emulators_dir, db, emu_profiles)
|
||||
exclusions = find_exclusion_notes(config, emulators_dir, emu_profiles)
|
||||
undeclared = find_undeclared_files(config, emulators_dir, db, emu_profiles, target_cores=target_cores)
|
||||
exclusions = find_exclusion_notes(config, emulators_dir, emu_profiles, target_cores=target_cores)
|
||||
|
||||
return {
|
||||
"platform": platform,
|
||||
@@ -866,6 +877,8 @@ def main():
|
||||
parser.add_argument("--list-emulators", action="store_true", help="List available emulators")
|
||||
parser.add_argument("--list-systems", action="store_true", help="List available systems")
|
||||
parser.add_argument("--include-archived", action="store_true")
|
||||
parser.add_argument("--target", "-t", help="Hardware target (e.g., switch, rpi4)")
|
||||
parser.add_argument("--list-targets", action="store_true", help="List available targets for the platform")
|
||||
parser.add_argument("--db", default=DEFAULT_DB)
|
||||
parser.add_argument("--platforms-dir", default=DEFAULT_PLATFORMS_DIR)
|
||||
parser.add_argument("--emulators-dir", default=DEFAULT_EMULATORS_DIR)
|
||||
@@ -879,6 +892,19 @@ def main():
|
||||
list_system_ids(args.emulators_dir)
|
||||
return
|
||||
|
||||
if args.list_targets:
|
||||
if not args.platform:
|
||||
parser.error("--list-targets requires --platform")
|
||||
from common import list_available_targets
|
||||
targets = list_available_targets(args.platform, args.platforms_dir)
|
||||
if not targets:
|
||||
print(f"No targets configured for platform '{args.platform}'")
|
||||
return
|
||||
for t in targets:
|
||||
aliases = f" (aliases: {', '.join(t['aliases'])})" if t['aliases'] else ""
|
||||
print(f" {t['name']:30s} {t['architecture']:10s} {t['core_count']:>4d} cores{aliases}")
|
||||
return
|
||||
|
||||
# Mutual exclusion
|
||||
modes = sum(1 for x in (args.platform, args.all, args.emulator, args.system) if x)
|
||||
if modes == 0:
|
||||
@@ -887,6 +913,10 @@ def main():
|
||||
parser.error("--platform, --all, --emulator, and --system are mutually exclusive")
|
||||
if args.standalone and not (args.emulator or args.system):
|
||||
parser.error("--standalone requires --emulator or --system")
|
||||
if args.target and not (args.platform or args.all):
|
||||
parser.error("--target requires --platform or --all")
|
||||
if args.target and (args.emulator or args.system):
|
||||
parser.error("--target is incompatible with --emulator and --system")
|
||||
|
||||
with open(args.db) as f:
|
||||
db = json.load(f)
|
||||
@@ -926,13 +956,37 @@ def main():
|
||||
# Load emulator profiles once for cross-reference (not per-platform)
|
||||
emu_profiles = load_emulator_profiles(args.emulators_dir)
|
||||
|
||||
target_cores_cache: dict[str, set[str] | None] = {}
|
||||
if args.target:
|
||||
from common import load_target_config
|
||||
skip = []
|
||||
for p in platforms:
|
||||
try:
|
||||
target_cores_cache[p] = load_target_config(p, args.target, args.platforms_dir)
|
||||
except FileNotFoundError:
|
||||
if args.all:
|
||||
target_cores_cache[p] = None
|
||||
else:
|
||||
print(f"ERROR: No target config for platform '{p}'", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
except ValueError as e:
|
||||
if args.all:
|
||||
print(f"INFO: Skipping {p}: {e}")
|
||||
skip.append(p)
|
||||
else:
|
||||
print(f"ERROR: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
platforms = [p for p in platforms if p not in skip]
|
||||
|
||||
# Group identical platforms (same function as generate_pack)
|
||||
groups = group_identical_platforms(platforms, args.platforms_dir)
|
||||
groups = group_identical_platforms(platforms, args.platforms_dir,
|
||||
target_cores_cache if args.target else None)
|
||||
all_results = {}
|
||||
group_results: list[tuple[dict, list[str]]] = []
|
||||
for group_platforms, representative in groups:
|
||||
config = load_platform_config(representative, args.platforms_dir)
|
||||
result = verify_platform(config, db, args.emulators_dir, emu_profiles)
|
||||
tc = target_cores_cache.get(representative) if args.target else None
|
||||
result = verify_platform(config, db, args.emulators_dir, emu_profiles, target_cores=tc)
|
||||
names = [load_platform_config(p, args.platforms_dir).get("platform", p) for p in group_platforms]
|
||||
group_results.append((result, names))
|
||||
for p in group_platforms:
|
||||
|
||||
@@ -1175,6 +1175,183 @@ class TestE2E(unittest.TestCase):
|
||||
resolved = resolve_platform_cores(config, profiles)
|
||||
self.assertIn("test_alias_core", resolved)
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# Target config tests (Task 1)
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
def _write_target_fixtures(self):
|
||||
"""Create target config fixtures for testing."""
|
||||
targets_dir = os.path.join(self.platforms_dir, "targets")
|
||||
os.makedirs(targets_dir, exist_ok=True)
|
||||
target_config = {
|
||||
"platform": "testplatform",
|
||||
"source": "test",
|
||||
"scraped_at": "2026-01-01T00:00:00Z",
|
||||
"targets": {
|
||||
"target-full": {
|
||||
"architecture": "x86_64",
|
||||
"cores": ["core_a", "core_b", "core_c"],
|
||||
},
|
||||
"target-minimal": {
|
||||
"architecture": "armv7",
|
||||
"cores": ["core_a"],
|
||||
},
|
||||
},
|
||||
}
|
||||
with open(os.path.join(targets_dir, "testplatform.yml"), "w") as f:
|
||||
yaml.dump(target_config, f)
|
||||
single_config = {
|
||||
"platform": "singleplatform",
|
||||
"source": "test",
|
||||
"scraped_at": "2026-01-01T00:00:00Z",
|
||||
"targets": {
|
||||
"only-target": {
|
||||
"architecture": "x86_64",
|
||||
"cores": ["core_a", "core_b"],
|
||||
},
|
||||
},
|
||||
}
|
||||
with open(os.path.join(targets_dir, "singleplatform.yml"), "w") as f:
|
||||
yaml.dump(single_config, f)
|
||||
overrides = {
|
||||
"testplatform": {
|
||||
"targets": {
|
||||
"target-full": {
|
||||
"aliases": ["full", "pc", "desktop"],
|
||||
"add_cores": ["core_d"],
|
||||
"remove_cores": ["core_c"],
|
||||
},
|
||||
"target-minimal": {
|
||||
"aliases": ["minimal", "arm"],
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
with open(os.path.join(targets_dir, "_overrides.yml"), "w") as f:
|
||||
yaml.dump(overrides, f)
|
||||
|
||||
def test_load_target_config(self):
|
||||
self._write_target_fixtures()
|
||||
from common import load_target_config
|
||||
cores = load_target_config("testplatform", "target-minimal", self.platforms_dir)
|
||||
self.assertEqual(cores, {"core_a"})
|
||||
|
||||
def test_target_alias_resolution(self):
|
||||
self._write_target_fixtures()
|
||||
from common import load_target_config
|
||||
cores = load_target_config("testplatform", "full", self.platforms_dir)
|
||||
self.assertEqual(cores, {"core_a", "core_b", "core_d"})
|
||||
|
||||
def test_target_unknown_error(self):
|
||||
self._write_target_fixtures()
|
||||
from common import load_target_config
|
||||
with self.assertRaises(ValueError) as ctx:
|
||||
load_target_config("testplatform", "nonexistent", self.platforms_dir)
|
||||
self.assertIn("target-full", str(ctx.exception))
|
||||
self.assertIn("target-minimal", str(ctx.exception))
|
||||
|
||||
def test_target_override_add_remove(self):
|
||||
self._write_target_fixtures()
|
||||
from common import load_target_config
|
||||
cores = load_target_config("testplatform", "full", self.platforms_dir)
|
||||
self.assertIn("core_d", cores)
|
||||
self.assertNotIn("core_c", cores)
|
||||
self.assertIn("core_a", cores)
|
||||
self.assertIn("core_b", cores)
|
||||
|
||||
def test_target_single_target_noop(self):
|
||||
self._write_target_fixtures()
|
||||
from common import load_target_config
|
||||
cores = load_target_config("singleplatform", "only-target", self.platforms_dir)
|
||||
self.assertEqual(cores, {"core_a", "core_b"})
|
||||
|
||||
def test_target_inherits(self):
|
||||
self._write_target_fixtures()
|
||||
targets_dir = os.path.join(self.platforms_dir, "targets")
|
||||
child_config = {
|
||||
"platform": "childplatform",
|
||||
"source": "test",
|
||||
"scraped_at": "2026-01-01T00:00:00Z",
|
||||
"targets": {
|
||||
"target-full": {
|
||||
"architecture": "x86_64",
|
||||
"cores": ["core_a"],
|
||||
},
|
||||
},
|
||||
}
|
||||
with open(os.path.join(targets_dir, "childplatform.yml"), "w") as f:
|
||||
yaml.dump(child_config, f)
|
||||
from common import load_target_config
|
||||
parent = load_target_config("testplatform", "target-minimal", self.platforms_dir)
|
||||
child = load_target_config("childplatform", "target-full", self.platforms_dir)
|
||||
self.assertEqual(parent, {"core_a"})
|
||||
self.assertEqual(child, {"core_a"})
|
||||
self.assertNotEqual(
|
||||
load_target_config("testplatform", "full", self.platforms_dir),
|
||||
child,
|
||||
)
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# Target filtering in resolve_platform_cores (Task 2)
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
def test_target_core_intersection(self):
|
||||
self._write_target_fixtures()
|
||||
profiles = {
|
||||
"core_a": {"type": "libretro", "systems": ["sys1"]},
|
||||
"core_b": {"type": "libretro", "systems": ["sys1"]},
|
||||
"core_c": {"type": "libretro", "systems": ["sys2"]},
|
||||
"core_d": {"type": "libretro", "systems": ["sys2"]},
|
||||
}
|
||||
config = {"cores": "all_libretro"}
|
||||
result = resolve_platform_cores(config, profiles)
|
||||
self.assertEqual(result, {"core_a", "core_b", "core_c", "core_d"})
|
||||
result = resolve_platform_cores(config, profiles, target_cores={"core_a", "core_b"})
|
||||
self.assertEqual(result, {"core_a", "core_b"})
|
||||
|
||||
def test_target_none_no_filter(self):
|
||||
profiles = {
|
||||
"core_a": {"type": "libretro", "systems": ["sys1"]},
|
||||
"core_b": {"type": "libretro", "systems": ["sys1"]},
|
||||
}
|
||||
config = {"cores": "all_libretro"}
|
||||
result = resolve_platform_cores(config, profiles, target_cores=None)
|
||||
self.assertEqual(result, {"core_a", "core_b"})
|
||||
|
||||
def test_verify_target_filtered(self):
|
||||
"""Verify with target_cores only reports files from filtered cores."""
|
||||
self._write_target_fixtures()
|
||||
core_a_path = os.path.join(self.emulators_dir, "core_a.yml")
|
||||
core_b_path = os.path.join(self.emulators_dir, "core_b.yml")
|
||||
with open(core_a_path, "w") as f:
|
||||
yaml.dump({
|
||||
"emulator": "CoreA", "type": "libretro", "systems": ["sys1"],
|
||||
"files": [{"name": "bios_a.bin", "required": True}],
|
||||
}, f)
|
||||
with open(core_b_path, "w") as f:
|
||||
yaml.dump({
|
||||
"emulator": "CoreB", "type": "libretro", "systems": ["sys1"],
|
||||
"files": [{"name": "bios_b.bin", "required": True}],
|
||||
}, f)
|
||||
|
||||
config = {"cores": "all_libretro", "systems": {"sys1": {"files": []}}}
|
||||
profiles = load_emulator_profiles(self.emulators_dir)
|
||||
|
||||
# Without target: both cores' files are undeclared
|
||||
undeclared = find_undeclared_files(config, self.emulators_dir, self.db, profiles)
|
||||
names = {u["name"] for u in undeclared}
|
||||
self.assertIn("bios_a.bin", names)
|
||||
self.assertIn("bios_b.bin", names)
|
||||
|
||||
# With target filtering to core_a only
|
||||
undeclared = find_undeclared_files(
|
||||
config, self.emulators_dir, self.db, profiles,
|
||||
target_cores={"core_a"},
|
||||
)
|
||||
names = {u["name"] for u in undeclared}
|
||||
self.assertIn("bios_a.bin", names)
|
||||
self.assertNotIn("bios_b.bin", names)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user