mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-04-17 22:32:31 -05:00
Compare commits
6 Commits
e1410ef4a6
...
814ad98c9c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
814ad98c9c | ||
|
|
1fcb948a00 | ||
|
|
4992af07e7 | ||
|
|
6a21a99c22 | ||
|
|
257ec1a527 | ||
|
|
38d605c7d5 |
30
README.md
30
README.md
@@ -2,31 +2,33 @@
|
||||
|
||||
Complete, verified collection of BIOS, firmware, and system files for retrogaming emulators.
|
||||
|
||||
> **8085** files | **4686.1 MB** | **7** platforms | **265** emulator profiles
|
||||
> **5593** files | **4681.6 MB** | **8** platforms | **275** emulator profiles
|
||||
|
||||
## Download
|
||||
|
||||
| Platform | Files | Verification | Pack |
|
||||
|----------|-------|-------------|------|
|
||||
| Batocera | 680 | md5 | [Download](../../releases/latest) |
|
||||
| EmuDeck | 164 | md5 | [Download](../../releases/latest) |
|
||||
| Lakka | 452 | existence | [Download](../../releases/latest) |
|
||||
| Batocera | 359 | md5 | [Download](../../releases/latest) |
|
||||
| EmuDeck | 161 | md5 | [Download](../../releases/latest) |
|
||||
| Lakka | 448 | existence | [Download](../../releases/latest) |
|
||||
| Recalbox | 346 | md5 | [Download](../../releases/latest) |
|
||||
| RetroArch | 452 | existence | [Download](../../releases/latest) |
|
||||
| RetroBat | 343 | md5 | [Download](../../releases/latest) |
|
||||
| RetroPie | 452 | existence | [Download](../../releases/latest) |
|
||||
| RetroArch | 448 | existence | [Download](../../releases/latest) |
|
||||
| RetroBat | 331 | md5 | [Download](../../releases/latest) |
|
||||
| RetroDECK | 117 | md5 | [Download](../../releases/latest) |
|
||||
| RetroPie | 448 | existence | [Download](../../releases/latest) |
|
||||
|
||||
## Coverage
|
||||
|
||||
| Platform | Coverage | Verified | Untested | Missing |
|
||||
|----------|----------|----------|----------|---------|
|
||||
| Batocera | 680/680 (100.0%) | 679 | 1 | 0 |
|
||||
| EmuDeck | 164/164 (100.0%) | 164 | 0 | 0 |
|
||||
| Lakka | 452/452 (100.0%) | 452 | 0 | 0 |
|
||||
| Batocera | 359/359 (100.0%) | 358 | 1 | 0 |
|
||||
| EmuDeck | 161/161 (100.0%) | 161 | 0 | 0 |
|
||||
| Lakka | 448/448 (100.0%) | 448 | 0 | 0 |
|
||||
| Recalbox | 346/346 (100.0%) | 346 | 0 | 0 |
|
||||
| RetroArch | 452/452 (100.0%) | 452 | 0 | 0 |
|
||||
| RetroBat | 343/343 (100.0%) | 343 | 0 | 0 |
|
||||
| RetroPie | 452/452 (100.0%) | 452 | 0 | 0 |
|
||||
| RetroArch | 448/448 (100.0%) | 448 | 0 | 0 |
|
||||
| RetroBat | 331/331 (100.0%) | 331 | 0 | 0 |
|
||||
| RetroDECK | 102/117 (87.2%) | 99 | 3 | 15 |
|
||||
| RetroPie | 448/448 (100.0%) | 448 | 0 | 0 |
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -40,4 +42,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
||||
|
||||
This repository provides BIOS files for personal backup and archival purposes.
|
||||
|
||||
*Auto-generated on 2026-03-18T14:18:36Z*
|
||||
*Auto-generated on 2026-03-19T16:27:21Z*
|
||||
|
||||
485
database.json
485
database.json
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"generated_at": "2026-03-19T12:13:36Z",
|
||||
"generated_at": "2026-03-19T16:27:21Z",
|
||||
"total_files": 5593,
|
||||
"total_size": 4909044289,
|
||||
"files": {
|
||||
@@ -71649,6 +71649,192 @@
|
||||
"SCPH-70004_BIOS_V12_EUR_200.ROM2": [
|
||||
"1bae895fbdd658cfb56c53cc2139282cc1e778de"
|
||||
],
|
||||
"tos100uk.img": [
|
||||
"9a6e4c88533a9eaa4d55cdc040e47443e0226eb2"
|
||||
],
|
||||
"tos106de.img": [
|
||||
"3b8cf5ffa41b252eb67f8824f94608fa4005d6dd"
|
||||
],
|
||||
"tos206us.img": [
|
||||
"ee58768bdfc602c9b14942ce5481e97dd24e7c83"
|
||||
],
|
||||
"saturn_bios.bin": [
|
||||
"2b8cb4f87580683eb4d760e4ed210813d667f0a2"
|
||||
],
|
||||
"sega-saturn:af5828fdff51384f99b3c4926be27762": [
|
||||
"2b8cb4f87580683eb4d760e4ed210813d667f0a2"
|
||||
],
|
||||
"bios7.bin": [
|
||||
"24f67bdea115a2c847c8813a262502ee1607b7df"
|
||||
],
|
||||
"bios9.bin": [
|
||||
"bfaac75f101c135e32e2aaf541de6b1be4c8c62d"
|
||||
],
|
||||
"dsi_bios7.bin": [
|
||||
"a3aa751eb6bdaaf8a827ba9e03576a6f1ab0f547"
|
||||
],
|
||||
"dsi_bios9.bin": [
|
||||
"7bf549b8be9e48ab0cdc9b0fdadd49a5131f97eb"
|
||||
],
|
||||
"gb_bios.bin": [
|
||||
"4ed31ec6b0b175bb109c0eb5fd3d193da823339f",
|
||||
"1db57a1e8b6e4096f811587f9eab0c6675fd9755"
|
||||
],
|
||||
"sgb_bios.bin": [
|
||||
"aa2f50a77dfb4823da96ba99309085a3c6278515",
|
||||
"369e6eb5e0c975eaa52a4a3f6ee07b2a3c3c16de"
|
||||
],
|
||||
"sgb1.boot.rom": [
|
||||
"aa2f50a77dfb4823da96ba99309085a3c6278515"
|
||||
],
|
||||
"SGB1.sfc": [
|
||||
"973e10840db683cf3faf61bd443090786b3a9f04"
|
||||
],
|
||||
"SGB1.sfc/program.rom": [
|
||||
"973e10840db683cf3faf61bd443090786b3a9f04"
|
||||
],
|
||||
"SGB2.sfc": [
|
||||
"e5b2922ca137051059e4269b236d07a22c07bc84"
|
||||
],
|
||||
"SGB2.sfc/program.rom": [
|
||||
"e5b2922ca137051059e4269b236d07a22c07bc84"
|
||||
],
|
||||
"fs-a1gt_kanjifont.rom": [
|
||||
"5aff2d9b6efc723bc395b0f96f0adfa83cc54a49"
|
||||
],
|
||||
"fs-a1wsx_kanjifont.rom": [
|
||||
"5aff2d9b6efc723bc395b0f96f0adfa83cc54a49"
|
||||
],
|
||||
"yrw801.rom": [
|
||||
"32760893ce06dbe3930627755ba065cc3d8ec6ca"
|
||||
],
|
||||
"fs-a1wsx_msx2psub.rom": [
|
||||
"fe0254cbfc11405b79e7c86c7769bd6322b04995"
|
||||
],
|
||||
"MSX2PEXT.rom": [
|
||||
"fe0254cbfc11405b79e7c86c7769bd6322b04995"
|
||||
],
|
||||
"fs-a1wsx_kanjibasic.rom": [
|
||||
"dcc3a67732aa01c4f2ee8d1ad886444a4dbafe06"
|
||||
],
|
||||
"fs-a1wsx_disk.rom": [
|
||||
"7ed7c55e0359737ac5e68d38cb6903f9e5d7c2b6"
|
||||
],
|
||||
"kick37350.A600": [
|
||||
"02843c4253bbd29aba535b0aa3bd9a85034ecde4"
|
||||
],
|
||||
"amiga-os-120.rom": [
|
||||
"11f9e62cf299f72184835b7b2a70a16333fc0d88"
|
||||
],
|
||||
"Kickstart v1.2 rev 33.180 (1986)(Commodore)(A500-A2000)[!].rom": [
|
||||
"11f9e62cf299f72184835b7b2a70a16333fc0d88"
|
||||
],
|
||||
"Kickstart v1.2 rev 33.180 (1986)(Commodore)(A500-A1000-A2000).rom": [
|
||||
"11f9e62cf299f72184835b7b2a70a16333fc0d88"
|
||||
],
|
||||
"amiga-os-130.rom": [
|
||||
"891e9a547772fe0c6c19b610baf8bc4ea7fcb785"
|
||||
],
|
||||
"Kickstart v1.3 rev 34.5 (1987)(Commodore)(A500-A1000-A2000-CDTV)[!].rom": [
|
||||
"891e9a547772fe0c6c19b610baf8bc4ea7fcb785"
|
||||
],
|
||||
"Kickstart v1.3 rev 34.5 (1987)(Commodore)(A500-A1000-A2000-CDTV).rom": [
|
||||
"891e9a547772fe0c6c19b610baf8bc4ea7fcb785"
|
||||
],
|
||||
"amiga-os-204.rom": [
|
||||
"c5839f5cb98a7a8947065c3ed2f14f5f42e334a1"
|
||||
],
|
||||
"Kickstart v2.04 rev 37.175 (1991)(Commodore)(A500+)[!].rom": [
|
||||
"c5839f5cb98a7a8947065c3ed2f14f5f42e334a1"
|
||||
],
|
||||
"Kickstart v2.04 rev 37.175 (1991)(Commodore)(A500+).rom": [
|
||||
"c5839f5cb98a7a8947065c3ed2f14f5f42e334a1"
|
||||
],
|
||||
"amiga-os-310-a600.rom": [
|
||||
"3b7f1493b27e212830f989f26ca76c02049f09ca"
|
||||
],
|
||||
"Kickstart v3.1 rev 40.63 (1993)(Commodore)(A500-A600-A2000)[!].rom": [
|
||||
"3b7f1493b27e212830f989f26ca76c02049f09ca"
|
||||
],
|
||||
"Kickstart v3.1 rev 40.63 (1993)(Commodore)(A500-A600-A2000).rom": [
|
||||
"3b7f1493b27e212830f989f26ca76c02049f09ca"
|
||||
],
|
||||
"amiga-ext-130-cdtv.rom": [
|
||||
"7ba40ffa17e500ed9fed041f3424bd81d9c907be"
|
||||
],
|
||||
"amiga-os-130-cdtv-ext.rom": [
|
||||
"7ba40ffa17e500ed9fed041f3424bd81d9c907be"
|
||||
],
|
||||
"CDTV Extended-ROM v1.0 (1991)(Commodore)(CDTV)[!].rom": [
|
||||
"7ba40ffa17e500ed9fed041f3424bd81d9c907be"
|
||||
],
|
||||
"CDTV Extended-ROM v1.0 (1992)(Commodore)(CDTV).rom": [
|
||||
"7ba40ffa17e500ed9fed041f3424bd81d9c907be"
|
||||
],
|
||||
"amiga-os-300-a1200.rom": [
|
||||
"70033828182fffc7ed106e5373a8b89dda76faa5"
|
||||
],
|
||||
"Kickstart v3.0 rev 39.106 (1992)(Commodore)(A1200)[!].rom": [
|
||||
"70033828182fffc7ed106e5373a8b89dda76faa5"
|
||||
],
|
||||
"amiga-os-310-a1200.rom": [
|
||||
"e21545723fe8374e91342617604f1b3d703094f1"
|
||||
],
|
||||
"Kickstart v3.1 rev 40.68 (1993)(Commodore)(A1200)[!].rom": [
|
||||
"e21545723fe8374e91342617604f1b3d703094f1"
|
||||
],
|
||||
"Kickstart v3.1 rev 40.68 (1993)(Commodore)(A1200).rom": [
|
||||
"e21545723fe8374e91342617604f1b3d703094f1"
|
||||
],
|
||||
"amiga-os-310-a4000.rom": [
|
||||
"5fe04842d04a489720f0f4bb0e46948199406f49"
|
||||
],
|
||||
"Kickstart v3.1 rev 40.68 (1993)(Commodore)(A4000).rom": [
|
||||
"5fe04842d04a489720f0f4bb0e46948199406f49"
|
||||
],
|
||||
"amiga-os-310-cd32.rom": [
|
||||
"3525be8887f79b5929e017b42380a79edfee542d"
|
||||
],
|
||||
"Kickstart v3.1 rev 40.60 (1993)(Commodore)(CD32).rom": [
|
||||
"3525be8887f79b5929e017b42380a79edfee542d"
|
||||
],
|
||||
"amiga-ext-310-cd32.rom": [
|
||||
"5bef3d628ce59cc02a66e6e4ae0da48f60e78f7f"
|
||||
],
|
||||
"amiga-os-310-cd32-ext.rom": [
|
||||
"5bef3d628ce59cc02a66e6e4ae0da48f60e78f7f"
|
||||
],
|
||||
"CD32 Extended-ROM rev 40.60 (1993)(Commodore)(CD32).rom": [
|
||||
"5bef3d628ce59cc02a66e6e4ae0da48f60e78f7f"
|
||||
],
|
||||
"custom0.sf2": [
|
||||
"286b2e1fb21cc79851da01666db6c0b0e88f25e3"
|
||||
],
|
||||
"colecovision.rom": [
|
||||
"45bedc4cbdeac66c7df59e9e599195c778d86a92"
|
||||
],
|
||||
"coleco.rom": [
|
||||
"45bedc4cbdeac66c7df59e9e599195c778d86a92"
|
||||
],
|
||||
"boot.rom": [
|
||||
"45bedc4cbdeac66c7df59e9e599195c778d86a92"
|
||||
],
|
||||
"IPL.bin": [
|
||||
"80b8744ff5e43585392f55546bd03a673d11ef5f",
|
||||
"ef9194ab4804aa0aa8540d846caf291b28331165",
|
||||
"f27c63e5394e2fd1606f70df004c4fc2d6027700",
|
||||
"a1837968288253ed541f2b11440b68f5a9b33875",
|
||||
"f3cd0c7c61cbcefa85e7de3aff4cfa50bc508714"
|
||||
],
|
||||
"basic11.rom": [
|
||||
"9451a1a09d8f75944dbd6f91193fc360f1de80ac"
|
||||
],
|
||||
"basic21.bin": [
|
||||
"03bbb386cf530e804363acdfc1d13e64cf28af2e"
|
||||
],
|
||||
"exos21.bin": [
|
||||
"55315b20fecb4441a07ee4bc5dc7153f396e0a2e"
|
||||
],
|
||||
"sony-playstation:239665b1a3dade1b5a52c06338011044": [
|
||||
"343883a7b555646da8cee54aadd2795b6e7dd070"
|
||||
],
|
||||
@@ -72060,12 +72246,6 @@
|
||||
"sega-mega-cd:baca1df271d7c11fe50087c0358f4eb5": [
|
||||
"2b125c0545afa089b617f2558e686ea723bdc06e"
|
||||
],
|
||||
"sega-saturn:af5828fdff51384f99b3c4926be27762": [
|
||||
"2b8cb4f87580683eb4d760e4ed210813d667f0a2"
|
||||
],
|
||||
"saturn_bios.bin": [
|
||||
"2b8cb4f87580683eb4d760e4ed210813d667f0a2"
|
||||
],
|
||||
"sega-saturn:85ec9ca47d8f6807718151cbcca8b964": [
|
||||
"df94c5b4d47eb3cc404d88b33a8fda237eaf4720"
|
||||
],
|
||||
@@ -72087,185 +72267,24 @@
|
||||
"sega-saturn:0306c0e408d6682dd2d86324bd4ac661": [
|
||||
"8c031bf9908fd0142fdd10a9cdd79389f8a3f2fc"
|
||||
],
|
||||
"tos100uk.img": [
|
||||
"9a6e4c88533a9eaa4d55cdc040e47443e0226eb2"
|
||||
"gexpress.pce": [
|
||||
"014881a959e045e00f4db8f52955200865d40280"
|
||||
],
|
||||
"tos106de.img": [
|
||||
"3b8cf5ffa41b252eb67f8824f94608fa4005d6dd"
|
||||
"pcfxbios.bin": [
|
||||
"1a77fd83e337f906aecab27a1604db064cf10074"
|
||||
],
|
||||
"tos206us.img": [
|
||||
"ee58768bdfc602c9b14942ce5481e97dd24e7c83"
|
||||
"sgb2_bios.bin": [
|
||||
"93407ea10d2f30ab96a314d8eca44fe160aea734",
|
||||
"f282b3aaf98f8423dab7d77f1aa0192be630f2fb"
|
||||
],
|
||||
"bios7.bin": [
|
||||
"24f67bdea115a2c847c8813a262502ee1607b7df"
|
||||
"flash.bin": [
|
||||
"94d44d7f9529ec1642ba3771ed3c5f756d5bc872"
|
||||
],
|
||||
"bios9.bin": [
|
||||
"bfaac75f101c135e32e2aaf541de6b1be4c8c62d"
|
||||
"plus3e-3.rom": [
|
||||
"65f031caa8148a5493afe42c41f4929deab26b4e"
|
||||
],
|
||||
"dsi_bios7.bin": [
|
||||
"a3aa751eb6bdaaf8a827ba9e03576a6f1ab0f547"
|
||||
],
|
||||
"dsi_bios9.bin": [
|
||||
"7bf549b8be9e48ab0cdc9b0fdadd49a5131f97eb"
|
||||
],
|
||||
"gb_bios.bin": [
|
||||
"4ed31ec6b0b175bb109c0eb5fd3d193da823339f",
|
||||
"1db57a1e8b6e4096f811587f9eab0c6675fd9755"
|
||||
],
|
||||
"sgb_bios.bin": [
|
||||
"aa2f50a77dfb4823da96ba99309085a3c6278515",
|
||||
"369e6eb5e0c975eaa52a4a3f6ee07b2a3c3c16de"
|
||||
],
|
||||
"sgb1.boot.rom": [
|
||||
"aa2f50a77dfb4823da96ba99309085a3c6278515"
|
||||
],
|
||||
"SGB1.sfc": [
|
||||
"973e10840db683cf3faf61bd443090786b3a9f04"
|
||||
],
|
||||
"SGB1.sfc/program.rom": [
|
||||
"973e10840db683cf3faf61bd443090786b3a9f04"
|
||||
],
|
||||
"SGB2.sfc": [
|
||||
"e5b2922ca137051059e4269b236d07a22c07bc84"
|
||||
],
|
||||
"SGB2.sfc/program.rom": [
|
||||
"e5b2922ca137051059e4269b236d07a22c07bc84"
|
||||
],
|
||||
"fs-a1gt_kanjifont.rom": [
|
||||
"5aff2d9b6efc723bc395b0f96f0adfa83cc54a49"
|
||||
],
|
||||
"fs-a1wsx_kanjifont.rom": [
|
||||
"5aff2d9b6efc723bc395b0f96f0adfa83cc54a49"
|
||||
],
|
||||
"yrw801.rom": [
|
||||
"32760893ce06dbe3930627755ba065cc3d8ec6ca"
|
||||
],
|
||||
"fs-a1wsx_msx2psub.rom": [
|
||||
"fe0254cbfc11405b79e7c86c7769bd6322b04995"
|
||||
],
|
||||
"MSX2PEXT.rom": [
|
||||
"fe0254cbfc11405b79e7c86c7769bd6322b04995"
|
||||
],
|
||||
"fs-a1wsx_kanjibasic.rom": [
|
||||
"dcc3a67732aa01c4f2ee8d1ad886444a4dbafe06"
|
||||
],
|
||||
"fs-a1wsx_disk.rom": [
|
||||
"7ed7c55e0359737ac5e68d38cb6903f9e5d7c2b6"
|
||||
],
|
||||
"kick37350.A600": [
|
||||
"02843c4253bbd29aba535b0aa3bd9a85034ecde4"
|
||||
],
|
||||
"amiga-os-120.rom": [
|
||||
"11f9e62cf299f72184835b7b2a70a16333fc0d88"
|
||||
],
|
||||
"Kickstart v1.2 rev 33.180 (1986)(Commodore)(A500-A2000)[!].rom": [
|
||||
"11f9e62cf299f72184835b7b2a70a16333fc0d88"
|
||||
],
|
||||
"Kickstart v1.2 rev 33.180 (1986)(Commodore)(A500-A1000-A2000).rom": [
|
||||
"11f9e62cf299f72184835b7b2a70a16333fc0d88"
|
||||
],
|
||||
"amiga-os-130.rom": [
|
||||
"891e9a547772fe0c6c19b610baf8bc4ea7fcb785"
|
||||
],
|
||||
"Kickstart v1.3 rev 34.5 (1987)(Commodore)(A500-A1000-A2000-CDTV)[!].rom": [
|
||||
"891e9a547772fe0c6c19b610baf8bc4ea7fcb785"
|
||||
],
|
||||
"Kickstart v1.3 rev 34.5 (1987)(Commodore)(A500-A1000-A2000-CDTV).rom": [
|
||||
"891e9a547772fe0c6c19b610baf8bc4ea7fcb785"
|
||||
],
|
||||
"amiga-os-204.rom": [
|
||||
"c5839f5cb98a7a8947065c3ed2f14f5f42e334a1"
|
||||
],
|
||||
"Kickstart v2.04 rev 37.175 (1991)(Commodore)(A500+)[!].rom": [
|
||||
"c5839f5cb98a7a8947065c3ed2f14f5f42e334a1"
|
||||
],
|
||||
"Kickstart v2.04 rev 37.175 (1991)(Commodore)(A500+).rom": [
|
||||
"c5839f5cb98a7a8947065c3ed2f14f5f42e334a1"
|
||||
],
|
||||
"amiga-os-310-a600.rom": [
|
||||
"3b7f1493b27e212830f989f26ca76c02049f09ca"
|
||||
],
|
||||
"Kickstart v3.1 rev 40.63 (1993)(Commodore)(A500-A600-A2000)[!].rom": [
|
||||
"3b7f1493b27e212830f989f26ca76c02049f09ca"
|
||||
],
|
||||
"Kickstart v3.1 rev 40.63 (1993)(Commodore)(A500-A600-A2000).rom": [
|
||||
"3b7f1493b27e212830f989f26ca76c02049f09ca"
|
||||
],
|
||||
"amiga-ext-130-cdtv.rom": [
|
||||
"7ba40ffa17e500ed9fed041f3424bd81d9c907be"
|
||||
],
|
||||
"amiga-os-130-cdtv-ext.rom": [
|
||||
"7ba40ffa17e500ed9fed041f3424bd81d9c907be"
|
||||
],
|
||||
"CDTV Extended-ROM v1.0 (1991)(Commodore)(CDTV)[!].rom": [
|
||||
"7ba40ffa17e500ed9fed041f3424bd81d9c907be"
|
||||
],
|
||||
"CDTV Extended-ROM v1.0 (1992)(Commodore)(CDTV).rom": [
|
||||
"7ba40ffa17e500ed9fed041f3424bd81d9c907be"
|
||||
],
|
||||
"amiga-os-300-a1200.rom": [
|
||||
"70033828182fffc7ed106e5373a8b89dda76faa5"
|
||||
],
|
||||
"Kickstart v3.0 rev 39.106 (1992)(Commodore)(A1200)[!].rom": [
|
||||
"70033828182fffc7ed106e5373a8b89dda76faa5"
|
||||
],
|
||||
"amiga-os-310-a1200.rom": [
|
||||
"e21545723fe8374e91342617604f1b3d703094f1"
|
||||
],
|
||||
"Kickstart v3.1 rev 40.68 (1993)(Commodore)(A1200)[!].rom": [
|
||||
"e21545723fe8374e91342617604f1b3d703094f1"
|
||||
],
|
||||
"Kickstart v3.1 rev 40.68 (1993)(Commodore)(A1200).rom": [
|
||||
"e21545723fe8374e91342617604f1b3d703094f1"
|
||||
],
|
||||
"amiga-os-310-a4000.rom": [
|
||||
"5fe04842d04a489720f0f4bb0e46948199406f49"
|
||||
],
|
||||
"Kickstart v3.1 rev 40.68 (1993)(Commodore)(A4000).rom": [
|
||||
"5fe04842d04a489720f0f4bb0e46948199406f49"
|
||||
],
|
||||
"amiga-os-310-cd32.rom": [
|
||||
"3525be8887f79b5929e017b42380a79edfee542d"
|
||||
],
|
||||
"Kickstart v3.1 rev 40.60 (1993)(Commodore)(CD32).rom": [
|
||||
"3525be8887f79b5929e017b42380a79edfee542d"
|
||||
],
|
||||
"amiga-ext-310-cd32.rom": [
|
||||
"5bef3d628ce59cc02a66e6e4ae0da48f60e78f7f"
|
||||
],
|
||||
"amiga-os-310-cd32-ext.rom": [
|
||||
"5bef3d628ce59cc02a66e6e4ae0da48f60e78f7f"
|
||||
],
|
||||
"CD32 Extended-ROM rev 40.60 (1993)(Commodore)(CD32).rom": [
|
||||
"5bef3d628ce59cc02a66e6e4ae0da48f60e78f7f"
|
||||
],
|
||||
"custom0.sf2": [
|
||||
"286b2e1fb21cc79851da01666db6c0b0e88f25e3"
|
||||
],
|
||||
"colecovision.rom": [
|
||||
"45bedc4cbdeac66c7df59e9e599195c778d86a92"
|
||||
],
|
||||
"coleco.rom": [
|
||||
"45bedc4cbdeac66c7df59e9e599195c778d86a92"
|
||||
],
|
||||
"boot.rom": [
|
||||
"45bedc4cbdeac66c7df59e9e599195c778d86a92"
|
||||
],
|
||||
"IPL.bin": [
|
||||
"80b8744ff5e43585392f55546bd03a673d11ef5f",
|
||||
"ef9194ab4804aa0aa8540d846caf291b28331165",
|
||||
"f27c63e5394e2fd1606f70df004c4fc2d6027700",
|
||||
"a1837968288253ed541f2b11440b68f5a9b33875",
|
||||
"f3cd0c7c61cbcefa85e7de3aff4cfa50bc508714"
|
||||
],
|
||||
"basic11.rom": [
|
||||
"9451a1a09d8f75944dbd6f91193fc360f1de80ac"
|
||||
],
|
||||
"basic21.bin": [
|
||||
"03bbb386cf530e804363acdfc1d13e64cf28af2e"
|
||||
],
|
||||
"exos21.bin": [
|
||||
"55315b20fecb4441a07ee4bc5dc7153f396e0a2e"
|
||||
"zx48.rom": [
|
||||
"5ea7c2b824672e914525d1d5c419d71b84a426a2"
|
||||
],
|
||||
"apple2gs1.rom": [
|
||||
"e4fc7560b69d062cb2da5b1ffbe11cd1ca03cc37"
|
||||
@@ -72282,9 +72301,6 @@
|
||||
"quasi88/n88knj1.rom": [
|
||||
"82e11a177af6a5091dd67f50a2f4bafda84d6556"
|
||||
],
|
||||
"gexpress.pce": [
|
||||
"014881a959e045e00f4db8f52955200865d40280"
|
||||
],
|
||||
"FMPAC.rom": [
|
||||
"9d789166e3caf28e4742fe933d962e99618c633d"
|
||||
],
|
||||
@@ -72322,66 +72338,20 @@
|
||||
"d64tano.rom": [
|
||||
"1983b4fb398e3dd9668d424c666c5a0b3f1e2b69"
|
||||
],
|
||||
"fd502.rom": [
|
||||
"10bdc5aa2d7d7f205f67b47b19003a4bd89defd1"
|
||||
],
|
||||
"d64_1.rom": [
|
||||
"f119506eaa3b4b70b9aa0dd83761e8cbe043d042"
|
||||
],
|
||||
"fs-5500_disk.rom": [
|
||||
"78cd7f847e77fd8cd51a647efb2725ba93f4c471"
|
||||
],
|
||||
"pcfxbios.bin": [
|
||||
"1a77fd83e337f906aecab27a1604db064cf10074"
|
||||
"FONT.BMP": [
|
||||
"b4f14e58030ed40fff2dc312b58ea4440bdf8cc5"
|
||||
],
|
||||
"sgb2_bios.bin": [
|
||||
"93407ea10d2f30ab96a314d8eca44fe160aea734",
|
||||
"f282b3aaf98f8423dab7d77f1aa0192be630f2fb"
|
||||
],
|
||||
"flash.bin": [
|
||||
"94d44d7f9529ec1642ba3771ed3c5f756d5bc872"
|
||||
],
|
||||
"plus3e-3.rom": [
|
||||
"65f031caa8148a5493afe42c41f4929deab26b4e"
|
||||
],
|
||||
"zx48.rom": [
|
||||
"5ea7c2b824672e914525d1d5c419d71b84a426a2"
|
||||
],
|
||||
"amiga-os-300-a4000.rom": [
|
||||
"f0b4e9e29e12218c2d5bd7020e4e785297d91fd7"
|
||||
],
|
||||
"Kickstart v3.0 rev 39.106 (1992)(Commodore)(A4000)[!].rom": [
|
||||
"f0b4e9e29e12218c2d5bd7020e4e785297d91fd7"
|
||||
],
|
||||
"monit10.rom": [
|
||||
"4e83a94ae5155bbea14d7331a5a8db82457bd5ae"
|
||||
],
|
||||
"focal10.rom": [
|
||||
"6386e58bc1bba5e76baec9e8a1ca4b99dc3c573f"
|
||||
],
|
||||
"disk_327.rom": [
|
||||
"28eefbb63047b26e4aec104aeeca74e2f9d0276c"
|
||||
],
|
||||
"b11m_bos.rom": [
|
||||
"7e9a30e38d7b78981999821640a68a201bb6df01"
|
||||
],
|
||||
"b11m_ext.rom": [
|
||||
"f087af69044432a1ef2431a72ac06946e32f2dd3"
|
||||
],
|
||||
"bas11m_0.rom": [
|
||||
"9d76f3eefd64e032c763fa1ebf9cd3d9bd22317a"
|
||||
],
|
||||
"bas11m_1.rom": [
|
||||
"34fa37599f2f9eb607390ef2458a3c22d87f09a9"
|
||||
],
|
||||
"terak.rom": [
|
||||
"273a9933b68a290c5aedcd6d69faa7b1d22c0344"
|
||||
],
|
||||
"upd7801g.bin": [
|
||||
"6e89d1227581c76441a53d605f9e324185f1da33"
|
||||
],
|
||||
"upd7801g.bios": [
|
||||
"6e89d1227581c76441a53d605f9e324185f1da33"
|
||||
],
|
||||
"BS-X.bios": [
|
||||
"604556b2e62860af18db5a77f2956ebc75450020"
|
||||
"2608_RYM.WAV": [
|
||||
"c65592330c9dd84011151daed52f9aec926b7e56"
|
||||
],
|
||||
"N88_0.ROM": [
|
||||
"d1ae642aed4f0584eeb81ff50180db694e5101d4"
|
||||
@@ -72431,11 +72401,44 @@
|
||||
"jisyo.rom": [
|
||||
"deef0cc2a9734ba891a6d6c022aa70ffc66f783e"
|
||||
],
|
||||
"FONT.BMP": [
|
||||
"b4f14e58030ed40fff2dc312b58ea4440bdf8cc5"
|
||||
"BS-X.bios": [
|
||||
"604556b2e62860af18db5a77f2956ebc75450020"
|
||||
],
|
||||
"2608_RYM.WAV": [
|
||||
"c65592330c9dd84011151daed52f9aec926b7e56"
|
||||
"upd7801g.bin": [
|
||||
"6e89d1227581c76441a53d605f9e324185f1da33"
|
||||
],
|
||||
"upd7801g.bios": [
|
||||
"6e89d1227581c76441a53d605f9e324185f1da33"
|
||||
],
|
||||
"amiga-os-300-a4000.rom": [
|
||||
"f0b4e9e29e12218c2d5bd7020e4e785297d91fd7"
|
||||
],
|
||||
"Kickstart v3.0 rev 39.106 (1992)(Commodore)(A4000)[!].rom": [
|
||||
"f0b4e9e29e12218c2d5bd7020e4e785297d91fd7"
|
||||
],
|
||||
"monit10.rom": [
|
||||
"4e83a94ae5155bbea14d7331a5a8db82457bd5ae"
|
||||
],
|
||||
"focal10.rom": [
|
||||
"6386e58bc1bba5e76baec9e8a1ca4b99dc3c573f"
|
||||
],
|
||||
"disk_327.rom": [
|
||||
"28eefbb63047b26e4aec104aeeca74e2f9d0276c"
|
||||
],
|
||||
"b11m_bos.rom": [
|
||||
"7e9a30e38d7b78981999821640a68a201bb6df01"
|
||||
],
|
||||
"b11m_ext.rom": [
|
||||
"f087af69044432a1ef2431a72ac06946e32f2dd3"
|
||||
],
|
||||
"bas11m_0.rom": [
|
||||
"9d76f3eefd64e032c763fa1ebf9cd3d9bd22317a"
|
||||
],
|
||||
"bas11m_1.rom": [
|
||||
"34fa37599f2f9eb607390ef2458a3c22d87f09a9"
|
||||
],
|
||||
"terak.rom": [
|
||||
"273a9933b68a290c5aedcd6d69faa7b1d22c0344"
|
||||
],
|
||||
"dmg0_rom.bin": [
|
||||
"1db57a1e8b6e4096f811587f9eab0c6675fd9755"
|
||||
|
||||
@@ -1,8 +1,54 @@
|
||||
emulator: "emux_gb"
|
||||
type: alias
|
||||
alias_of: "gambatte"
|
||||
profiled_date: "2026-03-18"
|
||||
emulator: "emux (Game Boy)"
|
||||
type: libretro
|
||||
source: "https://github.com/libretro/emux"
|
||||
profiled_date: "2026-03-19"
|
||||
core_version: "0.1"
|
||||
display_name: "Nintendo - Game Boy / Color (Emux GB)"
|
||||
note: "This core uses the same BIOS/firmware as gambatte. See emulators/gambatte.yml for details."
|
||||
files: []
|
||||
cores:
|
||||
- emux_gb
|
||||
systems:
|
||||
- nintendo-gb
|
||||
|
||||
notes: |
|
||||
emux is a multi-system emulator by Sebastien Ronsse, supporting CHIP-8,
|
||||
Game Boy, NES, and Sega Master System as separate libretro cores.
|
||||
|
||||
NOT an alias of Gambatte. Completely different codebase and BIOS requirements.
|
||||
Gambatte uses gb_bios.bin/gbc_bios.bin (optional, HLE fallback).
|
||||
emux_gb uses dmg_boot.bin (required, no HLE, core fails without it).
|
||||
|
||||
Boot ROM loading in controllers/mapper/gb_mapper.c:
|
||||
static char *bootrom_path = "dmg_boot.bin"; (line 32)
|
||||
gb_mapper->bootrom = file_map(PATH_SYSTEM, bootrom_path, 0, BOOTROM_SIZE);
|
||||
If file_map returns NULL, gb_mapper_init returns false — core cannot start.
|
||||
No HLE fallback, no skip logic.
|
||||
|
||||
Boot ROM is 256 bytes (BOOTROM_SIZE in gb_mapper.h:10), mapped at 0x0000-0x00FF.
|
||||
When register 0xFF50 (BOOT_LOCK) is written with non-zero, the bootrom region
|
||||
is removed from the memory map (lock_writeb, gb_mapper.c:130-138).
|
||||
|
||||
Memory map from mach/gb.c:
|
||||
0x0000-0x00FF Boot ROM (overlays ROM0 until BOOT_LOCK)
|
||||
0x0000-0x3FFF ROM bank 0 (16 KB)
|
||||
0x4000-0x7FFF ROM bank 1 (switchable, 16 KB)
|
||||
0x8000-0x9FFF VRAM (8 KB)
|
||||
0xA000-0xBFFF External RAM (8 KB)
|
||||
0xC000-0xDFFF WRAM (8 KB)
|
||||
0xFE00-0xFE9F OAM (160 bytes)
|
||||
0xFF80-0xFFFE HRAM (127 bytes)
|
||||
|
||||
The .info declares firmware_count=1, firmware0_opt=false.
|
||||
is_experimental=true. Core does not support save states, rewind, or netplay.
|
||||
|
||||
system_directory obtained via RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY
|
||||
(libretro/libretro.c:23-25), passed as "system-dir" config param.
|
||||
|
||||
files:
|
||||
- name: "dmg_boot.bin"
|
||||
system: nintendo-gb
|
||||
description: "Game Boy (DMG) boot ROM"
|
||||
required: true
|
||||
size: 256
|
||||
md5: "32fbbd84168d3482956eb3c5051637f5"
|
||||
source_ref: "controllers/mapper/gb_mapper.c:32 (bootrom_path), gb_mapper.h:10 (BOOTROM_SIZE=256)"
|
||||
note: "Mapped at 0x0000-0x00FF, overlays ROM0 until BOOT_LOCK (0xFF50) written. No HLE — core fails to init without this file."
|
||||
|
||||
@@ -1,8 +1,51 @@
|
||||
emulator: "emux_nes"
|
||||
type: alias
|
||||
alias_of: "fceumm"
|
||||
profiled_date: "2026-03-18"
|
||||
emulator: "emux (NES)"
|
||||
type: libretro
|
||||
source: "https://github.com/libretro/emux"
|
||||
profiled_date: "2026-03-19"
|
||||
core_version: "0.1"
|
||||
display_name: "Nintendo - NES / Famicom (Emux NES)"
|
||||
note: "This core uses the same BIOS/firmware as fceumm. See emulators/fceumm.yml for details."
|
||||
cores:
|
||||
- emux_nes
|
||||
systems:
|
||||
- nes
|
||||
|
||||
notes: |
|
||||
emux is a multi-system emulator by Sebastien Ronsse, supporting CHIP-8,
|
||||
Game Boy, NES, and Sega Master System as separate libretro cores.
|
||||
|
||||
NOT an alias of FCEUmm. Completely different codebase.
|
||||
The NES core does not require any BIOS or firmware files.
|
||||
The .info declares no firmware_count.
|
||||
|
||||
The NES has no boot ROM. CPU starts at the reset vector address
|
||||
stored at 0xFFFC-0xFFFD (cpu/rp2a03.c RESET_VECTOR).
|
||||
|
||||
Cartridge loading in controllers/mapper/nes_mapper.c:
|
||||
- iNES header parsed (magic 0x1A53454E)
|
||||
- Mapper number extracted from flags6/flags7
|
||||
- Supported mappers: NROM (0), MMC1 (1), MMC3 (4)
|
||||
- PRG-ROM and CHR-ROM mapped via file_map(PATH_DATA, ...)
|
||||
|
||||
Memory map from mach/nes.c:
|
||||
CPU bus:
|
||||
0x0000-0x07FF WRAM (2 KB, mirrored to 0x1FFF)
|
||||
0x2000-0x2007 PPU registers (mirrored to 0x3FFF)
|
||||
0x4000-0x4013 APU registers
|
||||
0x4014 Sprite DMA
|
||||
0x4016-0x4017 Controller ports
|
||||
0x6000-0x7FFF SRAM
|
||||
0x8000-0xFFFF PRG-ROM
|
||||
|
||||
PPU bus:
|
||||
0x0000-0x1FFF CHR-ROM/RAM (pattern tables)
|
||||
0x2000-0x2FFF VRAM (nametables, 4 KB)
|
||||
0x3F00-0x3F1F Palette RAM
|
||||
|
||||
system_directory obtained via RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY
|
||||
(libretro/libretro.c:23-25) but not used for any file loading.
|
||||
|
||||
is_experimental=true. No save states, rewind, or netplay support.
|
||||
Supported extensions: nes, bin, rom.
|
||||
|
||||
files: []
|
||||
# No BIOS, firmware, or system files of any kind.
|
||||
|
||||
@@ -1,8 +1,55 @@
|
||||
emulator: "emux_sms"
|
||||
type: alias
|
||||
alias_of: "gearsystem"
|
||||
profiled_date: "2026-03-18"
|
||||
emulator: "emux (SMS)"
|
||||
type: libretro
|
||||
source: "https://github.com/libretro/emux"
|
||||
profiled_date: "2026-03-19"
|
||||
core_version: "0.1"
|
||||
display_name: "Sega - Master System (Emux SMS)"
|
||||
note: "This core uses the same BIOS/firmware as gearsystem. See emulators/gearsystem.yml for details."
|
||||
files: []
|
||||
cores:
|
||||
- emux_sms
|
||||
systems:
|
||||
- sms
|
||||
|
||||
notes: |
|
||||
emux is a multi-system emulator by Sebastien Ronsse, supporting CHIP-8,
|
||||
Game Boy, NES, and Sega Master System as separate libretro cores.
|
||||
|
||||
NOT an alias of GearSystem. Completely different codebase.
|
||||
|
||||
BIOS loading in controllers/mapper/sms_mapper.c:
|
||||
static char *bios_path = "bios.sms"; (line 33)
|
||||
#define BIOS_SIZE 0x2000 (line 7, 8192 bytes)
|
||||
sms_mapper->bios = file_map(PATH_SYSTEM, bios_path, 0, BIOS_SIZE);
|
||||
If file_map returns NULL, init returns false — core cannot start.
|
||||
No HLE fallback, no skip logic.
|
||||
|
||||
BIOS enable/disable via slot control register port write (lines 74-90):
|
||||
bios_disable bit in control union. Reset clears bios_disable and
|
||||
adds BIOS region to memory map. Writing bios_disable=1 removes
|
||||
the BIOS overlay, exposing cartridge ROM underneath.
|
||||
|
||||
Cartridge loading delegated to sega_mapper controller:
|
||||
ROM mapped via file_map(PATH_DATA, ...), 16 KB banks (BANK_SIZE=0x4000),
|
||||
3 bank slots (NUM_BANKS=3), bank select registers at 0xFFFD-0xFFFF.
|
||||
|
||||
Memory map from mach/sms.c:
|
||||
0x0000-0xBFFF Mapper (BIOS overlay + cartridge ROM banks)
|
||||
0xC000-0xDFFF RAM (8 KB)
|
||||
0xE000-0xFFFF RAM mirror
|
||||
|
||||
The .info declares firmware_count=1, firmware0_opt=false.
|
||||
is_experimental=true. No save states, rewind, or netplay support.
|
||||
Supported extensions: sms, bms, bin, rom.
|
||||
|
||||
system_directory obtained via RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY
|
||||
(libretro/libretro.c:23-25), passed as "system-dir" config param.
|
||||
BIOS loaded from this directory via PATH_SYSTEM.
|
||||
|
||||
files:
|
||||
- name: "bios.sms"
|
||||
system: sms
|
||||
description: "Sega Master System BIOS"
|
||||
required: true
|
||||
size: 8192
|
||||
md5: "840481177270d5642a14ca71ee72844c"
|
||||
source_ref: "controllers/mapper/sms_mapper.c:7 (BIOS_SIZE=0x2000), :33 (bios_path), :167-172 (file_map)"
|
||||
note: "Mapped at 0x0000, overlays cartridge ROM until bios_disable bit set in slot control register. No HLE — core fails to init without this file."
|
||||
|
||||
@@ -135,12 +135,14 @@ files:
|
||||
|
||||
- name: exdos14isdos10uk.rom
|
||||
path: ep128emu/roms/exdos14isdos10uk.rom
|
||||
md5: f91c4a507cc6895bdd9c43df4f021df3
|
||||
required: false
|
||||
hle_fallback: true
|
||||
system: enterprise-128
|
||||
has_builtin: true
|
||||
size: 32768
|
||||
note: "EXDOS 1.4 + IS-DOS 1.0 (UK). Used for IS-DOS disk mode instead of exdos13.rom."
|
||||
source_ref: "core/core.cpp:295-298"
|
||||
source_ref: "core/core.cpp:253, roms/roms.hpp:30104 (built-in data, MD5 verified)"
|
||||
|
||||
# ============================================================
|
||||
# Enterprise 64/128 - file I/O and extensions
|
||||
@@ -183,12 +185,14 @@ files:
|
||||
|
||||
- name: epdos16f.rom
|
||||
path: ep128emu/roms/epdos16f.rom
|
||||
md5: 6593dff00ab32a4b1fc084674ededf2b
|
||||
required: false
|
||||
hle_fallback: true
|
||||
system: enterprise-128
|
||||
has_builtin: true
|
||||
size: 32768
|
||||
note: "EP-DOS 1.6f. Provides HFONT and CLKOFF for Hungarian locale. 32K across segments 0x06-0x07."
|
||||
source_ref: "core/core.cpp:252-255"
|
||||
source_ref: "core/core.cpp:236-237, roms/roms.hpp:43780 (built-in data, MD5 verified)"
|
||||
|
||||
- name: brd.rom
|
||||
path: ep128emu/roms/brd.rom
|
||||
@@ -200,25 +204,9 @@ files:
|
||||
note: "German (BRD) language extension. Auto-loaded when content locale is German."
|
||||
source_ref: "core/core.cpp:260-261"
|
||||
|
||||
# ============================================================
|
||||
# Enterprise 64/128 - optional extras (in .info, not in source)
|
||||
# ============================================================
|
||||
|
||||
- name: epd19hft.rom
|
||||
path: ep128emu/roms/epd19hft.rom
|
||||
md5: 12cfc9c7e48c8a16c2e09edbd926d467
|
||||
required: false
|
||||
system: enterprise-128
|
||||
note: "EP-DOS 1.9 with Hungarian font. Listed in .info file as optional firmware."
|
||||
source_ref: "ep128emu_core_libretro.info:firmware8"
|
||||
|
||||
- name: zt19hfnt.rom
|
||||
path: ep128emu/roms/zt19hfnt.rom
|
||||
md5: 653daaf7b9b29c2c4e577f489580f247
|
||||
required: false
|
||||
system: enterprise-128
|
||||
note: "ZozoTools 1.9 with Hungarian font. Listed in .info file as optional firmware."
|
||||
source_ref: "ep128emu_core_libretro.info:firmware9"
|
||||
# epd19hft.rom and zt19hfnt.rom: listed in .info (firmware8, firmware9) but
|
||||
# NOT referenced in core source code and NOT in builtin_rom map (roms.hpp:46516-46538).
|
||||
# The core never loads these files. Excluded from profile to avoid misleading users.
|
||||
|
||||
# ============================================================
|
||||
# Videoton TVC
|
||||
|
||||
@@ -1,8 +1,56 @@
|
||||
emulator: "fbalpha2012"
|
||||
type: alias
|
||||
alias_of: "fbneo"
|
||||
profiled_date: "2026-03-18"
|
||||
emulator: "FB Alpha 2012"
|
||||
type: frozen_snapshot
|
||||
source: "https://github.com/libretro/fbalpha2012"
|
||||
upstream: "https://www.fbalpha.com"
|
||||
profiled_date: "2026-03-19"
|
||||
core_version: "v0.2.97.29"
|
||||
display_name: "Arcade (FB Alpha 2012)"
|
||||
note: "This core uses the same BIOS/firmware as fbneo. See emulators/fbneo.yml for details."
|
||||
cores:
|
||||
- fbalpha2012
|
||||
systems:
|
||||
- arcade
|
||||
- neogeo
|
||||
- cps1
|
||||
- cps2
|
||||
- cps3
|
||||
- pgm
|
||||
|
||||
notes: |
|
||||
Frozen snapshot of Final Burn Alpha v0.2.97.29 (circa 2012), full version.
|
||||
NOT an alias of FBNeo — different codebase, different ROM set compatibility.
|
||||
Supports CPS-1, CPS-2, CPS-3, Neo Geo, PGM, Sega, Cave, Toaplan, and more.
|
||||
Exists for RAM-constrained platforms. Most users should use FBNeo.
|
||||
|
||||
Multiple arcade systems have BIOS ROMs marked BRF_BIOS in source:
|
||||
|
||||
Neo Geo (d_neogeo.cpp:923-1011): neogeo.zip ROM set contains ~35 BIOS variants
|
||||
- 68K BIOS: asia-s3.rom (default), sp-s2.sp1, sp-s.sp1, sp-u2.sp1, sp-e.sp1,
|
||||
vs-bios.rom, sp-j2.sp1, sp1.jipan.1024, sp-45.sp1, japan-j3.bin, neo-po.bin,
|
||||
neo-epo.bin, neodebug.bin, sp-1v1_3db8c.bin, 16 Universe BIOS variants,
|
||||
neopen.sp1, plus PCB/CD/trackball BIOS variants
|
||||
- Z80 BIOS: sm1.sm1 (sound program)
|
||||
- Graphics: sfix.sfix (text layer tiles)
|
||||
- Data: 000-lo.lo (zoom table)
|
||||
- Neo Geo CD: neocd.bin
|
||||
|
||||
PGM (d_pgm.cpp:303-379): per-game BIOS sets in each ROM ZIP
|
||||
- pgm_t01s.rom (text tiles), pgm_m01s.rom (samples),
|
||||
pgm_p01s.u20/pgm_p02s.u20 (68K BIOS v1/v2),
|
||||
ddp3_bios.u37, bios.u42, svg_bios.u49 (custom per-game)
|
||||
|
||||
CPS-3 (d_cps3.cpp): 18 SH-2 BIOS variants, 512 KB each, region-specific
|
||||
(see fbalpha2012_cps3.yml for full list)
|
||||
|
||||
Sega System 16B (d_sys16b.cpp:8900): ism2006v00.u1
|
||||
|
||||
All BIOS files are part of their respective game ROM set ZIPs (arcade standard).
|
||||
None are standalone system directory files. The .info note "BIOS files must be
|
||||
inside the ROM directory" refers to the ROM directory containing the game ZIPs,
|
||||
which already include their BIOS — no separate action needed by the user.
|
||||
|
||||
ROM path: g_rom_dir from content path. g_system_dir set but unused.
|
||||
Extensions: iso|zip|7z. need_fullpath=true, block_extract=true.
|
||||
|
||||
files: []
|
||||
|
||||
exclusion_note: "All BIOS ROMs (Neo Geo, PGM, CPS-3, Sega) are inside game ROM set ZIPs, not standalone system files. neogeo.zip is a parent ROM set loaded automatically by the emulator when a Neo Geo game is started."
|
||||
|
||||
@@ -1,8 +1,39 @@
|
||||
emulator: "fbalpha2012_cps1"
|
||||
type: alias
|
||||
alias_of: "fbneo"
|
||||
profiled_date: "2026-03-18"
|
||||
emulator: "FB Alpha 2012 CPS-1"
|
||||
type: frozen_snapshot
|
||||
source: "https://github.com/libretro/fbalpha2012_cps1"
|
||||
upstream: "https://www.fbalpha.com"
|
||||
profiled_date: "2026-03-19"
|
||||
core_version: "v0.2.97.28"
|
||||
display_name: "Arcade (FB Alpha 2012 CPS-1)"
|
||||
note: "This core uses the same BIOS/firmware as fbneo. See emulators/fbneo.yml for details."
|
||||
cores:
|
||||
- fbalpha2012_cps1
|
||||
systems:
|
||||
- cps1
|
||||
|
||||
notes: |
|
||||
Frozen snapshot of Final Burn Alpha v0.2.97.28 (circa 2012), CPS-1 only.
|
||||
NOT an alias of FBNeo — different codebase, different ROM set compatibility.
|
||||
Exists for RAM-constrained platforms (3DS, embedded). Most users should use FBNeo.
|
||||
|
||||
CPS-1 has no system BIOS. Each game's ROM set is self-contained:
|
||||
68000 program ROMs + Z80 sound program + GFX ROMs + (optional) QSound samples.
|
||||
QSound games (Cadillacs & Dinosaurs, etc.) include audio samples directly in
|
||||
their ROM set (cd-q1.1k through cd-q4.4k), not as a separate DSP BIOS.
|
||||
|
||||
Board PLDs (buf1, ioa1, prg1, rom1, sou1) are listed in ROM definitions as
|
||||
BRF_OPT (optional). These are GAL/PAL dumps for hardware preservation —
|
||||
never loaded by the emulator.
|
||||
|
||||
ROM path: ROMs are loaded from the content directory (g_rom_dir), not system
|
||||
directory. system_directory is obtained (libretro.cpp:1645) but never used.
|
||||
|
||||
The .info note "BIOS files must be inside the ROM directory" is misleading —
|
||||
there are no BIOS files for CPS-1. This note is generic across all FBA cores.
|
||||
|
||||
244 ROM sets defined (488 STD_ROM_PICK/FN entries = 244 games).
|
||||
is_experimental=false, need_fullpath=true, block_extract=true.
|
||||
|
||||
files: []
|
||||
# CPS-1 has no BIOS. All required data is in per-game ROM sets (ZIP archives).
|
||||
|
||||
exclusion_note: "CPS-1 arcade hardware has no system BIOS. Each game ROM set is self-contained."
|
||||
|
||||
@@ -1,8 +1,42 @@
|
||||
emulator: "fbalpha2012_cps2"
|
||||
type: alias
|
||||
alias_of: "fbneo"
|
||||
profiled_date: "2026-03-18"
|
||||
emulator: "FB Alpha 2012 CPS-2"
|
||||
type: frozen_snapshot
|
||||
source: "https://github.com/libretro/fbalpha2012_cps2"
|
||||
upstream: "https://www.fbalpha.com"
|
||||
profiled_date: "2026-03-19"
|
||||
core_version: "v0.2.97.28"
|
||||
display_name: "Arcade (FB Alpha 2012 CPS-2)"
|
||||
note: "This core uses the same BIOS/firmware as fbneo. See emulators/fbneo.yml for details."
|
||||
cores:
|
||||
- fbalpha2012_cps2
|
||||
systems:
|
||||
- cps2
|
||||
|
||||
notes: |
|
||||
Frozen snapshot of Final Burn Alpha v0.2.97.28 (circa 2012), CPS-2 only.
|
||||
NOT an alias of FBNeo — different codebase, different ROM set compatibility.
|
||||
Exists for RAM-constrained platforms (3DS, embedded). Most users should use FBNeo.
|
||||
|
||||
CPS-2 has no system BIOS. Each game's ROM set is self-contained:
|
||||
68000 program ROMs + Z80 sound program + GFX ROMs + QSound samples.
|
||||
|
||||
QSound is emulated in software (qs_c.c:QscInit, panning table + DSP emulation).
|
||||
No external QSound DSP ROM (dl-1425.bin) needed — unlike MAME, FBA emulates
|
||||
the QSound chip directly without requiring the DSP program ROM.
|
||||
|
||||
Z80 sound program loaded from game ROM set into CpsZRom buffer (cps.c:451,
|
||||
BurnLoadRom). QSound samples loaded into CpsQSam. All from the game ZIP.
|
||||
|
||||
Board PLDs (buf1, ioa1, prg2, rom1) referenced via A_BOARD_QSOUND_PLDS
|
||||
macro, all marked BRF_OPT. GAL/PAL dumps for hardware preservation only.
|
||||
Zero BRF_BIOS entries in entire codebase.
|
||||
|
||||
ROM path: ROMs loaded from content directory (g_rom_dir, libretro.cpp).
|
||||
g_system_dir is set (line 988) but never used for any file loading.
|
||||
|
||||
The .info note "BIOS files must be inside the ROM directory" is misleading —
|
||||
there are no BIOS files for CPS-2. Generic note across all FBA cores.
|
||||
|
||||
need_fullpath=true, block_extract=true, extensions=zip.
|
||||
|
||||
files: []
|
||||
|
||||
exclusion_note: "CPS-2 arcade hardware has no system BIOS. QSound DSP emulated in software (qs_c.c). Each game ROM set is self-contained."
|
||||
|
||||
@@ -1,8 +1,55 @@
|
||||
emulator: "fbalpha2012_cps3"
|
||||
type: alias
|
||||
alias_of: "fbneo"
|
||||
profiled_date: "2026-03-18"
|
||||
emulator: "FB Alpha 2012 CPS-3"
|
||||
type: frozen_snapshot
|
||||
source: "https://github.com/libretro/fbalpha2012_cps3"
|
||||
upstream: "https://www.fbalpha.com"
|
||||
profiled_date: "2026-03-19"
|
||||
core_version: "v0.2.97.29"
|
||||
display_name: "Arcade (FB Alpha 2012 CPS-3)"
|
||||
note: "This core uses the same BIOS/firmware as fbneo. See emulators/fbneo.yml for details."
|
||||
cores:
|
||||
- fbalpha2012_cps3
|
||||
systems:
|
||||
- cps3
|
||||
|
||||
notes: |
|
||||
Frozen snapshot of Final Burn Alpha v0.2.97.29 (circa 2012), CPS-3 only.
|
||||
NOT an alias of FBNeo — different codebase, different ROM set compatibility.
|
||||
Exists for RAM-constrained platforms. Most users should use FBNeo.
|
||||
|
||||
CPS-3 uses a Hitachi SH-2 CPU with per-game, per-region BIOS ROMs.
|
||||
Each BIOS is a 512 KB (0x080000) 29F400 flash chip dump, region-locked.
|
||||
BIOS ROMs are marked BRF_ESS | BRF_BIOS in the ROM definitions.
|
||||
|
||||
BIOS is loaded into RomBios (cps3run.cpp:1048-1064), byte-swapped to
|
||||
little-endian, then decrypted via cps3_decrypt_bios() (cps3run.cpp:255).
|
||||
The SH-2 boots from BIOS, which initializes hardware and loads the game
|
||||
program from simulated flash/CD-ROM.
|
||||
|
||||
BIOS files are INSIDE the game ROM set ZIP (arcade standard), not separate
|
||||
system directory files. Each game variant has its own region BIOS.
|
||||
|
||||
Unique BIOS ROMs across all ROM sets (CRC32 from source):
|
||||
sfiii_usa.29f400.u2 0xfb172a8e SF3: New Generation (USA)
|
||||
sfiii_japan.29f400.u2 0x74205250 SF3: New Generation (Japan)
|
||||
sfiii_hispanic.29f400.u2 0xd2b3cd48 SF3: New Generation (Hispanic)
|
||||
sfiii_asia_nocd.29f400.u2 0x73e32463 SF3: New Generation (Asia NoCD)
|
||||
sfiii2_usa.29f400.u2 0x75dd72e0 SF3: 2nd Impact (USA)
|
||||
sfiii2_japan.29f400.u2 0xfaea0a3e SF3: 2nd Impact (Japan)
|
||||
sfiii2_asia_nocd.29f400.u2 0xfd297c0d SF3: 2nd Impact (Asia NoCD)
|
||||
sfiii3_euro.29f400.u2 0x30bbf293 SF3: 3rd Strike (Euro)
|
||||
sfiii3_usa.29f400.u2 0xecc545c1 SF3: 3rd Strike (USA)
|
||||
sfiii3_japan_nocd.29f400.u2 0x1edc6366 SF3: 3rd Strike (Japan NoCD)
|
||||
jojo_usa.29f400.u2 0x8d40f7be JoJo's Venture (USA)
|
||||
jojo_japan.29f400.u2 0x02778f60 JoJo's Venture (Japan)
|
||||
jojo_asia_nocd.29f400.u2 0x05b4f953 JoJo's Venture (Asia NoCD)
|
||||
jojoba_japan.29f400.u2 0x3085478c JoJo's Bizarre Adventure (Japan)
|
||||
jojoba_japan_nocd.29f400.u2 0x4dab19f5 JoJo's Bizarre Adventure (Japan NoCD)
|
||||
jojoba_euro_nocd.29f400.u2 0x1ee2d679 JoJo's Bizarre Adventure (Euro NoCD)
|
||||
redearth_euro.29f400.u2 0x02e0f336 Red Earth (Euro)
|
||||
warzard_japan.29f400.u2 0xf8e2f0c6 Warzard (Japan)
|
||||
|
||||
18 unique BIOS ROMs, all 512 KB, all region-specific.
|
||||
These are arcade ROM set components, not user-supplied BIOS files.
|
||||
|
||||
files: []
|
||||
|
||||
exclusion_note: "CPS-3 BIOS ROMs (SH-2 flash, 18 variants) are part of each game's ROM set ZIP archive, not standalone system files. Users obtain them as part of the arcade ROM set, not separately."
|
||||
|
||||
@@ -1,8 +1,59 @@
|
||||
emulator: "fbalpha2012_neogeo"
|
||||
type: alias
|
||||
alias_of: "fbneo"
|
||||
profiled_date: "2026-03-18"
|
||||
emulator: "FB Alpha 2012 Neo Geo"
|
||||
type: frozen_snapshot
|
||||
source: "https://github.com/libretro/fbalpha2012_neogeo"
|
||||
upstream: "https://www.fbalpha.com"
|
||||
profiled_date: "2026-03-19"
|
||||
core_version: "v0.2.97.29"
|
||||
display_name: "Arcade (FB Alpha 2012 Neo Geo)"
|
||||
note: "This core uses the same BIOS/firmware as fbneo. See emulators/fbneo.yml for details."
|
||||
cores:
|
||||
- fbalpha2012_neogeo
|
||||
systems:
|
||||
- neogeo
|
||||
|
||||
notes: |
|
||||
Frozen snapshot of Final Burn Alpha v0.2.97.29 (circa 2012), Neo Geo only.
|
||||
NOT an alias of FBNeo — different codebase, different ROM set compatibility.
|
||||
Exists for RAM-constrained platforms. Most users should use FBNeo.
|
||||
|
||||
Neo Geo requires a BIOS ROM set (neogeo.zip) placed alongside game ROMs.
|
||||
The BIOS set contains ~35 variants (d_neogeo.cpp:903-968), all BRF_BIOS:
|
||||
|
||||
68K main BIOS (BRF_SELECT, one active at a time):
|
||||
asia-s3.rom 0x91b64be3 MVS Asia/Europe ver. 6 (default)
|
||||
sp-s2.sp1 0x9036d879 MVS Asia/Europe ver. 5
|
||||
sp-s.sp1 0xc7f2fa45 MVS Asia/Europe ver. 3 (4 slot)
|
||||
sp-u2.sp1 0xe72943de MVS USA ver. 5 (2 slot)
|
||||
sp-e.sp1 0x2723a5b5 MVS USA ver. 5 (6 slot)
|
||||
vs-bios.rom 0xf0e8f27d MVS Japan ver. 6
|
||||
sp-j2.sp1 0xacede59c MVS Japan ver. 5
|
||||
sp1.jipan.1024 0x9fb0abe4 MVS Japan ver. 3 (4 slot)
|
||||
sp-45.sp1 0x03cc9f6a NEO-MVH MV1C (512 KB)
|
||||
japan-j3.bin 0xdff6d41f MVS Japan (J3)
|
||||
neo-po.bin 0x16d0c132 AES Japan
|
||||
neo-epo.bin 0xd27a71f1 AES Asia
|
||||
neodebug.bin 0x698ebb7d Development Kit
|
||||
sp-1v1_3db8c.bin 0x162f0ebe Deck ver. 6 (Git Ver 1.3)
|
||||
uni-bios_4_0.rom 0xa7aab458 Universe BIOS 4.0
|
||||
(+ Universe BIOS 1.0-3.3, 15 versions)
|
||||
neopen.sp1 0xcb915e76 NeoOpen BIOS v0.1 beta
|
||||
|
||||
System ROMs (always required):
|
||||
sm1.sm1 0x94416d67 Z80 BIOS (sound program)
|
||||
sfix.sfix 0xc2ea0cfd Text layer tiles (fix layer)
|
||||
000-lo.lo 0x5a86cff2 Zoom table
|
||||
|
||||
Special sets:
|
||||
236-bios.sp1 0x853e6b96 Trackball BIOS
|
||||
sp-4x.sp1 0xb4590283 PCB BIOS
|
||||
spj.sp1 0x148dd727 PCB BIOS (Japan)
|
||||
neocd.bin 0xdf9de490 Neo Geo CD BIOS
|
||||
|
||||
neogeo.zip is a parent ROM set, loaded from g_rom_dir alongside game ZIPs.
|
||||
g_system_dir is set (libretro.cpp:3016) but never used for file loading.
|
||||
|
||||
The .info note "BIOS files must be inside the ROM directory" is accurate here:
|
||||
neogeo.zip must be in the same directory as game ROM ZIPs.
|
||||
|
||||
files: []
|
||||
|
||||
exclusion_note: "Neo Geo BIOS is a parent ROM set (neogeo.zip) placed in the ROM directory alongside game ZIPs. Not a system directory file — follows arcade ROM set convention."
|
||||
|
||||
38
emulators/fbneo_cps12.yml
Normal file
38
emulators/fbneo_cps12.yml
Normal file
@@ -0,0 +1,38 @@
|
||||
emulator: "FinalBurn Neo (CPS-1/CPS-2)"
|
||||
type: pure_libretro
|
||||
source: "https://github.com/libretro/FBNeo"
|
||||
upstream: "https://neo-source.com"
|
||||
profiled_date: "2026-03-19"
|
||||
core_version: "v1.0.0.03"
|
||||
display_name: "Arcade (FinalBurn Neo) (CPS-1 / CPS-2)"
|
||||
cores:
|
||||
- fbneo_cps12
|
||||
systems:
|
||||
- cps1
|
||||
- cps2
|
||||
|
||||
notes: |
|
||||
CPS-1/CPS-2 subset of FinalBurn Neo, current codebase (not frozen).
|
||||
Exists for platforms that cannot run the full FBNeo core.
|
||||
|
||||
CPS-1 and CPS-2 have no system BIOS. Each game ROM set is self-contained:
|
||||
68000 program + Z80 sound + GFX + QSound samples (CPS-2).
|
||||
|
||||
Zero BRF_BIOS entries in d_cps1.cpp and d_cps2.cpp. QSound emulated in
|
||||
software (qs_c.cpp:QscInit, panning table). No DSP ROM needed.
|
||||
|
||||
Board PLDs (buf1, ioa1, prg1/prg2, rom1, sou1) are BRF_OPT — GAL/PAL
|
||||
dumps for hardware preservation, never loaded by the emulator.
|
||||
|
||||
The .info lists firmware_count=1 but it's only hiscore.dat (optional,
|
||||
high score database in system/fbneo/). Not a BIOS.
|
||||
|
||||
ROM path: game ZIPs loaded from content directory.
|
||||
system/fbneo/ used only for optional extras: hiscore.dat, samples,
|
||||
cheats, blend files, IPS patches.
|
||||
|
||||
need_fullpath=false, extensions=zip|7z, savestate=deterministic.
|
||||
|
||||
files: []
|
||||
|
||||
exclusion_note: "CPS-1/CPS-2 arcade hardware has no system BIOS. QSound DSP emulated in software. hiscore.dat is optional high score support, not firmware."
|
||||
@@ -24,6 +24,7 @@ data_directories:
|
||||
source_type: zip
|
||||
for_platforms: [retroarch, lakka, retropie]
|
||||
local_cache: data/dolphin-sys
|
||||
strip_components: 2
|
||||
exclude: [Themes]
|
||||
description: "Dolphin system data (GameSettings, DSP, fonts, shaders)"
|
||||
|
||||
@@ -33,6 +34,7 @@ data_directories:
|
||||
source_type: zip
|
||||
for_platforms: [retroarch, lakka, retropie]
|
||||
local_cache: data/ppsspp-assets
|
||||
strip_components: 1
|
||||
description: "PPSSPP fonts, backgrounds, shaders, lang files"
|
||||
|
||||
# ref: bluemsx-libretro/system/ — system/Databases/ + system/Machines/
|
||||
|
||||
@@ -16,7 +16,7 @@ platforms:
|
||||
source_format: clrmamepro_dat
|
||||
hash_type: sha1
|
||||
schedule: weekly
|
||||
emulators: [pcsx_rearmed, beetle_psx, genesis_plus_gx, flycast, melonds, mgba, snes9x, mupen64plus, beetle_saturn, dolphin]
|
||||
cores: all_libretro
|
||||
|
||||
batocera:
|
||||
config: batocera.yml
|
||||
@@ -27,7 +27,7 @@ platforms:
|
||||
source_format: python_dict
|
||||
hash_type: md5
|
||||
schedule: weekly
|
||||
emulators: [flycast, dolphin, pcsx2, duckstation, rpcs3, ppsspp, beetle_psx, beetle_saturn, genesis_plus_gx, picodrive, fbneo, puae, hatari, fuse, opera, bluemsx, fmsx, np2kai, quasi88]
|
||||
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]
|
||||
|
||||
recalbox:
|
||||
config: recalbox.yml
|
||||
@@ -38,7 +38,7 @@ platforms:
|
||||
source_format: xml
|
||||
hash_type: md5
|
||||
schedule: monthly
|
||||
emulators: [flycast, dolphin, pcsx2, beetle_psx, beetle_saturn, genesis_plus_gx, picodrive, fbneo, puae, hatari, opera, bluemsx, fmsx]
|
||||
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:
|
||||
config: retrobat.yml
|
||||
@@ -49,7 +49,7 @@ platforms:
|
||||
source_format: json
|
||||
hash_type: md5
|
||||
schedule: weekly
|
||||
emulators: [duckstation, pcsx2, dolphin, rpcs3, ppsspp, cemu, xemu, flycast, beetle_psx, beetle_saturn, genesis_plus_gx, puae, opera]
|
||||
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]
|
||||
|
||||
emudeck:
|
||||
config: emudeck.yml
|
||||
@@ -61,7 +61,6 @@ platforms:
|
||||
source_format: bash_script+csv
|
||||
hash_type: md5
|
||||
schedule: weekly
|
||||
emulators: [duckstation, pcsx2, dolphin, rpcs3, ppsspp, cemu, xemu, vita3k, citra, melonds]
|
||||
# dragoonDorise/EmuDeck = official repo (creator's account, 3.4k stars)
|
||||
# EmuDeck/emudeck.github.io = official wiki (org account)
|
||||
|
||||
@@ -71,11 +70,27 @@ platforms:
|
||||
logo: "https://raw.githubusercontent.com/libretro/retroarch-assets/master/src/xmb/flatui/lakka.svg"
|
||||
scraper: libretro
|
||||
inherits_from: retroarch
|
||||
cores: all_libretro
|
||||
schedule: weekly
|
||||
|
||||
retrodeck:
|
||||
config: retrodeck.yml
|
||||
status: active
|
||||
logo: "https://raw.githubusercontent.com/RetroDECK/RetroDECK/main/res/icon.svg"
|
||||
scraper: retrodeck
|
||||
source_url: "https://github.com/RetroDECK/components"
|
||||
source_format: github_component_manifests
|
||||
hash_type: md5
|
||||
schedule: monthly
|
||||
emulators: [duckstation, pcsx2, dolphin, rpcs3, ppsspp, cemu, xemu, vita3k, melonds, xroar]
|
||||
# RetroDECK/components = separate repo containing per-component build recipes
|
||||
# Each component/<name>/component_manifest.json declares BIOS requirements
|
||||
# Scraper enumerates top-level dirs via GitHub API, fetches each manifest directly
|
||||
|
||||
retropie:
|
||||
config: retropie.yml
|
||||
status: archived # Last release: v4.8 (March 2022) - no update in 4 years
|
||||
logo: "https://avatars.githubusercontent.com/u/11378204"
|
||||
scraper: null
|
||||
cores: all_libretro
|
||||
schedule: null
|
||||
|
||||
@@ -76,11 +76,6 @@ shared_groups:
|
||||
md5: "7da1e5b7c482d4108d22a5b09631d967"
|
||||
crc32: "d271798b"
|
||||
size: 524350
|
||||
# NP2kai also accepts FONT.ROM (uppercase) — ref: libretro.c:1813
|
||||
- name: FONT.ROM
|
||||
destination: np2kai/FONT.ROM
|
||||
required: true
|
||||
md5: "2af6179d7de4893ea0b705c00e9a98d6"
|
||||
- name: 2608_bd.wav
|
||||
destination: np2kai/2608_bd.wav
|
||||
required: true
|
||||
|
||||
@@ -5,6 +5,165 @@ source: "https://raw.githubusercontent.com/batocera-linux/batocera.linux/master/
|
||||
base_destination: bios
|
||||
hash_type: md5
|
||||
verification_mode: md5
|
||||
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
|
||||
systems:
|
||||
atari-400-800:
|
||||
files:
|
||||
|
||||
@@ -5,6 +5,199 @@ source: "https://gitlab.com/recalbox/recalbox/-/raw/master/board/recalbox/fsover
|
||||
base_destination: bios
|
||||
hash_type: md5
|
||||
verification_mode: md5
|
||||
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
|
||||
systems:
|
||||
commodore-amiga:
|
||||
files:
|
||||
|
||||
@@ -4,6 +4,7 @@ dat_version: v1.19.0
|
||||
homepage: https://www.retroarch.com
|
||||
source: https://github.com/libretro/libretro-database/blob/master/dat/System.dat
|
||||
base_destination: system
|
||||
cores: all_libretro
|
||||
hash_type: sha1
|
||||
verification_mode: existence
|
||||
systems:
|
||||
|
||||
@@ -5,6 +5,165 @@ source: "https://raw.githubusercontent.com/RetroBat-Official/emulatorlauncher/ma
|
||||
base_destination: bios
|
||||
hash_type: md5
|
||||
verification_mode: md5
|
||||
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
|
||||
systems:
|
||||
3do:
|
||||
files:
|
||||
|
||||
486
platforms/retrodeck.yml
Normal file
486
platforms/retrodeck.yml
Normal file
@@ -0,0 +1,486 @@
|
||||
platform: RetroDECK
|
||||
version: ''
|
||||
homepage: "https://retrodeck.net"
|
||||
source: "https://github.com/RetroDECK/components"
|
||||
base_destination: bios
|
||||
hash_type: md5
|
||||
verification_mode: md5
|
||||
systems:
|
||||
sony-playstation:
|
||||
files:
|
||||
- name: psxonpsp660.bin
|
||||
destination: psxonpsp660.bin
|
||||
required: true
|
||||
md5: c53ca5908936d412331790f4426c6c33
|
||||
- name: scph5500.bin
|
||||
destination: scph5500.bin
|
||||
required: true
|
||||
md5: 8dd7d5296a650fac7319bce665a6a53c
|
||||
- name: scph5501.bin
|
||||
destination: scph5501.bin
|
||||
required: true
|
||||
md5: 490f666e1afb15b7362b406ed1cea246
|
||||
- name: scph5502.bin
|
||||
destination: scph5502.bin
|
||||
required: true
|
||||
md5: 32736f17079d0b2b7024407c39bd3050
|
||||
- name: scph7001.bin
|
||||
destination: scph7001.bin
|
||||
required: true
|
||||
md5: 1e68c231d0896b7eadcad1d7d8e76129
|
||||
- name: scph7002.bin
|
||||
destination: scph7002.bin
|
||||
required: true
|
||||
md5: b9d9a0286c33dc6b7237bb13cd46fdee
|
||||
- name: scph7003.bin
|
||||
destination: scph7003.bin
|
||||
required: true
|
||||
md5: 490f666e1afb15b7362b406ed1cea246
|
||||
- name: scph7502.bin
|
||||
destination: scph7502.bin
|
||||
required: true
|
||||
md5: b9d9a0286c33dc6b7237bb13cd46fdee
|
||||
- name: "scph9002(7502).bin"
|
||||
destination: "scph9002(7502).bin"
|
||||
required: true
|
||||
md5: b9d9a0286c33dc6b7237bb13cd46fdee
|
||||
- name: ps1_rom.bin
|
||||
destination: ps1_rom.bin
|
||||
required: true
|
||||
md5: 81bbe60ba7a3d1cea1d48c14cbcc647b
|
||||
- name: scph1000.bin
|
||||
destination: scph1000.bin
|
||||
required: true
|
||||
md5: 239665b1a3dade1b5a52c06338011044
|
||||
- name: scph1001.bin
|
||||
destination: scph1001.bin
|
||||
required: true
|
||||
md5: 924e392ed05558ffdb115408c263dccf
|
||||
- name: scph1002.bin
|
||||
destination: scph1002.bin
|
||||
required: true
|
||||
md5: 54847e693405ffeb0359c6287434cbef
|
||||
- name: scph100.bin
|
||||
destination: scph100.bin
|
||||
required: true
|
||||
md5: 8abc1b549a4a80954addc48ef02c4521
|
||||
- name: scph101.bin
|
||||
destination: scph101.bin
|
||||
required: true
|
||||
md5: 6e3735ff4c7dc899ee98981385f6f3d0
|
||||
- name: scph102A.bin
|
||||
destination: scph102A.bin
|
||||
required: true
|
||||
md5: b10f5e0e3d9eb60e5159690680b1e774
|
||||
- name: scph102B.bin
|
||||
destination: scph102B.bin
|
||||
required: true
|
||||
md5: de93caec13d1a141a40a79f5c86168d6
|
||||
- name: scph102C.bin
|
||||
destination: scph102C.bin
|
||||
required: true
|
||||
md5: de93caec13d1a141a40a79f5c86168d6
|
||||
- name: scph3000.bin
|
||||
destination: scph3000.bin
|
||||
required: true
|
||||
md5: 849515939161e62f6b866f6853006780
|
||||
- name: scph3500.bin
|
||||
destination: scph3500.bin
|
||||
required: true
|
||||
md5: cba733ceeff5aef5c32254f1d617fa62
|
||||
- name: scph5000.bin
|
||||
destination: scph5000.bin
|
||||
required: true
|
||||
md5: eb201d2d98251a598af467d4347bb62f
|
||||
nintendo-ds:
|
||||
files:
|
||||
- name: bios7.bin
|
||||
destination: bios7.bin
|
||||
required: true
|
||||
md5: df692a80a5b1bc90728bc3dfc76cd948
|
||||
- name: bios9.bin
|
||||
destination: bios9.bin
|
||||
required: true
|
||||
md5: a392174eb3e572fed6447e956bde4b25
|
||||
- name: firmware.bin
|
||||
destination: firmware.bin
|
||||
required: true
|
||||
md5: e45033d9b0fa6b0de071292bba7c9d13
|
||||
- name: biosdsi7.bin
|
||||
destination: biosdsi7.bin
|
||||
required: true
|
||||
sha256: 2946281e730e71f7cafdb125f5cb60fed944ca5d610ee1e082c441b602b5f4e2
|
||||
- name: biosdsi9.bin
|
||||
destination: biosdsi9.bin
|
||||
required: true
|
||||
sha256: 47538922a8e8a8e79b922ff1203863ef5c40d9c54656a8d2c89c56ece52029ce
|
||||
- name: dsifirmware.bin
|
||||
destination: dsifirmware.bin
|
||||
required: true
|
||||
sha256: 11a150e3729bdde3ae8f5e7fc8be67d8bfbc548a1d2e523da58aa826ca0ffa99
|
||||
sony-playstation-2:
|
||||
files:
|
||||
- name: ps2-0100j-20000117.bin
|
||||
destination: ps2-0100j-20000117.bin
|
||||
required: true
|
||||
md5: acf4730ceb38ac9d8c7d8e21f2614600
|
||||
- name: ps2-0101j-20000217.bin
|
||||
destination: ps2-0101j-20000217.bin
|
||||
required: true
|
||||
md5: b1459d7446c69e3e97e6ace3ae23dd1c
|
||||
- name: ps2-0120j-20001027-185015.bin
|
||||
destination: ps2-0120j-20001027-185015.bin
|
||||
required: true
|
||||
md5: f63bc530bd7ad7c026fcd6f7bd0d9525
|
||||
- name: ps2-0120j-20001027-191435.bin
|
||||
destination: ps2-0120j-20001027-191435.bin
|
||||
required: true
|
||||
md5: cee06bd68c333fc5768244eae77e4495
|
||||
- name: ps2-0150j-20010118.bin
|
||||
destination: ps2-0150j-20010118.bin
|
||||
required: true
|
||||
md5: 815ac991d8bc3b364696bead3457de7d
|
||||
- name: ps2-0160j-20010427.bin
|
||||
destination: ps2-0160j-20010427.bin
|
||||
required: true
|
||||
md5: ab55cceea548303c22c72570cfd4dd71
|
||||
- name: ps2-0170j-20030206.bin
|
||||
destination: ps2-0170j-20030206.bin
|
||||
required: true
|
||||
md5: 312ad4816c232a9606e56f946bc0678a
|
||||
- name: ps2-0200j-20040614.bin
|
||||
destination: ps2-0200j-20040614.bin
|
||||
required: true
|
||||
md5: 0eee5d1c779aa50e94edd168b4ebf42e
|
||||
- name: ps2-0210j-20040917.bin
|
||||
destination: ps2-0210j-20040917.bin
|
||||
required: true
|
||||
md5: 1ad977bb539fc9448a08ab276a836bbc
|
||||
- name: ps2-0110a-20000727.bin
|
||||
destination: ps2-0110a-20000727.bin
|
||||
required: true
|
||||
md5: a20c97c02210f16678ca3010127caf36
|
||||
- name: ps2-0120a-20000902.bin
|
||||
destination: ps2-0120a-20000902.bin
|
||||
required: true
|
||||
md5: 8db2fbbac7413bf3e7154c1e0715e565
|
||||
- name: ps2-0150a-20001228.bin
|
||||
destination: ps2-0150a-20001228.bin
|
||||
required: true
|
||||
md5: 8accc3c49ac45f5ae2c5db0adc854633
|
||||
- name: ps2-0160a-20010427.bin
|
||||
destination: ps2-0160a-20010427.bin
|
||||
required: true
|
||||
md5: b107b5710042abe887c0f6175f6e94bb
|
||||
- name: ps2-0160a-20011004.bin
|
||||
destination: ps2-0160a-20011004.bin
|
||||
required: true
|
||||
md5: 7200a03d51cacc4c14fcdfdbc4898431
|
||||
- name: ps2-0160a-20020207.bin
|
||||
destination: ps2-0160a-20020207.bin
|
||||
required: true
|
||||
md5: d5ce2c7d119f563ce04bc04dbc3a323e
|
||||
- name: ps2-0170a-20030325.bin
|
||||
destination: ps2-0170a-20030325.bin
|
||||
required: true
|
||||
md5: 8aa12ce243210128c5074552d3b86251
|
||||
- name: ps2-0190a-20030623.bin
|
||||
destination: ps2-0190a-20030623.bin
|
||||
required: true
|
||||
md5: 35461cecaa51712b300b2d6798825048
|
||||
- name: ps2-0200a-20040614.bin
|
||||
destination: ps2-0200a-20040614.bin
|
||||
required: true
|
||||
md5: d333558cc14561c1fdc334c75d5f37b7
|
||||
- name: ps2-0220a-20050620.bin
|
||||
destination: ps2-0220a-20050620.bin
|
||||
required: true
|
||||
md5: 929a14baca1776b00869f983aa6e14d2
|
||||
- name: ps2-0220a-20060210.bin
|
||||
destination: ps2-0220a-20060210.bin
|
||||
required: true
|
||||
md5: cb801b7920a7d536ba07b6534d2433ca
|
||||
- name: ps2-0220a-20060905.bin
|
||||
destination: ps2-0220a-20060905.bin
|
||||
required: true
|
||||
md5: 40c11c063b3b9409aa5e4058e984e30c
|
||||
- name: ps2-0230a-20080220.bin
|
||||
destination: ps2-0230a-20080220.bin
|
||||
required: true
|
||||
md5: 21038400dc633070a78ad53090c53017
|
||||
- name: ps2-0120e-20000902.bin
|
||||
destination: ps2-0120e-20000902.bin
|
||||
required: true
|
||||
md5: b7fa11e87d51752a98b38e3e691cbf17
|
||||
- name: ps2-0150e-20001228.bin
|
||||
destination: ps2-0150e-20001228.bin
|
||||
required: true
|
||||
md5: 838544f12de9b0abc90811279ee223c8
|
||||
- name: ps2-0160e-20010704.bin
|
||||
destination: ps2-0160e-20010704.bin
|
||||
required: true
|
||||
md5: 491209dd815ceee9de02dbbc408c06d6
|
||||
- name: ps2-0160e-20011004.bin
|
||||
destination: ps2-0160e-20011004.bin
|
||||
required: true
|
||||
md5: 8359638e857c8bc18c3c18ac17d9cc3c
|
||||
- name: ps2-0160e-20020319.bin
|
||||
destination: ps2-0160e-20020319.bin
|
||||
required: true
|
||||
md5: 0d2228e6fd4fb639c9c39d077a9ec10c
|
||||
- name: ps2-0170e-20030227.bin
|
||||
destination: ps2-0170e-20030227.bin
|
||||
required: true
|
||||
md5: 6e69920fa6eef8522a1d688a11e41bc6
|
||||
- name: ps2-0190e-20030623.bin
|
||||
destination: ps2-0190e-20030623.bin
|
||||
required: true
|
||||
md5: bd6415094e1ce9e05daabe85de807666
|
||||
- name: ps2-0200e-20040614.bin
|
||||
destination: ps2-0200e-20040614.bin
|
||||
required: true
|
||||
md5: dc752f160044f2ed5fc1f4964db2a095
|
||||
- name: ps2-0220e-20050620.bin
|
||||
destination: ps2-0220e-20050620.bin
|
||||
required: true
|
||||
md5: 573f7d4a430c32b3cc0fd0c41e104bbd
|
||||
- name: ps2-0220e-20060210.bin
|
||||
destination: ps2-0220e-20060210.bin
|
||||
required: true
|
||||
md5: af60e6d1a939019d55e5b330d24b1c25
|
||||
- name: ps2-0220e-20060905.bin
|
||||
destination: ps2-0220e-20060905.bin
|
||||
required: true
|
||||
md5: 80bbb237a6af9c611df43b16b930b683
|
||||
- name: ps2-0230e-20080220.bin
|
||||
destination: ps2-0230e-20080220.bin
|
||||
required: true
|
||||
md5: dc69f0643a3030aaa4797501b483d6c4
|
||||
- name: ps2-0160h-20010730.bin
|
||||
destination: ps2-0160h-20010730.bin
|
||||
required: true
|
||||
md5: 352d2ff9b3f68be7e6fa7e6dd8389346
|
||||
- name: ps2-0160h-20020426.bin
|
||||
destination: ps2-0160h-20020426.bin
|
||||
required: true
|
||||
md5: 315a4003535dfda689752cb25f24785c
|
||||
- name: ps2-0190h-20030623.bin
|
||||
destination: ps2-0190h-20030623.bin
|
||||
required: true
|
||||
md5: 07b562a3f0c4b9a55834df9bbc9bd0c3
|
||||
- name: ps2-0200h-20040614.bin
|
||||
destination: ps2-0200h-20040614.bin
|
||||
required: true
|
||||
md5: 3e3e030c0f600442fa05b94f87a1e238
|
||||
- name: ps2-0220h-20060905.bin
|
||||
destination: ps2-0220h-20060905.bin
|
||||
required: true
|
||||
md5: c37bce95d32b2be480f87dd32704e664
|
||||
- name: ps2-0220h-20060210.bin
|
||||
destination: ps2-0220h-20060210.bin
|
||||
required: true
|
||||
md5: 549a66d0c698635ca9fa3ab012da7129
|
||||
- name: ps2-0190c-20030623.bin
|
||||
destination: ps2-0190c-20030623.bin
|
||||
required: true
|
||||
md5: 1b6e631b536247756287b916f9396872
|
||||
- name: ps2-0190r-20030623.bin
|
||||
destination: ps2-0190r-20030623.bin
|
||||
required: true
|
||||
md5: 0c13357c01a25a886db2356bbe73d9f0
|
||||
pico8:
|
||||
files:
|
||||
- name: pico8
|
||||
destination: pico-8/pico8
|
||||
required: true
|
||||
- name: pico8.dat
|
||||
destination: pico-8/pico8.dat
|
||||
required: true
|
||||
- name: pico8_dyn
|
||||
destination: pico-8/pico8_dyn
|
||||
required: true
|
||||
sony-psp:
|
||||
files:
|
||||
- name: ppge_atlas.zim
|
||||
destination: ppge_atlas.zim
|
||||
required: false
|
||||
md5: 866855cc330b9b95cc69135fb7b41d38
|
||||
xbox:
|
||||
files:
|
||||
- name: mcpx_1.0.bin
|
||||
destination: mcpx_1.0.bin
|
||||
required: true
|
||||
md5: d49c52a4102f6df7bcf8d0617ac475ed
|
||||
- name: Complex.bin
|
||||
destination: Complex.bin
|
||||
required: true
|
||||
- name: Complex_4627v1.03.bin
|
||||
destination: Complex_4627v1.03.bin
|
||||
required: true
|
||||
- name: Complex_4627.bin
|
||||
destination: Complex_4627.bin
|
||||
required: true
|
||||
dragon32:
|
||||
files:
|
||||
- name: d32.rom
|
||||
destination: d32.rom
|
||||
required: true
|
||||
md5: d35177f73cf303c5565aa13ef8ca5251,3420b96031078a4ef408cad7bf21a33f
|
||||
- name: d64rom1.rom
|
||||
destination: d64rom1.rom
|
||||
required: true
|
||||
md5: 5f0bee59710e55f5880e74890912ed78,6ab639e65c6e8fd832cb0d8ad4da1b60
|
||||
- name: d64tano.rom
|
||||
destination: d64tano.rom
|
||||
required: true
|
||||
md5: be9bc86ee5eb401d0a40d0377f65fefa
|
||||
- name: d64tano2.rom
|
||||
destination: d64tano2.rom
|
||||
required: true
|
||||
md5: fd91edce7be5e7c2d88e46b76956a8aa
|
||||
- name: d200rom1.rom
|
||||
destination: d200rom1.rom
|
||||
required: true
|
||||
md5: be9bc86ee5eb401d0a40d0377f65fefa
|
||||
- name: d200rom2.rom
|
||||
destination: d200rom2.rom
|
||||
required: true
|
||||
md5: fd91edce7be5e7c2d88e46b76956a8aa
|
||||
- name: ddos10.rom
|
||||
destination: ddos10.rom
|
||||
required: true
|
||||
md5: 1c965da49b6c5459b8353630aa1482e7
|
||||
- name: ddos11c.rom
|
||||
destination: ddos11c.rom
|
||||
required: true
|
||||
md5: d8429af1a12f7438a4bf88a5b934cb3a
|
||||
- name: ddos12a.rom
|
||||
destination: ddos12a.rom
|
||||
required: true
|
||||
md5: 55e2535dbbed7f1a26b5f263d7c72c63
|
||||
- name: ddos40.rom
|
||||
destination: ddos40.rom
|
||||
required: true
|
||||
md5: 9ddc388632cd3c376b164ba5cfc64329
|
||||
- name: ddos42.rom
|
||||
destination: ddos42.rom
|
||||
required: true
|
||||
md5: c956a854cbc4b9d1e69c000f78368668
|
||||
- name: deltados.rom
|
||||
destination: deltados.rom
|
||||
required: true
|
||||
md5: 024eac3db20f1b5cf98c30a0e4743201
|
||||
- name: dplus48.rom
|
||||
destination: dplus48.rom
|
||||
required: true
|
||||
md5: ee6f24d893a52b8efea9f787855456b5
|
||||
- name: dplus49b.rom
|
||||
destination: dplus49b.rom
|
||||
required: true
|
||||
md5: 56f1b97314e4ca82491c465bb887059e
|
||||
- name: dplus50.rom
|
||||
destination: dplus50.rom
|
||||
required: true
|
||||
md5: 35de5d28da507ebb213a26e04241d940
|
||||
- name: sdose6.rom
|
||||
destination: sdose6.rom
|
||||
required: true
|
||||
md5: 9d85e6b7133f915c021156f4b9cdb512
|
||||
- name: sdose8.rom
|
||||
destination: sdose8.rom
|
||||
required: true
|
||||
md5: 167f409b7a4b992faabb784b061ab4c6
|
||||
- name: cp400bas.rom
|
||||
destination: cp400bas.rom
|
||||
required: true
|
||||
md5: f73da4d73d6db5cdb8b3cb6a50415e38
|
||||
trs80coco:
|
||||
files:
|
||||
- name: cp400extbas.rom
|
||||
destination: cp400extbas.rom
|
||||
required: true
|
||||
md5: 091581001577b4a83ccfd511829de0f1
|
||||
- name: cp400dsk.rom
|
||||
destination: cp400dsk.rom
|
||||
required: true
|
||||
md5: 16d3ab9bc935f0d5651ca3f0e3030846
|
||||
- name: color64extbas.rom
|
||||
destination: color64extbas.rom
|
||||
required: true
|
||||
md5: 0d9264ffa95ba493f2b5b0d488a49e13
|
||||
- name: xroarbios.rom
|
||||
destination: xroarbios.rom
|
||||
required: true
|
||||
md5: 28dc97df470fb8660ef61b81dfd34f4a
|
||||
- name: bas10.rom
|
||||
destination: bas10.rom
|
||||
required: true
|
||||
md5: a74f3d95b395dad7cdca19d560eeea74
|
||||
- name: bas11.rom
|
||||
destination: bas11.rom
|
||||
required: true
|
||||
md5: c73fb4bff9621c5ab17f6220b20db82f
|
||||
- name: bas12.rom
|
||||
destination: bas12.rom
|
||||
required: true
|
||||
md5: c933316c7d939532a13648850c1c2aa6
|
||||
- name: bas13.rom
|
||||
destination: bas13.rom
|
||||
required: true
|
||||
md5: c2fc43556eb6b7b25bdf5955bd9df825
|
||||
- name: bas14.rom
|
||||
destination: bas14.rom
|
||||
required: true
|
||||
md5: ac33e16f677b4db52548d426174b1aaa
|
||||
- name: coco3.rom
|
||||
destination: coco3.rom
|
||||
required: true
|
||||
md5: 7233c6c429f3ce1c7392f28a933e0b6f
|
||||
- name: extbas10.rom
|
||||
destination: extbas10.rom
|
||||
required: true
|
||||
md5: fda72f415afe99b36f953bb9bc1253da
|
||||
- name: extbas11.rom
|
||||
destination: extbas11.rom
|
||||
required: true
|
||||
md5: 21070aa0496142b886c562bf76d7c113
|
||||
- name: disk10.rom
|
||||
destination: disk10.rom
|
||||
required: true
|
||||
md5: a64b3ef9efcc066b18d35b134068d1cc
|
||||
- name: disk11.rom
|
||||
destination: disk11.rom
|
||||
required: true
|
||||
md5: 8cab28f4b7311b8df63c07bb3b59bfd5
|
||||
- name: fd502.rom
|
||||
destination: fd502.rom
|
||||
required: true
|
||||
md5: 8cab28f4b7311b8df63c07bb3b59bfd5
|
||||
- name: fd502ds.rom
|
||||
destination: fd502ds.rom
|
||||
required: true
|
||||
md5: b2d43757dc6851d866021ff6c4f59205
|
||||
- name: coco3p.rom
|
||||
destination: coco3p.rom
|
||||
required: true
|
||||
md5: 4ae57e5a8e7494e5485446fefedb580b
|
||||
- name: CCNPATCH.cas
|
||||
destination: CCNPATCH.cas
|
||||
required: true
|
||||
md5: 2c395ad58a842711931679b554483a90
|
||||
- name: CCNPATCH.wav
|
||||
destination: CCNPATCH.wav
|
||||
required: true
|
||||
md5: a17397fb5408647a11d23bab959d1f97
|
||||
- name: DBPATCH.cas
|
||||
destination: DBPATCH.cas
|
||||
required: true
|
||||
md5: 65e1aab5fc5cba1b7374d9dddec25d62
|
||||
- name: DBPATCH.wav
|
||||
destination: DBPATCH.wav
|
||||
required: true
|
||||
md5: 6a07aeee664d81047672e6ff3541a12a
|
||||
- name: disk.rom
|
||||
destination: disk.rom
|
||||
required: true
|
||||
md5: 5bfcb1ae090159c8dea542b5a7c0840f
|
||||
@@ -81,8 +81,10 @@ def find_missing(config: dict, db: dict) -> list[dict]:
|
||||
found = False
|
||||
if sha1 and sha1 in db.get("files", {}):
|
||||
found = True
|
||||
elif md5 and md5 in db.get("indexes", {}).get("by_md5", {}):
|
||||
found = True
|
||||
elif md5:
|
||||
by_md5 = db.get("indexes", {}).get("by_md5", {})
|
||||
md5_list = [m.strip() for m in md5.split(",") if m.strip()]
|
||||
found = any(m in by_md5 for m in md5_list)
|
||||
|
||||
if not found:
|
||||
missing.append({
|
||||
|
||||
@@ -58,19 +58,28 @@ def md5sum(source: str | Path | object) -> str:
|
||||
return h.hexdigest()
|
||||
|
||||
|
||||
_md5_composite_cache: dict[str, str] = {}
|
||||
|
||||
|
||||
def md5_composite(filepath: str | Path) -> str:
|
||||
"""Compute composite MD5 of a ZIP - matches Recalbox's Zip::Md5Composite().
|
||||
|
||||
Sorts filenames alphabetically, reads each file's contents in order,
|
||||
feeds everything into a single MD5 hasher. The result is independent
|
||||
of ZIP compression level or metadata.
|
||||
of ZIP compression level or metadata. Results are cached per path.
|
||||
"""
|
||||
key = str(filepath)
|
||||
cached = _md5_composite_cache.get(key)
|
||||
if cached is not None:
|
||||
return cached
|
||||
with zipfile.ZipFile(filepath) as zf:
|
||||
names = sorted(n for n in zf.namelist() if not n.endswith("/"))
|
||||
h = hashlib.md5()
|
||||
for name in names:
|
||||
h.update(zf.read(name))
|
||||
return h.hexdigest()
|
||||
result = h.hexdigest()
|
||||
_md5_composite_cache[key] = result
|
||||
return result
|
||||
|
||||
|
||||
def load_platform_config(platform_name: str, platforms_dir: str = "platforms") -> dict:
|
||||
@@ -116,9 +125,14 @@ def load_platform_config(platform_name: str, platforms_dir: str = "platforms") -
|
||||
(f.get("name"), f.get("destination", f.get("name")))
|
||||
for f in system.get("files", [])
|
||||
}
|
||||
existing_lower = {
|
||||
f.get("destination", f.get("name", "")).lower()
|
||||
for f in system.get("files", [])
|
||||
}
|
||||
for gf in shared_groups[group_name]:
|
||||
key = (gf.get("name"), gf.get("destination", gf.get("name")))
|
||||
if key not in existing:
|
||||
dest_lower = gf.get("destination", gf.get("name", "")).lower()
|
||||
if key not in existing and dest_lower not in existing_lower:
|
||||
system.setdefault("files", []).append(gf)
|
||||
existing.add(key)
|
||||
|
||||
@@ -339,6 +353,44 @@ def group_identical_platforms(
|
||||
return [(group, representatives[fp]) for fp, group in fingerprints.items()]
|
||||
|
||||
|
||||
def resolve_platform_cores(
|
||||
config: dict, profiles: dict[str, dict],
|
||||
) -> 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
|
||||
|
||||
Alias profiles are always excluded (they point to another profile).
|
||||
"""
|
||||
cores_config = config.get("cores")
|
||||
|
||||
if cores_config == "all_libretro":
|
||||
return {
|
||||
name for name, p in profiles.items()
|
||||
if "libretro" in p.get("type", "")
|
||||
and p.get("type") != "alias"
|
||||
}
|
||||
|
||||
if isinstance(cores_config, list):
|
||||
core_set = set(cores_config)
|
||||
return {
|
||||
name for name in profiles
|
||||
if name in core_set
|
||||
and profiles[name].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"
|
||||
}
|
||||
|
||||
|
||||
def safe_extract_zip(zip_path: str, dest_dir: str) -> None:
|
||||
"""Extract a ZIP file safely, preventing zip-slip path traversal."""
|
||||
dest = os.path.realpath(dest_dir)
|
||||
|
||||
@@ -93,15 +93,6 @@ def cross_reference(
|
||||
for sys_id in systems:
|
||||
platform_names.update(declared.get(sys_id, set()))
|
||||
|
||||
# data_directories: check if the emulator's data_dir refs are provided
|
||||
# by ANY platform for ANY system (not limited to matching system IDs,
|
||||
# since emulator profiles and platforms use different ID conventions)
|
||||
all_plat_dd_refs = set()
|
||||
for dd_set in platform_data_dirs.values():
|
||||
all_plat_dd_refs.update(dd_set)
|
||||
emu_dd_refs = {dd.get("ref", "") for dd in profile.get("data_directories", [])}
|
||||
covered_dd = emu_dd_refs & all_plat_dd_refs
|
||||
|
||||
gaps = []
|
||||
covered = []
|
||||
for f in emu_files:
|
||||
@@ -117,9 +108,6 @@ def cross_reference(
|
||||
continue
|
||||
|
||||
in_platform = fname in platform_names
|
||||
# files covered by shared data_directories are effectively in the platform pack
|
||||
if not in_platform and covered_dd:
|
||||
in_platform = True
|
||||
in_repo = _find_in_repo(fname, by_name, by_name_lower)
|
||||
|
||||
entry = {
|
||||
|
||||
@@ -233,7 +233,8 @@ Examples:
|
||||
sys.exit(1)
|
||||
|
||||
import tempfile
|
||||
zip_path = os.path.join(tempfile.gettempdir(), asset["name"])
|
||||
fd, zip_path = tempfile.mkstemp(suffix=".zip")
|
||||
os.close(fd)
|
||||
|
||||
print(f"Downloading {asset['name']} ({asset['size']:,} bytes)...")
|
||||
download_file(asset["browser_download_url"], zip_path, asset["size"])
|
||||
|
||||
@@ -53,8 +53,9 @@ def _verify_file_hash(path: str, expected_sha1: str = "",
|
||||
return True
|
||||
hashes = compute_hashes(path)
|
||||
if expected_sha1:
|
||||
return hashes["sha1"] == expected_sha1
|
||||
return hashes["md5"] == expected_md5
|
||||
return hashes["sha1"].lower() == expected_sha1.lower()
|
||||
md5_list = [m.strip().lower() for m in expected_md5.split(",") if m.strip()]
|
||||
return hashes["md5"].lower() in md5_list
|
||||
|
||||
|
||||
def fetch_large_file(name: str, dest_dir: str = ".cache/large",
|
||||
@@ -94,7 +95,7 @@ def fetch_large_file(name: str, dest_dir: str = ".cache/large",
|
||||
def _sanitize_path(raw: str) -> str:
|
||||
"""Strip path traversal components from a relative path."""
|
||||
raw = raw.replace("\\", "/")
|
||||
parts = [p for p in raw.split("/") if p and p != ".."]
|
||||
parts = [p for p in raw.split("/") if p and p not in ("..", ".")]
|
||||
return "/".join(parts)
|
||||
|
||||
|
||||
@@ -181,7 +182,7 @@ def _collect_emulator_extras(
|
||||
base_dest: str,
|
||||
emu_profiles: dict | None = None,
|
||||
) -> list[dict]:
|
||||
"""Collect extra files from emulator profiles not in the platform pack.
|
||||
"""Collect core requirement files from emulator profiles not in the platform pack.
|
||||
|
||||
Uses the same system-overlap matching as verify.py cross-reference:
|
||||
- Matches emulators by shared system IDs with the platform
|
||||
@@ -236,15 +237,15 @@ def generate_pack(
|
||||
platform_display = config.get("platform", platform_name)
|
||||
base_dest = config.get("base_destination", "")
|
||||
|
||||
suffix = "Complete_Pack" if include_extras else "BIOS_Pack"
|
||||
zip_name = f"{platform_display.replace(' ', '_')}_{suffix}.zip"
|
||||
zip_name = f"{platform_display.replace(' ', '_')}_BIOS_Pack.zip"
|
||||
zip_path = os.path.join(output_dir, zip_name)
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
total_files = 0
|
||||
missing_files = []
|
||||
user_provided = []
|
||||
seen_destinations = set()
|
||||
seen_destinations: set[str] = set()
|
||||
seen_lower: set[str] = set() # case-insensitive dedup for Windows/macOS
|
||||
# Per-file status: worst status wins (missing > untested > ok)
|
||||
file_status: dict[str, str] = {}
|
||||
file_reasons: dict[str, str] = {}
|
||||
@@ -276,6 +277,7 @@ def generate_pack(
|
||||
if already_packed:
|
||||
continue
|
||||
seen_destinations.add(dedup_key)
|
||||
seen_lower.add(dedup_key.lower())
|
||||
file_status.setdefault(dedup_key, "ok")
|
||||
instructions = file_entry.get("instructions", "Please provide this file manually.")
|
||||
instr_name = f"INSTRUCTIONS_{file_entry['name']}.txt"
|
||||
@@ -299,9 +301,13 @@ def generate_pack(
|
||||
_extract_zip_to_archive(tmp_path, full_dest, zf)
|
||||
else:
|
||||
zf.write(tmp_path, full_dest)
|
||||
seen_destinations.add(dedup_key)
|
||||
seen_lower.add(dedup_key.lower())
|
||||
file_status.setdefault(dedup_key, "ok")
|
||||
total_files += 1
|
||||
else:
|
||||
missing_files.append(file_entry["name"])
|
||||
file_status[dedup_key] = "missing"
|
||||
finally:
|
||||
if os.path.exists(tmp_path):
|
||||
os.unlink(tmp_path)
|
||||
@@ -316,16 +322,26 @@ def generate_pack(
|
||||
if status == "hash_mismatch" and verification_mode != "existence":
|
||||
zf_name = file_entry.get("zipped_file")
|
||||
if zf_name and local_path:
|
||||
inner_md5 = file_entry.get("md5", "")
|
||||
inner_result = check_inside_zip(local_path, zf_name, inner_md5)
|
||||
if inner_result == "ok":
|
||||
inner_md5_raw = file_entry.get("md5", "")
|
||||
inner_md5_list = (
|
||||
[m.strip() for m in inner_md5_raw.split(",") if m.strip()]
|
||||
if inner_md5_raw else [""]
|
||||
)
|
||||
zip_ok = False
|
||||
last_result = "not_in_zip"
|
||||
for md5_candidate in inner_md5_list:
|
||||
last_result = check_inside_zip(local_path, zf_name, md5_candidate)
|
||||
if last_result == "ok":
|
||||
zip_ok = True
|
||||
break
|
||||
if zip_ok:
|
||||
file_status.setdefault(dedup_key, "ok")
|
||||
elif inner_result == "not_in_zip":
|
||||
elif last_result == "not_in_zip":
|
||||
file_status[dedup_key] = "untested"
|
||||
file_reasons[dedup_key] = f"{zf_name} not found inside ZIP"
|
||||
elif inner_result == "error":
|
||||
elif last_result == "error":
|
||||
file_status[dedup_key] = "untested"
|
||||
file_reasons[dedup_key] = f"cannot read ZIP"
|
||||
file_reasons[dedup_key] = "cannot read ZIP"
|
||||
else:
|
||||
file_status[dedup_key] = "untested"
|
||||
file_reasons[dedup_key] = f"{zf_name} MD5 mismatch inside ZIP"
|
||||
@@ -338,6 +354,7 @@ def generate_pack(
|
||||
if already_packed:
|
||||
continue
|
||||
seen_destinations.add(dedup_key)
|
||||
seen_lower.add(dedup_key.lower())
|
||||
|
||||
extract = file_entry.get("extract", False)
|
||||
if extract and local_path.endswith(".zip"):
|
||||
@@ -346,30 +363,33 @@ def generate_pack(
|
||||
zf.write(local_path, full_dest)
|
||||
total_files += 1
|
||||
|
||||
# Tier 2: emulator extras (files cores need but platform doesn't declare)
|
||||
extra_count = 0
|
||||
if include_extras:
|
||||
emu_profiles = load_emulator_profiles(emulators_dir)
|
||||
extras = _collect_emulator_extras(
|
||||
config, emulators_dir, db,
|
||||
seen_destinations, base_dest, emu_profiles,
|
||||
)
|
||||
for fe in extras:
|
||||
dest = _sanitize_path(fe.get("destination", fe["name"]))
|
||||
if not dest:
|
||||
continue
|
||||
full_dest = f"{base_dest}/{dest}" if base_dest else dest
|
||||
if full_dest in seen_destinations:
|
||||
continue
|
||||
# Core requirements: files platform's cores need but YAML doesn't declare
|
||||
emu_profiles = load_emulator_profiles(emulators_dir)
|
||||
core_files = _collect_emulator_extras(
|
||||
config, emulators_dir, db,
|
||||
seen_destinations, base_dest, emu_profiles,
|
||||
)
|
||||
core_count = 0
|
||||
for fe in core_files:
|
||||
dest = _sanitize_path(fe.get("destination", fe["name"]))
|
||||
if not dest:
|
||||
continue
|
||||
full_dest = f"{base_dest}/{dest}" if base_dest else dest
|
||||
if full_dest in seen_destinations:
|
||||
continue
|
||||
# Skip case-insensitive duplicates (Windows/macOS FS safety)
|
||||
if full_dest.lower() in seen_lower:
|
||||
continue
|
||||
|
||||
local_path, status = resolve_file(fe, db, bios_dir, zip_contents)
|
||||
if status in ("not_found", "external", "user_provided"):
|
||||
continue
|
||||
local_path, status = resolve_file(fe, db, bios_dir, zip_contents)
|
||||
if status in ("not_found", "external", "user_provided"):
|
||||
continue
|
||||
|
||||
zf.write(local_path, full_dest)
|
||||
seen_destinations.add(full_dest)
|
||||
extra_count += 1
|
||||
total_files += 1
|
||||
zf.write(local_path, full_dest)
|
||||
seen_destinations.add(full_dest)
|
||||
seen_lower.add(full_dest.lower())
|
||||
core_count += 1
|
||||
total_files += 1
|
||||
|
||||
# Data directories from _data_dirs.yml
|
||||
for sys_id, system in sorted(config.get("systems", {}).items()):
|
||||
@@ -392,9 +412,10 @@ def generate_pack(
|
||||
src = os.path.join(root, fname)
|
||||
rel = os.path.relpath(src, local_path)
|
||||
full = f"{dd_prefix}/{rel}"
|
||||
if full in seen_destinations:
|
||||
if full in seen_destinations or full.lower() in seen_lower:
|
||||
continue
|
||||
seen_destinations.add(full)
|
||||
seen_lower.add(full.lower())
|
||||
zf.write(src, full)
|
||||
total_files += 1
|
||||
|
||||
@@ -408,8 +429,8 @@ def generate_pack(
|
||||
parts.append(f"{files_untested} untested")
|
||||
if files_miss:
|
||||
parts.append(f"{files_miss} missing")
|
||||
extras_msg = f", {extra_count} extras" if extra_count else ""
|
||||
print(f" {zip_path}: {total_files} files packed{extras_msg}, {', '.join(parts)} [{verification_mode}]")
|
||||
baseline = total_files - core_count
|
||||
print(f" {zip_path}: {total_files} files packed ({baseline} baseline + {core_count} from cores), {', '.join(parts)} [{verification_mode}]")
|
||||
|
||||
for key, reason in sorted(file_reasons.items()):
|
||||
status = file_status.get(key, "")
|
||||
@@ -453,8 +474,9 @@ def main():
|
||||
parser.add_argument("--db", default=DEFAULT_DB_FILE, help="Path to database.json")
|
||||
parser.add_argument("--bios-dir", default=DEFAULT_BIOS_DIR)
|
||||
parser.add_argument("--output-dir", "-o", default=DEFAULT_OUTPUT_DIR)
|
||||
# --include-extras is now a no-op: core requirements are always included
|
||||
parser.add_argument("--include-extras", action="store_true",
|
||||
help="Include emulator-recommended files not declared by platform")
|
||||
help="(no-op) Core requirements are always included")
|
||||
parser.add_argument("--emulators-dir", default="emulators")
|
||||
parser.add_argument("--offline", action="store_true",
|
||||
help="Skip data directory freshness check, use cache only")
|
||||
|
||||
@@ -24,14 +24,19 @@ from verify import verify_platform
|
||||
def compute_coverage(platform_name: str, platforms_dir: str, db: dict) -> dict:
|
||||
config = load_platform_config(platform_name, platforms_dir)
|
||||
result = verify_platform(config, db)
|
||||
present = result["ok"] + result["untested"]
|
||||
pct = (present / result["total"] * 100) if result["total"] > 0 else 0
|
||||
sc = result.get("status_counts", {})
|
||||
ok = sc.get("ok", 0)
|
||||
untested = sc.get("untested", 0)
|
||||
missing = sc.get("missing", 0)
|
||||
total = result["total_files"]
|
||||
present = ok + untested
|
||||
pct = (present / total * 100) if total > 0 else 0
|
||||
return {
|
||||
"platform": config.get("platform", platform_name),
|
||||
"total": result["total"],
|
||||
"verified": result["ok"],
|
||||
"untested": result["untested"],
|
||||
"missing": result["missing"],
|
||||
"total": total,
|
||||
"verified": ok,
|
||||
"untested": untested,
|
||||
"missing": missing,
|
||||
"present": present,
|
||||
"percentage": pct,
|
||||
"mode": config.get("verification_mode", "existence"),
|
||||
|
||||
@@ -68,14 +68,19 @@ def _status_icon(pct: float) -> str:
|
||||
def compute_coverage(platform_name: str, platforms_dir: str, db: dict) -> dict:
|
||||
config = load_platform_config(platform_name, platforms_dir)
|
||||
result = verify_platform(config, db)
|
||||
present = result["ok"] + result["untested"]
|
||||
pct = (present / result["total"] * 100) if result["total"] > 0 else 0
|
||||
sc = result.get("status_counts", {})
|
||||
ok = sc.get("ok", 0)
|
||||
untested = sc.get("untested", 0)
|
||||
missing = sc.get("missing", 0)
|
||||
total = result["total_files"]
|
||||
present = ok + untested
|
||||
pct = (present / total * 100) if total > 0 else 0
|
||||
return {
|
||||
"platform": config.get("platform", platform_name),
|
||||
"total": result["total"],
|
||||
"verified": result["ok"],
|
||||
"untested": result["untested"],
|
||||
"missing": result["missing"],
|
||||
"total": total,
|
||||
"verified": ok,
|
||||
"untested": untested,
|
||||
"missing": missing,
|
||||
"present": present,
|
||||
"percentage": pct,
|
||||
"mode": config.get("verification_mode", "existence"),
|
||||
@@ -792,7 +797,7 @@ def main():
|
||||
# Replace nav section (everything from \nnav: to the next top-level key or EOF)
|
||||
import re
|
||||
if "\nnav:" in content:
|
||||
content = re.sub(r'\nnav:.*', '\n' + nav_yaml.rstrip(), content, count=1, flags=re.DOTALL)
|
||||
content = re.sub(r'\nnav:\n(?:[ \t]+.*\n?)*', '\n' + nav_yaml, content, count=1)
|
||||
else:
|
||||
content += "\n" + nav_yaml
|
||||
with open("mkdocs.yml", "w") as f:
|
||||
|
||||
@@ -21,13 +21,15 @@ import json
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def run(cmd: list[str], label: str) -> tuple[bool, str]:
|
||||
"""Run a command. Returns (success, captured_output)."""
|
||||
print(f"\n--- {label} ---", flush=True)
|
||||
start = time.monotonic()
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, cwd=".")
|
||||
repo_root = str(Path(__file__).resolve().parent.parent)
|
||||
result = subprocess.run(cmd, capture_output=True, text=True, cwd=repo_root)
|
||||
elapsed = time.monotonic() - start
|
||||
|
||||
output = result.stdout
|
||||
@@ -71,9 +73,18 @@ def parse_pack_counts(output: str) -> dict[str, tuple[int, int]]:
|
||||
if m:
|
||||
current_label = m.group(1)
|
||||
continue
|
||||
frac_m = re.search(r"(\d+)/(\d+) files OK", line)
|
||||
if frac_m and "files packed" in line:
|
||||
ok, total = int(frac_m.group(1)), int(frac_m.group(2))
|
||||
if "files packed" not in line:
|
||||
continue
|
||||
# New format: "622 files packed (359 baseline + 263 from cores), 358/359 files OK"
|
||||
base_m = re.search(r"\((\d+) baseline", line)
|
||||
ok_m = re.search(r"(\d+)/(\d+) files OK", line)
|
||||
if base_m and ok_m:
|
||||
baseline = int(base_m.group(1))
|
||||
ok, total = int(ok_m.group(1)), int(ok_m.group(2))
|
||||
counts[current_label] = (ok, total)
|
||||
elif ok_m:
|
||||
# Fallback: old format without baseline
|
||||
ok, total = int(ok_m.group(1)), int(ok_m.group(2))
|
||||
counts[current_label] = (ok, total)
|
||||
return counts
|
||||
|
||||
@@ -121,8 +132,9 @@ def main():
|
||||
help="Skip data directory refresh")
|
||||
parser.add_argument("--output-dir", default="dist",
|
||||
help="Pack output directory (default: dist/)")
|
||||
# --include-extras is now a no-op: core requirements are always included
|
||||
parser.add_argument("--include-extras", action="store_true",
|
||||
help="Include Tier 2 emulator extras in packs")
|
||||
help="(no-op) Core requirements are always included")
|
||||
args = parser.parse_args()
|
||||
|
||||
results = {}
|
||||
|
||||
@@ -120,7 +120,8 @@ def _is_safe_tar_member(member: tarfile.TarInfo, dest: Path) -> bool:
|
||||
if member.name.startswith("/") or ".." in member.name.split("/"):
|
||||
return False
|
||||
resolved = (dest / member.name).resolve()
|
||||
if not str(resolved).startswith(str(dest.resolve())):
|
||||
dest_str = str(dest.resolve()) + os.sep
|
||||
if not str(resolved).startswith(dest_str) and str(resolved) != str(dest.resolve()):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@ import sys
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
|
||||
import yaml
|
||||
|
||||
from .base_scraper import BaseScraper, BiosRequirement, fetch_github_latest_tag
|
||||
|
||||
PLATFORM_NAME = "batocera"
|
||||
@@ -23,6 +25,12 @@ SOURCE_URL = (
|
||||
"master/package/batocera/core/batocera-scripts/scripts/batocera-systems"
|
||||
)
|
||||
|
||||
CONFIGGEN_DEFAULTS_URL = (
|
||||
"https://raw.githubusercontent.com/batocera-linux/batocera.linux/"
|
||||
"master/package/batocera/core/batocera-configgen/configs/"
|
||||
"configgen-defaults.yml"
|
||||
)
|
||||
|
||||
SYSTEM_SLUG_MAP = {
|
||||
"atari800": "atari-400-800",
|
||||
"atari5200": "atari-5200",
|
||||
@@ -91,6 +99,28 @@ class Scraper(BaseScraper):
|
||||
def __init__(self, url: str = SOURCE_URL):
|
||||
super().__init__(url=url)
|
||||
|
||||
def _fetch_cores(self) -> list[str]:
|
||||
"""Extract core names from Batocera configgen-defaults.yml."""
|
||||
try:
|
||||
req = urllib.request.Request(
|
||||
CONFIGGEN_DEFAULTS_URL,
|
||||
headers={"User-Agent": "retrobios-scraper/1.0"},
|
||||
)
|
||||
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||
raw = resp.read().decode("utf-8")
|
||||
except urllib.error.URLError as e:
|
||||
raise ConnectionError(
|
||||
f"Failed to fetch {CONFIGGEN_DEFAULTS_URL}: {e}"
|
||||
) from e
|
||||
data = yaml.safe_load(raw)
|
||||
cores: set[str] = set()
|
||||
for system, cfg in data.items():
|
||||
if system == "default" or not isinstance(cfg, dict):
|
||||
continue
|
||||
core = cfg.get("core")
|
||||
if core:
|
||||
cores.add(core)
|
||||
return sorted(cores)
|
||||
|
||||
def _extract_systems_dict(self, raw: str) -> dict:
|
||||
"""Extract and parse the 'systems' dict from the Python source via ast.literal_eval."""
|
||||
@@ -101,14 +131,26 @@ class Scraper(BaseScraper):
|
||||
start = match.start() + raw[match.start():].index("{")
|
||||
depth = 0
|
||||
i = start
|
||||
in_str = False
|
||||
str_ch = None
|
||||
while i < len(raw):
|
||||
if raw[i] == "{":
|
||||
ch = raw[i]
|
||||
if in_str:
|
||||
if ch == '\\':
|
||||
i += 2
|
||||
continue
|
||||
if ch == str_ch:
|
||||
in_str = False
|
||||
elif ch in ('"', "'"):
|
||||
in_str = True
|
||||
str_ch = ch
|
||||
elif ch == "{":
|
||||
depth += 1
|
||||
elif raw[i] == "}":
|
||||
elif ch == "}":
|
||||
depth -= 1
|
||||
if depth == 0:
|
||||
break
|
||||
elif raw[i] == "#":
|
||||
elif ch == "#":
|
||||
while i < len(raw) and raw[i] != "\n":
|
||||
i += 1
|
||||
i += 1
|
||||
@@ -120,10 +162,15 @@ class Scraper(BaseScraper):
|
||||
in_string = False
|
||||
string_char = None
|
||||
clean = []
|
||||
for j, ch in enumerate(line):
|
||||
if ch in ('"', "'") and j > 0 and line[j - 1] == '\\':
|
||||
j = 0
|
||||
while j < len(line):
|
||||
ch = line[j]
|
||||
if ch == '\\' and j + 1 < len(line):
|
||||
clean.append(ch)
|
||||
elif ch in ('"', "'") and not in_string:
|
||||
clean.append(line[j + 1])
|
||||
j += 2
|
||||
continue
|
||||
if ch in ('"', "'") and not in_string:
|
||||
in_string = True
|
||||
string_char = ch
|
||||
clean.append(ch)
|
||||
@@ -134,6 +181,7 @@ class Scraper(BaseScraper):
|
||||
break
|
||||
else:
|
||||
clean.append(ch)
|
||||
j += 1
|
||||
lines.append("".join(clean))
|
||||
|
||||
clean_dict_str = "\n".join(lines)
|
||||
@@ -226,6 +274,7 @@ class Scraper(BaseScraper):
|
||||
"base_destination": "bios",
|
||||
"hash_type": "md5",
|
||||
"verification_mode": "md5",
|
||||
"cores": self._fetch_cores(),
|
||||
"systems": systems,
|
||||
}
|
||||
|
||||
|
||||
@@ -227,8 +227,10 @@ class Scraper(BaseScraper):
|
||||
continue
|
||||
system = FUNCTION_HASH_MAP[func_name]
|
||||
func_start = func_match.start()
|
||||
remaining = script[func_start:]
|
||||
local_match = _RE_LOCAL_HASHES.search(remaining)
|
||||
next_func = _RE_FUNC.search(script, func_match.end())
|
||||
func_end = next_func.start() if next_func else len(script)
|
||||
func_body = script[func_start:func_end]
|
||||
local_match = _RE_LOCAL_HASHES.search(func_body)
|
||||
if local_match:
|
||||
hashes_raw = local_match.group(1)
|
||||
hashes = [h.strip() for h in hashes_raw.split() if h.strip()]
|
||||
|
||||
@@ -88,6 +88,20 @@ class Scraper(BaseScraper):
|
||||
def __init__(self, url: str = SOURCE_URL):
|
||||
super().__init__(url=url)
|
||||
|
||||
def _fetch_cores(self) -> list[str]:
|
||||
"""Extract unique core names from es_bios.xml bios elements."""
|
||||
raw = self._fetch_raw()
|
||||
root = ET.fromstring(raw)
|
||||
cores: set[str] = set()
|
||||
for bios_elem in root.findall(".//system/bios"):
|
||||
raw_core = bios_elem.get("core", "").strip()
|
||||
if not raw_core:
|
||||
continue
|
||||
for part in raw_core.split(","):
|
||||
name = part.strip()
|
||||
if name:
|
||||
cores.add(name)
|
||||
return sorted(cores)
|
||||
|
||||
def fetch_requirements(self) -> list[BiosRequirement]:
|
||||
"""Parse es_bios.xml and return BIOS requirements."""
|
||||
@@ -214,6 +228,7 @@ class Scraper(BaseScraper):
|
||||
"base_destination": "bios",
|
||||
"hash_type": "md5",
|
||||
"verification_mode": "md5",
|
||||
"cores": self._fetch_cores(),
|
||||
"systems": systems,
|
||||
}
|
||||
|
||||
|
||||
579
scripts/scraper/retrodeck_scraper.py
Normal file
579
scripts/scraper/retrodeck_scraper.py
Normal file
@@ -0,0 +1,579 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Scraper for RetroDECK BIOS requirements.
|
||||
|
||||
Source: https://github.com/RetroDECK/components
|
||||
Format: component_manifest.json committed at <component>/component_manifest.json
|
||||
Hash: MD5 primary, SHA256 for some entries (melonDS DSi BIOS)
|
||||
|
||||
RetroDECK verification logic:
|
||||
- MD5 or SHA256 checked against expected value per file
|
||||
- MD5 may be a list of multiple accepted hashes (xroar ROM variants) — joined
|
||||
as comma-separated string per retrobios convention
|
||||
- Files may declare paths via $bios_path, $saves_path, or $roms_path tokens
|
||||
- $saves_path entries (GameCube memory card directories) are excluded —
|
||||
these are directory placeholders, not BIOS files
|
||||
- $roms_path entries are included with a roms/ prefix in destination,
|
||||
consistent with Batocera's saves/ destination convention
|
||||
- Entries with no hash are emitted without an md5 field (existence-only),
|
||||
which is valid per the platform schema (e.g. pico-8 executables)
|
||||
|
||||
Component structure:
|
||||
RetroDECK/components (GitHub, main branch)
|
||||
├── <component>/component_manifest.json <- fetched directly via raw URL
|
||||
├── archive_later/ <- skipped
|
||||
└── archive_old/ <- skipped
|
||||
|
||||
BIOS may appear in three locations within a manifest:
|
||||
- top-level 'bios' key (melonDS, xemu, xroar, pico-8)
|
||||
- preset_actions.bios (duckstation, dolphin, pcsx2, ppsspp)
|
||||
- cores.bios (not yet seen in practice, kept for safety)
|
||||
|
||||
ppsspp quirk: preset_actions.bios is a bare dict, not a list.
|
||||
|
||||
Adding to watch.yml (maintainer step):
|
||||
from scraper.retrodeck_scraper import Scraper as RDS
|
||||
config = RDS().generate_platform_yaml()
|
||||
with open('platforms/retrodeck.yml', 'w') as f:
|
||||
yaml.dump(config, f, default_flow_style=False, allow_unicode=True, sort_keys=False)
|
||||
print(f'RetroDECK: {len(config["systems"])} systems, version={config["version"]}')
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
from .base_scraper import BaseScraper, BiosRequirement, fetch_github_latest_version
|
||||
except ImportError:
|
||||
from base_scraper import BaseScraper, BiosRequirement, fetch_github_latest_version
|
||||
|
||||
PLATFORM_NAME = "retrodeck"
|
||||
|
||||
COMPONENTS_REPO = "RetroDECK/components"
|
||||
COMPONENTS_BRANCH = "main"
|
||||
COMPONENTS_API_URL = (
|
||||
f"https://api.github.com/repos/{COMPONENTS_REPO}"
|
||||
f"/git/trees/{COMPONENTS_BRANCH}?recursive=0"
|
||||
)
|
||||
RAW_BASE_URL = (
|
||||
f"https://raw.githubusercontent.com/{COMPONENTS_REPO}"
|
||||
f"/{COMPONENTS_BRANCH}"
|
||||
)
|
||||
|
||||
# Top-level directories to ignore when enumerating components
|
||||
SKIP_DIRS = {"archive_later", "archive_old", "automation-tools", ".github"}
|
||||
|
||||
# Default local path for --manifests-dir (standard flatpak install)
|
||||
DEFAULT_LOCAL_MANIFESTS = (
|
||||
"/var/lib/flatpak/app/net.retrodeck.retrodeck"
|
||||
"/current/active/files/retrodeck/components"
|
||||
)
|
||||
|
||||
# RetroDECK system ID -> retrobios slug.
|
||||
# IDs absent from this map pass through unchanged (maintainer decides on slug).
|
||||
# IDs mapped to None are skipped entirely (no retrobios equivalent).
|
||||
SYSTEM_SLUG_MAP: dict[str, str | None] = {
|
||||
# Nintendo
|
||||
"nes": "nintendo-nes",
|
||||
"snes": "nintendo-snes",
|
||||
"snesna": "nintendo-snes",
|
||||
"n64": "nintendo-64",
|
||||
"n64dd": "nintendo-64dd",
|
||||
"gc": "nintendo-gamecube",
|
||||
"wii": "wii", # no retrobios slug yet — passes through
|
||||
"wiiu": "nintendo-wii-u",
|
||||
"switch": "nintendo-switch",
|
||||
"gb": "nintendo-gb",
|
||||
"gbc": "nintendo-gbc",
|
||||
"gba": "nintendo-gba",
|
||||
"nds": "nintendo-ds",
|
||||
"3ds": "nintendo-3ds",
|
||||
"n3ds": "nintendo-3ds", # azahar uses n3ds
|
||||
"fds": "nintendo-fds",
|
||||
"sgb": "nintendo-sgb",
|
||||
"virtualboy": "nintendo-virtual-boy",
|
||||
# Sony
|
||||
"psx": "sony-playstation",
|
||||
"ps2": "sony-playstation-2",
|
||||
"ps3": "sony-playstation-3",
|
||||
"psp": "sony-psp",
|
||||
"psvita": "sony-psvita",
|
||||
# Sega
|
||||
"megadrive": "sega-mega-drive",
|
||||
"genesis": "sega-mega-drive",
|
||||
"megacd": "sega-mega-cd",
|
||||
"megacdjp": "sega-mega-cd",
|
||||
"segacd": "sega-mega-cd",
|
||||
"saturn": "sega-saturn",
|
||||
"saturnjp": "sega-saturn",
|
||||
"dreamcast": "sega-dreamcast",
|
||||
"naomi": "sega-dreamcast-arcade",
|
||||
"naomi2": "sega-dreamcast-arcade",
|
||||
"atomiswave": "sega-dreamcast-arcade",
|
||||
"sega32x": "sega32x",
|
||||
"sega32xjp": "sega32x",
|
||||
"sega32xna": "sega32x",
|
||||
"gamegear": "sega-game-gear",
|
||||
"mastersystem": "sega-master-system",
|
||||
# NEC
|
||||
"tg16": "nec-pc-engine",
|
||||
"tg-cd": "nec-pc-engine",
|
||||
"pcengine": "nec-pc-engine",
|
||||
"pcenginecd": "nec-pc-engine",
|
||||
"pcfx": "nec-pc-fx",
|
||||
# SNK
|
||||
"neogeo": "snk-neogeo",
|
||||
"neogeocd": "snk-neogeo-cd",
|
||||
"neogeocdjp": "snk-neogeo-cd",
|
||||
# Atari
|
||||
"atari2600": "atari2600", # no retrobios slug yet — passes through
|
||||
"atari800": "atari-400-800",
|
||||
"atari5200": "atari-5200",
|
||||
"atari7800": "atari-7800",
|
||||
"atarilynx": "atari-lynx",
|
||||
"atarist": "atari-st",
|
||||
"atarijaguar": "jaguar",
|
||||
# Panasonic / Philips
|
||||
"3do": "panasonic-3do",
|
||||
"cdimono1": "cdi",
|
||||
"cdtv": "amigacdtv",
|
||||
# Microsoft
|
||||
"xbox": "xbox",
|
||||
# Commodore
|
||||
"amiga": "commodore-amiga",
|
||||
"amigacd32": "amigacd32",
|
||||
"c64": "commodore-c64",
|
||||
# Tandy / Dragon
|
||||
"coco": "trs80coco",
|
||||
"dragon32": "dragon32",
|
||||
"tanodragon": "dragon32", # Tano Dragon is a Dragon 32 clone
|
||||
# Other
|
||||
"colecovision": "coleco-colecovision",
|
||||
"intellivision": "mattel-intellivision",
|
||||
"o2em": "magnavox-odyssey2",
|
||||
"msx": "microsoft-msx",
|
||||
"msx2": "microsoft-msx",
|
||||
"fmtowns": "fmtowns",
|
||||
"scummvm": "scummvm",
|
||||
"dos": "dos",
|
||||
# Explicitly skipped — no retrobios equivalent
|
||||
"mess": None,
|
||||
}
|
||||
|
||||
# Matches all saves_path typo variants seen in the wild:
|
||||
# $saves_path, $saves_paths_path, $saves_paths_paths_path, etc.
|
||||
_SAVES_PATH_RE = re.compile(r"^\$saves_\w+/")
|
||||
|
||||
|
||||
def _fetch_bytes(url: str, token: str | None = None) -> bytes:
|
||||
headers = {"User-Agent": "retrobios-scraper/1.0"}
|
||||
if token:
|
||||
headers["Authorization"] = f"token {token}"
|
||||
req = urllib.request.Request(url, headers=headers)
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||
return resp.read()
|
||||
except urllib.error.URLError as e:
|
||||
raise ConnectionError(f"Failed to fetch {url}: {e}") from e
|
||||
|
||||
|
||||
def _fetch_json(url: str, token: str | None = None) -> dict | list:
|
||||
return json.loads(_fetch_bytes(url, token).decode("utf-8"))
|
||||
|
||||
|
||||
def _resolve_destination(raw_path: str, filename: str) -> str | None:
|
||||
"""Resolve a RetroDECK path token to a retrobios destination string.
|
||||
|
||||
Returns None if the entry should be excluded ($saves_path variants).
|
||||
$bios_path -> strip prefix; destination is bios-relative.
|
||||
$roms_path -> preserve roms/ prefix (Batocera saves/ convention).
|
||||
Bare directory paths get filename appended.
|
||||
"""
|
||||
if _SAVES_PATH_RE.match(raw_path):
|
||||
return None
|
||||
|
||||
if raw_path.startswith("$bios_path/"):
|
||||
remainder = raw_path[len("$bios_path/"):].strip("/")
|
||||
if not remainder or remainder == filename:
|
||||
return filename
|
||||
# Subdirectory path — append filename if path looks like a directory
|
||||
if not remainder.endswith(tuple(".bin .rom .zip .img .bin ".split())):
|
||||
return remainder.rstrip("/") + "/" + filename
|
||||
return remainder
|
||||
|
||||
if raw_path.startswith("$roms_path/"):
|
||||
remainder = raw_path[len("$roms_path/"):].strip("/")
|
||||
base = ("roms/" + remainder) if remainder else "roms"
|
||||
return base.rstrip("/") + "/" + filename
|
||||
|
||||
# No recognised token — treat as bios-relative
|
||||
remainder = raw_path.strip("/")
|
||||
if not remainder:
|
||||
return filename
|
||||
return remainder.rstrip("/") + "/" + filename
|
||||
|
||||
|
||||
def _normalise_md5(raw: str | list) -> str:
|
||||
"""Return a comma-separated MD5 string.
|
||||
|
||||
xroar declares a list of accepted hashes for ROM variants;
|
||||
retrobios platform schema accepts comma-separated MD5 strings.
|
||||
"""
|
||||
if isinstance(raw, list):
|
||||
return ",".join(str(h).strip().lower() for h in raw if h)
|
||||
return str(raw).strip().lower() if raw else ""
|
||||
|
||||
|
||||
def _coerce_bios_to_list(val: object) -> list:
|
||||
"""Ensure a bios value is always a list of dicts.
|
||||
|
||||
ppsspp declares preset_actions.bios as a bare dict, not a list.
|
||||
"""
|
||||
if isinstance(val, list):
|
||||
return val
|
||||
if isinstance(val, dict):
|
||||
return [val]
|
||||
return []
|
||||
|
||||
|
||||
def _parse_required(raw: object) -> bool:
|
||||
"""Coerce RetroDECK required field to bool.
|
||||
|
||||
Values seen: 'Required', 'Optional', 'At least one BIOS file required',
|
||||
'Optional, for boot logo', True, False, absent (None).
|
||||
Absent is treated as required.
|
||||
"""
|
||||
if isinstance(raw, bool):
|
||||
return raw
|
||||
if raw is None:
|
||||
return True
|
||||
return str(raw).strip().lower() not in ("optional", "false", "no", "0")
|
||||
|
||||
|
||||
def _parse_manifest(data: dict) -> list[BiosRequirement]:
|
||||
"""Parse one component_manifest.json into BiosRequirement objects."""
|
||||
requirements: list[BiosRequirement] = []
|
||||
seen: set[tuple[str, str]] = set()
|
||||
|
||||
for _component_key, component_val in data.items():
|
||||
if not isinstance(component_val, dict):
|
||||
continue
|
||||
|
||||
# Component-level system fallback (may be a list for multi-system components)
|
||||
comp_system = component_val.get("system", "")
|
||||
if isinstance(comp_system, list):
|
||||
comp_system = comp_system[0] if comp_system else ""
|
||||
comp_system = str(comp_system).strip().lower()
|
||||
|
||||
# Collect bios entries from all known locations
|
||||
bios_sources: list[list] = []
|
||||
|
||||
if "bios" in component_val:
|
||||
bios_sources.append(_coerce_bios_to_list(component_val["bios"]))
|
||||
|
||||
pa = component_val.get("preset_actions", {})
|
||||
if isinstance(pa, dict) and "bios" in pa:
|
||||
bios_sources.append(_coerce_bios_to_list(pa["bios"]))
|
||||
|
||||
cores = component_val.get("cores", {})
|
||||
if isinstance(cores, dict) and "bios" in cores:
|
||||
bios_sources.append(_coerce_bios_to_list(cores["bios"]))
|
||||
|
||||
if not bios_sources:
|
||||
continue
|
||||
|
||||
for bios_list in bios_sources:
|
||||
for entry in bios_list:
|
||||
if not isinstance(entry, dict):
|
||||
continue
|
||||
|
||||
filename = str(entry.get("filename", "")).strip()
|
||||
if not filename:
|
||||
continue
|
||||
|
||||
# System slug — entry-level preferred, component-level fallback
|
||||
entry_system = entry.get("system", comp_system)
|
||||
if isinstance(entry_system, list):
|
||||
entry_system = entry_system[0] if entry_system else comp_system
|
||||
entry_system = str(entry_system).strip().lower()
|
||||
|
||||
if entry_system in SYSTEM_SLUG_MAP:
|
||||
slug = SYSTEM_SLUG_MAP[entry_system]
|
||||
if slug is None:
|
||||
continue # explicitly skipped (e.g. mess)
|
||||
else:
|
||||
slug = entry_system # unknown — pass through
|
||||
|
||||
# Destination resolution
|
||||
paths_raw = entry.get("paths")
|
||||
if paths_raw is None:
|
||||
destination = filename
|
||||
elif isinstance(paths_raw, list):
|
||||
destination = None
|
||||
for p in paths_raw:
|
||||
resolved = _resolve_destination(str(p), filename)
|
||||
if resolved is not None:
|
||||
destination = resolved
|
||||
break
|
||||
if destination is None:
|
||||
continue # all paths were saves_path variants — skip
|
||||
else:
|
||||
destination = _resolve_destination(str(paths_raw), filename)
|
||||
if destination is None:
|
||||
continue # saves_path — skip
|
||||
|
||||
# Hash fields
|
||||
md5_val: str | None = None
|
||||
sha256_val: str | None = None
|
||||
|
||||
raw_md5 = entry.get("md5")
|
||||
if raw_md5:
|
||||
md5_val = _normalise_md5(raw_md5) or None
|
||||
|
||||
raw_sha256 = entry.get("sha256")
|
||||
if raw_sha256:
|
||||
sha256_val = str(raw_sha256).strip().lower() or None
|
||||
|
||||
required = _parse_required(entry.get("required"))
|
||||
|
||||
dedup_key = (slug, filename.lower())
|
||||
if dedup_key in seen:
|
||||
continue
|
||||
seen.add(dedup_key)
|
||||
|
||||
req = BiosRequirement(
|
||||
name=filename,
|
||||
system=slug,
|
||||
md5=md5_val,
|
||||
sha1=None,
|
||||
destination=destination,
|
||||
required=required,
|
||||
)
|
||||
req._sha256 = sha256_val # type: ignore[attr-defined]
|
||||
requirements.append(req)
|
||||
|
||||
return requirements
|
||||
|
||||
|
||||
class Scraper(BaseScraper):
|
||||
"""Scraper for RetroDECK component_manifest.json files.
|
||||
|
||||
Two modes:
|
||||
remote (default): fetches manifests directly from RetroDECK/components
|
||||
via GitHub raw URLs, enumerating components via the
|
||||
GitHub API tree endpoint
|
||||
local: reads manifests from a directory on disk
|
||||
(--manifests-dir or pass manifests_dir= to __init__)
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
manifests_dir: str | None = None,
|
||||
github_token: str | None = None,
|
||||
):
|
||||
super().__init__()
|
||||
self.manifests_dir = manifests_dir
|
||||
self.github_token = github_token or os.environ.get("GITHUB_TOKEN")
|
||||
self._release_version: str | None = None
|
||||
|
||||
# ── Remote ───────────────────────────────────────────────────────────────
|
||||
|
||||
def _list_component_dirs(self) -> list[str]:
|
||||
"""Return top-level component directory names from the GitHub API."""
|
||||
tree = _fetch_json(COMPONENTS_API_URL, self.github_token)
|
||||
return [
|
||||
item["path"]
|
||||
for item in tree.get("tree", [])
|
||||
if item["type"] == "tree" and item["path"] not in SKIP_DIRS
|
||||
]
|
||||
|
||||
def _fetch_remote_manifests(self) -> list[dict]:
|
||||
component_dirs = self._list_component_dirs()
|
||||
manifests: list[dict] = []
|
||||
for component in sorted(component_dirs):
|
||||
url = f"{RAW_BASE_URL}/{component}/component_manifest.json"
|
||||
print(f" Fetching {component}/component_manifest.json ...", file=sys.stderr)
|
||||
try:
|
||||
raw = _fetch_bytes(url, self.github_token)
|
||||
manifests.append(json.loads(raw.decode("utf-8")))
|
||||
except ConnectionError:
|
||||
pass # component has no manifest — skip silently
|
||||
except json.JSONDecodeError as e:
|
||||
print(f" WARNING: parse error in {component}: {e}", file=sys.stderr)
|
||||
return manifests
|
||||
|
||||
# ── Local ─────────────────────────────────────────────────────────────────
|
||||
|
||||
def _fetch_local_manifests(self) -> list[dict]:
|
||||
root = Path(self.manifests_dir)
|
||||
if not root.is_dir():
|
||||
raise FileNotFoundError(f"Manifests directory not found: {root}")
|
||||
manifests: list[dict] = []
|
||||
# Only scan top-level component directories; skip archive and hidden dirs
|
||||
for component_dir in sorted(root.iterdir()):
|
||||
if not component_dir.is_dir():
|
||||
continue
|
||||
if component_dir.name in SKIP_DIRS or component_dir.name.startswith("."):
|
||||
continue
|
||||
manifest_path = component_dir / "component_manifest.json"
|
||||
if not manifest_path.exists():
|
||||
continue
|
||||
try:
|
||||
with open(manifest_path) as f:
|
||||
manifests.append(json.load(f))
|
||||
except (json.JSONDecodeError, OSError) as e:
|
||||
print(f" WARNING: Could not parse {manifest_path}: {e}", file=sys.stderr)
|
||||
return manifests
|
||||
|
||||
# ── BaseScraper interface ─────────────────────────────────────────────────
|
||||
|
||||
def fetch_requirements(self) -> list[BiosRequirement]:
|
||||
manifests = (
|
||||
self._fetch_local_manifests()
|
||||
if self.manifests_dir
|
||||
else self._fetch_remote_manifests()
|
||||
)
|
||||
|
||||
requirements: list[BiosRequirement] = []
|
||||
seen: set[tuple[str, str]] = set()
|
||||
for manifest in manifests:
|
||||
for req in _parse_manifest(manifest):
|
||||
key = (req.system, req.name.lower())
|
||||
if key not in seen:
|
||||
seen.add(key)
|
||||
requirements.append(req)
|
||||
return requirements
|
||||
|
||||
def validate_format(self, raw_data: str) -> bool:
|
||||
try:
|
||||
return isinstance(json.loads(raw_data), dict)
|
||||
except json.JSONDecodeError:
|
||||
return False
|
||||
|
||||
def generate_platform_yaml(self) -> dict:
|
||||
requirements = self.fetch_requirements()
|
||||
|
||||
systems: dict[str, dict] = {}
|
||||
for req in requirements:
|
||||
systems.setdefault(req.system, {"files": []})
|
||||
entry: dict = {
|
||||
"name": req.name,
|
||||
"destination": req.destination,
|
||||
"required": req.required,
|
||||
}
|
||||
if req.md5:
|
||||
entry["md5"] = req.md5
|
||||
sha256 = getattr(req, "_sha256", None)
|
||||
if sha256 and not req.md5:
|
||||
entry["sha256"] = sha256
|
||||
systems[req.system]["files"].append(entry)
|
||||
|
||||
version = self._release_version or ""
|
||||
if not version:
|
||||
try:
|
||||
version = fetch_github_latest_version(COMPONENTS_REPO) or ""
|
||||
except (ConnectionError, OSError):
|
||||
pass
|
||||
|
||||
return {
|
||||
"platform": "RetroDECK",
|
||||
"version": version,
|
||||
"homepage": "https://retrodeck.net",
|
||||
"source": f"https://github.com/{COMPONENTS_REPO}",
|
||||
"base_destination": "bios",
|
||||
"hash_type": "md5",
|
||||
"verification_mode": "md5",
|
||||
"systems": systems,
|
||||
}
|
||||
|
||||
|
||||
def main() -> None:
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Scrape RetroDECK component_manifest.json BIOS requirements"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--manifests-dir", metavar="DIR",
|
||||
help=(
|
||||
"Read manifests from a local directory instead of fetching from GitHub. "
|
||||
f"Live install path: {DEFAULT_LOCAL_MANIFESTS}"
|
||||
),
|
||||
)
|
||||
parser.add_argument(
|
||||
"--token", metavar="TOKEN",
|
||||
help="GitHub personal access token (or set GITHUB_TOKEN env var)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--dry-run", action="store_true",
|
||||
help="Print per-system summary without generating output",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output", "-o", metavar="FILE",
|
||||
help="Write generated platform YAML to FILE",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--json", action="store_true",
|
||||
help="Print platform config as JSON (for debugging)",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
scraper = Scraper(manifests_dir=args.manifests_dir, github_token=args.token)
|
||||
|
||||
try:
|
||||
reqs = scraper.fetch_requirements()
|
||||
except (ConnectionError, FileNotFoundError) as e:
|
||||
print(f"Error: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if args.dry_run:
|
||||
by_system: dict[str, list] = {}
|
||||
for r in reqs:
|
||||
by_system.setdefault(r.system, []).append(r)
|
||||
for system, files in sorted(by_system.items()):
|
||||
req_c = sum(1 for f in files if f.required)
|
||||
opt_c = len(files) - req_c
|
||||
print(f" {system}: {req_c} required, {opt_c} optional")
|
||||
print(f"\nTotal: {len(reqs)} entries across {len(by_system)} systems")
|
||||
return
|
||||
|
||||
config = scraper.generate_platform_yaml()
|
||||
|
||||
if args.json:
|
||||
print(json.dumps(config, indent=2))
|
||||
return
|
||||
|
||||
if args.output:
|
||||
try:
|
||||
import yaml
|
||||
except ImportError:
|
||||
print("Error: PyYAML required (pip install pyyaml)", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
def _str_representer(dumper, data):
|
||||
if any(c in data for c in "()[]{}:#"):
|
||||
return dumper.represent_scalar("tag:yaml.org,2002:str", data, style='"')
|
||||
return dumper.represent_scalar("tag:yaml.org,2002:str", data)
|
||||
yaml.add_representer(str, _str_representer)
|
||||
|
||||
with open(args.output, "w") as f:
|
||||
yaml.dump(config, f, default_flow_style=False, allow_unicode=True, sort_keys=False)
|
||||
total = sum(len(s["files"]) for s in config["systems"].values())
|
||||
print(
|
||||
f"Written {total} entries across "
|
||||
f"{len(config['systems'])} systems to {args.output}"
|
||||
)
|
||||
return
|
||||
|
||||
systems = len(set(r.system for r in reqs))
|
||||
print(f"Scraped {len(reqs)} entries across {systems} systems. Use --dry-run, --json, or --output FILE.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -174,14 +174,17 @@ def validate_file(
|
||||
else:
|
||||
result.add_warning("File not referenced in any platform config - needs manual review")
|
||||
|
||||
if filepath.startswith("bios/"):
|
||||
parts = filepath.split("/")
|
||||
normalized = os.path.normpath(filepath)
|
||||
if os.path.islink(filepath):
|
||||
result.add_check(False, "Symlinks are not allowed")
|
||||
elif normalized.startswith("bios" + os.sep):
|
||||
parts = normalized.split(os.sep)
|
||||
if len(parts) >= 4:
|
||||
result.add_check(True, f"Correct placement: bios/{parts[1]}/{parts[2]}/")
|
||||
else:
|
||||
result.add_warning("File should be in bios/Manufacturer/Console/ structure")
|
||||
else:
|
||||
result.add_warning(f"File is not under bios/ directory")
|
||||
result.add_warning("File is not under bios/ directory")
|
||||
|
||||
if name_known and not sha1_known and not md5_known:
|
||||
result.add_info(
|
||||
|
||||
@@ -37,7 +37,7 @@ sys.path.insert(0, os.path.dirname(__file__))
|
||||
from common import (
|
||||
build_zip_contents_index, check_inside_zip, group_identical_platforms,
|
||||
load_emulator_profiles, load_platform_config, md5sum, md5_composite,
|
||||
resolve_local_file,
|
||||
resolve_local_file, resolve_platform_cores,
|
||||
)
|
||||
|
||||
DEFAULT_DB = "database.json"
|
||||
@@ -198,9 +198,7 @@ def find_undeclared_files(
|
||||
"""Find files needed by cores but not declared in platform config."""
|
||||
# Collect all filenames declared by this platform
|
||||
declared_names: set[str] = set()
|
||||
platform_systems: set[str] = set()
|
||||
for sys_id, system in config.get("systems", {}).items():
|
||||
platform_systems.add(sys_id)
|
||||
for fe in system.get("files", []):
|
||||
name = fe.get("name", "")
|
||||
if name:
|
||||
@@ -217,21 +215,15 @@ 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)
|
||||
undeclared = []
|
||||
seen = set()
|
||||
for emu_name, profile in sorted(profiles.items()):
|
||||
# Skip launchers — they don't use system_dir for BIOS
|
||||
if profile.get("type") == "launcher":
|
||||
if profile.get("type") in ("launcher", "alias"):
|
||||
continue
|
||||
emu_systems = set(profile.get("systems", []))
|
||||
# Only check emulators whose systems overlap with this platform
|
||||
if not emu_systems & platform_systems:
|
||||
if emu_name not in relevant:
|
||||
continue
|
||||
|
||||
# Skip if emulator's data_directories cover the files
|
||||
emu_dd = {dd.get("ref", "") for dd in profile.get("data_directories", [])}
|
||||
covered_by_dd = bool(emu_dd & declared_dd)
|
||||
|
||||
for f in profile.get("files", []):
|
||||
fname = f.get("name", "")
|
||||
if not fname or fname in seen:
|
||||
@@ -241,8 +233,6 @@ def find_undeclared_files(
|
||||
continue
|
||||
if fname in declared_names:
|
||||
continue
|
||||
if covered_by_dd:
|
||||
continue
|
||||
|
||||
in_repo = fname in by_name or fname.rsplit("/", 1)[-1] in by_name
|
||||
seen.add(fname)
|
||||
@@ -274,10 +264,12 @@ def find_exclusion_notes(
|
||||
for sys_id in config.get("systems", {}):
|
||||
platform_systems.add(sys_id)
|
||||
|
||||
relevant = resolve_platform_cores(config, profiles)
|
||||
notes = []
|
||||
for emu_name, profile in sorted(profiles.items()):
|
||||
emu_systems = set(profile.get("systems", []))
|
||||
if not emu_systems & platform_systems:
|
||||
# Match by core resolution OR system intersection (documents all potential emulators)
|
||||
if emu_name not in relevant and not (emu_systems & platform_systems):
|
||||
continue
|
||||
|
||||
emu_display = profile.get("emulator", emu_name)
|
||||
@@ -381,6 +373,11 @@ def verify_platform(
|
||||
for s in file_severity.values():
|
||||
counts[s] = counts.get(s, 0) + 1
|
||||
|
||||
# Count by file status (ok/untested/missing)
|
||||
status_counts: dict[str, int] = {}
|
||||
for s in file_status.values():
|
||||
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)
|
||||
@@ -390,6 +387,7 @@ def verify_platform(
|
||||
"verification_mode": mode,
|
||||
"total_files": len(file_status),
|
||||
"severity_counts": counts,
|
||||
"status_counts": status_counts,
|
||||
"undeclared_files": undeclared,
|
||||
"exclusion_notes": exclusions,
|
||||
"details": details,
|
||||
@@ -421,8 +419,9 @@ def print_platform_result(result: dict, group: list[str]) -> None:
|
||||
else:
|
||||
parts = [f"{ok_count}/{total} present"]
|
||||
else:
|
||||
untested = c.get(Severity.WARNING, 0)
|
||||
missing = c.get(Severity.CRITICAL, 0)
|
||||
sc = result.get("status_counts", {})
|
||||
untested = sc.get(Status.UNTESTED, 0)
|
||||
missing = sc.get(Status.MISSING, 0)
|
||||
parts = [f"{ok_count}/{total} OK"]
|
||||
if untested:
|
||||
parts.append(f"{untested} untested")
|
||||
|
||||
@@ -31,9 +31,9 @@ import yaml
|
||||
from common import (
|
||||
build_zip_contents_index, check_inside_zip, group_identical_platforms,
|
||||
load_emulator_profiles, load_platform_config, md5_composite, md5sum,
|
||||
resolve_local_file,
|
||||
resolve_local_file, resolve_platform_cores,
|
||||
)
|
||||
from verify import Severity, Status, verify_platform, find_undeclared_files
|
||||
from verify import Severity, Status, verify_platform, find_undeclared_files, find_exclusion_notes
|
||||
|
||||
|
||||
def _h(data: bytes) -> dict:
|
||||
@@ -471,13 +471,13 @@ class TestE2E(unittest.TestCase):
|
||||
profiles = load_emulator_profiles(self.emulators_dir)
|
||||
self.assertNotIn("test_alias", profiles)
|
||||
|
||||
def test_43_cross_ref_data_dir_suppresses_gaps(self):
|
||||
def test_43_cross_ref_data_dir_does_not_suppress_files(self):
|
||||
config = load_platform_config("test_md5", self.platforms_dir)
|
||||
profiles = load_emulator_profiles(self.emulators_dir)
|
||||
undeclared = find_undeclared_files(config, self.emulators_dir, self.db, profiles)
|
||||
names = {u["name"] for u in undeclared}
|
||||
# dd_covered.bin from TestEmuDD should NOT appear (data_dir match)
|
||||
self.assertNotIn("dd_covered.bin", names)
|
||||
# dd_covered.bin is a file entry, not data_dir content — still undeclared
|
||||
self.assertIn("dd_covered.bin", names)
|
||||
|
||||
def test_44_cross_ref_skips_launchers(self):
|
||||
config = load_platform_config("test_existence", self.platforms_dir)
|
||||
@@ -555,5 +555,91 @@ class TestE2E(unittest.TestCase):
|
||||
self.assertEqual(status, "user_provided")
|
||||
|
||||
|
||||
def test_resolve_cores_all_libretro(self):
|
||||
"""all_libretro resolves to all libretro-type profiles, excludes alias/standalone."""
|
||||
config = {"cores": "all_libretro", "systems": {"nes": {"files": []}}}
|
||||
profiles = {
|
||||
"fceumm": {"type": "libretro", "systems": ["nes"], "files": []},
|
||||
"dolphin_standalone": {"type": "standalone", "systems": ["gc"], "files": []},
|
||||
"gambatte": {"type": "pure_libretro", "systems": ["gb"], "files": []},
|
||||
"mednafen_psx_hw": {"type": "alias", "alias_of": "beetle_psx", "files": []},
|
||||
}
|
||||
result = resolve_platform_cores(config, profiles)
|
||||
self.assertEqual(result, {"fceumm", "gambatte"})
|
||||
|
||||
def test_resolve_cores_explicit_list(self):
|
||||
"""Explicit cores list matches against profile dict keys."""
|
||||
config = {"cores": ["fbneo", "opera"], "systems": {"arcade": {"files": []}}}
|
||||
profiles = {
|
||||
"fbneo": {"type": "pure_libretro", "systems": ["arcade"], "files": []},
|
||||
"opera": {"type": "libretro", "systems": ["3do"], "files": []},
|
||||
"mame": {"type": "libretro", "systems": ["arcade"], "files": []},
|
||||
}
|
||||
result = resolve_platform_cores(config, profiles)
|
||||
self.assertEqual(result, {"fbneo", "opera"})
|
||||
|
||||
def test_resolve_cores_fallback_systems(self):
|
||||
"""Missing cores: field falls back to system ID intersection."""
|
||||
config = {"systems": {"nes": {"files": []}}}
|
||||
profiles = {
|
||||
"fceumm": {"type": "libretro", "systems": ["nes"], "files": []},
|
||||
"dolphin": {"type": "libretro", "systems": ["gc"], "files": []},
|
||||
}
|
||||
result = resolve_platform_cores(config, profiles)
|
||||
self.assertEqual(result, {"fceumm"})
|
||||
|
||||
def test_resolve_cores_excludes_alias(self):
|
||||
"""Alias profiles never included even if name matches cores list."""
|
||||
config = {"cores": ["mednafen_psx_hw"], "systems": {}}
|
||||
profiles = {
|
||||
"mednafen_psx_hw": {"type": "alias", "alias_of": "beetle_psx", "files": []},
|
||||
}
|
||||
result = resolve_platform_cores(config, profiles)
|
||||
self.assertEqual(result, set())
|
||||
|
||||
|
||||
def test_cross_reference_uses_core_resolution(self):
|
||||
"""Cross-reference matches by cores: field, not system intersection."""
|
||||
config = {
|
||||
"cores": ["fbneo"],
|
||||
"systems": {
|
||||
"arcade": {"files": [{"name": "neogeo.zip", "md5": "abc"}]}
|
||||
}
|
||||
}
|
||||
profiles = {
|
||||
"fbneo": {
|
||||
"emulator": "FBNeo", "systems": ["snk-neogeo-mvs"],
|
||||
"type": "pure_libretro",
|
||||
"files": [
|
||||
{"name": "neogeo.zip", "required": True},
|
||||
{"name": "neocdz.zip", "required": True},
|
||||
],
|
||||
},
|
||||
}
|
||||
db = {"indexes": {"by_name": {"neocdz.zip": {"sha1": "x"}}}}
|
||||
undeclared = find_undeclared_files(config, self.emulators_dir, db, profiles)
|
||||
names = [u["name"] for u in undeclared]
|
||||
self.assertIn("neocdz.zip", names)
|
||||
self.assertNotIn("neogeo.zip", names)
|
||||
|
||||
def test_exclusion_notes_uses_core_resolution(self):
|
||||
"""Exclusion notes match by cores: field, not system intersection."""
|
||||
config = {
|
||||
"cores": ["desmume2015"],
|
||||
"systems": {"nds": {"files": []}}
|
||||
}
|
||||
profiles = {
|
||||
"desmume2015": {
|
||||
"emulator": "DeSmuME 2015", "type": "frozen_snapshot",
|
||||
"systems": ["nintendo-ds"],
|
||||
"files": [],
|
||||
"exclusion_note": "Frozen snapshot, code never loads BIOS",
|
||||
},
|
||||
}
|
||||
notes = find_exclusion_notes(config, self.emulators_dir, profiles)
|
||||
emu_names = [n["emulator"] for n in notes]
|
||||
self.assertIn("DeSmuME 2015", emu_names)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user