mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-10 13:33:24 -05:00
feat: add region filters and profiles
This commit is contained in:
1 parent
1c537c3a30
commit
45f89cc6c0
51 files changed
+4592
-260
No files matched your search
@@ -36,7 +36,7 @@ Pick your platform, download the ZIP, extract to the BIOS path.
|
||||
|----------|------|-----------|----------|
|
||||
| Batocera | 2.7 GB | `/userdata/bios/` | [Download](../../releases/latest) |
|
||||
| BizHawk | 2.4 GB | `Firmware/` | [Download](../../releases/latest) |
|
||||
| EmuDeck | 1.6 GB | `~/Emulation/bios/` | [Download](../../releases/latest) |
|
||||
| EmuDeck | 1.7 GB | `~/Emulation/bios/` | [Download](../../releases/latest) |
|
||||
| Lakka | 3.9 GB | `/storage/system/` | [Download](../../releases/latest) |
|
||||
| MiSTer FPGA | 23 MB | `/media/fat/games/` | [Download](../../releases/latest) |
|
||||
| ROCKNIX | 3.8 GB | `/storage/roms/bios/` | [Download](../../releases/latest) |
|
||||
@@ -58,17 +58,17 @@ These are the files an emulator loads from disk instead of carrying inside itsel
|
||||
|
||||
Each file is checked the way your platform checks it. Most compare a checksum, the fingerprint of a file's contents, which catches a corrupt or unexpected copy. RetroArch, Lakka and RetroPie only look for the filename, because that is all their code does: the Coverage table says which applies to you. Independently of that, the collection records five fingerprints per file, and wherever an emulator's code states an expected size or hash, that value is read from its source and rechecked here.
|
||||
|
||||
- **91 files** the platforms' emulators load are not in the collection yet, named in the [gap analysis](https://abdess.github.io/retrobios/gaps/)
|
||||
- **99 files** the platforms' emulators load are not in the collection yet, named in the [gap analysis](https://abdess.github.io/retrobios/gaps/)
|
||||
- **12 platforms** supported with platform-specific verification
|
||||
- **342 emulators** profiled from source (RetroArch cores + standalone)
|
||||
- **412 systems** handled by those emulators (NES, SNES, PlayStation, Saturn, Dreamcast, ...)
|
||||
- **354 emulators** profiled from source (RetroArch cores + standalone)
|
||||
- **415 systems** handled by those emulators (NES, SNES, PlayStation, Saturn, Dreamcast, ...)
|
||||
- **7,763 files**, each with its SHA1, MD5, SHA256, CRC32 and Adler-32 fingerprints: 2,478 system files, 2,746 arcade ROM sets, 2,539 game and engine data files
|
||||
- **531 of 2,478 system files** matched to dump-preservation catalogs (No-Intro, Redump, TOSEC); arcade sets and engine data fall outside what those catalogs index
|
||||
- **9854 MB** total collection size
|
||||
|
||||
## Supported systems
|
||||
|
||||
NES, SNES, Nintendo 64, GameCube, Wii, Game Boy, Game Boy Advance, Nintendo DS, Nintendo 3DS, Switch, PlayStation, PlayStation 2, PlayStation 3, PSP, PS Vita, Mega Drive, Saturn, Dreamcast, Game Gear, Master System, Neo Geo, Atari 2600, Atari 7800, Atari Lynx, Atari ST, MSX, PC Engine, TurboGrafx-16, ColecoVision, Intellivision, Commodore 64, Amiga, ZX Spectrum, Arcade (MAME), and 378+ more.
|
||||
NES, SNES, Nintendo 64, GameCube, Wii, Game Boy, Game Boy Advance, Nintendo DS, Nintendo 3DS, Switch, PlayStation, PlayStation 2, PlayStation 3, PSP, PS Vita, Mega Drive, Saturn, Dreamcast, Game Gear, Master System, Neo Geo, Atari 2600, Atari 7800, Atari Lynx, Atari ST, MSX, PC Engine, TurboGrafx-16, ColecoVision, Intellivision, Commodore 64, Amiga, ZX Spectrum, Arcade (MAME), and 381+ more.
|
||||
|
||||
Full list with per-file details: **[https://abdess.github.io/retrobios/](https://abdess.github.io/retrobios/)**
|
||||
|
||||
@@ -76,18 +76,18 @@ Full list with per-file details: **[https://abdess.github.io/retrobios/](https:/
|
||||
|
||||
| Platform | On its BIOS list | Files its emulators load | Checked by |
|
||||
|----------|-----------------:|-------------------------:|------------|
|
||||
| Batocera | 353/353 | 1,146/1,237 | MD5 hash |
|
||||
| Batocera | 353/353 | 1,146/1,245 | MD5 hash |
|
||||
| BizHawk | 118/118 | 367/368 | SHA1 hash |
|
||||
| EmuDeck | 161/161 | 404/404 | MD5 hash |
|
||||
| Lakka | 530/530 | 1,149/1,154 | file presence |
|
||||
| EmuDeck | 161/161 | 405/405 | MD5 hash |
|
||||
| Lakka | 530/530 | 1,150/1,155 | file presence |
|
||||
| MiSTer FPGA | 65/65 | - | MD5 hash |
|
||||
| ROCKNIX | 38/38 | 1,483/1,488 | MD5 hash |
|
||||
| Recalbox | 346/346 | 716/721 | MD5 hash |
|
||||
| RetroArch | 530/530 | 1,149/1,154 | file presence |
|
||||
| RetroBat | 341/341 | 808/898 | MD5 hash |
|
||||
| RetroDECK | 2,008/2,008 | 1,183/1,189 | MD5 hash |
|
||||
| RetroPie * | 530/530 | 1,149/1,154 | file presence |
|
||||
| RomM | 374/374 | 267/270 | MD5 hash |
|
||||
| ROCKNIX | 38/38 | 1,484/1,489 | MD5 hash |
|
||||
| Recalbox | 346/346 | 718/723 | MD5 hash |
|
||||
| RetroArch | 530/530 | 1,150/1,155 | file presence |
|
||||
| RetroBat | 341/341 | 808/906 | MD5 hash |
|
||||
| RetroDECK | 2,008/2,008 | 1,184/1,198 | MD5 hash |
|
||||
| RetroPie * | 530/530 | 1,150/1,155 | file presence |
|
||||
| RomM | 374/374 | 268/271 | MD5 hash |
|
||||
|
||||
Each fraction is what the pack has over what is needed, counting required and optional files alike since both ship. The first column is the BIOS list the platform publishes. The second counts files its emulators load that this list never mentions, found by reading their source code, and it is routinely several times larger. A short fraction means files are still missing, and they are named in the [gap analysis](https://abdess.github.io/retrobios/gaps/).
|
||||
That second number is a floor, not a ceiling: an emulator that accepts any file handed to it names none in its code, so nothing there can be counted.
|
||||
@@ -127,7 +127,7 @@ The [documentation site](https://abdess.github.io/retrobios/) provides:
|
||||
- **Per-emulator profiles** with source code references for every file
|
||||
- **Per-system pages** showing which emulators and platforms cover each console
|
||||
- **Gap analysis** identifying missing files and undeclared core requirements
|
||||
- **Cross-reference** mapping files across 12 platforms and 342 emulators
|
||||
- **Cross-reference** mapping files across 12 platforms and 354 emulators
|
||||
|
||||
## How it works
|
||||
|
||||
@@ -164,4 +164,4 @@ The scripts and tooling are released under the [MIT License](LICENSE).
|
||||
The BIOS and firmware files are not covered by that license: they are third-party system software, preserved and provided for personal backup, archival, and interoperability with emulation software.
|
||||
The legal reasoning is laid out in the [FAQ](https://abdess.github.io/retrobios/wiki/faq/#is-this-legal).
|
||||
|
||||
*Auto-generated on 2026-08-08T11:35:43Z*
|
||||
*Auto-generated on 2026-08-08T16:19:01Z*
|
||||
+4
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"generated_at": "2026-08-08T07:33:54Z",
|
||||
"generated_at": "2026-08-08T13:50:06Z",
|
||||
"total_files": 7763,
|
||||
"total_size": 10332160603,
|
||||
"files": {
|
||||
@@ -112964,6 +112964,9 @@
|
||||
"342-0077-b": [
|
||||
"e4fc7560b69d062cb2da5b1ffbe11cd1ca03cc37"
|
||||
],
|
||||
"Mac OS ROM": [
|
||||
"e4fc7560b69d062cb2da5b1ffbe11cd1ca03cc37"
|
||||
],
|
||||
"fx-scsi.rom": [
|
||||
"65482a23ac5c10a6095aee1db5824cca54ead6e5"
|
||||
],
|
||||
|
||||
@@ -28,7 +28,7 @@ notes: >
|
||||
files:
|
||||
- name: "lynxboot.img"
|
||||
description: "Atari Lynx boot ROM"
|
||||
region: "World"
|
||||
region: [world]
|
||||
required: true
|
||||
size: 512
|
||||
crc32: "0d973c9d"
|
||||
|
||||
@@ -23,7 +23,7 @@ notes: >
|
||||
files:
|
||||
- name: "syscard3.pce"
|
||||
description: "CD-ROM System Card 3.0 (Japan)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: true
|
||||
sha1: "79f5ff55dd10187c7fd7b8daab0b3ffbd1f56a2c"
|
||||
md5: "38179df8f4ac870017db21ebcbf53114"
|
||||
@@ -32,7 +32,7 @@ files:
|
||||
|
||||
- name: "syscard3u.pce"
|
||||
description: "CD-ROM System Card 3.0 (US / TurboGrafx-CD)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
sha1: "d02611d99921986147c753df14c7349b31d71950"
|
||||
md5: "0754f903b52e3b3342202bdafb13efa5"
|
||||
@@ -41,7 +41,7 @@ files:
|
||||
|
||||
- name: "syscard2.pce"
|
||||
description: "CD-ROM System Card 2.0 (Japan)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
sha1: "88da02e2503f7c32810f5d93a34849d470742b6d"
|
||||
md5: "3cdd6614a918616bfc41c862e889dd79"
|
||||
@@ -50,7 +50,7 @@ files:
|
||||
|
||||
- name: "syscard2u.pce"
|
||||
description: "CD-ROM System Card 2.0 (US / TurboGrafx-CD)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
sha1: "2bea3dac98f84b2f2f469fa77ea720b8770d598d"
|
||||
md5: "94279f315e8b52904f65ab3108542afe"
|
||||
@@ -59,7 +59,7 @@ files:
|
||||
|
||||
- name: "syscard1.pce"
|
||||
description: "CD-ROM System Card 1.0 (Japan)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
sha1: "a39a66da7de6ba94ab84d04eef7afeec7d4ee66a"
|
||||
md5: "2b7ccb3d86baa18f6402c176f3065082"
|
||||
@@ -68,7 +68,7 @@ files:
|
||||
|
||||
- name: "gexpress.pce"
|
||||
description: "Games Express CD Card"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
sha1: "014881a959e045e00f4db8f52955200865d40280"
|
||||
md5: "6d2cb14fc3e1f65ceb135633d1694122"
|
||||
|
||||
@@ -30,7 +30,7 @@ files:
|
||||
|
||||
- name: "scph5500.bin"
|
||||
description: "SCPH-5500 (v3.0 09-09-96 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: true
|
||||
hle_fallback: true
|
||||
size: 524288
|
||||
@@ -43,7 +43,7 @@ files:
|
||||
|
||||
- name: "scph5903.bin"
|
||||
description: "SCPH-5903 (System ROM Version 2.2 12/04/95 J), Video CD daughterboard"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
size: 1048576
|
||||
sha1: "15c94da3cc5a38a582429575af4198c487fe893c"
|
||||
@@ -56,7 +56,7 @@ files:
|
||||
|
||||
- name: "scph5501.bin"
|
||||
description: "SCPH-5501, 5503, 7003 (v3.0 11-18-96 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: true
|
||||
hle_fallback: true
|
||||
size: 524288
|
||||
@@ -79,7 +79,7 @@ files:
|
||||
|
||||
- name: "scph5502.bin"
|
||||
description: "SCPH-5502, 5552 (v3.0 01-06-97 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: true
|
||||
hle_fallback: true
|
||||
size: 524288
|
||||
@@ -99,7 +99,7 @@ files:
|
||||
|
||||
- name: "psxonpsp660.bin"
|
||||
description: "PSP embedded PS1 BIOS (region-free override)"
|
||||
region: "Auto"
|
||||
region: [world]
|
||||
required: false
|
||||
size: 524288
|
||||
sha1: "96880d1ca92a016ff054be5159bb06fe03cb4e14"
|
||||
@@ -112,7 +112,7 @@ files:
|
||||
|
||||
- name: "ps1_rom.bin"
|
||||
description: "PS3 embedded PS1 BIOS, first 512KB extracted (region-free override)"
|
||||
region: "Auto"
|
||||
region: [world]
|
||||
required: false
|
||||
sha1: "c40146361eb8cf670b19fdc9759190257803cab7"
|
||||
md5: "81bbe60ba7a3d1cea1d48c14cbcc647b"
|
||||
@@ -124,7 +124,7 @@ files:
|
||||
|
||||
- name: "openbios.bin"
|
||||
description: "OpenBIOS (open-source PS1 HLE BIOS, region-free override)"
|
||||
region: "Auto"
|
||||
region: [world]
|
||||
required: false
|
||||
size: 524288
|
||||
mode: libretro
|
||||
|
||||
+10
-1
@@ -10,7 +10,7 @@ core_version: "Git"
|
||||
display_name: "Nintendo - GameCube / Wii (Dolphin)"
|
||||
cores:
|
||||
- dolphin
|
||||
systems: [nintendo-gamecube, nintendo-wii]
|
||||
systems: [nintendo-gamecube, nintendo-wii, sega-triforce]
|
||||
|
||||
# File paths are relative to Dolphin User directory:
|
||||
# Standalone: User/GC/ and User/Wii/
|
||||
@@ -101,6 +101,15 @@ files:
|
||||
note: "Shift-JIS font for Japanese text. Free alternative bundled, real one from IPL dump preferred"
|
||||
source_ref: "Source/Core/Common/CommonPaths.h:134, Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp:129,234"
|
||||
|
||||
# -- Triforce firmware --
|
||||
# Loaded from User/Triforce/ only, no Sys fallback.
|
||||
- name: "segaboot.gcm"
|
||||
path: "Triforce/segaboot.gcm"
|
||||
system: sega-triforce
|
||||
required: true
|
||||
note: "SegaBoot, the Triforce system disc image. Read on every Triforce boot, min(size, 2 MiB) copied into the firmware buffer, nothing validated"
|
||||
source_ref: "Source/Core/Core/HW/DVD/AMMediaboard.cpp:159,512,531-551, Source/Core/Core/HW/DVD/DVDInterface.cpp:292-294, Source/Core/Core/System.h:147"
|
||||
|
||||
# -- GBA BIOS (for GC-GBA link) --
|
||||
# Used by integrated mGBA core for GameCube-GBA connectivity.
|
||||
# Loaded from User/GBA/gba_bios.bin (no Sys fallback).
|
||||
|
||||
+105
-105
@@ -47,7 +47,7 @@ files:
|
||||
|
||||
- name: "scph1000.bin"
|
||||
description: "SCPH-1000, DTL-H1000 (v1.0)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "239665b1a3dade1b5a52c06338011044"
|
||||
region_check: true
|
||||
@@ -57,7 +57,7 @@ files:
|
||||
|
||||
- name: "scph1001.bin"
|
||||
description: "SCPH-1001, 5003, DTL-H1201, H3001 (v2.2 12-04-95 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
md5: "924e392ed05558ffdb115408c263dccf"
|
||||
region_check: false
|
||||
@@ -67,7 +67,7 @@ files:
|
||||
|
||||
- name: "scph1002a.bin"
|
||||
description: "SCPH-1002, DTL-H1002 (v2.0 05-10-95 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
md5: "54847e693405ffeb0359c6287434cbef"
|
||||
region_check: false
|
||||
@@ -77,7 +77,7 @@ files:
|
||||
|
||||
- name: "scph1002b.bin"
|
||||
description: "SCPH-1002, DTL-H1102 (v2.1 07-17-95 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
md5: "417b34706319da7cf001e76e40136c23"
|
||||
region_check: false
|
||||
@@ -87,7 +87,7 @@ files:
|
||||
|
||||
- name: "scph1002c.bin"
|
||||
description: "SCPH-1002, DTL-H1202, H3002 (v2.2 12-04-95 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
md5: "e2110b8a2b97a8e0b857a45d32f7e187"
|
||||
region_check: false
|
||||
@@ -97,7 +97,7 @@ files:
|
||||
|
||||
- name: "dtlh1100.bin"
|
||||
description: "DTL-H1100 (v2.2 03-06-96 D)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "ca5cfc321f916756e3f0effbfaeba13b"
|
||||
region_check: true
|
||||
@@ -107,7 +107,7 @@ files:
|
||||
|
||||
- name: "scph3000.bin"
|
||||
description: "SCPH-3000, DTL-H1000H (v1.1 01-22-95)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "849515939161e62f6b866f6853006780"
|
||||
region_check: true
|
||||
@@ -117,7 +117,7 @@ files:
|
||||
|
||||
- name: "scph1001_v20.bin"
|
||||
description: "SCPH-1001, DTL-H1001 (v2.0 05-07-95 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
md5: "dc2b9bf8da62ec93e868cfd29f0d067d"
|
||||
region_check: false
|
||||
@@ -127,7 +127,7 @@ files:
|
||||
|
||||
- name: "scph3500.bin"
|
||||
description: "SCPH-3500 (v2.1 07-17-95 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "cba733ceeff5aef5c32254f1d617fa62"
|
||||
region_check: true
|
||||
@@ -137,7 +137,7 @@ files:
|
||||
|
||||
- name: "scph1001_v21.bin"
|
||||
description: "SCPH-1001, DTL-H1101 (v2.1 07-17-95 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
md5: "da27e8b6dab242d8f91a9b25d80c63b8"
|
||||
region_check: false
|
||||
@@ -147,7 +147,7 @@ files:
|
||||
|
||||
- name: "scph5000.bin"
|
||||
description: "SCPH-5000, DTL-H1200, H3000 (v2.2 12-04-95 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "57a06303dfa9cf9351222dfcbb4a29d9"
|
||||
region_check: true
|
||||
@@ -157,7 +157,7 @@ files:
|
||||
|
||||
- name: "scph5500.bin"
|
||||
description: "SCPH-5500 (v3.0 09-09-96 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: true
|
||||
md5: "8dd7d5296a650fac7319bce665a6a53c"
|
||||
region_check: true
|
||||
@@ -167,7 +167,7 @@ files:
|
||||
|
||||
- name: "scph5501.bin"
|
||||
description: "SCPH-5501, 5503, 7003 (v3.0 11-18-96 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: true
|
||||
md5: "490f666e1afb15b7362b406ed1cea246"
|
||||
region_check: false
|
||||
@@ -177,7 +177,7 @@ files:
|
||||
|
||||
- name: "scph5502.bin"
|
||||
description: "SCPH-5502, 5552 (v3.0 01-06-97 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: true
|
||||
md5: "32736f17079d0b2b7024407c39bd3050"
|
||||
region_check: false
|
||||
@@ -187,7 +187,7 @@ files:
|
||||
|
||||
- name: "scph7000.bin"
|
||||
description: "SCPH-7000, 7500, 9000 (v4.0 08-18-97 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "8e4c14f567745eff2f0408c8129f72a6"
|
||||
region_check: true
|
||||
@@ -197,7 +197,7 @@ files:
|
||||
|
||||
- name: "scph7000w.bin"
|
||||
description: "SCPH-7000W (v4.1 11-14-97 A)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "b84be139db3ee6cbd075630aa20a6553"
|
||||
region_check: false
|
||||
@@ -207,7 +207,7 @@ files:
|
||||
|
||||
- name: "scph7001.bin"
|
||||
description: "SCPH-7001, 7501, 7503, 9001, 9003, 9903 (v4.1 12-16-97 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
md5: "1e68c231d0896b7eadcad1d7d8e76129"
|
||||
region_check: false
|
||||
@@ -217,7 +217,7 @@ files:
|
||||
|
||||
- name: "scph7002.bin"
|
||||
description: "SCPH-7002, 7502, 9002 (v4.1 12-16-97 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
md5: "b9d9a0286c33dc6b7237bb13cd46fdee"
|
||||
region_check: false
|
||||
@@ -227,7 +227,7 @@ files:
|
||||
|
||||
- name: "scph100.bin"
|
||||
description: "SCPH-100 (v4.3 03-11-00 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "8abc1b549a4a80954addc48ef02c4521"
|
||||
region_check: true
|
||||
@@ -237,7 +237,7 @@ files:
|
||||
|
||||
- name: "scph101_v44.bin"
|
||||
description: "SCPH-101 (v4.4 03-24-00 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
md5: "9a09ab7e49b422c007e6d54d7c49b965"
|
||||
region_check: false
|
||||
@@ -247,7 +247,7 @@ files:
|
||||
|
||||
- name: "scph101_v45.bin"
|
||||
description: "SCPH-101 (v4.5 05-25-00 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
md5: "6e3735ff4c7dc899ee98981385f6f3d0"
|
||||
region_check: false
|
||||
@@ -257,7 +257,7 @@ files:
|
||||
|
||||
- name: "scph102_v44.bin"
|
||||
description: "SCPH-102 (v4.4 03-24-00 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
md5: "b10f5e0e3d9eb60e5159690680b1e774"
|
||||
region_check: true
|
||||
@@ -267,7 +267,7 @@ files:
|
||||
|
||||
- name: "scph102_v45.bin"
|
||||
description: "SCPH-102 (v4.5 05-25-00 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
md5: "de93caec13d1a141a40a79f5c86168d6"
|
||||
region_check: true
|
||||
@@ -277,7 +277,7 @@ files:
|
||||
|
||||
- name: "scph1000r.bin"
|
||||
description: "SCPH-1000R (v4.5 05-25-00 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "476d68a94ccec3b9c8303bbd1daf2810"
|
||||
region_check: true
|
||||
@@ -291,7 +291,7 @@ files:
|
||||
|
||||
- name: "ps2_scph18000.bin"
|
||||
description: "PS2, SCPH-18000 (v5.0 10-27-00 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "d8f485717a5237285e4d7c5f881b7f32"
|
||||
region_check: true
|
||||
@@ -301,7 +301,7 @@ files:
|
||||
|
||||
- name: "ps2_scph30003.bin"
|
||||
description: "PS2, SCPH-30003 (v5.0 09-02-00 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
md5: "71f50ef4f4e17c163c78908e16244f7d"
|
||||
region_check: true
|
||||
@@ -311,7 +311,7 @@ files:
|
||||
|
||||
- name: "ps2_dtlh10000_0117.bin"
|
||||
description: "PS2, DTL-H10000 (v5.0 01/17/00 T)"
|
||||
region: "Auto"
|
||||
region: [world]
|
||||
required: false
|
||||
md5: "32f2e4d5ff5ee11072a6bc45530f5765"
|
||||
region_check: true
|
||||
@@ -321,7 +321,7 @@ files:
|
||||
|
||||
- name: "ps2_scph10000_0117.bin"
|
||||
description: "PS2, SCPH-10000 (v5.0 01/17/00 T)"
|
||||
region: "Auto"
|
||||
region: [world]
|
||||
required: false
|
||||
md5: "acf4730ceb38ac9d8c7d8e21f2614600"
|
||||
region_check: true
|
||||
@@ -331,7 +331,7 @@ files:
|
||||
|
||||
- name: "ps2_dtlh10000_0217.bin"
|
||||
description: "PS2, DTL-H10000 (v5.0 02/17/00 T)"
|
||||
region: "Auto"
|
||||
region: [world]
|
||||
required: false
|
||||
md5: "acf9968c8f596d2b15f42272082513d1"
|
||||
region_check: true
|
||||
@@ -341,7 +341,7 @@ files:
|
||||
|
||||
- name: "ps2_scph10000_15000.bin"
|
||||
description: "PS2, SCPH-10000/SCPH-15000 (v5.0 02/17/00 T)"
|
||||
region: "Auto"
|
||||
region: [world]
|
||||
required: false
|
||||
md5: "b1459d7446c69e3e97e6ace3ae23dd1c"
|
||||
region_check: true
|
||||
@@ -351,7 +351,7 @@ files:
|
||||
|
||||
- name: "ps2_dtlh10000_0224.bin"
|
||||
description: "PS2, DTL-H10000 (v5.0 02/24/00 T)"
|
||||
region: "Auto"
|
||||
region: [world]
|
||||
required: false
|
||||
md5: "d3f1853a16c2ec18f3cd1ae655213308"
|
||||
region_check: true
|
||||
@@ -361,7 +361,7 @@ files:
|
||||
|
||||
- name: "ps2_dtlh30001.bin"
|
||||
description: "PS2, DTL-H30001 (v5.0 07/27/00 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
md5: "63e6fd9b3c72e0d7b920e80cf76645cd"
|
||||
region_check: true
|
||||
@@ -371,7 +371,7 @@ files:
|
||||
|
||||
- name: "ps2_scph30001_0727.bin"
|
||||
description: "PS2, SCPH-30001 (v5.0 07/27/00 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
md5: "a20c97c02210f16678ca3010127caf36"
|
||||
region_check: true
|
||||
@@ -381,7 +381,7 @@ files:
|
||||
|
||||
- name: "ps2_scph30001_0902.bin"
|
||||
description: "PS2, SCPH-30001 (v5.0 09/02/00 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
md5: "8db2fbbac7413bf3e7154c1e0715e565"
|
||||
region_check: true
|
||||
@@ -391,7 +391,7 @@ files:
|
||||
|
||||
- name: "ps2_dtlh30002.bin"
|
||||
description: "PS2, DTL-H30002 (v5.0 09/02/00 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
md5: "91c87cb2f2eb6ce529a2360f80ce2457"
|
||||
region_check: true
|
||||
@@ -401,7 +401,7 @@ files:
|
||||
|
||||
- name: "ps2_dtlh30102_0902.bin"
|
||||
description: "PS2, DTL-H30102 (v5.0 09/02/00 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
md5: "3016b3dd42148a67e2c048595ca4d7ce"
|
||||
region_check: true
|
||||
@@ -411,7 +411,7 @@ files:
|
||||
|
||||
- name: "ps2_scph30002_0902.bin"
|
||||
description: "PS2, SCPH-30002/SCPH-30003/SCPH-30004 (v5.0 09/02/00 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
md5: "b7fa11e87d51752a98b38e3e691cbf17"
|
||||
region_check: true
|
||||
@@ -421,7 +421,7 @@ files:
|
||||
|
||||
- name: "ps2_scph18000_gh003.bin"
|
||||
description: "PS2, SCPH-18000 (GH-003) (v5.0 10/27/00 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "f63bc530bd7ad7c026fcd6f7bd0d9525"
|
||||
region_check: true
|
||||
@@ -431,7 +431,7 @@ files:
|
||||
|
||||
- name: "ps2_scph18000_gh008.bin"
|
||||
description: "PS2, SCPH-18000 (GH-008) (v5.0 10/27/00 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "cee06bd68c333fc5768244eae77e4495"
|
||||
region_check: true
|
||||
@@ -441,7 +441,7 @@ files:
|
||||
|
||||
- name: "ps2_dtlh30101.bin"
|
||||
description: "PS2, DTL-H30101 (v5.0 12/28/00 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
md5: "0bf988e9c7aaa4c051805b0fa6eb3387"
|
||||
region_check: true
|
||||
@@ -451,7 +451,7 @@ files:
|
||||
|
||||
- name: "ps2_scph30001_35001.bin"
|
||||
description: "PS2, SCPH-30001/SCPH-35001 (v5.0 12/28/00 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
md5: "8accc3c49ac45f5ae2c5db0adc854633"
|
||||
region_check: true
|
||||
@@ -461,7 +461,7 @@ files:
|
||||
|
||||
- name: "ps2_dtlh30102_1228.bin"
|
||||
description: "PS2, DTL-H30102 (v5.0 12/28/00 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
md5: "6f9a6feb749f0533aaae2cc45090b0ed"
|
||||
region_check: true
|
||||
@@ -471,7 +471,7 @@ files:
|
||||
|
||||
- name: "ps2_scph30002_1228.bin"
|
||||
description: "PS2, SCPH-30002/SCPH-30003/SCPH-30004/SCHP-35002/SCPH-35003/SCPH-35004 (v5.0 12/28/00 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
md5: "838544f12de9b0abc90811279ee223c8"
|
||||
region_check: true
|
||||
@@ -481,7 +481,7 @@ files:
|
||||
|
||||
- name: "ps2_dtlh30000.bin"
|
||||
description: "PS2, DTL-H30000 (v5.0 01/18/01 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "bb6bbc850458fff08af30e969ffd0175"
|
||||
region_check: true
|
||||
@@ -491,7 +491,7 @@ files:
|
||||
|
||||
- name: "ps2_scph30000_35000.bin"
|
||||
description: "PS2, SCPH-30000/SCPH-35000 (v5.0 01/18/01 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "815ac991d8bc3b364696bead3457de7d"
|
||||
region_check: true
|
||||
@@ -501,7 +501,7 @@ files:
|
||||
|
||||
- name: "ps2_scph30001r_0427.bin"
|
||||
description: "PS2, SCPH-30001R (v5.0 04/27/01 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
md5: "b107b5710042abe887c0f6175f6e94bb"
|
||||
region_check: true
|
||||
@@ -511,7 +511,7 @@ files:
|
||||
|
||||
- name: "ps2_scph30000_0427.bin"
|
||||
description: "PS2, SCPH-30000 (v5.0 04/27/01 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "ab55cceea548303c22c72570cfd4dd71"
|
||||
region_check: true
|
||||
@@ -521,7 +521,7 @@ files:
|
||||
|
||||
- name: "ps2_scph30001r_0704.bin"
|
||||
description: "PS2, SCPH-30001R (v5.0 07/04/01 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
md5: "18bcaadb9ff74ed3add26cdf709fff2e"
|
||||
region_check: true
|
||||
@@ -531,7 +531,7 @@ files:
|
||||
|
||||
- name: "ps2_scph30002r_0704.bin"
|
||||
description: "PS2, SCPH-30002R/SCPH-30003R/SCPH-30004R (v5.0 07/04/01 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
md5: "491209dd815ceee9de02dbbc408c06d6"
|
||||
region_check: true
|
||||
@@ -541,7 +541,7 @@ files:
|
||||
|
||||
- name: "ps2_scph30001r_1004.bin"
|
||||
description: "PS2, SCPH-30001R (v5.0 10/04/01 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
md5: "7200a03d51cacc4c14fcdfdbc4898431"
|
||||
region_check: true
|
||||
@@ -551,7 +551,7 @@ files:
|
||||
|
||||
- name: "ps2_scph30002r_1004.bin"
|
||||
description: "PS2, SCPH-30002R/SCPH-30003R/SCPH-30004R (v5.0 10/04/01 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
md5: "8359638e857c8bc18c3c18ac17d9cc3c"
|
||||
region_check: true
|
||||
@@ -561,7 +561,7 @@ files:
|
||||
|
||||
- name: "ps2_scph30005r.bin"
|
||||
description: "PS2, SCPH-30005R/SCPH-30006R/SCPH-30007R (v5.0 07/30/01 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "352d2ff9b3f68be7e6fa7e6dd8389346"
|
||||
region_check: true
|
||||
@@ -571,7 +571,7 @@ files:
|
||||
|
||||
- name: "ps2_scph39001.bin"
|
||||
description: "PS2, SCPH-39001 (v5.0 02/07/02 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
md5: "d5ce2c7d119f563ce04bc04dbc3a323e"
|
||||
region_check: true
|
||||
@@ -581,7 +581,7 @@ files:
|
||||
|
||||
- name: "ps2_scph39002.bin"
|
||||
description: "PS2, SCPH-39002/SCPH-39003/SCPH-39004 (v5.0 03/19/02 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
md5: "0d2228e6fd4fb639c9c39d077a9ec10c"
|
||||
region_check: true
|
||||
@@ -591,7 +591,7 @@ files:
|
||||
|
||||
- name: "ps2_scph37000_39000.bin"
|
||||
description: "PS2, SCPH-37000/SCPH-39000 (v5.0 04/26/02 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "72da56fccb8fcd77bba16d1b6f479914"
|
||||
region_check: true
|
||||
@@ -601,7 +601,7 @@ files:
|
||||
|
||||
- name: "ps2_scph39008.bin"
|
||||
description: "PS2, SCPH-39008 (v5.0 04/26/02 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
md5: "5b1f47fbeb277c6be2fccdd6344ff2fd"
|
||||
region_check: true
|
||||
@@ -611,7 +611,7 @@ files:
|
||||
|
||||
- name: "ps2_scph39005.bin"
|
||||
description: "PS2, SCPH-39005/SCPH-39006/SCPH-39007 (v5.0 04/26/02 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "315a4003535dfda689752cb25f24785c"
|
||||
region_check: true
|
||||
@@ -621,7 +621,7 @@ files:
|
||||
|
||||
- name: "ps2_dtlh50000.bin"
|
||||
description: "PS2, DTL-H50000 (v5.0 02/06/03 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "54ecde087258557e2ddb5c3ddb004028"
|
||||
region_check: true
|
||||
@@ -631,7 +631,7 @@ files:
|
||||
|
||||
- name: "ps2_scph50000_55000.bin"
|
||||
description: "PS2, SCPH-50000/SCPH-55000 (v5.0 02/06/03 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "312ad4816c232a9606e56f946bc0678a"
|
||||
region_check: true
|
||||
@@ -641,7 +641,7 @@ files:
|
||||
|
||||
- name: "ps2_dtlh50002.bin"
|
||||
description: "PS2, DTL-H50002 (v5.0 02/27/03 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
md5: "666018ffec65c5c7e04796081295c6c7"
|
||||
region_check: true
|
||||
@@ -651,7 +651,7 @@ files:
|
||||
|
||||
- name: "ps2_scph50002_0227.bin"
|
||||
description: "PS2, SCPH-50002/SCPH-50003/SCPH-50004 (v5.0 02/27/03 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
md5: "6e69920fa6eef8522a1d688a11e41bc6"
|
||||
region_check: true
|
||||
@@ -661,7 +661,7 @@ files:
|
||||
|
||||
- name: "ps2_dtlh50001_0325.bin"
|
||||
description: "PS2, DTL-H50001 (v5.0 03/25/03 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
md5: "eb960de68f0c0f7f9fa083e9f79d0360"
|
||||
region_check: true
|
||||
@@ -671,7 +671,7 @@ files:
|
||||
|
||||
- name: "ps2_scph50001_0325.bin"
|
||||
description: "PS2, SCPH-50001 (v5.0 03/25/03 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
md5: "8aa12ce243210128c5074552d3b86251"
|
||||
region_check: true
|
||||
@@ -681,7 +681,7 @@ files:
|
||||
|
||||
- name: "ps2_dtlh50009.bin"
|
||||
description: "PS2, DTL-H50009 (v5.0 02/24/03 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "240d4c5ddd4b54069bdc4a3cd2faf99d"
|
||||
region_check: true
|
||||
@@ -691,7 +691,7 @@ files:
|
||||
|
||||
- name: "ps2_desr5000.bin"
|
||||
description: "PS2, DESR-5000/DESR-5100/DESR-7000/DESR-7100 (v5.0 10/28/03 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "1c6cd089e6c83da618fbf2a081eb4888"
|
||||
region_check: true
|
||||
@@ -701,7 +701,7 @@ files:
|
||||
|
||||
- name: "ps2_scph55000_0623.bin"
|
||||
description: "PS2, SCPH-55000 (v5.0 06/23/03 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "463d87789c555a4a7604e97d7db545d1"
|
||||
region_check: true
|
||||
@@ -711,7 +711,7 @@ files:
|
||||
|
||||
- name: "ps2_dtlh50001_0623.bin"
|
||||
description: "PS2, DTL-H50001 (v5.0 06/23/03 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
md5: "ab9d49ad40ae49f19856ad187777b1b3"
|
||||
region_check: true
|
||||
@@ -721,7 +721,7 @@ files:
|
||||
|
||||
- name: "ps2_scph50001_0623.bin"
|
||||
description: "PS2, SCPH-50001/SCPH-50010 (v5.0 06/23/03 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
md5: "35461cecaa51712b300b2d6798825048"
|
||||
region_check: true
|
||||
@@ -731,7 +731,7 @@ files:
|
||||
|
||||
- name: "ps2_scph50002_0623.bin"
|
||||
description: "PS2, SCPH-50002/SCPH-50003/SCPH-50004 (v5.0 06/23/03 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
md5: "bd6415094e1ce9e05daabe85de807666"
|
||||
region_check: true
|
||||
@@ -741,7 +741,7 @@ files:
|
||||
|
||||
- name: "ps2_scph50006.bin"
|
||||
description: "PS2, SCPH-50006/SCPH-50007 (v5.0 06/23/03 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "2e70ad008d4ec8549aada8002fdf42fb"
|
||||
region_check: true
|
||||
@@ -751,7 +751,7 @@ files:
|
||||
|
||||
- name: "ps2_scph50005.bin"
|
||||
description: "PS2, SCPH-50005 (v5.0 06/23/03 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "50d5b97b57d8c9b6534adcb46c2027d4"
|
||||
region_check: true
|
||||
@@ -761,7 +761,7 @@ files:
|
||||
|
||||
- name: "ps2_scph50008.bin"
|
||||
description: "PS2, SCPH-50008 (v5.0 06/23/03 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
md5: "b53d51edc7fc086685e31b811dc32aad"
|
||||
region_check: true
|
||||
@@ -771,7 +771,7 @@ files:
|
||||
|
||||
- name: "ps2_scph50009.bin"
|
||||
description: "PS2, SCPH-50009 (v5.0 06/23/03 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "1b6e631b536247756287b916f9396872"
|
||||
region_check: true
|
||||
@@ -781,7 +781,7 @@ files:
|
||||
|
||||
- name: "ps2_scph50000_0822.bin"
|
||||
description: "PS2, SCPH-50000 (v5.0 08/22/03 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "00da1b177096cfd2532c8fa22b43e667"
|
||||
region_check: true
|
||||
@@ -791,7 +791,7 @@ files:
|
||||
|
||||
- name: "ps2_scph50004_0822.bin"
|
||||
description: "PS2, SCPH-50004 (v5.0 08/22/03 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
md5: "afde410bd026c16be605a1ae4bd651fd"
|
||||
region_check: true
|
||||
@@ -801,7 +801,7 @@ files:
|
||||
|
||||
- name: "ps2_scph50011.bin"
|
||||
description: "PS2, SCPH-50011 (v5.0 03/29/04 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
md5: "81f4336c1de607dd0865011c0447052e"
|
||||
region_check: true
|
||||
@@ -811,7 +811,7 @@ files:
|
||||
|
||||
- name: "ps2_scph70000.bin"
|
||||
description: "PS2, SCPH-70000 (v5.0 06/14/04 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "0eee5d1c779aa50e94edd168b4ebf42e"
|
||||
region_check: true
|
||||
@@ -821,7 +821,7 @@ files:
|
||||
|
||||
- name: "ps2_scph70001.bin"
|
||||
description: "PS2, SCPH-70001/SCPH-70011/SCPH-70012 (v5.0 06/14/04 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
md5: "d333558cc14561c1fdc334c75d5f37b7"
|
||||
region_check: true
|
||||
@@ -831,7 +831,7 @@ files:
|
||||
|
||||
- name: "ps2_scph70002.bin"
|
||||
description: "PS2, SCPH-70002/SCPH-70003/SCPH-70004/SCPH-70008 (v5.0 06/14/04 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
md5: "dc752f160044f2ed5fc1f4964db2a095"
|
||||
region_check: true
|
||||
@@ -841,7 +841,7 @@ files:
|
||||
|
||||
- name: "ps2_scph70002_alt.bin"
|
||||
description: "PS2, SCPH-70002 (v5.0 06/14/04 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
md5: "7ebb4fc5eab6f79a27d76ac9aad392b2"
|
||||
region_check: true
|
||||
@@ -851,7 +851,7 @@ files:
|
||||
|
||||
- name: "ps2_dtlh70002.bin"
|
||||
description: "PS2, DTL-H70002 (v5.0 06/14/04 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
md5: "63ead1d74893bf7f36880af81f68a82d"
|
||||
region_check: true
|
||||
@@ -861,7 +861,7 @@ files:
|
||||
|
||||
- name: "ps2_scph70005.bin"
|
||||
description: "PS2, SCPH-70005/SCPH-70006/SCPH-70007 (v5.0 06/14/04 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "3e3e030c0f600442fa05b94f87a1e238"
|
||||
region_check: true
|
||||
@@ -871,7 +871,7 @@ files:
|
||||
|
||||
- name: "ps2_desr5500.bin"
|
||||
description: "PS2, DESR-5500/DESR-5700/DESR-7500/DESR-7700 (v5.0 09/17/04 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "1ad977bb539fc9448a08ab276a836bbc"
|
||||
region_check: true
|
||||
@@ -881,7 +881,7 @@ files:
|
||||
|
||||
- name: "ps2_dtlh75000.bin"
|
||||
description: "PS2, DTL-H75000 (v5.0 06/20/05 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "bf0078ba5e19d57eae18047407f3b6e5"
|
||||
region_check: true
|
||||
@@ -891,7 +891,7 @@ files:
|
||||
|
||||
- name: "ps2_scph75000.bin"
|
||||
description: "PS2, SCPH-75000 (v5.0 06/20/05 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "eb4f40fcf4911ede39c1bbfe91e7a89a"
|
||||
region_check: true
|
||||
@@ -901,7 +901,7 @@ files:
|
||||
|
||||
- name: "ps2_dtlh75000a.bin"
|
||||
description: "PS2, DTL-H75000A (v5.0 06/20/05 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
md5: "9959ad7a8685cad66206e7752ca23f8b"
|
||||
region_check: true
|
||||
@@ -911,7 +911,7 @@ files:
|
||||
|
||||
- name: "ps2_scph75001.bin"
|
||||
description: "PS2, SCPH-75001/SCPH-75010 (v5.0 06/20/05 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
md5: "929a14baca1776b00869f983aa6e14d2"
|
||||
region_check: true
|
||||
@@ -921,7 +921,7 @@ files:
|
||||
|
||||
- name: "ps2_scph75002.bin"
|
||||
description: "PS2, SCPH-75002/SCPH-75003/SCPH-75004/SCPH-75008 (v5.0 06/20/05 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
md5: "573f7d4a430c32b3cc0fd0c41e104bbd"
|
||||
region_check: true
|
||||
@@ -931,7 +931,7 @@ files:
|
||||
|
||||
- name: "ps2_scph75006.bin"
|
||||
description: "PS2, SCPH-75006 (v5.0 06/20/05 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "df63a604e8bff5b0599bd1a6c2721bd0"
|
||||
region_check: true
|
||||
@@ -941,7 +941,7 @@ files:
|
||||
|
||||
- name: "ps2_scph77000.bin"
|
||||
description: "PS2, SCPH-77000 (v5.0 02/10/06 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "5b1ba4bb914406fae75ab8e38901684d"
|
||||
region_check: true
|
||||
@@ -951,7 +951,7 @@ files:
|
||||
|
||||
- name: "ps2_scph77001.bin"
|
||||
description: "PS2, SCPH-77001/SCPH-77010 (v5.0 02/10/06 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
md5: "cb801b7920a7d536ba07b6534d2433ca"
|
||||
region_check: true
|
||||
@@ -961,7 +961,7 @@ files:
|
||||
|
||||
- name: "ps2_scph77002.bin"
|
||||
description: "PS2, SCPH-77002/SCPH-77003/SCPH-77004/SCPH-77008 (v5.0 02/10/06 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
md5: "af60e6d1a939019d55e5b330d24b1c25"
|
||||
region_check: true
|
||||
@@ -971,7 +971,7 @@ files:
|
||||
|
||||
- name: "ps2_scph77006.bin"
|
||||
description: "PS2, SCPH-77006/SCPH-77007 (v5.0 02/10/06 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "549a66d0c698635ca9fa3ab012da7129"
|
||||
region_check: true
|
||||
@@ -981,7 +981,7 @@ files:
|
||||
|
||||
- name: "ps2_dtlh90000_j.bin"
|
||||
description: "PS2, DTL-H90000 (v5.0 09/05/06 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "5e2014472c88f74f7547d8c2c60eca45"
|
||||
region_check: true
|
||||
@@ -991,7 +991,7 @@ files:
|
||||
|
||||
- name: "ps2_scph79000_90000.bin"
|
||||
description: "PS2, SCPH-79000/SCPH-90000 (v5.0 09/05/06 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "5de9d0d730ff1e7ad122806335332524"
|
||||
region_check: true
|
||||
@@ -1001,7 +1001,7 @@ files:
|
||||
|
||||
- name: "ps2_dtlh90000_a.bin"
|
||||
description: "PS2, DTL-H90000 (v5.0 09/05/06 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
md5: "21fe4cad111f7dc0f9af29477057f88d"
|
||||
region_check: true
|
||||
@@ -1011,7 +1011,7 @@ files:
|
||||
|
||||
- name: "ps2_scph79001_90001.bin"
|
||||
description: "PS2, SCPH-79001/SCPH-79010/SCPH-90001 (v5.0 09/05/06 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
md5: "40c11c063b3b9409aa5e4058e984e30c"
|
||||
region_check: true
|
||||
@@ -1021,7 +1021,7 @@ files:
|
||||
|
||||
- name: "ps2_scph79002_90002.bin"
|
||||
description: "PS2, SCPH-79002/SCPH-79003/SCPH-79004/SCPH-79008/SCPH-90002/SCPH-90003/SCPH-90004 (v5.0 09/05/06 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
md5: "80bbb237a6af9c611df43b16b930b683"
|
||||
region_check: true
|
||||
@@ -1031,7 +1031,7 @@ files:
|
||||
|
||||
- name: "ps2_scph79006_90006.bin"
|
||||
description: "PS2, SCPH-79006/SCPH-79007/SCPH-90006/SCPH-90007 (v5.0 09/05/06 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "c37bce95d32b2be480f87dd32704e664"
|
||||
region_check: true
|
||||
@@ -1041,7 +1041,7 @@ files:
|
||||
|
||||
- name: "ps2_scph90000_0220.bin"
|
||||
description: "PS2, SCPH-90000 (v5.0 02/20/08 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "80ac46fa7e77b8ab4366e86948e54f83"
|
||||
region_check: true
|
||||
@@ -1051,7 +1051,7 @@ files:
|
||||
|
||||
- name: "ps2_scph90001_0220.bin"
|
||||
description: "PS2, SCPH-90001/SCPH-90010 (v5.0 02/20/08 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
md5: "21038400dc633070a78ad53090c53017"
|
||||
region_check: true
|
||||
@@ -1061,7 +1061,7 @@ files:
|
||||
|
||||
- name: "ps2_scph90002_0220.bin"
|
||||
description: "PS2, SCPH-90002/SCPH-90003/SCPH-90004/SCPH-90008 (v5.0 02/20/08 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
md5: "dc69f0643a3030aaa4797501b483d6c4"
|
||||
region_check: true
|
||||
@@ -1071,7 +1071,7 @@ files:
|
||||
|
||||
- name: "ps2_scph90005_0220.bin"
|
||||
description: "PS2, SCPH-90005/SCPH-90006/SCPH-90007 (v5.0 02/20/08 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "30d56e79d89fbddf10938fa67fe3f34e"
|
||||
region_check: true
|
||||
@@ -1081,7 +1081,7 @@ files:
|
||||
|
||||
- name: "ps2_kdl22px300.bin"
|
||||
description: "PS2, KDL-22PX300 (v5.0 04/15/10 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
md5: "93ea3bcee4252627919175ff1b16a1d9"
|
||||
region_check: true
|
||||
@@ -1094,7 +1094,7 @@ files:
|
||||
|
||||
- name: "openbios.bin"
|
||||
description: "OpenBIOS"
|
||||
region: "Auto"
|
||||
region: [world]
|
||||
required: false
|
||||
md5: null
|
||||
region_check: false
|
||||
|
||||
@@ -37,7 +37,7 @@ notes: |
|
||||
files:
|
||||
- name: "lynxboot.img"
|
||||
description: "Atari Lynx boot ROM"
|
||||
region: "World"
|
||||
region: [world]
|
||||
required: true
|
||||
size: 512
|
||||
sha1: "e4ed47fae31693e016b081c6bda48da5b70d7ccb"
|
||||
|
||||
@@ -75,7 +75,6 @@ files:
|
||||
- "Supercharger_BIOS.bin"
|
||||
- "supercharger_bios.bin"
|
||||
- "SUPERCHARGER_BIOS.bin"
|
||||
region: "NTSC"
|
||||
required: false
|
||||
hle_fallback: true
|
||||
size: 2048
|
||||
@@ -89,7 +88,6 @@ files:
|
||||
aliases:
|
||||
- "Supercharger_BIOS_PAL.bin"
|
||||
- "SUPERCHARGER_BIOS_PAL.bin"
|
||||
region: "PAL"
|
||||
required: false
|
||||
hle_fallback: true
|
||||
size: 2048
|
||||
|
||||
@@ -25,28 +25,28 @@ notes: >
|
||||
files:
|
||||
- name: "syscard3.pce"
|
||||
description: "CD-ROM System Card 3.0 (Japan)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: true
|
||||
source_ref: "libretro.cpp:1051-1052, mednafen/settings.cpp:40"
|
||||
note: "Default value for pce_cdbios option."
|
||||
|
||||
- name: "syscard2.pce"
|
||||
description: "CD-ROM System Card 2.0 (Japan)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
source_ref: "libretro.cpp:1053-1054"
|
||||
note: "pce_cdbios = 'System Card 2'."
|
||||
|
||||
- name: "syscard1.pce"
|
||||
description: "CD-ROM System Card 1.0 (Japan)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
source_ref: "libretro.cpp:1055-1056"
|
||||
note: "pce_cdbios = 'System Card 1'."
|
||||
|
||||
- name: "gexpress.pce"
|
||||
description: "Games Express CD Card"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
aliases: ["gecard.pce"]
|
||||
source_ref: "libretro.cpp:1057-1058, mednafen/settings.cpp:41, mednafen/pce/pce.cpp:373-411"
|
||||
@@ -54,14 +54,14 @@ files:
|
||||
|
||||
- name: "syscard3u.pce"
|
||||
description: "CD-ROM System Card 3.0 (US / TurboGrafx-CD)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
source_ref: "libretro.cpp:1059-1060"
|
||||
note: "pce_cdbios = 'System Card 3 US'. Not in .info."
|
||||
|
||||
- name: "syscard2u.pce"
|
||||
description: "CD-ROM System Card 2.0 (US / TurboGrafx-CD)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
source_ref: "libretro.cpp:1061-1062"
|
||||
note: "pce_cdbios = 'System Card 2 US'. Not in .info."
|
||||
@@ -23,28 +23,28 @@ notes: >
|
||||
files:
|
||||
- name: "syscard3.pce"
|
||||
description: "CD-ROM System Card 3.0 (Japan)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: true
|
||||
source_ref: "libretro.c:65, libretro.c:1896-1901"
|
||||
note: "Default value for pce_fast_cdbios option."
|
||||
|
||||
- name: "syscard2.pce"
|
||||
description: "CD-ROM System Card 2.0 (Japan)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
source_ref: "libretro.c:1903"
|
||||
note: "pce_fast_cdbios = 'System Card 2'."
|
||||
|
||||
- name: "syscard1.pce"
|
||||
description: "CD-ROM System Card 1.0 (Japan)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
source_ref: "libretro.c:1905"
|
||||
note: "pce_fast_cdbios = 'System Card 1'."
|
||||
|
||||
- name: "gexpress.pce"
|
||||
description: "Games Express CD Card"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
aliases: ["gecard.pce"]
|
||||
source_ref: "libretro.c:1906-1907"
|
||||
@@ -52,14 +52,14 @@ files:
|
||||
|
||||
- name: "syscard3u.pce"
|
||||
description: "CD-ROM System Card 3.0 (US / TurboGrafx-CD)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
source_ref: "libretro.c:1908-1909"
|
||||
note: "pce_fast_cdbios = 'System Card 3 US'. Not in .info."
|
||||
|
||||
- name: "syscard2u.pce"
|
||||
description: "CD-ROM System Card 2.0 (US / TurboGrafx-CD)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
source_ref: "libretro.c:1910-1911"
|
||||
note: "pce_fast_cdbios = 'System Card 2 US'. Not in .info."
|
||||
@@ -27,28 +27,28 @@ notes: >
|
||||
files:
|
||||
- name: "syscard3.pce"
|
||||
description: "CD-ROM System Card 3.0 (Japan)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: true
|
||||
source_ref: "libretro.cpp:972-973, mednafen/settings.cpp:37"
|
||||
note: "Default value for sgx_cdbios option."
|
||||
|
||||
- name: "syscard2.pce"
|
||||
description: "CD-ROM System Card 2.0 (Japan)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
source_ref: "libretro.cpp:974-975"
|
||||
note: "sgx_cdbios = 'System Card 2'."
|
||||
|
||||
- name: "syscard1.pce"
|
||||
description: "CD-ROM System Card 1.0 (Japan)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
source_ref: "libretro.cpp:976-977"
|
||||
note: "sgx_cdbios = 'System Card 1'."
|
||||
|
||||
- name: "gexpress.pce"
|
||||
description: "Games Express CD Card"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
source_ref: "libretro.cpp:978-979, mednafen/pce_fast/pce.cpp:330-367,453-454"
|
||||
note: "sgx_cdbios = 'Games Express' or auto-detected via DetectGECD()."
|
||||
@@ -0,0 +1,328 @@
|
||||
emulator: Oricutron
|
||||
type: standalone
|
||||
core_classification: other
|
||||
source: "https://github.com/pete-gordon/oricutron"
|
||||
upstream: "https://www.petergordon.org.uk/oricutron/"
|
||||
profiled_date: "2026-08-08"
|
||||
source_commit: "002279fce9fa756d1d63cdc40ae97939eb7de7ed"
|
||||
core_version: "1.2.0"
|
||||
display_name: "Tangerine - Oric (Oricutron)"
|
||||
cores:
|
||||
- oricutron
|
||||
systems:
|
||||
- oric
|
||||
- oricatmos
|
||||
mode: standalone
|
||||
|
||||
notes: |
|
||||
Oric-1, Oric-1 16K, Oric Atmos, Telestrat and Pravetz 8D emulator by Peter
|
||||
Gordon. Recalbox packages it for its oricatmos system, ES-DE offers it for
|
||||
oric alongside MAME and starts it with the working directory set to the
|
||||
emulator directory.
|
||||
|
||||
ROM names are configured without the extension: load_rom appends ".rom"
|
||||
(machine.c:1089-1093). The defaults are set in preinit_gui (gui.c:2283-2297)
|
||||
behind ROMPREFIX, which is "roms/" everywhere except AmigaOS
|
||||
("PROGDIR:roms/") and the emscripten build ("assets/roms/") (system.h:38-68).
|
||||
A path read from oricutron.cfg goes through read_config_path
|
||||
(main.c:341-347), which prepends the program directory resolved from argv[0]
|
||||
(main.c:196-236), so a relative path resolves against the directory holding
|
||||
the binary rather than the working directory.
|
||||
|
||||
The machine ROM is loaded by init_machine for the selected machine type and
|
||||
a failure returns SDL_FALSE, after which the emulator exits
|
||||
(main.c:1442-1446). The four disk controller ROMs are loaded once at startup
|
||||
by load_diskroms (main.c:1407, machine.c:1249-1255) whatever the machine and
|
||||
the drive setting; a failure only clears the matching flag and startup
|
||||
continues. Of those flags only pravetzrom_valid is read afterwards
|
||||
(machine.c:1055): the Microdisc, BD-500 and Jasmin buffers are mapped into
|
||||
the CPU address space whether or not the file was read, so a missing file
|
||||
leaves the selected controller running on zeroed memory.
|
||||
|
||||
Size is the only property the code checks. A machine ROM is rejected above
|
||||
16384 bytes and is loaded right-aligned in its bank, so a shorter file is
|
||||
accepted (machine.c:1101-1118). A disk controller ROM is read as one
|
||||
fixed-length block, which fails when the file is shorter than that length
|
||||
and ignores anything past it (machine.c:1120-1126). No hash is computed
|
||||
anywhere in the tree.
|
||||
|
||||
Telestrat banks 0 to 4 hold RAM unless the config names a file for them
|
||||
(machine.c:2029-2038). The Twilighte board is off by default and is switched
|
||||
on with twilighte_board in oricutron.cfg (main.c:660). Its bank config is
|
||||
opened as a bare relative path (oric_twilighte_board_plugin.c:132) and the
|
||||
bank filenames in it are read with read_config_string rather than
|
||||
read_config_path (oric_twilighte_board_plugin.c:174,182), so both resolve
|
||||
against the working directory and not the program directory.
|
||||
|
||||
files:
|
||||
- name: basic11b.rom
|
||||
description: "Oric Atmos BASIC 1.1b ROM"
|
||||
required: true
|
||||
max_size: 16384
|
||||
validation: [size]
|
||||
source_ref: "gui.c:2283 (default roms/basic11b), main.c:553 (atmosrom), machine.c:2006-2007 (MACH_ATMOS)"
|
||||
note: >
|
||||
Atmos is the machine oricutron.cfg selects, so this is the ROM a stock
|
||||
configuration needs. Also named by twilbankrom06 in the Twilighte board
|
||||
config (plugins/twilighte_board/twilighte.cfg:3).
|
||||
|
||||
- name: basic10.rom
|
||||
description: "Oric-1 BASIC 1.0 ROM"
|
||||
required: true
|
||||
max_size: 16384
|
||||
validation: [size]
|
||||
source_ref: "gui.c:2284 (default roms/basic10), main.c:554 (oric1rom), machine.c:1932-1933 (MACH_ORIC1_16K), machine.c:1969-1970 (MACH_ORIC1)"
|
||||
note: "Serves both the 48K Oric-1 and the 16K Oric-1, selected with -m oric1 or -m o16k."
|
||||
|
||||
- name: pravetzt.rom
|
||||
description: "Pravetz 8D BASIC ROM"
|
||||
required: true
|
||||
max_size: 16384
|
||||
validation: [size]
|
||||
source_ref: "gui.c:2288 (default roms/pravetzt), main.c:558 (pravetzrom), machine.c:2082-2083 (MACH_PRAVETZ)"
|
||||
note: "Selected with -m pravetz. roms/pravetzt-1.0.pch and .sym ship for an earlier revision of this ROM."
|
||||
|
||||
- name: teleass.rom
|
||||
description: "Telestrat TELEASS ROM, bank 5"
|
||||
required: true
|
||||
max_size: 16384
|
||||
validation: [size]
|
||||
source_ref: "gui.c:2295 (default roms/teleass), main.c:588-589 (telebank5), machine.c:2029-2033 (MACH_TELESTRAT)"
|
||||
|
||||
- name: hyperbas.rom
|
||||
description: "Telestrat HYPER BASIC ROM, bank 6"
|
||||
required: true
|
||||
max_size: 16384
|
||||
validation: [size]
|
||||
source_ref: "gui.c:2296 (default roms/hyperbas), main.c:588-589 (telebank6), machine.c:2029-2033 (MACH_TELESTRAT)"
|
||||
|
||||
- name: telmon24.rom
|
||||
description: "Telestrat TELEMON 2.4 ROM, bank 7"
|
||||
required: true
|
||||
max_size: 16384
|
||||
validation: [size]
|
||||
source_ref: "gui.c:2297 (default roms/telmon24), main.c:588-589 (telebank7), machine.c:2029-2033 (MACH_TELESTRAT)"
|
||||
note: "Bank 7 is the bank mapped at reset (machine.c:2041-2043)."
|
||||
|
||||
- name: microdis.rom
|
||||
description: "Microdisc disk controller ROM"
|
||||
required: false
|
||||
min_size: 8192
|
||||
validation: [size]
|
||||
source_ref: "gui.c:2285 (default roms/microdis), main.c:555 (mdiscrom), machine.c:1251, machine.c:904-911 (mapped at $E000)"
|
||||
note: >
|
||||
Conditional: needed for the Microdisc controller, which is the drive type
|
||||
a disk image falls back to when none is given (main.c:1409-1410,
|
||||
1488-1489). Loaded whatever the machine and not fatal, and
|
||||
setup_for_microdisc raises romdis, so an absent file leaves the $E000
|
||||
overlay reading zeroes.
|
||||
|
||||
- name: bd500.rom
|
||||
description: "ByteDrive BD-500 disk controller ROM"
|
||||
required: false
|
||||
min_size: 8192
|
||||
validation: [size]
|
||||
source_ref: "gui.c:2286 (default roms/bd500), main.c:556 (bd500rom), machine.c:1252, machine.c:937-944 (mapped at $E000)"
|
||||
note: >
|
||||
Conditional: needed for the BD-500 controller, selected with -k bd500 or
|
||||
by autodetecting a BD-500 image (main.c:1478-1481). Same non-fatal load
|
||||
and same zeroed overlay as the Microdisc ROM. The rom16 and dos70 config
|
||||
keys change how ROMDIS disables the chips (oricutron.cfg:98-106).
|
||||
|
||||
- name: jasmin.rom
|
||||
description: "Jasmin disk controller ROM"
|
||||
required: false
|
||||
min_size: 2048
|
||||
validation: [size]
|
||||
source_ref: "gui.c:2287 (default roms/jasmin), main.c:557 (jasminrom), machine.c:1253, machine.c:836-844 (mapped at $F800)"
|
||||
note: >
|
||||
Conditional: needed for the Jasmin controller, selected with -k jasmin or
|
||||
by autodetecting a Jasmin image (main.c:1468-1471). setup_for_jasmin
|
||||
leaves romdis clear, so the machine still boots from its BASIC ROM.
|
||||
|
||||
- name: 8dos2.rom
|
||||
description: "Pravetz 8D floppy controller boot PROM"
|
||||
required: false
|
||||
min_size: 512
|
||||
validation: [size]
|
||||
source_ref: "gui.c:2289 (default roms/8dos2), main.c:559 (pravetz8drom), machine.c:1254, machine.c:1053-1058 (mapped at $320-$3FF)"
|
||||
note: >
|
||||
Conditional: needed for the Pravetz 8D drive. The only disk ROM whose
|
||||
load flag is read back, so the Pravetz machine boots without it and only
|
||||
the drive is lost. oricutron.cfg:44-45 gives roms/8dos as the other
|
||||
value for this key, and roms/8dos.sym ships for it.
|
||||
|
||||
- name: kernel.rom
|
||||
description: "Orix kernel, Twilighte board ROM bank 7"
|
||||
required: false
|
||||
max_size: 16384
|
||||
validation: [size]
|
||||
source_ref: "plugins/twilighte_board/twilighte.cfg:2 (twilbankrom07), plugins/twilighte_board/oric_twilighte_board_plugin.c:167-216 (parse and load), plugins/twilighte_board/oric_twilighte_board_plugin.c:63-115 (load_rom_twilighte)"
|
||||
note: >
|
||||
Loaded only when twilighte_board is on (main.c:660). A configured bank
|
||||
file that cannot be read makes twilighte_oric_init return NULL.
|
||||
ReadMe.txt:506-519 records the copy in the tree as Orix v2022.1 and
|
||||
points at repo.orix.oric.org for later builds.
|
||||
|
||||
- name: shell.rom
|
||||
description: "Orix shell, Twilighte board ROM bank 5"
|
||||
required: false
|
||||
max_size: 16384
|
||||
validation: [size]
|
||||
source_ref: "plugins/twilighte_board/twilighte.cfg:4 (twilbankrom05), plugins/twilighte_board/oric_twilighte_board_plugin.c:167-216 (parse and load), plugins/twilighte_board/oric_twilighte_board_plugin.c:63-115 (load_rom_twilighte)"
|
||||
note: >
|
||||
Loaded only when twilighte_board is on (main.c:660). ReadMe.txt:506
|
||||
names kernel.rom in bank 7 and shell.rom in bank 5 as the minimum
|
||||
configuration for the board to start.
|
||||
|
||||
analysis:
|
||||
gui_images:
|
||||
source_ref: "gui.c:100-117 (gimgs table), gui.c:425-492 (gimg_load), gui.c:2464-2466 (init_gui)"
|
||||
lookup: "program directory + IMAGEPREFIX, which is images/ everywhere except AmigaOS and the emscripten build (system.h:38-68)"
|
||||
validation: "uncompressed 24-bit BMP, one plane, at the exact width and height declared in the table"
|
||||
fatal: true
|
||||
note: >
|
||||
init_gui returns SDL_FALSE on the first image it cannot read and the
|
||||
emulator exits (main.c:1416-1420). The set ships in the source tree and
|
||||
in the binary releases, and packagers install it beside the binary:
|
||||
the RetroDECK recipe copies the whole images directory
|
||||
(RetroDECK/components archive_later/oricutron/component_recipe.json) and
|
||||
the ES-DE guide has Linux users take images and roms out of the Windows
|
||||
archive (USERGUIDE.md:3377-3391).
|
||||
files:
|
||||
- statusbar.bmp
|
||||
- disk_ejected.bmp
|
||||
- disk_idle.bmp
|
||||
- disk_active.bmp
|
||||
- disk_modified.bmp
|
||||
- disk_modactive.bmp
|
||||
- tape_ejected.bmp
|
||||
- tape_pause.bmp
|
||||
- tape_play.bmp
|
||||
- tape_stop.bmp
|
||||
- tape_record.bmp
|
||||
- avirec.bmp
|
||||
- gfx_oric1kbd.bmp
|
||||
- gfx_atmoskbd.bmp
|
||||
- gfx_pravetzkbd.bmp
|
||||
conditional_entries:
|
||||
note: >
|
||||
gfx_oric1kbd.bmp sits behind WWW_NO_ORIC1 and gfx_pravetzkbd.bmp
|
||||
behind WWW_NO_PRAVETZ (gui.c:112-118, gui.h:114-120), both of which
|
||||
only cut down the emscripten build. A native build carries all
|
||||
fifteen.
|
||||
non_fatal:
|
||||
- filename: winicon.bmp
|
||||
source_ref: "gui.c:2306-2307"
|
||||
note: "Read with SDL_LoadBMP and passed to SDL_COMPAT_WM_SetIcon, which takes a null surface."
|
||||
unused:
|
||||
- filename: status_bar.bmp
|
||||
reason: "the gimgs table names statusbar.bmp; no code path reads this second file"
|
||||
|
||||
rom_sidecars:
|
||||
note: >
|
||||
Both sidecars are keyed on the ROM basename from the config, so renaming
|
||||
or relocating a ROM moves them with it.
|
||||
|
||||
symbols:
|
||||
pattern: "<rom basename>.sym"
|
||||
source_ref: "machine.c:1129-1133 (load_rom), monitor.c:2676-2691 (mon_new_symbols)"
|
||||
note: >
|
||||
Read for all ten ROMs right after the ROM itself, with verbose off, so
|
||||
an absent file is silent. Fills the debugger symbol tables and nothing
|
||||
else. Compiled out under WWW_NO_MONITOR.
|
||||
shipped:
|
||||
- basic10.sym
|
||||
- basic11b.sym
|
||||
- pravetzt.sym
|
||||
- teleass.sym
|
||||
- telmon24.sym
|
||||
- microdis.sym
|
||||
- jasmin.sym
|
||||
- bd500.sym
|
||||
- 8dos2.sym
|
||||
referenced_but_not_shipped:
|
||||
- hyperbas.sym
|
||||
|
||||
patches:
|
||||
pattern: "<rom basename>.pch"
|
||||
source_ref: "machine.c:1726-1740 (load_patches), machine.c:1933,1970,2007,2033,2083 (call sites)"
|
||||
note: >
|
||||
Read for the machine ROMs only, never for the disk controller ROMs.
|
||||
Carries the keyboard layout, the CLOAD/CSAVE filename decoding
|
||||
addresses, the tape turbo hook addresses and any number of literal
|
||||
byte patches. An absent file returns silently and leaves
|
||||
pch_tt_available false (machine.c:1810-1815), so tape turbo stays off
|
||||
and tapes load at real speed (tape.c:243, 1322-1462).
|
||||
shipped:
|
||||
- basic10.pch
|
||||
- basic11b.pch
|
||||
- pravetzt.pch
|
||||
- telmon24.pch
|
||||
referenced_but_not_shipped:
|
||||
- teleass.pch
|
||||
- hyperbas.pch
|
||||
|
||||
unconfigured_basenames:
|
||||
note: >
|
||||
These sidecars ship for ROM basenames no default config names. They
|
||||
take effect only once a config key points at that basename, and each
|
||||
one implies a ROM dump Oricutron can run.
|
||||
files:
|
||||
- filename: Bas122uk.pch
|
||||
implies: "Oric BASIC 1.22 UK, as an atmosrom value"
|
||||
- filename: Bas122fr.pch
|
||||
implies: "Oric BASIC 1.22 French, as an atmosrom value"
|
||||
- filename: Bas122de.pch
|
||||
implies: "Oric BASIC 1.22 German, as an atmosrom value"
|
||||
- filename: pravetzt-1.0.pch
|
||||
implies: "Pravetz BASIC revision 1.0, as a pravetzrom value"
|
||||
- filename: pravetzt-1.0.sym
|
||||
implies: "Pravetz BASIC revision 1.0, as a pravetzrom value"
|
||||
- filename: 8dos.sym
|
||||
implies: "the earlier Pravetz 8-DOS boot PROM, offered as pravetz8drom in oricutron.cfg:44"
|
||||
- filename: cumana.sym
|
||||
implies: "a Cumana controller ROM; its symbols run from $F800, the window jasminrom feeds"
|
||||
- filename: sedoric.sym
|
||||
implies: "a Sedoric DOS ROM; its symbols name the Microdisc FDC registers at $310-$318"
|
||||
|
||||
unused_resources:
|
||||
- filename: empty_orix.rom
|
||||
reason: "no reference anywhere in the tree; not named by any config default nor by twilighte.cfg"
|
||||
- filename: orixbank1.rom
|
||||
reason: "no reference anywhere in the tree; loadable only once a twilbankromNN key names it"
|
||||
- filename: orixbank2.rom
|
||||
reason: "no reference anywhere in the tree; loadable only once a twilbankromNN key names it"
|
||||
- filename: orixbank3.rom
|
||||
reason: "no reference anywhere in the tree; loadable only once a twilbankromNN key names it"
|
||||
- filename: orixbank4.rom
|
||||
reason: "no reference anywhere in the tree; loadable only once a twilbankromNN key names it"
|
||||
- filename: orixbank5.rom
|
||||
reason: "no reference anywhere in the tree; loadable only once a twilbankromNN key names it"
|
||||
- filename: orixbank6.rom
|
||||
reason: "no reference anywhere in the tree; loadable only once a twilbankromNN key names it"
|
||||
- filename: orixbank7.rom
|
||||
reason: "no reference anywhere in the tree; loadable only once a twilbankromNN key names it"
|
||||
|
||||
user_supplied_paths:
|
||||
note: "Named on the command line or in the config, with no fixed filename"
|
||||
entries:
|
||||
- what: "disk, tape and snapshot images"
|
||||
source_ref: "main.c:1361-1504, oricutron.cfg:63-86"
|
||||
- what: "debugger symbol file, -s or --symbols"
|
||||
source_ref: "main.c:1533-1545"
|
||||
- what: "breakpoint list, -r or --breakpoint with a leading colon"
|
||||
source_ref: "main.c:1547-1574"
|
||||
- what: "keyboard mapping, autoload_keyboard_mapping"
|
||||
source_ref: "main.c:665 (config key), main.c:2188, keyboard.c:587-598, oricutron.cfg:220"
|
||||
- what: "CH376 sdcard and usbdrive trees, telestrat mode"
|
||||
source_ref: "plugins/ch376/ch376.c:1186, ReadMe.txt:512-514"
|
||||
|
||||
generated_files:
|
||||
written_only:
|
||||
- printer_out.txt
|
||||
- "AVI captures"
|
||||
- "snapshots"
|
||||
- "tape captures"
|
||||
- "debugger log and memory dumps"
|
||||
note: "Produced by the emulator, none is user-supplied"
|
||||
@@ -0,0 +1,71 @@
|
||||
emulator: Parallel Launcher
|
||||
type: standalone
|
||||
core_classification: launcher
|
||||
source: "https://gitlab.com/parallel-launcher/parallel-launcher"
|
||||
upstream: "https://gitlab.com/parallel-launcher/parallel-n64"
|
||||
profiled_date: "2026-08-08"
|
||||
source_commit: "8f2924c213f7c98118ab5a86fc21c452b77a7057"
|
||||
upstream_commit: "11574ac28d27faaaef4b5bc3c1ca50fb2ed98b83"
|
||||
core_version: "9.0.5"
|
||||
display_name: "Nintendo - Nintendo 64 (Parallel Launcher)"
|
||||
cores:
|
||||
- parallel-launcher
|
||||
- ca.parallel_launcher.ParallelLauncher
|
||||
systems:
|
||||
- nintendo-64
|
||||
- nintendo-64dd
|
||||
|
||||
notes: |
|
||||
Qt front end that installs and drives its own RetroArch and its own fork of
|
||||
ParaLLEl N64. RetroArch is fetched from buildbot.libretro.com as a stable
|
||||
release archive (retroarch-updater.cpp:24-30). The core,
|
||||
parallel_n64_next_libretro, is fetched from parallel-launcher.ca
|
||||
(updates.cpp:190-213) into retro-data/cores. Core version at this profiling
|
||||
is 2.28.
|
||||
|
||||
Neither lands in a user RetroArch install. The system directory the core
|
||||
reads from is retro-data/system under the launcher's own data directory,
|
||||
written into a private retroarch.cfg on every launch (retroarch.cpp:100-129).
|
||||
RetroArch is spawned as `-L <core> --config <cfg> <rom>`
|
||||
(retroarch.cpp:760-781). The launcher also rewrites the core's .info into
|
||||
retro-data/info at every startup (main.cpp:149-156), which is how RetroArch
|
||||
learns of the 64DD firmware entry.
|
||||
|
||||
Content is restricted to cartridge formats at every entry point: command line
|
||||
and file associations take .z64 .n64 .v64 .bps (main.cpp:65-94), the add-ROM
|
||||
dialog the same (main-window.cpp:607-616), the library scanner *.[nvz]64
|
||||
(rom-finder.cpp:271-278), romhacking.com downloads .z64 (sync.cpp:238). A .bps
|
||||
is patched to a cartridge ROM before launch (play.cpp:721-757). No --subsystem
|
||||
argument is passed either, so neither route into the core's disk path opens:
|
||||
the header check at libretro.c:545-548 and the cart-and-disk subsystem at
|
||||
libretro.c:104-114.
|
||||
|
||||
Graphics plugin is chosen per ROM among ParaLLEl-RDP, Glide64, angrylion,
|
||||
GLideN64, Rice, glN64 and OGRE (settings.hpp:13-22, retroarch.cpp:55-64).
|
||||
|
||||
Selecting glN64 also reads gles2n64.conf from the system directory root at
|
||||
plugin init, and writes it out with defaults when it is absent
|
||||
(glN64Config.c:246-254, Config_WriteConfig at glN64Config.c:95).
|
||||
|
||||
Three paths the fork computes but never reads. GLideN64 builds without
|
||||
FreeType here, so the font file name it resolves for on-screen text is
|
||||
assigned and dropped (TextDrawer.cpp:145-188). Its per-game ini is compiled in
|
||||
as a header and the external copy is skipped because the launcher writes INI
|
||||
Behaviour as disabled on every launch (retroarch.cpp:468,
|
||||
Config_mupenplus.cpp:170-241, libretro.c:2030-2040). Rice's ini reader and
|
||||
writer sit behind #if 0 (RiceConfig.cpp:453-463, Video.cpp:327-333).
|
||||
|
||||
files:
|
||||
- name: "64DD_IPL.bin"
|
||||
system: nintendo-64dd
|
||||
required: false
|
||||
description: "64DD IPL ROM"
|
||||
note: "Read from the system directory root when disk content is loaded, with no size or hash check. The core drops into its dead-emulator loop when it is absent. Content counts as disk when its header carries a 64DD magic word, or when it arrives on the second slot of the cart-and-disk subsystem."
|
||||
source_ref: "libretro/libretro.c:622-666"
|
||||
|
||||
- name: "gles2n64rom.conf"
|
||||
system: nintendo-64
|
||||
required: false
|
||||
description: "glN64 per-ROM settings database"
|
||||
note: "Read at plugin init when the glN64 graphics plugin is selected, keyed on the name in the ROM header. Global settings stand when absent."
|
||||
source_ref: "gles2n64/src/glN64Config.c:185-228"
|
||||
@@ -0,0 +1,121 @@
|
||||
emulator: PCSX2 Legacy
|
||||
type: standalone
|
||||
core_classification: frozen_snapshot
|
||||
bios_mode: agnostic
|
||||
source: "https://github.com/PCSX2/pcsx2"
|
||||
upstream: "https://github.com/PCSX2/pcsx2"
|
||||
profiled_date: "2026-08-08"
|
||||
source_commit: "aaaf29557fe63870ede815dd6d2e48a98279ca59"
|
||||
core_version: "1.6.0"
|
||||
display_name: "Sony - PlayStation 2 (PCSX2 Legacy)"
|
||||
cores:
|
||||
- pcsx2-legacy
|
||||
- PCSX2-LEGACY
|
||||
systems:
|
||||
- sony-playstation-2
|
||||
mode: standalone
|
||||
|
||||
notes: |
|
||||
wxWidgets line, frozen at 1.6.0. Emulation is split across separate plugin
|
||||
binaries loaded at runtime: GSdx, Spu2-X, LilyPad, cdvdGigaherz, DEV9ghzdrk,
|
||||
USBnull and FWnull. Default BIOS folder is bios/ under the user documents
|
||||
directory.
|
||||
ref: pcsx2/gui/AppConfig.cpp:76-80, pcsx2/gui/AppConfig.cpp:192-195
|
||||
|
||||
BIOS selection lists every file in the folder with a *.* mask and keeps the
|
||||
ones whose romdir carries a RESET entry followed by a readable ROMVER, which
|
||||
supplies the version, the zone letter and the build date. Neither size nor
|
||||
hash is checked; the length test that would reject small images is commented
|
||||
out. A missing or zero-length image raises BiosLoadFailed.
|
||||
ref: pcsx2/gui/Panels/BiosSelectorPanel.cpp:151-177,
|
||||
pcsx2/ps2/BiosTools.cpp:74-167, pcsx2/ps2/BiosTools.cpp:325-341,
|
||||
pcsx2/ps2/BiosTools.cpp:261-323
|
||||
|
||||
The three companion ROMs derive from the selected BIOS path. Each extension
|
||||
is first appended to the full name, then substituted for the existing one, so
|
||||
a BIOS at scph39001.bin is followed by scph39001.bin.rom1 then scph39001.rom1.
|
||||
A missing companion is logged and skipped, and a read error is downgraded to a
|
||||
warning. Every read is truncated to its destination region: 256 KB for rom1,
|
||||
512 KB for rom2, 1.75 MB for erom.
|
||||
ref: pcsx2/ps2/BiosTools.cpp:190-229, pcsx2/ps2/BiosTools.cpp:298-300,
|
||||
pcsx2/MemoryTypes.h:21-24
|
||||
|
||||
The nvm and mec companions follow the same naming rule and are written with
|
||||
defaults when absent, so neither is user-supplied: 1024 zero bytes plus an
|
||||
iLink id for nvm, the four bytes 03 06 02 00 for mec.
|
||||
ref: pcsx2/CDVD/CDVD.cpp:93-122, pcsx2/CDVD/CDVD.cpp:138-182
|
||||
|
||||
Two entries pair an XOR fold of the whole 4 MB ROM region with a ROMVER
|
||||
version to select debugger thread offsets. They gate nothing and cover no
|
||||
file on disk.
|
||||
ref: pcsx2/ps2/BiosTools.cpp:57-62, pcsx2/ps2/BiosTools.cpp:175-181,
|
||||
pcsx2/ps2/BiosTools.cpp:305-313
|
||||
|
||||
An IRX image is injected at ROM+0x3C0000 when one is named on the command
|
||||
line. The path is whatever the user passes, so there is no fixed file.
|
||||
ref: pcsx2/ps2/BiosTools.cpp:302-303, pcsx2/gui/AppInit.cpp:542
|
||||
|
||||
Shipped in the distribution tree and read from it: GameIndex.dbf, the
|
||||
widescreen patch archive cheats_ws.zip, which is skipped when a cheats_ws
|
||||
folder holds the patches, the pcsx2_Main and pcsx2_Iconized catalogs under
|
||||
Langs, and the external shader pair shaders/GSdx.fx and
|
||||
shaders/GSdx_FX_Settings.ini, off by default.
|
||||
ref: pcsx2/gui/AppGameDatabase.h:53, pcsx2/gui/AppCoreThread.cpp:459-465,
|
||||
pcsx2/gui/i18n.cpp:82-89, plugins/GSdx/GSdx.cpp:441-443
|
||||
|
||||
host.irx sits behind a comment. ps2hw.dat is read only by zerogs and
|
||||
zzogl-pg, both gated on EXTRA_PLUGINS, which carries no default and is off,
|
||||
and both absent from the Windows solution that builds the release. USBqemu
|
||||
ships in the same disused solution and GSdx_legacy is gated on GSDX_LEGACY.
|
||||
ref: pcsx2/ps2/BiosTools.cpp:296, cmake/SelectPcsx2Plugins.cmake:178-209,
|
||||
cmake/BuildParameters.cmake:41-50, old_plugins.sln
|
||||
|
||||
files:
|
||||
- name: ps2-0230a-20080220.bin
|
||||
system: sony-playstation-2
|
||||
required: true
|
||||
description: "PS2 console BIOS"
|
||||
note: "Any file in the BIOS folder whose romdir holds RESET and ROMVER is accepted, whatever its name or size. Naming convention ps2-VVVVr-YYYYMMDD.bin carries version, region and date."
|
||||
source_ref: "pcsx2/ps2/BiosTools.cpp:261-323, pcsx2/ps2/BiosTools.cpp:74-167"
|
||||
|
||||
- name: rom1.bin
|
||||
system: sony-playstation-2
|
||||
required: false
|
||||
max_size: 262144
|
||||
description: "DVD player ROM"
|
||||
note: "Tried as {bios}.rom1 then {biosbase}.rom1. Read into the 256 KB ROM1 region and truncated to it. Logged and skipped when absent."
|
||||
source_ref: "pcsx2/ps2/BiosTools.cpp:190-229, pcsx2/ps2/BiosTools.cpp:298, pcsx2/MemoryTypes.h:22"
|
||||
|
||||
- name: ROM2.BIN
|
||||
system: sony-playstation-2
|
||||
required: false
|
||||
max_size: 524288
|
||||
description: "Chinese ROM extension"
|
||||
note: "Tried as {bios}.rom2 then {biosbase}.rom2. Read into the 512 KB ROM2 region and truncated to it. Present on Chinese region consoles."
|
||||
source_ref: "pcsx2/ps2/BiosTools.cpp:190-229, pcsx2/ps2/BiosTools.cpp:299, pcsx2/MemoryTypes.h:23"
|
||||
|
||||
- name: EROM.BIN
|
||||
system: sony-playstation-2
|
||||
required: false
|
||||
max_size: 1835008
|
||||
description: "DVD player extension ROM"
|
||||
note: "Tried as {bios}.erom then {biosbase}.erom. Read into the 1.75 MB EROM region and truncated to it."
|
||||
source_ref: "pcsx2/ps2/BiosTools.cpp:190-229, pcsx2/ps2/BiosTools.cpp:300, pcsx2/MemoryTypes.h:24"
|
||||
|
||||
- name: eeprom.dat
|
||||
system: sony-playstation-2
|
||||
required: false
|
||||
hle_fallback: true
|
||||
size: 64
|
||||
description: "DEV9 network adapter EEPROM"
|
||||
note: "Opened by the DEV9ghzdrk plugin from the working directory and mapped over 64 bytes. A compiled-in EEPROM holding the default MAC stands in when the file is missing or cannot be mapped."
|
||||
source_ref: "plugins/dev9ghzdrk/DEV9.cpp:176-227, plugins/dev9ghzdrk/DEV9.cpp:71-81"
|
||||
|
||||
- name: flash.dat
|
||||
system: sony-playstation-2
|
||||
required: false
|
||||
hle_fallback: true
|
||||
max_size: 8650752
|
||||
description: "DEV9 SmartMedia flash image"
|
||||
note: "Opened by the DEV9ghzdrk plugin from the working directory at init and read as 1024 blocks of 16 pages of 512 bytes plus 16 ECC bytes. The card is filled with 0xFF when the file is absent or short."
|
||||
source_ref: "plugins/dev9ghzdrk/flash.cpp:60-83, plugins/dev9ghzdrk/flash.cpp:21-28"
|
||||
@@ -0,0 +1,169 @@
|
||||
emulator: PCSX2x6
|
||||
type: standalone
|
||||
core_classification: enhanced_fork
|
||||
bios_mode: agnostic
|
||||
source: "https://github.com/PS2Homebrew-arcade/pcsx2x6"
|
||||
upstream: "https://github.com/PCSX2/pcsx2"
|
||||
profiled_date: "2026-08-08"
|
||||
source_commit: "e69b265ef4bc98565deffb7c4a4b7d08143d4cf9"
|
||||
upstream_commit: "5c611f85e13ba250a69c155c678482b219b10d48"
|
||||
core_version: "v0.2.20"
|
||||
display_name: "Namco - System 246 / 256 (PCSX2x6)"
|
||||
cores:
|
||||
- pcsx2x6
|
||||
- PCSX2X6
|
||||
systems:
|
||||
- namco-system246
|
||||
- namco-system256
|
||||
|
||||
notes: |
|
||||
Namco System 246 and 256 arcade boards, which are Sony COH-H PlayStation 2
|
||||
motherboards. The Namco hardware sits on the DEV9 bus: ATA and ATAPI media,
|
||||
the ACCORE interrupt controller, JVS input, the expansion RAM board and the
|
||||
settings SRAM. Super 256 is the 256 with a higher bus clock, selected by
|
||||
platform=super256 in the game config.
|
||||
ref: pcsx2/DEV9/ACATA.cpp, pcsx2/DEV9/ACCORE.cpp, pcsx2/DEV9/ACJV.cpp,
|
||||
pcsx2/DEV9/ACRAM.cpp, pcsx2/DEV9/ACSRAM.cpp, pcsx2/VMManager.cpp:1382-1418
|
||||
|
||||
The BIOS must come from a COH-H board. A romver whose region letter is T
|
||||
followed by Z is read as zone COH-H, region 11, and the EXTINFO serial names
|
||||
the board: 20040519-145634 for System 256, 20021119-163841 for System 246
|
||||
Rack C, 20000901-114731 for the COH-H A-000-010. Boot aborts when no image
|
||||
loads.
|
||||
ref: pcsx2/ps2/BiosTools.cpp:149-153, pcsx2/ps2/BiosTools.cpp:178-181,
|
||||
pcsx2/VMManager.cpp:1657-1667
|
||||
|
||||
Detection is by content. The scan lists the BIOS folder with a * mask, keeps
|
||||
files between 4 and 8 MB, and accepts the ones whose romdir carries a RESET
|
||||
entry followed by a readable ROMVER; EXTINFO supplies the serial. The picker
|
||||
in the settings window runs the same romdir test without the size filter, so
|
||||
a manually selected image of any length is accepted. No name and no hash is
|
||||
matched anywhere.
|
||||
ref: pcsx2/ps2/BiosTools.cpp:16-17, pcsx2/ps2/BiosTools.cpp:80-207,
|
||||
pcsx2/ps2/BiosTools.cpp:268-292,
|
||||
pcsx2-qt/Settings/BIOSSettingsWidget.cpp:65-73
|
||||
|
||||
Companions derive from the selected BIOS path. Each extension is appended to
|
||||
the full name first, then substituted for the existing one, so an image at
|
||||
COH-H31100.rom0 is followed by COH-H31100.rom0.rom1 then COH-H31100.rom1.
|
||||
The project's dumper writes that exact set from a live board: .rom0 of
|
||||
0x400000, .rom1 of 0x400000, .rom2 of 0x80000, .nvm of 1024 bytes and .mec
|
||||
of 4 bytes, all named after the board model.
|
||||
ref: pcsx2/ps2/BiosTools.cpp:224-251, pcsx2/CDVD/CDVD.cpp:164-167,
|
||||
biosdrain dump.c:32-36, biosdrain dump.c:70-120
|
||||
|
||||
A game is launched from an .acgame config in INI form. It names the boot elf,
|
||||
the security dongle image in the memcards folder, an optional save card, the
|
||||
CD/DVD/HDD image and the settings SRAM, and everything but the seven
|
||||
character NM gameid can be resolved from the shipped database. Those files
|
||||
are the game itself, one set per title.
|
||||
ref: pcsx2/VMManager.cpp:1345-1490, pcsx2/VMManager.cpp:2667-2670,
|
||||
pcsx2/DEV9/ACSRAM.cpp:17-40
|
||||
|
||||
The 256 regional signature normally comes from the iLinkId held in the nvm.
|
||||
A 256Region entry in the .acgame feeds ASIA4, ASIA5 or JAPAN into the
|
||||
mechacon read instead, which the Taiko games use to pick their region.
|
||||
ref: pcsx2/CDVD/CDVD.cpp:2595-2608, pcsx2/VMManager.cpp:1371-1378
|
||||
|
||||
Read from the distribution tree and versioned with the build: the shader
|
||||
sources every GS backend compiles at startup, the OSD fonts and fullscreen
|
||||
UI textures, and the achievement notification sounds.
|
||||
ref: pcsx2/GS/Renderers/Common/GSDevice.cpp:363-366,
|
||||
pcsx2/ImGui/ImGuiFullscreen.cpp:300, pcsx2/ImGui/ImGuiFullscreen.cpp:396,
|
||||
pcsx2/Achievements.cpp:1158, pcsx2/Achievements.cpp:1214,
|
||||
pcsx2/Achievements.cpp:1339
|
||||
|
||||
files:
|
||||
- name: COH-H31100.rom0
|
||||
system: namco-system246
|
||||
required: true
|
||||
min_size: 4194304
|
||||
max_size: 8388608
|
||||
validation: [size]
|
||||
description: "COH-H arcade board BIOS"
|
||||
note: "Any image whose romdir holds RESET and ROMVER is accepted, whatever its name. The 4 to 8 MB range gates the folder scan; a manually selected image skips it. Named after the board model by the dumper, extension rom0."
|
||||
source_ref: "pcsx2/ps2/BiosTools.cpp:268-292, pcsx2/ps2/BiosTools.cpp:80-207, pcsx2/ps2/BiosTools.cpp:327-379"
|
||||
|
||||
- name: COH-H31100.rom1
|
||||
system: namco-system246
|
||||
required: false
|
||||
max_size: 4194304
|
||||
description: "ROM1 board region"
|
||||
note: "Tried as {bios}.rom1 then {biosbase}.rom1. Read into the 4 MB ROM1 region and truncated to it. Logged and skipped when absent. Mapped at 0xB0000000, where rom0:ACDEV declares rom1: on a COH-H."
|
||||
source_ref: "pcsx2/ps2/BiosTools.cpp:224-251, pcsx2/ps2/BiosTools.cpp:376, pcsx2/MemoryTypes.h:14, pcsx2/Memory.cpp:416"
|
||||
|
||||
- name: COH-H31100.rom2
|
||||
system: namco-system246
|
||||
required: false
|
||||
max_size: 4194304
|
||||
description: "ROM2 board region"
|
||||
note: "Tried as {bios}.rom2 then {biosbase}.rom2. Read into the 4 MB ROM2 region and truncated to it. The dumper reads 512 KB off the board."
|
||||
source_ref: "pcsx2/ps2/BiosTools.cpp:224-251, pcsx2/ps2/BiosTools.cpp:377, pcsx2/MemoryTypes.h:15"
|
||||
|
||||
- name: COH-H31100.nvm
|
||||
system: namco-system246
|
||||
required: false
|
||||
hle_fallback: true
|
||||
size: 1024
|
||||
description: "Board NVRAM"
|
||||
note: "Read from the BIOS path with the extension replaced by nvm. Carries the iLinkId the 256 games read as a regional signature; a hardcoded id stands in when the file is missing or blank there. A default image is built when the file cannot be read."
|
||||
source_ref: "pcsx2/CDVD/CDVD.cpp:47, pcsx2/CDVD/CDVD.cpp:140-178, pcsx2/CDVD/CDVD.cpp:2608"
|
||||
|
||||
- name: COH-H31100.mec
|
||||
system: namco-system246
|
||||
required: false
|
||||
hle_fallback: true
|
||||
size: 4
|
||||
description: "Mechacon version"
|
||||
note: "Read from the BIOS path with the extension replaced by mec. Written with 0x0104020A, the version of a COH-H31100, when absent."
|
||||
source_ref: "pcsx2/CDVD/CDVD.cpp:51-52, pcsx2/CDVD/CDVD.cpp:196-209"
|
||||
|
||||
- name: eeprom.dat
|
||||
system: namco-system246
|
||||
required: false
|
||||
hle_fallback: true
|
||||
size: 64
|
||||
description: "DEV9 EEPROM"
|
||||
note: "Opened from the working directory at DEV9 init and mapped over 64 bytes. A compiled-in 64 byte image stands in when the file is missing or cannot be mapped."
|
||||
source_ref: "pcsx2/DEV9/DEV9.cpp:67-77, pcsx2/DEV9/DEV9.cpp:116-167"
|
||||
|
||||
- name: flash.dat
|
||||
system: namco-system246
|
||||
required: false
|
||||
hle_fallback: true
|
||||
max_size: 8650752
|
||||
description: "DEV9 SmartMedia flash image"
|
||||
note: "Opened from the working directory at DEV9 init and read as 1024 blocks of 16 pages of 512 bytes plus 16 ECC bytes. The card is filled with 0xFF when the file is absent or short."
|
||||
source_ref: "pcsx2/DEV9/flash.cpp:9-16, pcsx2/DEV9/flash.cpp:62-86, pcsx2/DEV9/DEV9.cpp:114"
|
||||
|
||||
- name: GameIndex.yaml
|
||||
path: bin/resources/GameIndex.yaml
|
||||
system: namco-system246
|
||||
required: false
|
||||
description: "Arcade game database"
|
||||
note: "Keyed by NM gameid, one entry per arcade title, carrying the board variant, the media type, the boot program and the GS fixes. Supplies the platform and media values a minimal .acgame leaves out. Console error when missing."
|
||||
source_ref: "pcsx2/GameDatabase.cpp:39, pcsx2/GameDatabase.cpp:979-990, pcsx2/VMManager.cpp:1385-1415"
|
||||
|
||||
- name: RedumpDatabase.yaml
|
||||
path: bin/resources/RedumpDatabase.yaml
|
||||
system: namco-system246
|
||||
required: false
|
||||
description: "Disc hash database"
|
||||
note: "Read on demand to match a disc against its catalogue entry in the game summary. Console error when missing."
|
||||
source_ref: "pcsx2/GameDatabase.cpp:1086, pcsx2/GameDatabase.cpp:1143-1152"
|
||||
|
||||
- name: patches.zip
|
||||
path: bin/resources/patches.zip
|
||||
system: namco-system246
|
||||
required: false
|
||||
description: "Bundled patch archive"
|
||||
note: "Opened read-only from the resources folder. Built-in patches are unavailable and an on-screen error is shown when it cannot be opened. Fetched from the patches repository at build time, not committed."
|
||||
source_ref: "pcsx2/Patch.cpp:117, pcsx2/Patch.cpp:289-315, .github/workflows/linux_build_qt.yml:129"
|
||||
|
||||
- name: game_controller_db.txt
|
||||
path: bin/resources/game_controller_db.txt
|
||||
system: namco-system246
|
||||
required: false
|
||||
description: "SDL controller mapping database"
|
||||
note: "A copy in the user data root takes precedence over the one in resources. Fed to SDL as a hint; without either, SDL falls back to its built-in mappings and an error is logged."
|
||||
source_ref: "pcsx2/Input/SDLInputSource.cpp:24, pcsx2/Input/SDLInputSource.cpp:614-629"
|
||||
@@ -24,7 +24,7 @@ files:
|
||||
|
||||
- name: "scph5500.bin"
|
||||
description: "SCPH-5500 (v3.0 09-09-96 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
size: 524288
|
||||
required: false
|
||||
hle_fallback: true
|
||||
@@ -36,7 +36,7 @@ files:
|
||||
|
||||
- name: "scph5501.bin"
|
||||
description: "SCPH-5501 (v3.0 11-18-96 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
size: 524288
|
||||
required: false
|
||||
hle_fallback: true
|
||||
@@ -48,7 +48,7 @@ files:
|
||||
|
||||
- name: "scph5502.bin"
|
||||
description: "SCPH-5502 (v3.0 01-06-97 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
size: 524288
|
||||
required: false
|
||||
hle_fallback: true
|
||||
@@ -60,7 +60,7 @@ files:
|
||||
|
||||
- name: "psxonpsp660.bin"
|
||||
description: "PSP embedded PS1 BIOS (region detected at runtime)"
|
||||
region: "Auto"
|
||||
region: [world]
|
||||
size: 524288
|
||||
required: false
|
||||
hle_fallback: true
|
||||
@@ -70,7 +70,7 @@ files:
|
||||
|
||||
- name: "scph101.bin"
|
||||
description: "SCPH-101 (v4.4 03-24-00 A) - PSone US"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
size: 524288
|
||||
required: false
|
||||
hle_fallback: true
|
||||
@@ -80,7 +80,7 @@ files:
|
||||
|
||||
- name: "scph7001.bin"
|
||||
description: "SCPH-7001 (v4.1 12-16-97 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
size: 524288
|
||||
required: false
|
||||
hle_fallback: true
|
||||
@@ -89,7 +89,7 @@ files:
|
||||
|
||||
- name: "scph1001.bin"
|
||||
description: "SCPH-1001 (v2.2 12-04-95 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
size: 524288
|
||||
required: false
|
||||
hle_fallback: true
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
emulator: PrBoom+
|
||||
type: standalone
|
||||
core_classification: game_engine
|
||||
source: "https://github.com/coelckers/prboom-plus"
|
||||
upstream: "https://github.com/coelckers/prboom-plus"
|
||||
profiled_date: "2026-08-08"
|
||||
source_commit: "969515162c5aebea4ad7a125ee178dcad3d576ad"
|
||||
core_version: "2.6.66"
|
||||
display_name: "Doom (PrBoom+)"
|
||||
cores:
|
||||
- prboom-plus
|
||||
systems:
|
||||
- doom
|
||||
mode: standalone
|
||||
|
||||
notes: |
|
||||
Doom source port forked from PrBoom, with the software and OpenGL renderers
|
||||
in a single binary. FindIWADFile accepts doom2f, doom2, plutonia, tnt, doom,
|
||||
doom1, doomu, freedoom2, freedoom1, freedm, hacx, chex, rekkrsa, bfgdoom2
|
||||
and bfgdoom (d_main.c:144-165). ES-DE runs it as "prboom-plus -iwad <file>",
|
||||
so the IWAD is content, not a system file.
|
||||
|
||||
prboom-plus.wad holds the engine's own lumps: the palette, the water
|
||||
colormap, the sine, tangent and gamma tables, the HUD layouts, the status
|
||||
bar digits, the crosshairs, the dog and plasma sprites, the GLSL shader
|
||||
sources, and the DEHACKED patches applied to Chex Quest, the BFG Edition
|
||||
and NERVE.WAD. M_LoadDefaults installs it as wad_files[0] and aborts with
|
||||
"PrBoom-Plus.wad not found. Can't continue." when I_FindFile returns
|
||||
nothing (m_misc.c:1723-1724). It loads even under -noload, which caps the
|
||||
autoload loop at that one entry (d_main.c:1877-1879). Lumps coming from it
|
||||
are tagged LUMP_PRBOOM so a PWAD can override them (w_wad.c:185-196).
|
||||
|
||||
The tree ships the inputs, not the file: data/CMakeLists.txt builds it with
|
||||
the rdatawad tool from the raw palette, lumps, PPM graphics and WAV sounds
|
||||
under data/, then installs it into PRBOOMDATADIR, which is
|
||||
<prefix>/share/prboom-plus (CMakeLists.txt:188-189, data/CMakeLists.txt:370).
|
||||
|
||||
I_FindFile searches, in order: the config directory, the working directory,
|
||||
PRBOOMDATADIR, $DOOMWADDIR, the build-time DOOMWADDIR, $HOME/doom, $HOME,
|
||||
/usr/local/share/games/doom, /usr/share/games/doom, /usr/local/share/doom,
|
||||
/usr/share/doom, then every entry of $DOOMWADPATH (SDL/i_system.c:437-449,
|
||||
471-505). The working directory entry carries no prefix, so an absolute
|
||||
name resolves on that pass. Existence is the only test, and the requested
|
||||
extension is appended only when the name as written is missing
|
||||
(SDL/i_system.c:532-538). The config directory is the directory holding the
|
||||
binary on Windows (SDL/i_system.c:289-311), and ~/.prboom-plus or the
|
||||
XDG path from SDL_GetPrefPath elsewhere (SDL/i_system.c:347-383). The macOS
|
||||
build replaces the list with ~/Library/Application Support/PrBoom-Plus, the
|
||||
bundle resource path, and the bare name (MAC/i_system.m:57-59, 117-142).
|
||||
|
||||
snd_midiplayer picks the MIDI backend and defaults to fluidsynth on Windows,
|
||||
sdl elsewhere (m_misc.c:444-450). Only the sdl value leaves
|
||||
use_experimental_music clear and routes MIDI through SDL_mixer
|
||||
(SDL/i_sound.c:1662-1668, 1221-1225); every other value brings up the
|
||||
in-tree players. Their init results are recorded once at startup
|
||||
(SDL/i_sound.c:1383-1384) and consulted per song, a player that failed to
|
||||
init being logged and passed over for the next entry of music_player_order
|
||||
(SDL/i_sound.c:1511-1538). fluidsynth is the only one reading an external
|
||||
file. The OPL2 player that follows it takes its instruments from the IWAD
|
||||
GENMIDI lump (MUSIC/oplplayer.c:337-354).
|
||||
|
||||
files:
|
||||
- name: "prboom-plus.wad"
|
||||
system: doom
|
||||
description: "PrBoom+ engine data WAD"
|
||||
required: true
|
||||
size: 357768
|
||||
md5: "1164ae723c9181698cc41dde6a341132"
|
||||
sha1: "8a799c6615702aac72016b784e30ea1abd3e197b"
|
||||
crc32: "a4626dfa"
|
||||
source_ref: "m_misc.c:1723-1724 (I_FindFile then I_Error), d_main.c:1873-1895 (autoload), w_wad.c:185-196 (LUMP_PRBOOM tagging), CMakeLists.txt:41 (PACKAGE_TARNAME), CMakeLists.txt:234-235 (WAD_DATA), data/CMakeLists.txt:364-370 (rdatawad build and install)"
|
||||
note: >
|
||||
The code checks existence only, then rejects a file whose header is
|
||||
neither IWAD nor PWAD (w_wad.c:216-218). Hashes are those of the file
|
||||
rdatawad produces from the data/ assets at this revision, which matches
|
||||
the copy in the v2.6.66 release archive byte for byte. They identify the
|
||||
2.6.66 build; other versions carry different lumps.
|
||||
|
||||
- name: "TimGM6mb.sf2"
|
||||
system: doom
|
||||
description: "General MIDI soundfont for the fluidsynth MIDI player"
|
||||
required: false
|
||||
hle_fallback: true
|
||||
source_ref: "m_misc.c:445-446 (_WIN32 defaults for snd_midiplayer and snd_soundfont), MUSIC/flplayer.c:193-202 (I_FindFile2 then fluid_synth_sfload)"
|
||||
note: >
|
||||
Named by the Windows default of snd_soundfont, where snd_midiplayer also
|
||||
defaults to fluidsynth, so a stock Windows configuration reads it. Any
|
||||
soundfont works: the value is a config string and I_FindFile2 appends
|
||||
.sf2 only when the name as written is missing. A load failure returns 0
|
||||
from fl_init, and the song falls through to the next player on the list,
|
||||
the built-in OPL2 synth.
|
||||
|
||||
- name: "default-GM.sf3"
|
||||
system: doom
|
||||
description: "General MIDI soundfont named by the non-Windows soundfont default"
|
||||
required: false
|
||||
hle_fallback: true
|
||||
source_ref: "m_misc.c:448-449 (non-_WIN32 defaults, /usr/share/sounds/sf3/default-GM.sf3), MUSIC/flplayer.c:193-202 (I_FindFile2 then fluid_synth_sfload)"
|
||||
note: >
|
||||
The default is an absolute path, which resolves through the unprefixed
|
||||
working directory entry of the search table. Reached once snd_midiplayer
|
||||
is set away from its sdl default, since sdl keeps the fluidsynth player
|
||||
out of the picture entirely. Same fallback to the OPL2 synth as the
|
||||
Windows default.
|
||||
|
||||
analysis:
|
||||
engine_wad_contents:
|
||||
source_ref: "data/CMakeLists.txt:24-340 (rdatawad input lists), data/CMakeLists.txt:342-362 (WAD_SRC and WAD_CMDLINE)"
|
||||
note: >
|
||||
What rdatawad packs into prboom-plus.wad, in the order the command line
|
||||
builds it.
|
||||
palette: palette.rgb
|
||||
lumps:
|
||||
- "SWITCHES, ANIMATED"
|
||||
- "the CR* colour translation tables"
|
||||
colormaps: "WATERMAP, between C_START and C_END"
|
||||
tables:
|
||||
note: "Between B_START and B_END"
|
||||
entries:
|
||||
- "SINETABL, TANGTABL, TANTOANG, GAMMATBL"
|
||||
- "CHEXDEH, BFGBEX, NERVEBEX"
|
||||
- "GLSHADOW, GLFP, GLVP"
|
||||
- "-PRBHUD-, read by hu_stuff.c:1144"
|
||||
- "the M_* OpenGL automap icons listed in gl_map.c:48-68"
|
||||
sounds: "DSDGSIT, DSDGATK, DSDGACT, DSDGDTH, DSDGPAIN"
|
||||
graphics: "the DIG* status bar digits, STBR*, the BOX* frame pieces, STKEYS6-8, STCFN096, M_BUTT1/2, M_COLORS, M_PALNO, M_PALSEL, M_VBOX, CROSS1-3"
|
||||
flats: "-N0_TEX-, between FF_START and FF_END"
|
||||
sprites: "TNT1A0, the DOGS* frames, PLS1* and PLS2*, between SS_START and SS_END"
|
||||
|
||||
shader_override:
|
||||
files:
|
||||
- "shaders/glvp.txt"
|
||||
- "shaders/glfp.txt"
|
||||
source_ref: "gl_shader.c:144-152 (path built from I_DoomExeDir), gl_shader.c:84-129 (ReadLump)"
|
||||
note: >
|
||||
Read from the config directory when present. ReadLump falls back to the
|
||||
GLVP and GLFP lumps in the ns_prboom namespace when the file is missing,
|
||||
and those lumps are built into prboom-plus.wad
|
||||
(data/CMakeLists.txt:60-61). No distribution ships the .txt form: it is
|
||||
an override slot, and the shipped content is already covered by the WAD.
|
||||
|
||||
generated_files:
|
||||
note: "Written by the program itself, none is user-supplied"
|
||||
entries:
|
||||
- filename: "prboom-plus.cfg"
|
||||
source_ref: "m_misc.c:1586-1600 (BOOM_CFG, path from I_DoomExeDir), m_misc.c:1606 (read)"
|
||||
- filename: "RGB2PAL.dat"
|
||||
source_ref: "gl_hires.c:938-953 (read), gl_hires.c:962-967 (opened wb and written)"
|
||||
note: >
|
||||
A 24-bit RGB to palette lookup table built from V_GetPlaypal, so its
|
||||
contents follow whichever IWAD is loaded. gl_hires.c:920-936 prefers
|
||||
an RGB2PAL lump of the right length when a WAD supplies one.
|
||||
- filename: "<hires image>.cache"
|
||||
source_ref: "gl_hires.c:1144-1147 (read), gl_hires.c:1179 (gld_HiRes_WriteCache)"
|
||||
note: "Keyed on the source image mtime and discarded when it differs."
|
||||
- filename: "levelstat.txt"
|
||||
source_ref: "e6y.c:968"
|
||||
- filename: "levelinfo.txt"
|
||||
source_ref: "d_main.c:1049-1060, behind GL_DOOM and PRBOOM_DEBUG"
|
||||
- filename: "temp_a.nut, temp_v.nut"
|
||||
source_ref: "m_misc.c:1065-1066 (cap_tempfile1, cap_tempfile2)"
|
||||
|
||||
user_supplied_paths:
|
||||
note: "Named on the command line or in the config, with no fixed filename"
|
||||
entries:
|
||||
- what: "IWAD, -iwad or autodetected from the standard names"
|
||||
source_ref: "d_main.c:144-165 (standard_iwads), d_main.c:977-990 (FindIWADFile)"
|
||||
- what: "PWADs, -file"
|
||||
source_ref: "d_main.c:1906-1927"
|
||||
- what: "DEHACKED and BEX patches, -deh, plus the dehfile_1 and dehfile_2 config keys and the autoload directories"
|
||||
source_ref: "d_main.c:2036-2055, d_main.c:2081-2099, m_misc.c:336-337"
|
||||
- what: "extra autoloaded WADs, wadfile_1 and wadfile_2"
|
||||
source_ref: "m_misc.c:334-335, d_main.c:1873-1895"
|
||||
- what: "autoload directories, <config dir>/autoload/doom-all and one per IWAD"
|
||||
source_ref: "d_main.c:1400-1414 (GetAutoloadBaseDir), d_main.c:1500-1519 (D_AutoloadIWadDir)"
|
||||
- what: "demos, -playdemo, -fastdemo, -record"
|
||||
source_ref: "r_demo.c:89-119, g_game.c:2570, g_game.c:3150"
|
||||
- what: "external high resolution texture packs, under textures/, flats/ and patches/"
|
||||
source_ref: "gl_hires.c:467-631 (hires_paths), gl_hires.c:703-719 (gl_texture_hires_dir or I_DoomExeDir)"
|
||||
- what: "savegames, DOOMSAVEDIR or the config directory"
|
||||
source_ref: "d_main.c:1019-1032, m_menu.c:957"
|
||||
- what: "response files, @name"
|
||||
source_ref: "d_main.c:1094-1112"
|
||||
|
||||
external_programs:
|
||||
note: "Invoked as processes, not read as data files"
|
||||
entries:
|
||||
- what: "ffmpeg, video capture and muxing"
|
||||
source_ref: "m_misc.c:1062-1064 (cap_soundcommand, cap_videocommand, cap_muxcommand)"
|
||||
- what: "wget then unzip, fetching a WAD a game server asks for"
|
||||
source_ref: "d_client.c:236-257 (execlp), doc/prboom-plus.6:200-206"
|
||||
- what: "getwad_cmdline, run through system() when a needed WAD is missing"
|
||||
source_ref: "r_demo.c:1641-1704 (D_TryGetWad), m_misc.c:1029 (default empty, so the path is inert until configured)"
|
||||
@@ -0,0 +1,83 @@
|
||||
emulator: puNES
|
||||
type: standalone
|
||||
core_classification: other
|
||||
source: "https://github.com/punesemu/puNES"
|
||||
upstream: "https://github.com/punesemu/puNES"
|
||||
profiled_date: "2026-08-08"
|
||||
source_commit: "2ed5b1b2cffcde48ff36f359091756d17d8fe193"
|
||||
core_version: "0.112"
|
||||
display_name: "Nintendo - NES / Famicom (puNES)"
|
||||
cores:
|
||||
- punes
|
||||
systems:
|
||||
- nintendo-nes
|
||||
- nintendo-fds
|
||||
mode: standalone
|
||||
|
||||
notes: |
|
||||
Qt emulator of the NES, the Famicom and the Family Computer Disk System,
|
||||
also playing NSF and NSFE music files. A cartridge boots with nothing
|
||||
supplied: the 2A03, the PPU and every mapper are emulated in software.
|
||||
|
||||
Four names are fixed by the code. disksys.rom and gamegenie.rom share one
|
||||
four-step lookup (src/core/fds.c:265-309, src/core/cheat.c:74-108): the path
|
||||
recorded in the configuration, the working directory, the directory holding
|
||||
the loaded image, then the bios folder of the data directory. nes20db.xml
|
||||
and dip.cfg are read from the XDG data directories under a puNES
|
||||
subdirectory, then the base of the data directory, then the application
|
||||
directory (src/gui/nes20db.cpp:50-79, src/gui/dipswitch.cpp:61-85); the
|
||||
installer writes both into the shared data directory
|
||||
(CMakeLists.txt:227-228).
|
||||
|
||||
The data directory is the application directory under --portable,
|
||||
%APPDATA%/puNES on Windows and ~/.local/share/puNES elsewhere
|
||||
(src/gui/qt.cpp:561-575), so the bios folder is ~/.local/share/puNES/bios.
|
||||
|
||||
No length and no hash is ever compared. fds_load_bios computes a CRC32 of
|
||||
the BIOS it loaded (src/core/fds.c:329) and the only reader of that value is
|
||||
the log line at src/core/fds.c:502-503.
|
||||
|
||||
The iNES 1.0 game database is compiled in as src/core/database.h and carries
|
||||
the Vs. System PPU type and the region, so Vs. System titles need no
|
||||
external file. The uPD7756 ADPCM samples of mappers 018, 086 and 072 come
|
||||
from the Misc ROM area of the NES 2.0 dump itself
|
||||
(src/core/mappers/mapper_018.c:51, mapper_086.c:56, mapper_072.c:45).
|
||||
loadWaveFiles, which would read <data>/samples/<name>/NN.wav
|
||||
(src/core/mappers/waveFile.cpp:76-84), is declared and defined but called
|
||||
from nowhere in the tree, so no wave file is ever opened.
|
||||
|
||||
Palettes, shaders, cheat imports and soft patches are opened from paths the
|
||||
user picks in a file dialog and no name is fixed by the code, so they carry
|
||||
no file entry here. A palette shorter than 192 bytes is refused and a
|
||||
built-in table is used instead (src/core/palette.c:39-64,
|
||||
src/video/gfx.c:323-334).
|
||||
|
||||
files:
|
||||
- name: "disksys.rom"
|
||||
system: nintendo-fds
|
||||
description: "Family Computer Disk System BIOS"
|
||||
required: true
|
||||
size: 8192
|
||||
source_ref: "src/core/fds.c:34,259-332 (fds_load_bios); src/core/fds.c:191-193 (caller); src/core/mappers/mapper_FDS.c:81 (mapping)"
|
||||
note: "8192 bytes are read into PRG ROM and mapped at 0xE000. When none of the four locations holds the file, fds_load_bios logs FDS;bios not found and returns EXIT_ERROR, which the caller propagates, so the disk image does not load. A short read only logs FDS;error on reading bios."
|
||||
|
||||
- name: "gamegenie.rom"
|
||||
system: nintendo-nes
|
||||
description: "Game Genie ROM, booted in place of the cartridge to reach the code entry screen"
|
||||
required: false
|
||||
source_ref: "src/core/cheat.c:30,69-120 (gamegenie_check_rom_present),121-179 (gamegenie_load_rom); src/core/ines.c:97-99 (caller)"
|
||||
note: "Loaded only while the cheat mode is set to Game Genie. The whole file is read, its length taken from the file itself. Absent, gamegenie_load_rom returns early and the cartridge boots directly."
|
||||
|
||||
- name: "nes20db.xml"
|
||||
system: nintendo-nes
|
||||
description: "NES 2.0 header database, matched by ROM hash"
|
||||
required: false
|
||||
source_ref: "src/gui/nes20db.cpp:31,49-81 (nes20db_search); src/core/ines.c:276-278, src/core/unif.c:316 (callers); CMakeLists.txt:227 (install)"
|
||||
note: "Supplies mapper, submapper, mirroring, region, RAM sizes and Vs. System hardware for dumps whose own header is absent or wrong. A miss leaves info.mapper.nes20db.in_use false and the header carried by the ROM is used."
|
||||
|
||||
- name: "dip.cfg"
|
||||
system: nintendo-nes
|
||||
description: "DIP switch database for multicart and Vs. System boards, keyed by CRC32"
|
||||
required: false
|
||||
source_ref: "src/gui/dipswitch.cpp:30,60-86 (dipswitch_search); src/core/mappers.c:44 (caller); CMakeLists.txt:228 (install)"
|
||||
note: "Read on every mapper init. A miss leaves dipswitch.used false, which mappers 001, 043, 045, 176, 215, 412 and 447 and the selection dialog at src/gui/qt.cpp:754 all guard on, so the board runs on its reset value."
|
||||
@@ -0,0 +1,144 @@
|
||||
emulator: Rosalie's Mupen GUI
|
||||
type: standalone
|
||||
core_classification: community_fork
|
||||
source: "https://github.com/Rosalie241/RMG"
|
||||
upstream: "https://github.com/Rosalie241/RMG"
|
||||
profiled_date: "2026-08-08"
|
||||
source_commit: "9dced048f747085f942f55941dd4304279acc145"
|
||||
core_version: "v0.9.0"
|
||||
display_name: "Nintendo - Nintendo 64 (Rosalie's Mupen GUI)"
|
||||
cores:
|
||||
- rosalies-mupen-gui
|
||||
- RMG
|
||||
- com.github.Rosalie241.RMG
|
||||
systems:
|
||||
- nintendo-64
|
||||
- nintendo-64dd
|
||||
mode: standalone
|
||||
|
||||
notes: |
|
||||
Qt front end by Rosalie Wanders built around its own vendored fork of
|
||||
mupen64plus-core, compiled with OSD=0, NEW_DYNAREC=1 and VULKAN=0
|
||||
(Source/3rdParty/CMakeLists.txt:74-96). Forks of the RSP plugins hle, cxd4
|
||||
and parallel, the video plugins GLideN64, parallel and angrylion-plus, and
|
||||
the raphnetraw input plugin are vendored alongside it, plus RMG's own audio
|
||||
and input plugins. ES-DE runs it as `--nogui -q <rom>` for both n64 and
|
||||
n64dd. Against mupen64plus-core 6dca4c15 the media loader, the DD region
|
||||
enum and load_dd_rom are unchanged; the fork diverges on SDL3, video
|
||||
capture, netplay and the dynarec.
|
||||
|
||||
Cartridge boot needs no boot ROM. The PIF boot ROM is simulated
|
||||
(device/pif/bootrom_hle.c:48, reached from r4300/interrupt.c:448,499) and the
|
||||
CIC-NUS-6105 challenge is answered by an algorithm rather than a stored
|
||||
table (device/pif/n64_cic_nus_6105.c:93).
|
||||
|
||||
Which 64DD IPL is used is not a user choice. scan_and_expand_disk_format
|
||||
accepts a disk only when one of eight candidate blocks holds system data
|
||||
carrying a known region word, and marks it development when that block sits
|
||||
at index 2, 3, 10 or 11 (device/dd/disk.c:412-475). The core hands the
|
||||
resulting region to the front end, which answers with one of three
|
||||
configured paths, or with nothing when the region stayed unknown
|
||||
(RMG-Core/MediaLoader.cpp:47-74). All three settings default to empty
|
||||
(RMG-Core/Settings.cpp:284-292) and are filled from a file dialog filtered
|
||||
on *.n64 (RMG/UserInterface/Dialog/SettingsDialog.cpp:1377-1390).
|
||||
|
||||
A bare .ndd or .d64 opens as a disk with no cartridge
|
||||
(RMG-Core/Rom.cpp:111-133), which is the route ES-DE takes, so the IPL is
|
||||
the only boot ROM and the core bails before starting when it is absent
|
||||
(main/main.c:1723-1739). Passing a cartridge with --disk
|
||||
(RMG/main.cpp:219,232,287) boots the cartridge and switches 64DD off instead.
|
||||
Disk content inside a .zip or .7z is extracted to the user cache directory
|
||||
first (RMG-Core/MediaLoader.cpp:164-215, RMG-Core/Archive.cpp:188-227).
|
||||
|
||||
Transfer Pak carts come from the input profile, which copies its GameboyRom
|
||||
and GameboySave paths into the per player core settings
|
||||
(RMG-Input/main.cpp:317-318,469-480, RMG-Core/MediaLoader.cpp:76-114). The
|
||||
core rejects a ROM under 32 KiB and an unknown cart type byte at 0x147
|
||||
(device/gb/gb_cart.c:999-1012) and fixes no name, size or hash, so neither
|
||||
the cart nor its save carries an entry here.
|
||||
|
||||
Data the install places in the shared data directory (CMakeLists.txt:130-153;
|
||||
/usr/share/RMG, or Data beside the executable for portable and Windows
|
||||
builds, Directories.cpp:406-431): mupen64plus.ini, read as the ROM database
|
||||
(main/rom.c:585-604) and used by GLideN64 to locate that same directory
|
||||
(mupenplus/CommonAPIImpl_mupenplus.cpp:77-85); GLideN64.custom.ini, per ROM
|
||||
overrides keyed on the uppercased header name
|
||||
(mupenplus/Config_mupenplus.cpp:329-348); font.ttf, reached only after the
|
||||
configured font name fails both as a system font path and as a full path
|
||||
(TextDrawer.cpp:202-230); Cheats/<name>.cht, named from the header CRC pair
|
||||
and country code, or from the ROM MD5 when all three are zero, and merged
|
||||
with a user copy under the config directory
|
||||
(RMG-Core/Cheats.cpp:82-116,579-598); InputProfileDB.json
|
||||
(RMG-Input/Utilities/InputProfileDB.cpp:26-36); gamecontrollerdb.txt
|
||||
(RMG-Input/main.cpp:1063-1069); and for VRU builds
|
||||
vosk-model-small-en-us-0.15.zip, extracted into the user data directory when
|
||||
any of the thirteen model files it checks for is missing
|
||||
(RMG-Input/VRU.cpp:154-232). The published AppImage is the features-on build,
|
||||
so the model ships with it (.github/workflows/build.yml:10,79,91).
|
||||
|
||||
Optional files with no canonical artifact: a .qss stylesheet under Styles in
|
||||
the shared data directory (RMG/UserInterface/MainWindow.cpp:405-425), hi-res
|
||||
texture packs under hires_texture in the user data directory
|
||||
(GLideN64 Config.cpp:118-123), and alp_screen.vert and alp_screen.frag, which
|
||||
angrylion-plus opens as working directory relative paths before falling back
|
||||
to its embedded shaders (angrylion-plus output/vdac.c:120-175,257-258).
|
||||
|
||||
Written rather than obtained: mupen64plus.cfg in the config directory,
|
||||
GLideN64.ini written there by the plugin settings pages and read back from
|
||||
both that directory and the shared data directory
|
||||
(GLideNUI/Config_GLideNUI.cpp:20-21,45-46, GLideNUI/Settings.cpp:352-368),
|
||||
the mempak, eeprom, flashram and sram saves opened with default
|
||||
content when absent (main/main.c:1712-1716), 64DD disk saves in the format
|
||||
the SaveDiskFormat setting selects (main/main.c:1286-1298), savestates,
|
||||
screenshots, the ROM browser header cache
|
||||
(RMG-Core/CachedRomHeaderAndSettings.cpp:152), the GLideN64 texture cache and
|
||||
texture dumps (GLideN64 Config.cpp:120-123) and the parallel-rdp pipeline
|
||||
cache (video-parallel vulkan/device.cpp:760,834).
|
||||
|
||||
Two files in the tree this build never reads: the vendored core's
|
||||
data/mupencheat.txt, which RMG neither installs nor parses since cheats use
|
||||
its own .cht format and the core's cheat.c opens no file, and cxd4's
|
||||
rsp_conf.bin, whose reader is the zilmar variant of update_conf behind
|
||||
#if !defined(M64P_PLUGIN_API); the variant this build compiles takes settings
|
||||
from the core config API and ignores its filename argument
|
||||
(mupen64plus-rsp-cxd4/module.c:89-99,652-673).
|
||||
|
||||
files:
|
||||
- name: "64DD_IPL_JP.n64"
|
||||
aliases: ["64DD_IPL_v12_JPN.bin", "64DD_IPL.bin"]
|
||||
system: nintendo-64dd
|
||||
required: true
|
||||
description: "64DD IPL ROM (Japanese retail)"
|
||||
note: >
|
||||
Selected when the disk system data holds the Japanese region word
|
||||
0xe848d316, which covers the retail library. The path is set in the
|
||||
settings dialog and no name is fixed by the code. Loaded whole, then
|
||||
byteswapped after identifying the first word as one of 0x80270740 (z64),
|
||||
0x40072780 (n64) or 0x27804007 (v64); any other value disables 64DD.
|
||||
Size is not checked and no hash is computed.
|
||||
source_ref: "Source/RMG-Core/MediaLoader.cpp:47-74, Source/RMG-Core/Settings.cpp:284-286, Source/3rdParty/mupen64plus-core/src/main/main.c:1190-1255, Source/3rdParty/mupen64plus-core/src/device/dd/disk.c:434-445"
|
||||
|
||||
- name: "64DD_IPL_US.n64"
|
||||
aliases: ["64DD_IPL_USA.bin", "IPL_USA.n64"]
|
||||
system: nintendo-64dd
|
||||
required: true
|
||||
description: "64DD IPL ROM (American retail)"
|
||||
note: >
|
||||
Selected when the disk system data holds the US region word 0x2263ee56.
|
||||
The other two IPL settings are not consulted for such a disk, so no
|
||||
substitution is possible. Same load path, format check and absence of
|
||||
size or hash validation as the Japanese image.
|
||||
source_ref: "Source/RMG-Core/MediaLoader.cpp:54-56, Source/RMG-Core/Settings.cpp:287-289, Source/3rdParty/mupen64plus-core/src/main/main.c:1190-1255, Source/3rdParty/mupen64plus-core/src/device/dd/disk.c:434-445"
|
||||
|
||||
- name: "64DD_IPL_DEV.n64"
|
||||
aliases: ["64DD_IPL_DEV.bin", "IPL_DEV.n64"]
|
||||
system: nintendo-64dd
|
||||
required: true
|
||||
description: "64DD IPL ROM (Japanese development)"
|
||||
note: >
|
||||
Selected when the disk system data holds the development region word
|
||||
0x00000000, and also whenever the valid system data block sits at index
|
||||
2, 3, 10 or 11, which is the development disk layout. Same load path,
|
||||
format check and absence of size or hash validation as the Japanese
|
||||
image.
|
||||
source_ref: "Source/RMG-Core/MediaLoader.cpp:57-59, Source/RMG-Core/Settings.cpp:290-292, Source/3rdParty/mupen64plus-core/src/main/main.c:1190-1255, Source/3rdParty/mupen64plus-core/src/device/dd/disk.c:412-475"
|
||||
@@ -0,0 +1,150 @@
|
||||
emulator: ruffle
|
||||
type: standalone
|
||||
core_classification: game_engine
|
||||
source: "https://github.com/ruffle-rs/ruffle"
|
||||
upstream: "https://github.com/ruffle-rs/ruffle"
|
||||
profiled_date: "2026-08-08"
|
||||
source_commit: "f3a1263e164dc4cb56211ab1f113e1907730ce89"
|
||||
core_version: "0.5.0"
|
||||
display_name: "Adobe Flash (Ruffle)"
|
||||
cores:
|
||||
- ruffle
|
||||
systems:
|
||||
- flash
|
||||
mode: standalone
|
||||
|
||||
notes: |
|
||||
Flash Player and Adobe AIR runtime written in Rust, playing SWF files of any
|
||||
ActionScript version, as a desktop binary or as a WebAssembly browser build.
|
||||
Distribution is by nightly build: Batocera compiles the nightly-2026-01-31 tag
|
||||
and installs ruffle_desktop as ruffle, RetroDECK pulls the rs.ruffle.Ruffle
|
||||
Flathub build. ES-DE runs it as `ruffle --fullscreen <rom>`, Batocera as
|
||||
`ruffle <rom>`, RetroBat as `ruffle.exe --graphics <renderer> --force-align
|
||||
-q <quality> <rom>`, RetroDECK as `ruffle --graphics vulkan --config
|
||||
/var/data/ruffle --save-directory <saves>/ruffle <rom>`. The ActionScript
|
||||
class library is compiled by build_playerglobal into playerglobal_avm1.swf and
|
||||
playerglobal_avm2.swf and linked into the binary from OUT_DIR
|
||||
(core/src/avm1/globals.rs:89, core/src/avm2/globals.rs:546), so startup reads
|
||||
nothing. Locale texts, wgsl shaders, the window icon and the OpenH264 license
|
||||
are embedded the same way (desktop/src/gui/locale.rs:7-12,
|
||||
desktop/src/app.rs:449, render/wgpu/src/shaders.rs:28-76).
|
||||
|
||||
H.264 video is the one external dependency. external_video is a default
|
||||
feature and pulls ruffle_video_external with openh264
|
||||
(desktop/Cargo.toml:26,83,90), and enable_openh264 defaults to true
|
||||
(desktop/src/preferences.rs:283,307). OpenH264Codec::load first dlopens the
|
||||
system sonames libopenh264.so.7, libopenh264.so.2.4.1 and libopenh264.so on
|
||||
Linux, then falls back to <cache>/ruffle/video/ and fetches the Cisco binary
|
||||
from ciscobinary.openh264.org bz2-compressed into it
|
||||
(video/external/src/decoder/openh264.rs:53-56,109-133,173-191,
|
||||
desktop/src/player.rs:234-241). Whether the library was already there or was
|
||||
just downloaded, its SHA-256 is checked before loading and the size is read
|
||||
only to format the mismatch message
|
||||
(video/external/src/decoder/openh264.rs:135-146), and WelsGetCodecVersion must
|
||||
answer 2.4.1 exactly (video/external/src/decoder/openh264.rs:46,153-166). The
|
||||
cache root comes from dirs::cache_dir(), ~/.cache/ruffle by default, and
|
||||
--cache-directory moves it (desktop/src/cli.rs:29-33,140). Without the library
|
||||
the player still starts: ExternalVideoBackend wraps SoftwareVideoBackend, so
|
||||
H.263, Screen Video and VP6 keep decoding and only H.264 streams fail
|
||||
(video/external/src/backend.rs:41,51-69,73-80).
|
||||
|
||||
Text uses system fonts. fontdb::Database::load_system_fonts() fills the
|
||||
database at startup (desktop/src/app.rs:418-419), and both the Flash device
|
||||
fonts and the egui menu bar resolve family names against it, mmapping whatever
|
||||
face the host answers with (desktop/src/backends/ui.rs:281-314,395-421,
|
||||
desktop/src/gui/controller.rs:690-733). The families asked for are Times New
|
||||
Roman, Arial, Courier New and their metric-compatible substitutes, plus the
|
||||
CJK, Hebrew, Arabic and Thai faces (desktop/src/player.rs:361-412,
|
||||
desktop/src/gui/controller.rs:569-641), and on Unix fontconfig sorts the
|
||||
fallback chain (desktop/src/backends/ui.rs:318-329,460-472). No name, path,
|
||||
size or hash is fixed anywhere. The compiled-in Noto Sans subset sits behind
|
||||
the default_font feature, which the desktop build does not enable
|
||||
(core/src/player.rs:82-83, core/Cargo.toml:82,96, desktop/Cargo.toml:22), so
|
||||
an unmatched family leaves the run without that face rather than falling back
|
||||
to a bundled one.
|
||||
|
||||
Files the player writes rather than expects: preferences.toml, bookmarks.toml
|
||||
and recents.toml under --config, each defaulted when absent
|
||||
(desktop/src/preferences.rs:53-95), .sol SharedObjects under --save-directory
|
||||
(frontend-utils/src/backends/storage.rs:31-60), and the log file
|
||||
(desktop/src/main.rs:156-166). ruffle-bundle.toml is the manifest inside a
|
||||
.ruf bundle, which is content alongside the .swf rather than a runtime input
|
||||
(frontend-utils/src/bundle/info.rs:11, frontend-utils/src/bundle.rs:42-53).
|
||||
mms.cfg is not
|
||||
implemented, the name appears only in an AVM2 error string
|
||||
(core/src/avm2/error_messages.rs:237).
|
||||
|
||||
files:
|
||||
# -- OpenH264 2.4.1 decoder, one build per OS and architecture --
|
||||
|
||||
- name: libopenh264-2.4.1-linux64.7.so
|
||||
path: "video/libopenh264-2.4.1-linux64.7.so"
|
||||
system: flash
|
||||
required: false
|
||||
description: "OpenH264 2.4.1 decoder library, Linux x86_64"
|
||||
sha256: "1392d21466bc638e68151b716d5b2086d54cd812afd43253f1adb5b6e0185f51"
|
||||
validation: [sha256]
|
||||
source_ref: "video/external/src/decoder/openh264.rs:65-68, 104-146, 173-191"
|
||||
|
||||
- name: libopenh264-2.4.1-linux-arm64.7.so
|
||||
path: "video/libopenh264-2.4.1-linux-arm64.7.so"
|
||||
system: flash
|
||||
required: false
|
||||
description: "OpenH264 2.4.1 decoder library, Linux aarch64"
|
||||
sha256: "e8ea7e42855ceb4a90e7bd0b3abeba0c58b5f97166e8b0a30eefd58e099557a4"
|
||||
validation: [sha256]
|
||||
source_ref: "video/external/src/decoder/openh264.rs:73-76, 104-146, 173-191"
|
||||
|
||||
- name: libopenh264-2.4.1-linux-arm.7.so
|
||||
path: "video/libopenh264-2.4.1-linux-arm.7.so"
|
||||
system: flash
|
||||
required: false
|
||||
description: "OpenH264 2.4.1 decoder library, Linux arm"
|
||||
sha256: "fd1dfb27d30bb72e903c9d2b4c650104a4369d2e7ffe8a4a533e8db2e7e9b19e"
|
||||
validation: [sha256]
|
||||
source_ref: "video/external/src/decoder/openh264.rs:69-72, 104-146, 173-191"
|
||||
|
||||
- name: libopenh264-2.4.1-linux32.7.so
|
||||
path: "video/libopenh264-2.4.1-linux32.7.so"
|
||||
system: flash
|
||||
required: false
|
||||
description: "OpenH264 2.4.1 decoder library, Linux x86"
|
||||
sha256: "b7cf0e407f99056d90cbf62787a34820a7595b2129b165319d50766e00a66704"
|
||||
validation: [sha256]
|
||||
source_ref: "video/external/src/decoder/openh264.rs:61-64, 104-146, 173-191"
|
||||
|
||||
- name: libopenh264-2.4.1-mac-arm64.dylib
|
||||
path: "video/libopenh264-2.4.1-mac-arm64.dylib"
|
||||
system: flash
|
||||
required: false
|
||||
description: "OpenH264 2.4.1 decoder library, macOS aarch64"
|
||||
sha256: "213ff93831cfa3dd6d7ad0c3a3403a6ceedf4ac1341e1278b5b869d42fefb496"
|
||||
validation: [sha256]
|
||||
source_ref: "video/external/src/decoder/openh264.rs:81-84, 104-146, 173-191"
|
||||
|
||||
- name: libopenh264-2.4.1-mac-x64.dylib
|
||||
path: "video/libopenh264-2.4.1-mac-x64.dylib"
|
||||
system: flash
|
||||
required: false
|
||||
description: "OpenH264 2.4.1 decoder library, macOS x86_64"
|
||||
sha256: "cc0ba518a63791c37571f3c851f0aa03a4fbda5410acc214ecd4f24f8d1c478e"
|
||||
validation: [sha256]
|
||||
source_ref: "video/external/src/decoder/openh264.rs:77-80, 104-146, 173-191"
|
||||
|
||||
- name: openh264-2.4.1-win64.dll
|
||||
path: "video/openh264-2.4.1-win64.dll"
|
||||
system: flash
|
||||
required: false
|
||||
description: "OpenH264 2.4.1 decoder library, Windows x86_64"
|
||||
sha256: "081b0c081480d177cbfddfbc90b1613640e702f875897b30d8de195cde73dd34"
|
||||
validation: [sha256]
|
||||
source_ref: "video/external/src/decoder/openh264.rs:89-92, 104-146, 173-191"
|
||||
|
||||
- name: openh264-2.4.1-win32.dll
|
||||
path: "video/openh264-2.4.1-win32.dll"
|
||||
system: flash
|
||||
required: false
|
||||
description: "OpenH264 2.4.1 decoder library, Windows x86"
|
||||
sha256: "83270149640469c994a62cc32a6d8c0413cd7b802b7f1f2f532159f5bdc1cedd"
|
||||
validation: [sha256]
|
||||
source_ref: "video/external/src/decoder/openh264.rs:85-88, 104-146, 173-191"
|
||||
+22
-22
@@ -26,7 +26,7 @@ files:
|
||||
|
||||
- name: "scph5500.bin"
|
||||
description: "SCPH-5500 (v3.0 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: true
|
||||
size: 524288
|
||||
validation: [size, sha256]
|
||||
@@ -38,7 +38,7 @@ files:
|
||||
|
||||
- name: "scph5501.bin"
|
||||
description: "SCPH-5501 (v3.0 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: true
|
||||
size: 524288
|
||||
validation: [size, sha256]
|
||||
@@ -50,7 +50,7 @@ files:
|
||||
|
||||
- name: "scph5502.bin"
|
||||
description: "SCPH-5502 (v3.0 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: true
|
||||
size: 524288
|
||||
validation: [size, sha256]
|
||||
@@ -63,7 +63,7 @@ files:
|
||||
# Japan alternatives
|
||||
- name: "scph1000.bin"
|
||||
description: "SCPH-1000 (v1.0 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
size: 524288
|
||||
validation: [size, sha256]
|
||||
@@ -72,7 +72,7 @@ files:
|
||||
|
||||
- name: "scph_v11j.bin"
|
||||
description: "v1.1 J"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
size: 524288
|
||||
validation: [size, sha256]
|
||||
@@ -81,7 +81,7 @@ files:
|
||||
|
||||
- name: "scph3000.bin"
|
||||
description: "SCPH-3000 (v2.1 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
size: 524288
|
||||
validation: [size, sha256]
|
||||
@@ -90,7 +90,7 @@ files:
|
||||
|
||||
- name: "scph3500.bin"
|
||||
description: "SCPH-3500 (v2.2 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
size: 524288
|
||||
validation: [size, sha256]
|
||||
@@ -100,7 +100,7 @@ files:
|
||||
|
||||
- name: "scph5000.bin"
|
||||
description: "v2.2 J (alt)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
size: 524288
|
||||
validation: [size, sha256]
|
||||
@@ -109,7 +109,7 @@ files:
|
||||
|
||||
- name: "scph7000.bin"
|
||||
description: "SCPH-7000 (v4.0 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
size: 524288
|
||||
validation: [size, sha256]
|
||||
@@ -118,7 +118,7 @@ files:
|
||||
|
||||
- name: "scph7500.bin"
|
||||
description: "SCPH-7500 (v4.1 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
size: 524288
|
||||
validation: [size, sha256]
|
||||
@@ -127,7 +127,7 @@ files:
|
||||
|
||||
- name: "scph9000.bin"
|
||||
description: "SCPH-9000 (v4.3 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
size: 524288
|
||||
validation: [size, sha256]
|
||||
@@ -137,7 +137,7 @@ files:
|
||||
# North America alternatives
|
||||
- name: "scph1001.bin"
|
||||
description: "SCPH-1001 (v2.0 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
size: 524288
|
||||
validation: [size, sha256]
|
||||
@@ -146,7 +146,7 @@ files:
|
||||
|
||||
- name: "scph5001.bin"
|
||||
description: "SCPH-5001 (v2.1 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
size: 524288
|
||||
validation: [size, sha256]
|
||||
@@ -155,7 +155,7 @@ files:
|
||||
|
||||
- name: "scph_v22a.bin"
|
||||
description: "v2.2 A"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
size: 524288
|
||||
validation: [size, sha256]
|
||||
@@ -164,7 +164,7 @@ files:
|
||||
|
||||
- name: "scph7001.bin"
|
||||
description: "SCPH-7001 (v4.1 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
size: 524288
|
||||
validation: [size, sha256]
|
||||
@@ -173,7 +173,7 @@ files:
|
||||
|
||||
- name: "scph101.bin"
|
||||
description: "SCPH-101 (v4.5 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
size: 524288
|
||||
validation: [size, sha256]
|
||||
@@ -183,7 +183,7 @@ files:
|
||||
# Europe alternatives
|
||||
- name: "scph1002.bin"
|
||||
description: "SCPH-1002 (v2.0 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
size: 524288
|
||||
validation: [size, sha256]
|
||||
@@ -192,7 +192,7 @@ files:
|
||||
|
||||
- name: "scph_v21e.bin"
|
||||
description: "v2.1 E"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
size: 524288
|
||||
validation: [size, sha256]
|
||||
@@ -201,7 +201,7 @@ files:
|
||||
|
||||
- name: "scph_v22e.bin"
|
||||
description: "v2.2 E"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
size: 524288
|
||||
validation: [size, sha256]
|
||||
@@ -210,7 +210,7 @@ files:
|
||||
|
||||
- name: "scph7002.bin"
|
||||
description: "SCPH-7002 (v4.1 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
size: 524288
|
||||
validation: [size, sha256]
|
||||
@@ -219,7 +219,7 @@ files:
|
||||
|
||||
- name: "scph102.bin"
|
||||
description: "SCPH-102 (v4.4 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
size: 524288
|
||||
validation: [size, sha256]
|
||||
@@ -228,7 +228,7 @@ files:
|
||||
|
||||
- name: "scph102_v45.bin"
|
||||
description: "v4.5 E"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
size: 524288
|
||||
validation: [size, sha256]
|
||||
|
||||
@@ -0,0 +1,279 @@
|
||||
emulator: SDL2TRS
|
||||
type: standalone
|
||||
core_classification: community_fork
|
||||
source: "https://gitlab.com/jengun/sdltrs"
|
||||
upstream: "http://sdltrs.sourceforge.net"
|
||||
profiled_date: "2026-08-08"
|
||||
source_commit: "a9b30ff3cfe2b44ccff094fcd5d3c5b60e78b8a2"
|
||||
core_version: "1.2.35"
|
||||
display_name: "Tandy - TRS-80 (SDL2TRS)"
|
||||
cores:
|
||||
- sdl2trs
|
||||
- sdltrs
|
||||
systems:
|
||||
- trs80
|
||||
mode: standalone
|
||||
|
||||
notes: |
|
||||
TRS-80 Model I, III, 4 and 4P emulator. Jens Guenther's GitLab tree continues
|
||||
Mark Grebe's SDLTRS, itself an SDL port of Tim Mann's xtrs, whose licence
|
||||
still ships as xtrs.LICENSE. The sdl2 branch builds the sdl2trs binary that
|
||||
ES-DE and RetroDECK look for; master builds sdltrs for hosts without SDL2
|
||||
(README.md:106-111). The branches diverge only in the display and paste code:
|
||||
trs_cmd_rom.c and trs_memory.c are byte-identical between them.
|
||||
|
||||
Three ROM slots exist, one per model family, and trs_rom_init picks one from
|
||||
trs_model. Model I takes romfile1, Model III and Model 4 share romfile3, and
|
||||
Model 4P takes romfile4p (trs_cmd_rom.c:868-894). The defaults are level2.rom,
|
||||
model3.rom and model4p.rom, each prefixed by ROM_PATH (trs_options.c:900-902).
|
||||
ROM_PATH is a build define left empty unless a packager sets it
|
||||
(trs_options.c:53-54, CMakeLists.txt:42), so a stock build resolves the names
|
||||
against the working directory.
|
||||
|
||||
-rom, -r and -romfile write to the slot of the model selected at the moment
|
||||
they are parsed (trs_options.c:611-622). Options are applied in command-line
|
||||
order after the config file has been read (trs_options.c:1020-1040), so -m has
|
||||
to come first to redirect them, and a model key in the config file otherwise
|
||||
decides. -rom1, -rom3 and -rom4p address a slot directly. The GUI ROM browser
|
||||
filters on .bin, .hex, .iii and .rom (trs_sdl_gui.h:63, trs_sdl_gui.c:2591-2597).
|
||||
|
||||
trs_load_rom sniffs the first byte. A colon means Intel hex, and load_hex
|
||||
rejects the file on a line checksum mismatch. A 1 or a 5 on Model III or later
|
||||
means a MODELA/III CMD file, rejected above 14 KB. Anything else is read as
|
||||
raw binary with no check (trs_cmd_rom.c:720-767, 779-843), which is the path a
|
||||
dump takes. No hash is computed anywhere in the tree. rom_write drops any byte
|
||||
at or past MAX_ROM_SIZE, 18432 (trs_memory.c:63, 742-745), and trs_rom_size,
|
||||
the extent of the plain ROM mapping, is capped at 14336 (trs_cmd_rom.c:896-898).
|
||||
|
||||
A failed load is not fatal. Model I and Model III/4 fall back to trs_fakerom,
|
||||
which prints "You do not have a ROM image installed for Model" and spins.
|
||||
Model 4P falls back to trs_rom4p, Pete Cervasio's free boot ROM, which boots
|
||||
Model 4 operating systems from floppy (trs_cmd_rom.c:871-893). -stringy
|
||||
overlays the built-in Exatron Stringy Floppy ROM at 0x3000 over the Model I
|
||||
image (trs_cmd_rom.c:875-876), and -hdboot rewrites the loaded Model I ROM in
|
||||
place to boot from a hard drive (trs_cmd_rom.c:447-610, 882-883).
|
||||
|
||||
The clones run off the same three slots and are recognised from the ROM data
|
||||
rather than from an option (trs_clones.c:8-20, html/Features.html), so their
|
||||
larger images load as a Model I or a Model III ROM. None has a filename of its
|
||||
own anywhere in the tree.
|
||||
|
||||
files:
|
||||
- name: level2.rom
|
||||
description: "TRS-80 Model I ROM"
|
||||
required: true
|
||||
max_size: 18432
|
||||
source_ref: "src/trs_options.c:81 (romfile1), src/trs_options.c:900 (default name), src/trs_cmd_rom.c:871-884 (trs_model 1)"
|
||||
note: >
|
||||
Model I is the model a stock configuration selects (trs_options.c:927), so
|
||||
this is the slot an unconfigured run reads. Without it the machine runs the
|
||||
compiled-in diagnostic stub and nothing else. max_size is the shared ROM
|
||||
buffer ceiling: a longer file is truncated, never rejected, and only the
|
||||
first 14336 bytes reach the plain memory map.
|
||||
|
||||
- name: model3.rom
|
||||
description: "TRS-80 Model III and Model 4 ROM"
|
||||
required: true
|
||||
max_size: 18432
|
||||
source_ref: "src/trs_options.c:82 (romfile3), src/trs_options.c:901 (default name), src/trs_cmd_rom.c:885-889 (trs_model 3 and 4)"
|
||||
note: >
|
||||
Serves Model III and Model 4 alike; the stub text spells out that Model 4
|
||||
mode needs a Model 3 image (trs_cmd_rom.c:110-115). This is the one slot
|
||||
that also accepts a MODELA/III CMD file, which html/index.html presents as
|
||||
the substitute for a real Model III ROM. Same truncating ceiling as the
|
||||
Model I slot.
|
||||
|
||||
- name: model4p.rom
|
||||
description: "TRS-80 Model 4P boot ROM"
|
||||
required: false
|
||||
max_size: 18432
|
||||
source_ref: "src/trs_options.c:83 (romfile4p), src/trs_options.c:902 (default name), src/trs_cmd_rom.c:890-893 (trs_model 5)"
|
||||
note: >
|
||||
Optional because the compiled-in replacement is a working boot ROM rather
|
||||
than a stub. html/index.html states the free ROM boots only Model 4
|
||||
operating systems, not Model III, and only from floppy, so the original
|
||||
image is what restores Model III mode and hard-drive booting. Hard drive
|
||||
unit 0 is tried before floppy by the Radio Shack ROM, and F2 held at boot
|
||||
forces floppy (html/Features.html).
|
||||
|
||||
analysis:
|
||||
embedded_roms:
|
||||
mechanism: "Uint8 arrays in trs_cmd_rom.c, copied into the ROM buffer by trs_load_compiled_rom (trs_cmd_rom.c:769-777)"
|
||||
extraction: "hashes computed from the decoded byte literals"
|
||||
|
||||
entries:
|
||||
- symbol: trs_fakerom
|
||||
role: "Fallback for Model I and Model III/4 when the slot cannot be read"
|
||||
source_ref: "src/trs_cmd_rom.c:56-116, src/trs_cmd_rom.c:873,888"
|
||||
load_address: 0
|
||||
size: 461
|
||||
crc32: 5f85edf9
|
||||
md5: de9617738e7ee6633d305777e0ce6a15
|
||||
sha1: 42736ddff1944fcd4631168d35f773fc824eeb9b
|
||||
note: >
|
||||
Writes "You do not have a ROM image installed for Model" plus the model
|
||||
digit to the screen and halts on a JR -2. It runs no TRS-80 software,
|
||||
which is why both slots it covers are required.
|
||||
|
||||
- symbol: trs_rom4p
|
||||
role: "Fallback for Model 4P"
|
||||
source_ref: "src/trs_cmd_rom.c:126-193, src/trs_cmd_rom.c:892"
|
||||
load_address: 0
|
||||
size: 518
|
||||
crc32: 57ec35ee
|
||||
md5: c8551e6aa36c4bea256d3073c7255ed5
|
||||
sha1: 60d4ff043525256fa3072e59ba283a3cb98399f4
|
||||
note: >
|
||||
Pete Cervasio's free Model 4P boot ROM version 0.06, carried under a
|
||||
copy-and-modify notice in the header. Reads and runs a boot sector and
|
||||
reports CRC errors, a missing boot sector and an empty drive.
|
||||
utilities/xtrsrom4p.z80 is its source and xtrsrom4p.README its manual.
|
||||
|
||||
- symbol: trs_romesf
|
||||
role: "Exatron Stringy Floppy firmware, overlaid on the Model I image"
|
||||
source_ref: "src/trs_cmd_rom.c:199-441, src/trs_cmd_rom.c:875-876"
|
||||
load_address: 12288
|
||||
size: 1920
|
||||
crc32: 99e3fdc6
|
||||
md5: 235d1eb412cfc5696aad8a0a2dc6c53a
|
||||
sha1: b2612037600031b5290bace247951e1f99700b36
|
||||
note: >
|
||||
Loaded over 0x3000-0x377F whenever -stringy is set, on top of whatever
|
||||
the Model I slot supplied, and leaves trs_rom_size at 0x3780. Taken
|
||||
from the ESF Advanced Programmer's Guide and held to be public domain
|
||||
by the header. utilities/esfrom.z80 is its source.
|
||||
|
||||
extended_rom_images:
|
||||
note: >
|
||||
The clones and the Holmes video card need images larger than a stock ROM,
|
||||
and the memory mapper reads the extra regions straight out of the same
|
||||
buffer. They arrive through romfile1 or romfile3 like any other image and
|
||||
are recognised from the ROM data, so the emulator defines no filename for
|
||||
them and no option selects them.
|
||||
source_ref: "src/trs_clones.c:8-20 (clone table), src/trs_memory.c:63 (MAX_ROM_SIZE)"
|
||||
layouts:
|
||||
- machine: "Prologica CP-300, CP-500 and CP-500 M80"
|
||||
slot: model3.rom
|
||||
image_size: 16384
|
||||
source_ref: "src/trs_cp500.c:1-22, src/trs_cp500.c:153-160, src/trs_memory.c:1052-1054"
|
||||
note: >
|
||||
14 KB of patched Model III ROM plus a 2 KB resident monitor read back
|
||||
from 0x3800-0x3FFF and banked into 0x3000-0x37FF. README.md:21 and
|
||||
html/Features.html name CP-300 and CP-500 for this 16 KB image.
|
||||
|
||||
- machine: "Holmes VID-80 (VX-3) on Model III"
|
||||
slot: model3.rom
|
||||
image_size: 18432
|
||||
source_ref: "src/trs_memory.c:577-586, src/trs_memory.c:1057-1080"
|
||||
note: "4 KB VID-80 ROM with the Model III ROM appended behind it; this pairing is what sets MAX_ROM_SIZE."
|
||||
|
||||
- machine: "TCS SpeedMaster, also sold as Genie IIs"
|
||||
slot: level2.rom
|
||||
image_size: 16384
|
||||
source_ref: "src/trs_memory.c:1025-1048"
|
||||
note: "BASIC at 0x0000-0x2FFF and a ROM card read from 0x1000-0x3FFF into 0x4000-0xBFFF. Needs -speedup tcs (html/Features.html)."
|
||||
|
||||
- machine: "Aster CT-80"
|
||||
slot: level2.rom
|
||||
image_size: 14336
|
||||
source_ref: "src/trs_memory.c:962-994"
|
||||
note: "BASIC at 0x0000-0x2FFF plus a 2 KB boot ROM at 0x3000-0x37FF, mirrored into 0xEC00 in CP/M mode."
|
||||
|
||||
- machine: "EACA EG 3200 Genie III"
|
||||
slot: level2.rom
|
||||
image_size: 14336
|
||||
source_ref: "src/trs_memory.c:909-931"
|
||||
note: "Bank 1 is read through trs_rom_size, so the 14 KB cap on the plain mapping is the ceiling."
|
||||
|
||||
- machine: "TCS Genie IIIs"
|
||||
slot: level2.rom
|
||||
image_size: 12288
|
||||
source_ref: "src/trs_memory.c:932-961"
|
||||
note: "Reads 0x0000-0x2FFF only; the rest of the address space is banked RAM, video and the HRG window."
|
||||
|
||||
- machine: "LNW Research LNW80/II"
|
||||
slot: level2.rom
|
||||
image_size: 12288
|
||||
source_ref: "src/trs_memory.c:995-1024"
|
||||
note: "Reads 0x0000-0x2FFF only. -speedup lnw sets the 4 MHz clock and the low 16 KB banking."
|
||||
|
||||
character_generators:
|
||||
source_ref: "src/trs_chars.c:12 (trs_char_data), src/trs_options.c:404-436 (charset names), src/trs_sdl_interface.c:585"
|
||||
note: >
|
||||
Every character set is a compiled-in bitmap table selected by -charset1,
|
||||
-charset3 and -charset4. The tree holds no external font file and reads
|
||||
none. The sets cover the Model I generators, the Model III and Model 4
|
||||
Katakana and international sets, and the Video Genie, System 80, HT-1080Z,
|
||||
Meritum and Aster CT-80 clones.
|
||||
|
||||
frontend_conventions:
|
||||
note: >
|
||||
Two frontends fix names and paths the emulator itself does not know. Both
|
||||
belong to the platform layer.
|
||||
entries:
|
||||
- frontend: ES-DE
|
||||
source_ref: "resources/systems/linux/es_systems.xml:2211-2213, USERGUIDE.md:3334-3362"
|
||||
note: >
|
||||
Passes -rom %ROMPATH%/trs-80/level2.rom on all three launch commands
|
||||
and -disk0 %ROMPATH%/trs-80/boot.dsk on the DOS Diskette one, with
|
||||
boot.dsk documented as a copy of a TRS-80 DOS, NewDos/80 v2.0
|
||||
recommended. No command passes -m, so the slot -rom writes to follows
|
||||
the config file, which is Model I unless the user changed it, and
|
||||
model3.rom and model4p.rom are never named. USERGUIDE.md:3338 records
|
||||
that both filenames are case sensitive.
|
||||
- frontend: RetroDECK
|
||||
source_ref: "RetroDECK/components archive_later/sdl2trs/component_manifest.json"
|
||||
note: >
|
||||
Declares level1.rom, level2.rom, level3.rom, level4.rom, level4p.rom
|
||||
and boot.dsk under $roms_path/trs-80. Of those the emulator opens only
|
||||
level2.rom by default; level3.rom, level4.rom and level4p.rom are
|
||||
renames of the model3.rom and model4p.rom slots, and level1.rom is a
|
||||
Level I BASIC image for the Model I slot. The manifest hashes come
|
||||
from the frontend, not from the emulator, which computes none.
|
||||
|
||||
shipped_content:
|
||||
source_ref: "CMakeLists.txt:50, CMakeLists.txt:145-146"
|
||||
files:
|
||||
- diskimages/utility.dsk
|
||||
- diskimages/cpmutil.dsk
|
||||
note: >
|
||||
Installed into the shared data directory by the emulator's own packaging
|
||||
and carrying the IMPORT, EXPORT, SETTIME, XTRSHARD, XTRSMOUS and CP/M
|
||||
helper programs whose sources sit in utilities/. No code path names them:
|
||||
they reach the machine only when a user mounts them with -disk.
|
||||
|
||||
user_supplied_paths:
|
||||
note: "Named on the command line, in the config file or in a disk set, with no fixed filename"
|
||||
entries:
|
||||
- what: "floppy images in drives 0-7, JV1, JV3 and DMK"
|
||||
source_ref: "src/trs_options.c:589-594, src/trs_disk.c:789-921"
|
||||
- what: "hard drive images, units 0-3"
|
||||
source_ref: "src/trs_options.c:595-600, src/trs_hard.c:212-220, src/trs_hard.c:528-609"
|
||||
- what: "cassette images, .cas, .cpt and .wav"
|
||||
source_ref: "src/trs_options.c:583-588, src/trs_cassette.c:526-553"
|
||||
- what: "stringy floppy wafers in drives 0-7, .esf"
|
||||
source_ref: "src/trs_options.c:601-606, src/trs_stringy.c:278-316, src/trs_stringy.c:342-346"
|
||||
- what: "disk set, a plain list of the paths above"
|
||||
source_ref: "src/trs_options.c:607-610, src/trs_disk.c:3765-3802"
|
||||
- what: "CMD program loaded straight into memory, from a bare argument"
|
||||
source_ref: "src/trs_options.c:1014-1015, src/trs_cmd_rom.c:845-866"
|
||||
- what: "serial port tty, defaulting to /dev/ttyS0 on Linux"
|
||||
source_ref: "src/trs_uart.c:54-56, src/trs_uart.c:144-202"
|
||||
|
||||
generated_files:
|
||||
written_and_read_back:
|
||||
- filename: ".sdltrs.t8c"
|
||||
source_ref: "src/trs_options.c:939-955, src/trs_options.c:1049"
|
||||
note: "Configuration, written to the home directory on first run when none is found."
|
||||
- filename: "<name>.t8s"
|
||||
source_ref: "src/trs_state_save.c:38-66, src/trs_state_save.c:68-113"
|
||||
note: "Emulator state, written and read by the GUI or a bare command-line argument."
|
||||
- filename: "blank disk, wafer and hard drive images"
|
||||
source_ref: "src/trs_mkdisk.c:192-308, src/trs_stringy.c:187-197"
|
||||
written_only:
|
||||
- filename: "trsprn####.txt"
|
||||
source_ref: "src/trs_printer.c:60-81"
|
||||
note: "Printer output, numbered up to 9999 in the printer directory."
|
||||
- filename: "screenshots as BMP"
|
||||
source_ref: "src/trs_sdl_gui.c:2673"
|
||||
note: "Produced by the emulator, none is user-supplied"
|
||||
@@ -0,0 +1,84 @@
|
||||
emulator: SheepShaver
|
||||
type: standalone
|
||||
core_classification: other
|
||||
source: "https://github.com/cebix/macemu"
|
||||
upstream: "https://github.com/cebix/macemu"
|
||||
profiled_date: "2026-08-08"
|
||||
source_commit: "96e512bd6376e78a2869f16dcc8a9028bce5ee72"
|
||||
core_version: "2.4"
|
||||
display_name: "Apple - Macintosh PowerPC (SheepShaver)"
|
||||
cores:
|
||||
- sheepshaver
|
||||
systems:
|
||||
- apple-macintosh
|
||||
mode: standalone
|
||||
|
||||
notes: |
|
||||
PowerPC Macintosh emulator living in the macemu tree alongside Basilisk II,
|
||||
version 2.4 (version.h:24-25). Runs MacOS 7.5.2 through 9.0.4 (README.md) on
|
||||
a PCI PowerMac or NewWorld ROM image.
|
||||
|
||||
ROM loading (main_unix.cpp:581-613, main_windows.cpp:329-360): the path comes
|
||||
from the "rom" prefs item (prefs_items.cpp:45). When that is empty, the file
|
||||
named ROM is opened, then the file named Mac OS ROM (main_unix.cpp:162-163,
|
||||
main_windows.cpp:58-59). Neither found raises STR_NO_ROM_FILE_ERR and
|
||||
main_unix.cpp:987 aborts startup. The filename does not select the format:
|
||||
both names go through the same decoder.
|
||||
|
||||
At most ROM_SIZE = 0x400000 bytes are read (cpu_emulation.h:30). DecodeROM
|
||||
(rom_patches.cpp:148-200) accepts two forms. A read of exactly 4 MB is taken
|
||||
as a plain image and copied as is. A buffer opening on <CHRP-BOOT> is
|
||||
decompressed instead, either LZSS through the lzss-offset and lzss-size
|
||||
constants (decode_lzss, rom_patches.cpp:80) or parcel by parcel through
|
||||
parcels-offset and parcels-size (decode_parcels, rom_patches.cpp:123).
|
||||
Anything else fails, and main_unix.cpp:602-609 picks the message from the
|
||||
file size on disk: STR_ROM_SIZE_ERR when it is not 4 MB,
|
||||
STR_ROM_FILE_READ_ERR otherwise (user_strings.cpp:66-67).
|
||||
|
||||
PatchROM (rom_patches.cpp:671-731), called from InitAll at main.cpp:161,
|
||||
reads the nanokernel ID at offset 0x30d064 of the decoded image and accepts
|
||||
Boot TNT, Boot Alchemy, Boot Zanzibar, Boot Gazelle, Boot Gossamer and
|
||||
NewWorld (rom_patches.cpp:682-695). Any other string raises
|
||||
STR_UNSUPPORTED_ROM_TYPE_ERR. The free space checks at
|
||||
rom_patches.cpp:698-707 and the four patch passes at rom_patches.cpp:710-713
|
||||
then rewrite the image in memory, and rom_patches.cpp:729 copies its last
|
||||
megabyte past the 4 MB mark for the 68k emulator. Nothing is checksummed:
|
||||
the ROM checksum at offset 0 is only printed by a debug build
|
||||
(rom_patches.cpp:674).
|
||||
|
||||
MacOS itself and the applications live in disk images referenced by the
|
||||
disk, floppy and cdrom prefs items, supplied by the user.
|
||||
|
||||
Two files are installed by the emulator's own Makefile into
|
||||
$(datadir)/SheepShaver (Makefile.in:21,162-164) and read from there:
|
||||
keycodes keycode to Mac keycode table, read only when the keycodes
|
||||
prefs item is true, which defaults to false
|
||||
(prefs_unix.cpp:117). Overridable with keycodefile.
|
||||
configure.ac:754,775 picks ../SDL/keycodes or keycodes
|
||||
according to the video backend. video_x.cpp:79,1200,
|
||||
video_sdl2.cpp:97,1231
|
||||
tunconfig script run to configure the tunN interface for ethernet.
|
||||
Overridable with etherconfig. ether_unix.cpp:108,381
|
||||
The SDL2 backend falls back to BasiliskII_keycodes in the same directory
|
||||
(video_sdl2.cpp:98,1232).
|
||||
|
||||
The preferences file (.sheepshaver_prefs, prefs_unix.cpp:49;
|
||||
SheepShaver_prefs, prefs_windows.cpp:66) and the NVRAM file
|
||||
(.sheepshaver_nvram, xpram_unix.cpp:30; SheepShaver_nvram.dat,
|
||||
xpram_windows.cpp:31) are written by the emulator when absent. Passing a
|
||||
.sheepvm directory on the command line reads the preferences from inside it
|
||||
instead (main_unix.cpp:371-384, prefs_unix.cpp:57-69).
|
||||
|
||||
files:
|
||||
- name: ROM
|
||||
aliases: ["Mac OS ROM"]
|
||||
required: true
|
||||
validation: [signature]
|
||||
description: "PowerPC Macintosh ROM, 4 MB PCI PowerMac image or CHRP-BOOT Mac OS ROM"
|
||||
source_ref: "src/Unix/main_unix.cpp:162-163,585-593,987, src/Windows/main_windows.cpp:58-59,330-344, src/rom_patches.cpp:148-200,671-695, src/main.cpp:161"
|
||||
note: >
|
||||
No size is enforced on its own: DecodeROM takes a 4 MB read as a plain
|
||||
image and any other length only through the <CHRP-BOOT> header. The check
|
||||
every accepted image passes is the nanokernel ID at offset 0x30d064 of
|
||||
the decoded 4 MB image, which PatchROM matches against Boot TNT, Boot
|
||||
Alchemy, Boot Zanzibar, Boot Gazelle, Boot Gossamer and NewWorld.
|
||||
@@ -0,0 +1,167 @@
|
||||
emulator: simple64
|
||||
type: standalone
|
||||
core_classification: community_fork
|
||||
source: "https://github.com/simple64/simple64"
|
||||
upstream: "https://github.com/simple64/simple64"
|
||||
profiled_date: "2026-08-08"
|
||||
source_commit: "d8c969c7b932e3d76e6a25549d76348839dbaefd"
|
||||
core_version: "v2024.12.1"
|
||||
display_name: "Nintendo - Nintendo 64 (simple64)"
|
||||
cores:
|
||||
- simple64
|
||||
- simple64-gui
|
||||
- io.github.simple64.simple64
|
||||
systems:
|
||||
- nintendo-64
|
||||
mode: standalone
|
||||
|
||||
notes: |
|
||||
Qt front end by Logan McNaughton over a vendored fork of mupen64plus-core,
|
||||
with parallel-rsp and parallel-rdp as the only RSP and video plugins
|
||||
(wiki, simple64-FAQ.md). The monorepo holds the core, the GUI, the SDL2 audio
|
||||
plugin, the Qt and raphnetraw input plugins and both parallel components as
|
||||
squashed subtrees (pull_repos.sh). The core is compiled with M64P_PARALLEL,
|
||||
M64P_NETPLAY and NO_ASM, and its source list leaves out src/osd and every
|
||||
dynarec (mupen64plus-core/CMakeLists.txt:26,28-104). ES-DE runs the release
|
||||
binary as `--nogui <rom>`; the GUI registers only verbose, nogui and test
|
||||
(simple64-gui/main.cpp:14-24). Development stopped and the repository was
|
||||
archived in February 2025, five commits past v2024.12.1, touching only the
|
||||
README, the build script and a netplay dialog.
|
||||
|
||||
Boot runs the real PIF ROM when one is present, which is where this fork
|
||||
parts company with mupen64plus upstream: upstream keeps open_pif behind the
|
||||
M64CMD_PIF_OPEN command with a caller supplied length and ships no such image
|
||||
in data/, while simple64 opens the file itself at a fixed length and ships
|
||||
both images. get_pif_path names pif.pal.rom for SYSTEM_PAL and pif.ntsc.rom
|
||||
for SYSTEM_NTSC and SYSTEM_MPAL, then resolves it against the shared data
|
||||
path (mupen64plus-core/src/main/main.c:1565-1580;
|
||||
mupen64plus-core/src/api/config.c:1610-1625;
|
||||
mupen64plus-core/src/osal/files_unix.c:150-170). The GUI passes the directory
|
||||
holding the executable as that path (simple64-gui/mainwindow.cpp:986-988,
|
||||
mupen64plus-core/src/api/frontend.c:59,90), and build.sh copies both images
|
||||
there (build.sh:26), so a release install always carries them.
|
||||
|
||||
Absence is not fatal. The lookup returns NULL when the file is not found and
|
||||
the result reaches fopen unchecked; on a failed open, open_pif is never
|
||||
called and g_start_address keeps its initialiser 0xa4000040
|
||||
(mupen64plus-core/src/main/main.c:112,1624-1633). A digest that matches none
|
||||
of the four accepted values returns M64ERR_INPUT_INVALID, which main_run
|
||||
discards, leaving the same address. pif_bootrom_hle_execute returns
|
||||
immediately when the address is 0xbfc00000 and otherwise simulates IPL1 and
|
||||
IPL2 in full, quiescing the RSP, PI, VI and AI, deriving s3 to s7 from pif24,
|
||||
copying IPL3 into DMEM and planting the stub CIC x105 needs
|
||||
(mupen64plus-core/src/device/pif/bootrom_hle.c:48-151). It runs on cold boot
|
||||
and on both reset paths (mupen64plus-core/src/main/main.c:1977;
|
||||
mupen64plus-core/src/device/r4300/interrupt.c:445,496). The CIC challenge
|
||||
reads no file either: process_cic_challenge answers with the CIC-NUS-6105
|
||||
algorithm (mupen64plus-core/src/device/pif/pif.c:252-260,
|
||||
mupen64plus-core/src/device/pif/n64_cic_nus_6105.c).
|
||||
|
||||
64DD is not reachable. The GUI's media loader supplies only the two Game Boy
|
||||
cartridge callbacks and leaves set_dd_rom_region, get_dd_rom and get_dd_disk
|
||||
null, the two functions that would have answered being commented out
|
||||
(simple64-gui/interface/common.cpp:129-163,240;
|
||||
mupen64plus-core/src/api/m64p_types.h:186-221). load_dd_rom is only reached
|
||||
after load_dd_disk succeeds, and load_dd_disk bails on a null callback, so
|
||||
the IPL image the core can otherwise load and byteswap is never requested
|
||||
(mupen64plus-core/src/main/main.c:1175-1247,1722-1736). No IPL entry follows
|
||||
from that, and the system list carries no 64DD line.
|
||||
|
||||
Transfer Pak carts come from Player1GBROM and Player1GBRAM through
|
||||
Player4GBROM and Player4GBRAM, filled by a file dialog
|
||||
(simple64-gui/mainwindow.cpp:84-117,
|
||||
simple64-gui/interface/common.cpp:87-127). The pair is a Game Boy cartridge
|
||||
and its save, supplied by the player, and the code fixes no name, size or
|
||||
hash for either, so neither carries an entry here.
|
||||
|
||||
The Voice Recognition Unit is the one asset the user has to go and get. The
|
||||
core's VRU controller forwards every word command to the input plugin
|
||||
(mupen64plus-core/src/device/controllers/vru_controller.c:147-235), and
|
||||
setupVosk loads libvosk from beside the executable, then hands
|
||||
vosk-model-small-en-us-0.15 under the user data directory to vosk_model_new
|
||||
once conf/mfcc.conf is found inside it (simple64-input-qt/main.cpp:769-813).
|
||||
Choosing "Emulate VRU" for controller 4 tests that same file and, when it is
|
||||
missing, downloads
|
||||
https://alphacephei.com/vosk/models/vosk-model-small-en-us-0.15.zip and
|
||||
unpacks it there with 7za (simple64-input-qt/configdialog.cpp:22-62,93-115).
|
||||
build.sh fetches only the Vosk library, never the model (build.sh:89-101,
|
||||
158-160), so no release carries it.
|
||||
|
||||
Data the build places beside the executable: mupen64plus.ini, parsed as the
|
||||
ROM database and keyed on MD5 then CRC pair, which supplies save type, player
|
||||
count, rumble, Transfer Pak, Controller Pak, bio pak and expansion memory per
|
||||
title (mupen64plus-core/src/main/rom.c:190-235,674-691; build.sh:25); and
|
||||
cheats.json, pulled from simple64/cheat-parser at build time and read by
|
||||
header CRC pair and country code (simple64-gui/cheats.cpp:230-239;
|
||||
build.sh:106-111). Neither is hashed or version checked, romdatabase_open
|
||||
logs and returns when the file is absent, and loadCheatData yields an empty
|
||||
object, so both degrade rather than fail.
|
||||
|
||||
Two data files in the tree this build never reads: data/font.ttf, whose only
|
||||
consumer is src/osd/osd.c, absent from the source list, leaving
|
||||
osd_new_message as the inline no-op behind M64P_OSD
|
||||
(mupen64plus-core/src/osd/osd.h:93,104,118-121); and data/mupencheat.txt,
|
||||
referenced nowhere under src/ since cheats come from the GUI's own JSON. The
|
||||
parallel-rdp shaders are compiled in through shaders/slangmosh.hpp rather
|
||||
than loaded (parallel-rdp-standalone/parallel-rdp/rdp_renderer.cpp:36), and
|
||||
the file reads in parallel-rsp/main.cpp belong to a test harness that sits
|
||||
outside that plugin's CMake target.
|
||||
|
||||
Written rather than obtained: mupen64plus.cfg in the config directory and
|
||||
simple64-gui.ini beside the executable
|
||||
(simple64-gui/mainwindow.cpp:249), the Controller Pak, EEPROM, FlashRAM and
|
||||
SRAM saves opened with default content when absent
|
||||
(mupen64plus-core/src/main/main.c:1160-1173,1717-1720), the per ROM save
|
||||
metadata (mupen64plus-core/src/main/rom.c:263,347), savestates and
|
||||
screenshots (mupen64plus-core/src/main/screenshot.c:174), and the Discord
|
||||
game SDK and Vosk shared libraries, which are resolved by name at runtime
|
||||
and are code rather than data (simple64-gui/mainwindow.cpp:356,
|
||||
simple64-input-qt/main.cpp:773).
|
||||
|
||||
files:
|
||||
- name: "pif.ntsc.rom"
|
||||
system: nintendo-64
|
||||
required: false
|
||||
hle_fallback: true
|
||||
region: [world]
|
||||
size: 1984
|
||||
md5:
|
||||
- "5c124e7948ada85da603a522782940d0"
|
||||
- "4921d5f2165dee6e2496f4388c4c81da"
|
||||
validation: [md5]
|
||||
description: "PIF boot ROM (NTSC)"
|
||||
note: >
|
||||
Chosen for SYSTEM_NTSC and SYSTEM_MPAL. rom_country_code_to_system_type
|
||||
returns SYSTEM_PAL only for country codes 0x44, 0x46, 0x49, 0x50, 0x53,
|
||||
0x55, 0x58 and 0x59 and falls through to SYSTEM_NTSC for 0x37, 0x41,
|
||||
0x45, 0x4a and, on its default arm, every other code, which is why the
|
||||
region is recorded as world rather than an enumeration. main_run reads
|
||||
1984 bytes, the size of the PIF ROM window, and open_pif digests exactly
|
||||
that span, so the size given here is the length consumed rather than a
|
||||
check the code performs. Two digests are accepted: the current one, which
|
||||
is the image shipped in data/ and the No-Intro entry "[BIOS] Nintendo 64
|
||||
- PIF (Japan, USA).bin" at 1984 bytes, and the digest of the earlier dump
|
||||
kept for compatibility. On a match the image is byteswapped into PIF
|
||||
memory and the reset vector moves to 0xbfc00000; on a mismatch the core
|
||||
logs "Invalid PIF ROM" and boots through the HLE bootrom instead.
|
||||
source_ref: "mupen64plus-core/src/main/main.c:1565-1580,1624-1633,2097-2135, mupen64plus-core/src/main/rom.c:502-525, mupen64plus-core/src/device/pif/pif.h:36"
|
||||
|
||||
- name: "pif.pal.rom"
|
||||
system: nintendo-64
|
||||
required: false
|
||||
hle_fallback: true
|
||||
region: [europe, australia]
|
||||
size: 1984
|
||||
md5:
|
||||
- "d4232dc935cad0650ac2664d52281f3a"
|
||||
- "2b6eec586faa43f3462333b844834554"
|
||||
validation: [md5]
|
||||
description: "PIF boot ROM (PAL)"
|
||||
note: >
|
||||
Chosen for SYSTEM_PAL, which covers the eight country codes 0x44 German,
|
||||
0x46 French, 0x49 Italian, 0x50 European, 0x53 Spanish, 0x55 Australian,
|
||||
0x58 and 0x59 European. Same read length, digest span and fallback as the
|
||||
NTSC image. The current digest is the image shipped in data/ and the
|
||||
No-Intro entry "[BIOS] Nintendo 64 - PIF (Europe).bin" at 1984 bytes; the
|
||||
second value is the earlier dump the fork still accepts.
|
||||
source_ref: "mupen64plus-core/src/main/main.c:1565-1580,1624-1633,2097-2135, mupen64plus-core/src/main/rom.c:502-525, mupen64plus-core/src/device/pif/pif.h:36"
|
||||
@@ -0,0 +1,67 @@
|
||||
emulator: solarus
|
||||
type: standalone
|
||||
core_classification: game_engine
|
||||
source: "https://gitlab.com/solarus-games/solarus"
|
||||
upstream: "https://gitlab.com/solarus-games/solarus"
|
||||
profiled_date: "2026-08-08"
|
||||
source_commit: "bc2707d1fba2ec1e6529857f87202a20998bff13"
|
||||
core_version: "2.2.0"
|
||||
display_name: "Solarus Game Engine"
|
||||
cores:
|
||||
- solarus
|
||||
systems:
|
||||
- solarus
|
||||
mode: standalone
|
||||
|
||||
notes: |
|
||||
2D action-RPG engine running quests scripted in Lua, built as libsolarus with
|
||||
the solarus-run command-line player and the Qt Solarus Launcher on top. ES-DE
|
||||
runs `solarus-run <rom>` on a single .solarus file, Batocera as `solarus-run
|
||||
-fullscreen=yes -cursor-visible=no -lua-console=no -quit-combo=<hotkey>+<start>
|
||||
-joypad-num<n>=<index> <rom>` off a bbecaf31 pin, RetroDECK ships the v2.0.3
|
||||
solarus-launcher AppImage.
|
||||
|
||||
The quest arrives as the content argument. get_quest_path takes the last
|
||||
non-option argv and otherwise falls back to SOLARUS_DEFAULT_QUEST, which
|
||||
defaults to "." (MainLoop.cpp:108-121, cmake/AddOptions.cmake:8,
|
||||
Common.h:119-123). open_quest mounts that path through PhysFS directly, then
|
||||
tries <path>/data, <path>/data.solarus and <path>/data.solarus.zip, each both
|
||||
relative and under PHYSFS_getBaseDir (QuestFiles.cpp:151-165), and the quest
|
||||
counts as found once quest.dat resolves (QuestFiles.cpp:228-233). The rest
|
||||
comes out of that mount: quest.dat and project_db.dat
|
||||
(CurrentQuest.cpp:44,49), fonts under fonts/ as .png, .ttf, .otf, .ttc or .fon
|
||||
(FontResource.cpp:65-101), shaders under shaders/ (Shader.cpp:113), tilesets,
|
||||
sprites and music (Tileset.cpp:156-174, SpriteAnimationSet.cpp:48,
|
||||
MusicSystem.cpp:136-142).
|
||||
|
||||
Startup reads one file from outside the quest. System::initialize probes
|
||||
SDL_GetBasePath() then SOLARUS_DATADIR_PATH for gamecontrollerdb.txt and hands
|
||||
whichever it finds to SDL through SDL_HINT_GAMECONTROLLERCONFIG_FILE
|
||||
(System.cpp:71-99). The GLSL fallbacks are raw string literals in
|
||||
DefaultShaders.h:57,114, and glad, hqx and snes_spc compile in from
|
||||
third_party/.
|
||||
|
||||
Files the engine writes rather than expects: settings.dat and savegames under
|
||||
the quest write directory named in quest.dat (Settings.cpp:149-166,240-280,
|
||||
Savegame.cpp:114), the write root from SDL_GetPrefPath (QuestFiles.cpp:540),
|
||||
and error.txt under SOLARUS_FILE_LOGGING (Logger.cpp:29-45). Batocera's
|
||||
002-pad.patch adds a read of /userdata/system/configs/solarus/pads.ini, which
|
||||
its own generator rewrites at every launch. The editor and launcher subtrees
|
||||
build separately from the root CMakeLists, which adds only cli/, tests/ and
|
||||
third_party/, and their assets are Qt resources compiled into those binaries
|
||||
(editor/cmake/AddQtFiles.cmake:46-50, launcher/cmake/addTarget.cmake:30).
|
||||
|
||||
files:
|
||||
- name: gamecontrollerdb.txt
|
||||
system: solarus
|
||||
category: game_data
|
||||
required: false
|
||||
description: "SDL gamepad mappings applied on top of the built-in table"
|
||||
source_ref: "src/core/System.cpp:71-99, cmake/AddDefines.cmake:44-49, cmake/AddInstallTargets.cmake:23-28"
|
||||
note: >-
|
||||
Looked up next to the executable first, then under SOLARUS_DATADIR_PATH,
|
||||
share/solarus by default (cmake/AddInstallDirs.cmake:14). Installed from
|
||||
assets/gamecontrollerdb.txt when SOLARUS_GAMECONTROLLERDB_INSTALL is on,
|
||||
which is the default (cmake/AddOptions.cmake:51), and carried in the
|
||||
release archives (.gitlab-ci.yml:306). Both probes are guarded by an
|
||||
existence test, and neither hitting leaves SDL on its built-in mappings.
|
||||
@@ -0,0 +1,116 @@
|
||||
emulator: SUPER ZSNES
|
||||
type: standalone
|
||||
core_classification: other
|
||||
source: "https://www.zsnes.com"
|
||||
upstream: "https://www.zsnes.com"
|
||||
profiled_date: "2026-08-08"
|
||||
core_version: "v0.230b"
|
||||
display_name: "Nintendo - SNES / SFC (SUPER ZSNES)"
|
||||
cores:
|
||||
- superzsnes
|
||||
- SUPERZSNES
|
||||
systems:
|
||||
- nintendo-snes
|
||||
mode: standalone
|
||||
|
||||
notes: |
|
||||
Super Famicom emulator by zsKnight and _Demo_, written from scratch with a
|
||||
GPU-rendered PPU and unrelated to the ZSNES codebase. Shipped as binaries
|
||||
only, for Windows, macOS, Linux and Android; the desktop builds are Unity
|
||||
Mono, so the emulator is Assembly-CSharp.dll and the per-game tables are
|
||||
serialized into the level0 scene. The Windows and Linux 0.230b builds carry
|
||||
the same tables and the same emulation sources.
|
||||
|
||||
One NEC uPD7725 interpreter serves every cartridge whose header declares a
|
||||
DSP. RomLoader.GetROMType reads the cartridge type byte at $FFD6 and returns
|
||||
COProcessorType.DSP when the low nibble is 3 to 6 and the high nibble is 0,
|
||||
or when the high nibble is F and the subtype byte at $FFBF is 1 or 2.
|
||||
MainMemoryMap.InitSpecialChips builds the DSP1 object for that type alone.
|
||||
The DSP1 constructor picks the filename from a table keyed on the CRC32 of
|
||||
the 21 raw header title bytes, taken as Latin-1 and re-encoded UTF-8,
|
||||
falling back to dsp1b.rom when no entry matches. Words are assembled little
|
||||
endian, 2048 24-bit program words from offset 0 then 1024 16-bit data words
|
||||
from offset 6144.
|
||||
|
||||
Three locations are tried in order: the folder holding the loaded ROM, the
|
||||
Unity persistent data folder (ZEMU Software Inc./SUPERZSNES), then the
|
||||
folder holding the executable. None of them goes through the srm and bps
|
||||
path overrides. When all three miss, a "Need DSP ROM" dialog names the file
|
||||
and the chip runs on a zero-filled 8192-byte buffer.
|
||||
|
||||
No other file is read. SA-1, SPC7110, S-RTC, Cx4, OBC-1, Super Game Boy and
|
||||
the ST01x and ST018 enum entries build no chip, the SPC700 IPL is a 64-byte
|
||||
array in CPUSPC700, and the enhancement data, the audio replacement data and
|
||||
testgame.smc are Unity resources inside the build. Save RAM, save states,
|
||||
cheats, bps patches and MSU-1 packs are all named after the loaded ROM.
|
||||
|
||||
files:
|
||||
# NEC uPD7725 microcode, program and data ROM in one file.
|
||||
# The name is chosen by CRC32 of the 21-byte header title; dsp1b.rom is the
|
||||
# default when no entry in the table matches.
|
||||
|
||||
- name: "dsp1b.rom"
|
||||
aliases: ["SNES_dsp1b.rom"]
|
||||
system: nintendo-snes
|
||||
description: "NEC uPD7725 DSP-1B microcode"
|
||||
required: true
|
||||
size: 8192
|
||||
crc32: 465c4e1c
|
||||
validation: [crc32]
|
||||
source_ref: "Assets/Scripts/SpecialChips/DSP1.cs:88-89 (name and expected CRC32 1180454428), DSP1.cs:151-155 (compare, warn on mismatch), Assets/Scripts/SpecialChips/DSP1Core.cs:756-769 (8192-byte layout)"
|
||||
note: "Default for every DSP cartridge with no table entry, so it covers DSP-1 and DSP-1A titles as well. A mismatched CRC32 warns and the data is used anyway."
|
||||
|
||||
- name: "dsp2.rom"
|
||||
aliases: ["SNES_dsp2.rom"]
|
||||
system: nintendo-snes
|
||||
description: "NEC uPD7725 DSP-2 microcode"
|
||||
required: true
|
||||
size: 8192
|
||||
source_ref: "Assets/Scripts/SpecialChips/DSP1.cs:98-114 (table lookup, no CRC32 check), SUPERZSNES_Data/level0 (VersionNo.dspDetections entry 1, title CRC32 0x84690f8b)"
|
||||
note: "Selected for header title 'DUNGEON MASTER' (Dungeon Master)."
|
||||
|
||||
- name: "dsp3.rom"
|
||||
aliases: ["SNES_dsp3.rom"]
|
||||
system: nintendo-snes
|
||||
description: "NEC uPD7725 DSP-3 microcode"
|
||||
required: true
|
||||
size: 8192
|
||||
source_ref: "Assets/Scripts/SpecialChips/DSP1.cs:98-114 (table lookup, no CRC32 check), SUPERZSNES_Data/level0 (VersionNo.dspDetections entry 2, title CRC32 0xfa7f8076)"
|
||||
note: "Selected for the half-width katakana header title of SD Gundam GX, 'SD' then $B6 $DE $DD $C0 $DE $D1 then 'GX'."
|
||||
|
||||
- name: "dsp4.rom"
|
||||
aliases: ["SNES_dsp4.rom"]
|
||||
system: nintendo-snes
|
||||
description: "NEC uPD7725 DSP-4 microcode"
|
||||
required: true
|
||||
size: 8192
|
||||
source_ref: "Assets/Scripts/SpecialChips/DSP1.cs:98-114 (table lookup, no CRC32 check), SUPERZSNES_Data/level0 (VersionNo.dspDetections entry 3, title CRC32 0x42578645)"
|
||||
note: "Selected for header title 'TOP GEAR 3000' (Top Gear 3000)."
|
||||
|
||||
# Seta parts. The table routes their cartridges to the same uPD7725
|
||||
# interpreter, which reads the first 8192 bytes and checks neither size nor
|
||||
# hash.
|
||||
|
||||
- name: "st010.rom"
|
||||
aliases: ["SNES_st010.rom"]
|
||||
system: nintendo-snes
|
||||
description: "Seta ST010 microcode"
|
||||
required: true
|
||||
source_ref: "Assets/Scripts/SpecialChips/DSP1.cs:98-114 (table lookup, no CRC32 check), SUPERZSNES_Data/level0 (VersionNo.dspDetections entry 4, title CRC32 0x04e7aca6)"
|
||||
note: "Selected for header title 'F1 ROC II' with $00 as the 21st byte (F1 ROC II: Race of Champions)."
|
||||
|
||||
- name: "st011.rom"
|
||||
aliases: ["SNES_st011.rom"]
|
||||
system: nintendo-snes
|
||||
description: "Seta ST011 microcode"
|
||||
required: true
|
||||
source_ref: "Assets/Scripts/SpecialChips/DSP1.cs:98-114 (table lookup, no CRC32 check), SUPERZSNES_Data/level0 (VersionNo.dspDetections entry 5, title CRC32 0xc96d71c1)"
|
||||
note: "Selected for header title '2DAN MORITA SHOUGI' with $00 as the 21st byte (Hayazashi Nidan Morita Shougi)."
|
||||
|
||||
- name: "st018.rom"
|
||||
aliases: ["SNES_st018.rom"]
|
||||
system: nintendo-snes
|
||||
description: "Seta ST018 firmware"
|
||||
required: true
|
||||
source_ref: "Assets/Scripts/SpecialChips/DSP1.cs:98-114 (table lookup, no CRC32 check), SUPERZSNES_Data/level0 (VersionNo.dspDetections entry 6, title CRC32 0x85885c5e)"
|
||||
note: "Selected for header title 'NIDAN MORITASHOGI2' (Hayazashi Nidan Morita Shougi 2)."
|
||||
@@ -32,7 +32,7 @@ notes: |
|
||||
files:
|
||||
- name: "scph5500.bin"
|
||||
description: "SCPH-5500 (v3.0 09-09-96 J)"
|
||||
region: "NTSC-J"
|
||||
region: [japan]
|
||||
required: false
|
||||
md5: "8dd7d5296a650fac7319bce665a6a53c"
|
||||
size: 524288
|
||||
@@ -42,7 +42,7 @@ files:
|
||||
|
||||
- name: "scph5501.bin"
|
||||
description: "SCPH-5501, 5503, 7003 (v3.0 11-18-96 A)"
|
||||
region: "NTSC-U"
|
||||
region: [north-america]
|
||||
required: false
|
||||
md5: "490f666e1afb15b7362b406ed1cea246"
|
||||
size: 524288
|
||||
@@ -52,7 +52,7 @@ files:
|
||||
|
||||
- name: "scph5502.bin"
|
||||
description: "SCPH-5502, 5552 (v3.0 01-06-97 E)"
|
||||
region: "PAL"
|
||||
region: [europe]
|
||||
required: false
|
||||
md5: "32736f17079d0b2b7024407c39bd3050"
|
||||
size: 524288
|
||||
@@ -62,7 +62,7 @@ files:
|
||||
|
||||
- name: "psxonpsp660.bin"
|
||||
description: "PSP, SCPH-1000R (v4.5 05-25-00 J)"
|
||||
region: "Auto"
|
||||
region: [world]
|
||||
required: false
|
||||
md5: "c53ca5908936d412331790f4426c6c33"
|
||||
size: 524288
|
||||
@@ -72,7 +72,7 @@ files:
|
||||
|
||||
- name: "ps1_rom.bin"
|
||||
description: "PS3 embedded PS1 BIOS (v5.0 06-23-03 A)"
|
||||
region: "Auto"
|
||||
region: [world]
|
||||
required: false
|
||||
md5: "81bbe60ba7a3d1cea1d48c14cbcc647b"
|
||||
size: [524288, 4194304, 4089584]
|
||||
|
||||
@@ -0,0 +1,387 @@
|
||||
emulator: Triforce
|
||||
type: standalone
|
||||
core_classification: enhanced_fork
|
||||
source: "https://github.com/REG-Linux/dolphin-triforce"
|
||||
upstream: "https://github.com/dolphin-emu/dolphin"
|
||||
profiled_date: "2026-08-08"
|
||||
source_commit: "5c456e8ff0da0235be2cf1d55ba7bb2115b0cae2"
|
||||
core_version: "2407"
|
||||
display_name: "Namco / Sega / Nintendo - Triforce (Dolphin Triforce)"
|
||||
cores:
|
||||
- triforce
|
||||
- dolphin-triforce
|
||||
systems:
|
||||
- sega-triforce
|
||||
- nintendo-wii-arcade
|
||||
- nintendo-gamecube
|
||||
- nintendo-wii
|
||||
mode: standalone
|
||||
|
||||
notes: |
|
||||
crediar's Dolphin fork, which adds the Triforce arcade boards (AM-Baseboard on
|
||||
the serial interface, AM-Mediaboard on the disc interface) and the Wii Arcade
|
||||
boards on top of GameCube and Wii emulation. The tree self-reports Dolphin 2407
|
||||
(CMake/ScmRevGen.cmake:34-35) and builds DolphinQt. crediar publishes the source
|
||||
on crediar.dev; the commit profiled here is the REG-Linux mirror of it, whose
|
||||
Data/Sys tree matches the Hancock33 and ReaverTeknoGods mirrors byte for byte.
|
||||
ES-DE looks for dolphin-emu-triforce on Linux and Triforce.exe or
|
||||
Triforce/DolphinWX.exe on Windows, RetroBat installs it as dolphin-triforce.
|
||||
|
||||
segaboot.gcm is the Triforce firmware. DVDInterface::Init calls
|
||||
AMMediaboard::Init only when EXI serial port 1 holds the AM Mediaboard, so the
|
||||
read happens on every Triforce launch and never otherwise
|
||||
(Source/Core/Core/HW/DVD/DVDInterface.cpp:289-294). Init resolves the file under
|
||||
Sys/Triforce, with no User fallback, and copies min(file size, 2 MiB) into the
|
||||
firmware buffer without checking anything
|
||||
(Source/Core/Core/HW/DVD/AMMediaboard.cpp:215-233). A missing file raises a
|
||||
modal alert and returns before the buffer is filled, leaving it at 0xFF. The
|
||||
guest reads that buffer whenever the firmware is mapped
|
||||
(Source/Core/Core/HW/DVD/AMMediaboard.cpp:1083-1090), which is how the Service
|
||||
and Test menus are reached. The tree ships the copy documented below, SegaBoot
|
||||
v3.11 with a RELSAB header, and the mediaboard answers GetSegaBootVersion for
|
||||
both 3.09 and 3.11 layouts
|
||||
(Source/Core/Core/HW/DVD/AMMediaboard.cpp:1268-1269, 1397-1398).
|
||||
|
||||
Triforce mode also swaps the GameCube boot ROM name: GetBootROMPath returns
|
||||
IPL_TRI.bin instead of IPL.bin, User first then Sys
|
||||
(Source/Core/Core/Config/MainSettings.cpp:614-625). Load_BS2 reads at most 2 MiB,
|
||||
CRC32s what it read against a table of Redump dumps and accepts 0xD1883221 as
|
||||
the Triforce revision (Source/Core/Core/Boot/Boot.cpp:391-436). On that match it
|
||||
patches OSReport and maps SegaBoot as firmware so the guest IPL can chain into
|
||||
it (Source/Core/Core/Boot/Boot.cpp:482-489). SkipIPL defaults to true
|
||||
(Source/Core/Core/Config/MainSettings.cpp:37) and EmulatedBS2 otherwise copies
|
||||
the disc straight into DIMM memory and clears the firmware mapping
|
||||
(Source/Core/Core/Boot/Boot_BS2Emu.cpp:332-347), so games run without any IPL.
|
||||
|
||||
Boot ROM directories follow the legacy naming: GetDirectoryForRegion defaults to
|
||||
RegionDirectoryStyle::Legacy (Source/Core/Core/Config/MainSettings.h:369-370) and
|
||||
every boot ROM caller takes the default, so NTSC-J resolves to JAP. FindIPLDump
|
||||
probes USA, EUR and JAP and stops there
|
||||
(Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp:180-189).
|
||||
|
||||
Wii Arcade runs as IOSC console type RVA, reached three ways: the RVA expansion
|
||||
device in slot A (Source/Core/Core/IOS/IOSC.cpp:213-218), an RVA.txt file in the
|
||||
System Menu title data (Source/Core/Core/Boot/Boot_BS2Emu.cpp:387-397), or a
|
||||
ticket issued by Root-CA10000000-XS10000000
|
||||
(Source/Core/Core/IOS/ES/Formats.cpp:490-493). That console type switches to the
|
||||
development common key and root key held in the binary
|
||||
(Source/Core/Core/IOS/IOSC.cpp:603-615).
|
||||
|
||||
Save data the emulator creates on first run is keyed on the disc, so none of it
|
||||
can be supplied: tridimm_<game-id>.bin and backup_<game-id>.bin
|
||||
(Source/Core/Core/HW/DVD/AMMediaboard.cpp:201-202), the baseboard backup
|
||||
tribackup_<game-id>.bin
|
||||
(Source/Core/Core/HW/EXI/EXI_DeviceAMBaseboard.cpp:77-100), the card reader image
|
||||
tricard_<game-id>.bin
|
||||
(Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp:1147-1310), and the three Wii
|
||||
Arcade SRAM images under User/Arcade, sized 0x1400, 0x1A40 and 0x8000 by game
|
||||
type (Source/Core/Core/HW/EXI/EXI_DeviceRVA.cpp:1249-1285).
|
||||
|
||||
Recognised Triforce titles are F-Zero AX, F-Zero AX Monster Ride, Mario Kart
|
||||
Arcade GP 1 and 2, Virtua Striker 3 and 4, Gekitou Pro Yakyuu and Key of Avalon
|
||||
(Source/Core/Core/HW/DVD/AMMediaboard.h:25-36).
|
||||
|
||||
# Two roots. Paths starting with Sys/ are relative to the installation data
|
||||
# directory returned by File::GetSysDirectory (Source/Core/Common/FileUtil.cpp:800),
|
||||
# which is where the code looks and nowhere else. Every other path is relative to
|
||||
# the user directory (GC/, GBA/, Wii/, Triforce/, Arcade/, Load/), which the code
|
||||
# tries first, falling back to the same relative path under Sys/ for the GameCube
|
||||
# boot ROMs and DSP ROMs.
|
||||
|
||||
files:
|
||||
# -- Triforce firmware --
|
||||
- name: "segaboot.gcm"
|
||||
path: "Sys/Triforce/segaboot.gcm"
|
||||
description: "SegaBoot, the Triforce system disc image held on the AM-Mediaboard"
|
||||
system: sega-triforce
|
||||
size: 2097152
|
||||
sha1: "87918619c632ac9de270d4fdf396a57eb57fc620"
|
||||
required: true
|
||||
source_ref: "Source/Core/Core/HW/DVD/AMMediaboard.cpp:215-233, Source/Core/Core/HW/DVD/DVDInterface.cpp:289-294"
|
||||
note: >
|
||||
Read on every Triforce launch, Sys/Triforce only with no User fallback.
|
||||
Nothing is validated, min(file size, 2 MiB) is copied into the firmware
|
||||
buffer. The hash is the copy the fork's own Data/Sys tree carries, SegaBoot
|
||||
v3.11.
|
||||
|
||||
# -- Triforce GameCube boot ROM --
|
||||
# Region-specific directory, User/GC/<region>/ then Sys/GC/<region>/.
|
||||
# One dump (Redump "Triforce (ARCADE BOOTROM)", DEV revision 1.0) serves every
|
||||
# directory, the region only decides where the code looks.
|
||||
- name: "IPL_TRI.bin"
|
||||
path: "GC/USA/IPL_TRI.bin"
|
||||
description: "Triforce boot ROM, NTSC-U directory"
|
||||
system: sega-triforce
|
||||
region: [north-america]
|
||||
size: 2097152
|
||||
crc32: "d1883221"
|
||||
validation: [crc32]
|
||||
required: false
|
||||
hle_fallback: true
|
||||
source_ref: "Source/Core/Common/CommonPaths.h:149, Source/Core/Core/Config/MainSettings.cpp:614-625, Source/Core/Core/Boot/Boot.cpp:400,415-436"
|
||||
note: >
|
||||
CRC32 is computed over the first 2 MiB and compared against the Redump
|
||||
table, 0xD1883221 being the Triforce revision. An unknown value warns and
|
||||
loading continues. A match also maps SegaBoot as firmware
|
||||
(Source/Core/Core/Boot/Boot.cpp:482-489).
|
||||
|
||||
- name: "IPL_TRI.bin"
|
||||
path: "GC/EUR/IPL_TRI.bin"
|
||||
description: "Triforce boot ROM, PAL directory"
|
||||
system: sega-triforce
|
||||
region: [europe]
|
||||
size: 2097152
|
||||
crc32: "d1883221"
|
||||
validation: [crc32]
|
||||
required: false
|
||||
hle_fallback: true
|
||||
source_ref: "Source/Core/Core/Config/MainSettings.cpp:620-623"
|
||||
|
||||
- name: "IPL_TRI.bin"
|
||||
path: "GC/JAP/IPL_TRI.bin"
|
||||
description: "Triforce boot ROM, NTSC-J directory"
|
||||
system: sega-triforce
|
||||
region: [japan]
|
||||
size: 2097152
|
||||
crc32: "d1883221"
|
||||
validation: [crc32]
|
||||
required: false
|
||||
hle_fallback: true
|
||||
source_ref: "Source/Core/Core/Config/MainSettings.cpp:587-612,620-623"
|
||||
note: "JAP is the directory the legacy naming style returns for NTSC-J."
|
||||
|
||||
# -- AM-Mediaboard system state --
|
||||
- name: "trinetcfg.bin"
|
||||
path: "Triforce/trinetcfg.bin"
|
||||
description: "Mediaboard network configuration"
|
||||
system: sega-triforce
|
||||
required: false
|
||||
unsourceable: "created empty by the emulator on first Triforce launch, written by the guest, no hardware dump equivalent"
|
||||
source_ref: "Source/Core/Core/HW/DVD/AMMediaboard.cpp:169-177,198"
|
||||
|
||||
- name: "trinetctrl.bin"
|
||||
path: "Triforce/trinetctrl.bin"
|
||||
description: "Mediaboard network control block"
|
||||
system: sega-triforce
|
||||
required: false
|
||||
unsourceable: "created empty by the emulator on first Triforce launch, written by the guest, no hardware dump equivalent"
|
||||
source_ref: "Source/Core/Core/HW/DVD/AMMediaboard.cpp:169-177,199"
|
||||
|
||||
- name: "triextra.bin"
|
||||
path: "Triforce/triextra.bin"
|
||||
description: "Mediaboard extra settings block"
|
||||
system: sega-triforce
|
||||
required: false
|
||||
unsourceable: "created empty by the emulator on first Triforce launch, written by the guest, no hardware dump equivalent"
|
||||
source_ref: "Source/Core/Core/HW/DVD/AMMediaboard.cpp:169-177,200"
|
||||
|
||||
# -- Wii Arcade --
|
||||
- name: "RVA.txt"
|
||||
path: "Wii/title/00000001/00000002/data/RVA.txt"
|
||||
description: "Marker that switches the emulated console to Wii Arcade mode"
|
||||
system: nintendo-wii-arcade
|
||||
required: false
|
||||
unsourceable: "user-created marker inside the emulated NAND, only its presence is tested, no file is distributed"
|
||||
source_ref: "Source/Core/Common/CommonPaths.h:165, Source/Core/Core/Boot/Boot_BS2Emu.cpp:387-397"
|
||||
note: >
|
||||
Opened for read inside the System Menu title data. Success sets console type
|
||||
RVA and forces the RGB region settings. The RVA expansion device in slot A
|
||||
reaches the same console type without it
|
||||
(Source/Core/Core/IOS/IOSC.cpp:213-218).
|
||||
|
||||
# -- GameCube boot ROM --
|
||||
# User/GC/<region>/ then Sys/GC/<region>/. Also the preferred source for the
|
||||
# GameCube fonts when present.
|
||||
- name: "IPL.bin"
|
||||
path: "GC/USA/IPL.bin"
|
||||
description: "GameCube NTSC-U boot ROM"
|
||||
region: [north-america]
|
||||
size: 2097152
|
||||
required: false
|
||||
hle_fallback: true
|
||||
source_ref: "Source/Core/Common/CommonPaths.h:148, Source/Core/Core/Config/MainSettings.cpp:627-630, Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp:109-118,182-183"
|
||||
note: "Needed to boot the GameCube menu and to take the fonts from a real dump."
|
||||
|
||||
- name: "IPL.bin"
|
||||
path: "GC/EUR/IPL.bin"
|
||||
description: "GameCube PAL boot ROM"
|
||||
region: [europe]
|
||||
size: 2097152
|
||||
required: false
|
||||
hle_fallback: true
|
||||
source_ref: "Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp:184-185"
|
||||
|
||||
- name: "IPL.bin"
|
||||
path: "GC/JAP/IPL.bin"
|
||||
description: "GameCube NTSC-J boot ROM"
|
||||
region: [japan]
|
||||
size: 2097152
|
||||
required: false
|
||||
hle_fallback: true
|
||||
source_ref: "Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp:186-187"
|
||||
|
||||
# -- DSP ROMs --
|
||||
# User/GC/ then Sys/GC/. Used by DSP LLE, and dsp_coef.bin also by the HLE
|
||||
# polyphase resampler.
|
||||
- name: "dsp_rom.bin"
|
||||
path: "GC/dsp_rom.bin"
|
||||
description: "DSP instruction ROM"
|
||||
size: 8192
|
||||
validation: [size]
|
||||
known_hash_adler32: "0x66f334fe"
|
||||
adler32_byteswap: true
|
||||
required: false
|
||||
hle_fallback: true
|
||||
source_ref: "Source/Core/Common/CommonPaths.h:145, Source/Core/Core/HW/DSPLLE/DSPLLE.cpp:87-105,109-118, Source/Core/Core/DSP/DSPCore.h:44, Source/Core/Core/DSP/DSPCore.cpp:36-38"
|
||||
note: >
|
||||
LoadDSPRom rejects any file whose length differs from DSP_IROM_BYTE_SIZE and
|
||||
swaps every 16 bit word before storing it, so the Adler32 in the source is
|
||||
taken over the swapped data (Source/Core/Core/HW/DSPLLE/DSPLLE.cpp:100-102).
|
||||
The pair is matched against seven known sets, the first being the Nintendo
|
||||
ROMs; a miss asks the user whether to stop
|
||||
(Source/Core/Core/DSP/DSPCore.cpp:60-84).
|
||||
|
||||
- name: "dsp_coef.bin"
|
||||
path: "GC/dsp_coef.bin"
|
||||
description: "DSP coefficient ROM"
|
||||
size: 4096
|
||||
validation: [size]
|
||||
known_hash_adler32: "0xf3b93527"
|
||||
adler32_byteswap: true
|
||||
required: false
|
||||
hle_fallback: true
|
||||
source_ref: "Source/Core/Common/CommonPaths.h:146, Source/Core/Core/HW/DSPLLE/DSPLLE.cpp:87-105,110-119, Source/Core/Core/DSP/DSPCore.h:58, Source/Core/Core/HW/DSPHLE/UCodes/AX.cpp:61-62"
|
||||
note: >
|
||||
Also read by the HLE AX ucode for polyphase resampling. Adler32 is taken over
|
||||
the byte-swapped data (Source/Core/Core/HW/DSPLLE/DSPLLE.cpp:100-102).
|
||||
|
||||
# -- GameCube fonts --
|
||||
# Sys/GC/ only. Skipped when an IPL dump is available, the fonts are then read
|
||||
# out of the dump at 0x1aff00 and 0x1fcf00 instead.
|
||||
- name: "font_western.bin"
|
||||
path: "Sys/GC/font_western.bin"
|
||||
description: "Windows-1252 font used by GameCube and Wii text rendering"
|
||||
size: 9589
|
||||
required: false
|
||||
hle_fallback: true
|
||||
source_ref: "Source/Core/Common/CommonPaths.h:142, Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp:131,203-247"
|
||||
note: >
|
||||
Size is the official font length the code reads from an IPL dump, 0x2575
|
||||
(Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp:234-236). A standalone file is
|
||||
read without any check.
|
||||
|
||||
- name: "font_japanese.bin"
|
||||
path: "Sys/GC/font_japanese.bin"
|
||||
description: "Shift-JIS font used by GameCube and Wii text rendering"
|
||||
size: 303693
|
||||
required: false
|
||||
hle_fallback: true
|
||||
source_ref: "Source/Core/Common/CommonPaths.h:143, Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp:130,203-247"
|
||||
note: "Size is the official font length read from an IPL dump, 0x4a24d."
|
||||
|
||||
# -- GBA BIOS --
|
||||
- name: "gba_bios.bin"
|
||||
path: "GBA/gba_bios.bin"
|
||||
description: "GBA BIOS for the GameCube to Game Boy Advance link"
|
||||
required: false
|
||||
hle_fallback: true
|
||||
source_ref: "Source/Core/Common/CommonPaths.h:155, Source/Core/Common/FileUtil.cpp:917-919, Source/Core/Core/HW/GBACore.cpp:202-206,336-353"
|
||||
note: >
|
||||
Loaded into the integrated mGBA core, which fails to start without it. The
|
||||
path is overridable by the Main.GBA BIOS setting
|
||||
(Source/Core/Core/Config/MainSettings.cpp:361).
|
||||
|
||||
# -- Wii system files --
|
||||
- name: "SYSCONF"
|
||||
path: "Wii/shared2/sys/SYSCONF"
|
||||
description: "Wii system configuration"
|
||||
size: 16384
|
||||
validation: [size]
|
||||
required: false
|
||||
hle_fallback: true
|
||||
source_ref: "Source/Core/Common/CommonPaths.h:87,127, Source/Core/Core/SysConf.cpp:19,54-65"
|
||||
note: >
|
||||
SysConf::Load rejects any size other than SYSCONF_SIZE and falls back to
|
||||
built-in defaults. Generated by the emulator, can come from a NAND backup
|
||||
instead.
|
||||
|
||||
- name: "setting.txt"
|
||||
path: "Wii/title/00000001/00000002/data/setting.txt"
|
||||
description: "Wii region, area and serial settings"
|
||||
size: 256
|
||||
required: false
|
||||
hle_fallback: true
|
||||
source_ref: "Source/Core/Common/CommonPaths.h:163, Source/Core/Core/Boot/Boot_BS2Emu.cpp:412-424, Source/Core/Common/SettingsHandler.h:21,26"
|
||||
note: >
|
||||
Read into a fixed 256 byte buffer, then rewritten during Wii boot emulation
|
||||
from the region settings table.
|
||||
|
||||
- name: "nand.bin"
|
||||
path: null
|
||||
description: "BootMii NAND backup, imported through the NAND importer"
|
||||
required: false
|
||||
hle_fallback: true
|
||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:26-40,42-57"
|
||||
note: >
|
||||
Accepted at 0x21000000 bytes, or 0x21000400 when the OTP and SEEPROM dump is
|
||||
appended. Any other size is rejected.
|
||||
|
||||
- name: "keys.bin"
|
||||
path: "Wii/keys.bin"
|
||||
description: "OTP and SEEPROM dump holding the console keys"
|
||||
size: 1024
|
||||
required: false
|
||||
hle_fallback: true
|
||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:19,76-88,288-298, Source/Core/Core/IOS/IOSC.cpp:639-665"
|
||||
note: >
|
||||
Asked for at import time when not appended to the NAND image, and written to
|
||||
the Wii root. Read at every boot, absence falls back to the built-in default
|
||||
key set.
|
||||
|
||||
- name: "clientca.pem"
|
||||
path: "Wii/clientca.pem"
|
||||
description: "SSL client certificate extracted from IOS13"
|
||||
required: false
|
||||
hle_fallback: true
|
||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:201-286"
|
||||
note: "Located inside the IOS13 content during NAND import by its leading bytes."
|
||||
|
||||
- name: "clientcakey.pem"
|
||||
path: "Wii/clientcakey.pem"
|
||||
description: "SSL client private key extracted from IOS13"
|
||||
required: false
|
||||
hle_fallback: true
|
||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:237"
|
||||
|
||||
- name: "rootca.pem"
|
||||
path: "Wii/rootca.pem"
|
||||
description: "SSL root certificate extracted from IOS13"
|
||||
required: false
|
||||
hle_fallback: true
|
||||
source_ref: "Source/Core/DiscIO/NANDImporter.cpp:238"
|
||||
|
||||
- name: "WiiSD.raw"
|
||||
path: "Load/WiiSD.raw"
|
||||
description: "Virtual SD card image for the Wii SD slot"
|
||||
required: false
|
||||
hle_fallback: true
|
||||
source_ref: "Source/Core/Common/CommonPaths.h:160, Source/Core/Common/FileUtil.cpp:909"
|
||||
note: "Created by the emulator, path overridable by the General WiiSDCardPath setting."
|
||||
|
||||
- name: "Wii System Menu"
|
||||
path: null
|
||||
description: "Wii System Menu WAD, installed into the emulated NAND"
|
||||
required: false
|
||||
hle_fallback: true
|
||||
source_ref: "Source/Core/DolphinQt/MenuBar.cpp:290,1098-1112"
|
||||
note: "Needed to boot the Wii Menu. Installed through Tools, Install WAD."
|
||||
|
||||
- name: "codehandler.bin"
|
||||
path: "Sys/codehandler.bin"
|
||||
description: "Gecko code handler used to apply cheat codes"
|
||||
required: false
|
||||
hle_fallback: true
|
||||
source_ref: "Source/Core/Common/CommonPaths.h:167, Source/Core/Core/GeckoCode.cpp:121-134"
|
||||
note: >
|
||||
Read from the Sys root. Cheats are refused when it is missing and when it
|
||||
exceeds the installer window.
|
||||
@@ -0,0 +1,116 @@
|
||||
emulator: veesem
|
||||
type: standalone
|
||||
core_classification: other
|
||||
source: "https://github.com/sp1187/veesem"
|
||||
upstream: "https://github.com/sp1187/veesem"
|
||||
profiled_date: "2026-08-08"
|
||||
source_commit: "42fb8f07e7418f16ccfb7364127aef2217dda641"
|
||||
core_version: "0.2"
|
||||
display_name: "VTech - V.Smile (veesem)"
|
||||
cores:
|
||||
- veesem
|
||||
systems:
|
||||
- vtech-vsmile
|
||||
mode: standalone
|
||||
|
||||
notes: |
|
||||
V.Smile emulator written from scratch on top of a SunPlus SPG200 (unSP)
|
||||
implementation, distributed as source only. No distribution packages it;
|
||||
ES-DE offers it for its vsmile system beside MAME and the MAME libretro
|
||||
core, and finds it by the binary name or the com.github.sp1187.veesem
|
||||
flatpak.
|
||||
|
||||
The system ROM has no fixed name and no search path. -sysrom takes a path
|
||||
(main.cc:64-70) which is opened directly (ui.cc:191), and the open is the
|
||||
only check the tree performs: the read asks for sizeof *sysrom, the
|
||||
1024*1024-word buffer of vsmile.h:17, so a shorter file keeps the zeros the
|
||||
buffer was value-initialised with and anything past 2 MiB is never reached.
|
||||
No size test, no hash, no signature anywhere. Every word is passed through
|
||||
SDL_SwapLE16 after the read (ui.cc:196-197), so the image is consumed as
|
||||
little-endian 16-bit words.
|
||||
|
||||
Without -sysrom the buffer is synthesised instead: zero-filled, with 0x31
|
||||
written into the odd word of each pair over 0xFFFC0-0xFFFDB, called a
|
||||
"game-compatible dummy system ROM" in the code (ui.cc:198-204) and a "dummy
|
||||
ROM without boot animation" in the README.
|
||||
|
||||
Nothing boots from the system ROM. Extmem::Reset leaves address_decode at 0
|
||||
(extmem.cc:7-10), routing every external address to ROMCSB, and bank 0 stays
|
||||
the cartridge under all three decode modes (extmem.cc:20-52). The CPU takes
|
||||
its reset vector from 0xfff7 (cpu.cc:91), FIQ from 0xfff6, IRQs from
|
||||
0xfff8+n and BREAK from 0xfff5 (cpu.cc:116,125,228), all inside bank 0, so
|
||||
the vectors always come from the cartridge. The system ROM sits on CSB3
|
||||
(vsmile.cc:198-200), reachable at 0x300000-0x3FFFFF only once cartridge code
|
||||
selects decode mode 2 or 3.
|
||||
|
||||
Region is a jumper, not a file. The low nibble of Port C carries the region
|
||||
code and bit 4 the VTech logo jumper (vsmile.cc:148-152), defaulting to 0xe
|
||||
with the logo on (main.cc:44-45) and settable with -region and -novtech. One
|
||||
system ROM image serves every region; the table at vsmile.cc:3-25 maps the
|
||||
sixteen jumper values to the intro each revision produces, and names
|
||||
revisions 1.00, 1.02 and 1.03.
|
||||
|
||||
files:
|
||||
- name: vsmile_v103.bin
|
||||
description: "V.Smile system ROM, revision 1.03"
|
||||
required: false
|
||||
hle_fallback: true
|
||||
size: 2097152
|
||||
source_ref: "src/main.cc:64-70 (-sysrom), src/ui/ui.cc:189-197 (load), src/core/vsmile/vsmile.h:17 (buffer), src/core/vsmile/vsmile.cc:198-200 (CSB3), src/core/vsmile/vsmile.cc:5,13 (jumper values for 1.03)"
|
||||
note: >
|
||||
Jumper 0x2 gives Italian and 0xa UK English without subtitle on this
|
||||
revision, the two values 1.02 assigns the other way round.
|
||||
|
||||
- name: vsmile_v102.bin
|
||||
description: "V.Smile system ROM, revision 1.02"
|
||||
required: false
|
||||
hle_fallback: true
|
||||
size: 2097152
|
||||
source_ref: "src/main.cc:64-70 (-sysrom), src/ui/ui.cc:189-197 (load), src/core/vsmile/vsmile.h:17 (buffer), src/core/vsmile/vsmile.cc:198-200 (CSB3), src/core/vsmile/vsmile.cc:4-9,13,17-18 (jumper values for 1.02+)"
|
||||
note: >
|
||||
The revision the "1.02+" entries describe: 0x0 and 0x1 suppress the
|
||||
V.Smile screen, 0x3 to 0x5 select US English variants and 0xe and 0xf
|
||||
carry the "Learning System" subtitle.
|
||||
|
||||
- name: vsmile_v100.bin
|
||||
description: "V.Smile system ROM, revision 1.00"
|
||||
required: false
|
||||
hle_fallback: true
|
||||
size: 2097152
|
||||
source_ref: "src/main.cc:64-70 (-sysrom), src/ui/ui.cc:189-197 (load), src/core/vsmile/vsmile.h:17 (buffer), src/core/vsmile/vsmile.cc:198-200 (CSB3), src/core/vsmile/vsmile.cc:13,20-24 (jumper values and behaviour for 1.00)"
|
||||
note: >
|
||||
Its standard English subtitle is "TV Learning System", and with no
|
||||
cartridge and a region code below 0x6 it shows a blinking blue VTech
|
||||
logo in white.
|
||||
|
||||
analysis:
|
||||
user_supplied_paths:
|
||||
note: "Named on the command line, with no fixed filename"
|
||||
entries:
|
||||
- what: "cartridge ROM, the positional argument"
|
||||
source_ref: "src/main.cc:119-126, src/ui/ui.cc:180-187"
|
||||
note: >
|
||||
Read into a 4M-word buffer and mapped over ROMCSB, CSB1 and CSB2
|
||||
(vsmile.cc:168-190). The dual-ROM cartridges pair a 4 MiB ROM on the
|
||||
first two banks with a 2 MiB ROM on CSB2, which the code reads from
|
||||
the same dump at word 0x200000.
|
||||
- what: "system ROM, -sysrom"
|
||||
source_ref: "src/main.cc:64-70, src/ui/ui.cc:189-197"
|
||||
|
||||
generated_files:
|
||||
- what: "Art Studio CSB2 NVRAM, -art-nvram FILE"
|
||||
source_ref: "src/main.cc:71-77 (flag), src/ui/ui.cc:206-221 (read), src/ui/ui.cc:153-170 (write)"
|
||||
note: >
|
||||
Save data rather than a dump to obtain. The 128K-word buffer is
|
||||
allocated before the stream is tested (ui.cc:210-211), so an absent or
|
||||
unreadable file leaves a blank NVRAM, and -art on its own selects the
|
||||
same cartridge type with no file at all (ui.cc:218-220). It backs the
|
||||
drawing area of V.Smile Art Studio (vsmile.cc:180-196) and is flushed
|
||||
to the same path when the machine is unloaded.
|
||||
|
||||
settings_storage:
|
||||
source_ref: "src/ui/ui.cc:126"
|
||||
note: >
|
||||
io.IniFilename is nullptr, so Dear ImGui reads and writes no imgui.ini.
|
||||
Every setting comes from the command line or the UI, and no other path
|
||||
in the tree is opened.
|
||||
@@ -0,0 +1,211 @@
|
||||
emulator: visual-pinball
|
||||
type: standalone
|
||||
core_classification: game_engine
|
||||
source: "https://github.com/vpinball/vpinball"
|
||||
upstream: "https://github.com/vpinball/vpinball"
|
||||
profiled_date: "2026-08-08"
|
||||
source_commit: "afc7c38ffb1078076ec523c37b30055061c93ff1"
|
||||
core_version: "10.8.0"
|
||||
display_name: "Visual Pinball X"
|
||||
cores:
|
||||
- vpinball
|
||||
- visual-pinball
|
||||
systems:
|
||||
- vpinball
|
||||
mode: standalone
|
||||
|
||||
notes: |
|
||||
Pinball table simulator. The table is the content: .vpx and .vpt files are
|
||||
OLE Compound Documents holding the geometry, textures, sounds and a VBScript
|
||||
program, read through POLE on non-Windows platforms. The standalone branch
|
||||
ports the Windows original by embedding Wine's VBScript engine, stubbing COM
|
||||
and ATL from Wine and ReactOS sources, and linking libpinmame, libaltsound,
|
||||
libdmdutil, libdof and BASS in place of the Windows COM servers
|
||||
(standalone/README.md:11-120). ES-DE finds VPinballX_BGFX or VPinballX_GL and
|
||||
runs `<binary> -play <rom>`, Batocera builds the standalone tree at
|
||||
3ec37c7f9a into /usr/bin/vpinball and runs `VPinballX_GL -PrefPath <dir> -Ini
|
||||
<dir>/VPinballX.ini -Play <rom>`, RetroBat drives the Windows build with its
|
||||
registered VPinMAME COM server.
|
||||
|
||||
Two path roots decide where everything is read from. m_szMyPath is the
|
||||
install directory, m_szMyPrefPath is ~/.vpinball or whatever -PrefPath names,
|
||||
and loading a table sets the working directory to the table's own folder
|
||||
(main.cpp:408, src/parts/pintable.cpp:2497,11165). Batocera's 001-default-path.patch
|
||||
redirects the PinMAME root from $HOME/.pinmame to <prefpath>/pinmame.
|
||||
|
||||
Table scripts pull their libraries through GetTextFile, which walks seven
|
||||
locations in order: the working directory, then user/, scripts/ and tables/
|
||||
under the install directory, then the same three under the preferences path
|
||||
(src/parts/pintable.cpp:470-480, main.h:118-119, main.cpp:890-892). The
|
||||
scripts/ directory the build ships carries the ~70 .vbs files tables include
|
||||
by name, core.vbs and controller.vbs beside the per-manufacturer machine
|
||||
scripts (WPC.vbs, sam.vbs, s11.vbs, zac.vbs and the rest), and the editor's
|
||||
completion parser reads core.vbs over the same search order
|
||||
(codeview.cpp:3233-3247, standalone/cmake/CMakeLists_gl-linux-x64.txt:698).
|
||||
The same four roots are handed to table scripts as the UserDirectory,
|
||||
TablesDirectory, MusicDirectory and ScriptsDirectory properties, each falling
|
||||
back from the install directory to the table folder to the preferences path
|
||||
(src/parts/pintable.cpp:484-560).
|
||||
|
||||
The rest of the install tree is read the same way and ships with the binary:
|
||||
assets/ holds BallEnv.exr, AODither.webp and EnvMap.webp (pin3d.cpp:475-477),
|
||||
the bumper and kicker textures (src/parts/bumper.cpp:246-290,
|
||||
src/parts/kicker.cpp:273-319), TableSizes.csv, which the live UI reads to
|
||||
guess the glass position of a table that does not declare one
|
||||
(src/core/TableDB.cpp:11-14, LiveUI.cpp:1705-1706), vpx.html for the built-in
|
||||
web server (standalone/inc/webserver/WebServer.cpp:32) and
|
||||
Default_gamecontrollerdb.txt; shader10.8.0/ holds the .glfx and .fxh sources
|
||||
compiled at runtime (src/renderer/Shader.cpp:1419); flexdmd/ holds the bitmap
|
||||
fonts UltraDMD asks for by resource name, the FlexDMD.Resources. prefix being
|
||||
stripped before the lookup (standalone/inc/flexdmd/UltraDMD.cpp:31-40,
|
||||
standalone/inc/flexdmd/AssetManager.cpp:84-90,232).
|
||||
|
||||
Two parts of that tree ship without a reader on this target. The four starter
|
||||
tables in assets/, blankTable.vpx, exampleTable.vpx, lightSeqTable.vpx and
|
||||
strippedTable.vpx, are loaded only by InitBuiltinTable, whose body is one
|
||||
`#ifndef __STANDALONE__` block (src/parts/pintable.cpp:1641-1663), and
|
||||
docs/Changelog.txt is read by dialogs/AboutDialog.cpp:27-31, which is not in
|
||||
the standalone source list at all.
|
||||
|
||||
PinMAME is where the emulation happens. Tables that recreate a real solid
|
||||
state machine hand a ROM name to the controller, which resolves its root as
|
||||
the pinmame folder beside the table, else the PinMAMEPath setting, else
|
||||
$HOME/.pinmame (standalone/inc/vpinmame/VPinMAMEController.cpp:165-180). libpinmame then hangs roms,
|
||||
nvram, samples, cfg, hi, inp, memcard and sta off that root
|
||||
(pinmame src/libpinmame/libpinmame.cpp:816-823). Every ROM set is a game, one
|
||||
zip per machine; PinMAME declares no BIOS root set anywhere in its tree, and
|
||||
carries neither GAME_IS_BIOS_ROOT nor ROM_SYSTEM_BIOS, so nothing in the ROM
|
||||
path is shared between machines.
|
||||
|
||||
Three shared data files sit outside that per-machine content and are listed
|
||||
below. Two more that the MAME lineage would suggest are unreachable here. The
|
||||
mechanical sample set named by pinmame_sample_names, "*pinmame" followed by
|
||||
eighteen .wav members (pinmame src/wpc/wpcsam.c:31-57), is dead twice over:
|
||||
readsamples returns before opening anything unless options.use_samples is set
|
||||
(pinmame src/common.c:271), the two files that register that option live in
|
||||
src/windows/config.c and src/unix/sound.c and neither is in the libpinmame
|
||||
source list, and PinmameSetConfig zeroes the whole options struct
|
||||
(pinmame src/libpinmame/libpinmame.cpp:807); independently, SAMPLESINTERFACE
|
||||
is defined at pinmame src/wpc/wpcsam.h:23 and no driver in the tree
|
||||
references it. history.dat and mameinfo.dat (pinmame src/datafile.c:619-623,
|
||||
649-653) are read only by displayhistory, a page of the MAME on-screen menu
|
||||
(pinmame src/usrintrf.c:2846-2851), and by the listing path in
|
||||
pinmame src/info.c:820 which libpinmame never calls; VPX passes NULL for
|
||||
fn_IsKeyPressed, the thirteenth member of PinmameConfig
|
||||
(standalone/inc/vpinmame/VPinMAMEController.cpp:145-160, pinmame src/libpinmame/libpinmame.h:418-433),
|
||||
so osd_is_key_pressed always answers 0
|
||||
(pinmame src/libpinmame/libpinmame.cpp:355-361) and the menu cannot be opened.
|
||||
|
||||
The pinmame observations were read at f102dc6cce7eb51e64427dd0b52bed37dbd2adea.
|
||||
|
||||
files:
|
||||
- name: gamecontrollerdb.txt
|
||||
system: vpinball
|
||||
category: game_data
|
||||
required: false
|
||||
description: "SDL game controller mappings applied at input init"
|
||||
source_ref: "pininput.cpp:1027-1043"
|
||||
note: >-
|
||||
Read from the preferences path. When it is absent the install copy at
|
||||
assets/Default_gamecontrollerdb.txt is copied there first, and a missing
|
||||
source only logs. SDL_GameControllerAddMappingsFromFile returns a count
|
||||
that is logged and never acted on, so neither an absent nor an unparsable
|
||||
file stops startup; SDL keeps its built-in mappings. Standalone reads
|
||||
controllers through the SDL game controller layer rather than the joystick
|
||||
layer unless GamepadOverride is set to 0, which is what makes this file
|
||||
the place a pinball cabinet's encoder gets described.
|
||||
|
||||
- name: hiscore.dat
|
||||
system: vpinball
|
||||
category: game_data
|
||||
required: false
|
||||
description: "MAME high score memory map database, read once per machine start"
|
||||
source_ref: "pinmame src/hiscore.c:18,223-230, pinmame src/cpuexec.c:332-334,393,1755"
|
||||
note: >-
|
||||
hs_open is called unconditionally at the top of cpu_run for every PinMAME
|
||||
machine, and the whole body sits behind `if (f)`, so an absent file is a
|
||||
no-op. It resolves to a bare filename against the working directory, which
|
||||
is the table's own folder: libpinmame sets paths for eight file types and
|
||||
FILETYPE_HIGHSCORE_DB is not among them, and the option table that would
|
||||
supply a default is linked only by the Windows COM build
|
||||
(pinmame src/libpinmame/libpinmame.cpp:816-823,
|
||||
pinmame src/libpinmame/fileio.c:140, pinmame src/win32com/VPinMAMEConfig.cpp:127).
|
||||
|
||||
- name: cheat.dat
|
||||
system: vpinball
|
||||
category: game_data
|
||||
required: false
|
||||
description: "MAME cheat database, read when the per-ROM ini enables cheats"
|
||||
source_ref: "pinmame src/cheat.c:8434,8602-8614, pinmame src/mame.c:578-583, standalone/inc/vpinmame/VPinMAMEController.cpp:295-321"
|
||||
note: >-
|
||||
Off by default: PinmameSetConfig zeroes options, and VPX only turns it on
|
||||
from the `cheat` key of <pinmame root>/ini/<romname>.ini, which it also
|
||||
rewrites on every run. With it on, InitCheat loads the database and an
|
||||
absent file leaves the menu showing "cheat database not found"
|
||||
(pinmame src/cheat.c:2463). Same bare-filename resolution as hiscore.dat.
|
||||
|
||||
analysis:
|
||||
shipped_with_the_binary:
|
||||
note: >-
|
||||
Copied into the build output next to the executable and installed as one
|
||||
unit by every distribution, so they arrive with the emulator rather than
|
||||
being sourced separately.
|
||||
source_ref: "standalone/cmake/CMakeLists_gl-linux-x64.txt:695-720"
|
||||
entries:
|
||||
- what: "scripts/, the .vbs library tables include"
|
||||
source_ref: "src/parts/pintable.cpp:470-480, main.h:118-119, main.cpp:890-892, codeview.cpp:3233-3247"
|
||||
- what: "assets/, textures, TableSizes.csv, vpx.html, the two defaults"
|
||||
source_ref: "pin3d.cpp:475-477, src/parts/bumper.cpp:246-290, src/parts/kicker.cpp:273-319, src/core/TableDB.cpp:11-14, LiveUI.cpp:1705-1706, standalone/inc/webserver/WebServer.cpp:32"
|
||||
- what: "shader10.8.0/, .glfx and .fxh compiled at runtime"
|
||||
source_ref: "src/renderer/Shader.cpp:1419"
|
||||
- what: "flexdmd/, the bitmap fonts UltraDMD names as FlexDMD.Resources.*"
|
||||
source_ref: "standalone/inc/flexdmd/UltraDMD.cpp:31-40, standalone/inc/flexdmd/AssetManager.cpp:84-90,232"
|
||||
- what: "user/ and tables/, the two roots exposed to table scripts as directory properties"
|
||||
source_ref: "src/parts/pintable.cpp:484-521"
|
||||
|
||||
shipped_without_a_reader:
|
||||
note: "Copied into the build output but unreachable in the standalone binary"
|
||||
entries:
|
||||
- what: "assets/blankTable.vpx, exampleTable.vpx, lightSeqTable.vpx, strippedTable.vpx"
|
||||
source_ref: "src/parts/pintable.cpp:1641-1663"
|
||||
note: "InitBuiltinTable is one #ifndef __STANDALONE__ block; the editor's New Table entries are the only callers."
|
||||
- what: "docs/Changelog.txt"
|
||||
source_ref: "dialogs/AboutDialog.cpp:27-31, standalone/cmake/CMakeLists_gl-linux-x64.txt:52-693"
|
||||
note: "The About dialog is not in the standalone source list."
|
||||
|
||||
user_supplied_paths:
|
||||
note: "Per table, keyed on the table folder or the ROM name, with no fixed filename"
|
||||
entries:
|
||||
- what: "PinMAME ROM sets, <pinmame root>/roms/<romname>.zip"
|
||||
source_ref: "standalone/inc/vpinmame/VPinMAMEController.cpp:165-180, pinmame src/libpinmame/libpinmame.cpp:816"
|
||||
note: >-
|
||||
One zip per machine. The root is the pinmame folder beside the table
|
||||
when present, else the PinMAMEPath setting, else $HOME/.pinmame;
|
||||
Batocera patches that last fallback to <prefpath>/pinmame.
|
||||
- what: "backglass, <table basename>.directb2s"
|
||||
source_ref: "standalone/inc/b2s/forms/FormBackglass.cpp:372-415"
|
||||
note: "Version-checked against the B2S server on load; absence only warns."
|
||||
- what: "Serum colorizations, <pinmame root>/AltColor/"
|
||||
source_ref: "standalone/inc/vpinmame/VPinMAMEController.cpp:55-59"
|
||||
note: "Handed to libdmdutil as a path when the AltColor setting is on and the directory exists."
|
||||
- what: "alternative sound packages, under the PinMAME root by ROM name"
|
||||
source_ref: "standalone/inc/vpinmame/VPinMAMEController.cpp:336-338"
|
||||
- what: "PUP video packs, <table folder>/pupvideos"
|
||||
source_ref: "standalone/inc/pup/PUPManager.cpp:12, standalone/inc/vpinmame/VPinMAMEController.cpp:61-65"
|
||||
- what: "table music, music/ beside the install, the table or the preferences path"
|
||||
source_ref: "src/audio/audioplayer.cpp:75-77, src/parts/pintable.cpp:524-545"
|
||||
- what: "table sidecars, the .vbs script override and the per-table .ini"
|
||||
source_ref: "src/parts/pintable.cpp:704,794"
|
||||
|
||||
generated_files:
|
||||
- what: "VPinballX.ini, the settings file"
|
||||
source_ref: "main.cpp:901-914"
|
||||
note: >-
|
||||
Created on first run under the preferences path, or read from beside the
|
||||
binary when one is there. Batocera seeds it from
|
||||
assets/Default_VPinballX.ini and rewrites it before every launch.
|
||||
- what: "VPReg.ini, per-table settings; vpinball.log"
|
||||
source_ref: "Logger.cpp:58, standalone/README.md:512-534"
|
||||
- what: "PinMAME state: nvram/, cfg/, hi/, inp/, memcard/, sta/ and ini/"
|
||||
source_ref: "pinmame src/libpinmame/libpinmame.cpp:817-823, standalone/inc/vpinmame/VPinMAMEController.cpp:186-198"
|
||||
note: "The ini directory is created when missing; the others are written by PinMAME as the machine runs."
|
||||
+12
-3
@@ -4,7 +4,7 @@
|
||||
"platform": "batocera",
|
||||
"display_name": "Batocera",
|
||||
"version": "1.0",
|
||||
"generated": "2026-08-08T09:27:23Z",
|
||||
"generated": "2026-08-08T15:53:07Z",
|
||||
"base_destination": "bios",
|
||||
"detect": [
|
||||
{
|
||||
@@ -236,8 +236,8 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"total_files": 1599,
|
||||
"total_size": 2866042839,
|
||||
"total_files": 1600,
|
||||
"total_size": 2868139991,
|
||||
"files": [
|
||||
{
|
||||
"dest": "panafz1.bin",
|
||||
@@ -12241,6 +12241,15 @@
|
||||
"Clock Signal (CLK)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "Triforce/segaboot.gcm",
|
||||
"sha1": "7297a11be143011ceb75a0720f96855003415a5d",
|
||||
"size": 2097152,
|
||||
"repo_path": "bios/Sega/Triforce/segaboot.gcm",
|
||||
"cores": [
|
||||
"Dolphin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "GBA/gba_bios.bin",
|
||||
"sha1": "300c20df6731a33952ded8c436f7f186d25d3492",
|
||||
|
||||
+12
-3
@@ -4,7 +4,7 @@
|
||||
"platform": "emudeck",
|
||||
"display_name": "EmuDeck",
|
||||
"version": "1.0",
|
||||
"generated": "2026-08-08T08:00:05Z",
|
||||
"generated": "2026-08-08T15:53:16Z",
|
||||
"base_destination": "bios",
|
||||
"detect": [
|
||||
{
|
||||
@@ -249,8 +249,8 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"total_files": 525,
|
||||
"total_size": 1770429115,
|
||||
"total_files": 526,
|
||||
"total_size": 1772526267,
|
||||
"files": [
|
||||
{
|
||||
"dest": "colecovision.rom",
|
||||
@@ -1066,6 +1066,15 @@
|
||||
"Dolphin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "Triforce/segaboot.gcm",
|
||||
"sha1": "7297a11be143011ceb75a0720f96855003415a5d",
|
||||
"size": 2097152,
|
||||
"repo_path": "bios/Sega/Triforce/segaboot.gcm",
|
||||
"cores": [
|
||||
"Dolphin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "GBA/gba_bios.bin",
|
||||
"sha1": "300c20df6731a33952ded8c436f7f186d25d3492",
|
||||
|
||||
+12
-3
@@ -4,7 +4,7 @@
|
||||
"platform": "lakka",
|
||||
"display_name": "Lakka",
|
||||
"version": "1.0",
|
||||
"generated": "2026-08-08T09:27:56Z",
|
||||
"generated": "2026-08-08T15:53:41Z",
|
||||
"base_destination": "system",
|
||||
"detect": [
|
||||
{
|
||||
@@ -15,8 +15,8 @@
|
||||
}
|
||||
],
|
||||
"standalone_copies": [],
|
||||
"total_files": 1684,
|
||||
"total_size": 4207511677,
|
||||
"total_files": 1685,
|
||||
"total_size": 4209608829,
|
||||
"files": [
|
||||
{
|
||||
"dest": "3do_arcade_saot.bin",
|
||||
@@ -4739,6 +4739,15 @@
|
||||
"DirectXBox"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "Triforce/segaboot.gcm",
|
||||
"sha1": "7297a11be143011ceb75a0720f96855003415a5d",
|
||||
"size": 2097152,
|
||||
"repo_path": "bios/Sega/Triforce/segaboot.gcm",
|
||||
"cores": [
|
||||
"Dolphin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "Wii/shared2/sys/SYSCONF",
|
||||
"sha1": "3256c026284a24fb99d2ec1558d95db3b5dcc2e9",
|
||||
|
||||
+12
-3
@@ -4,7 +4,7 @@
|
||||
"platform": "recalbox",
|
||||
"display_name": "Recalbox",
|
||||
"version": "1.0",
|
||||
"generated": "2026-08-08T11:34:11Z",
|
||||
"generated": "2026-08-08T15:53:48Z",
|
||||
"base_destination": "bios",
|
||||
"detect": [
|
||||
{
|
||||
@@ -236,8 +236,8 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"total_files": 1186,
|
||||
"total_size": 2495283065,
|
||||
"total_files": 1187,
|
||||
"total_size": 2497380217,
|
||||
"files": [
|
||||
{
|
||||
"dest": "3do/panafz1.bin",
|
||||
@@ -3718,6 +3718,15 @@
|
||||
"Dolphin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "Triforce/segaboot.gcm",
|
||||
"sha1": "7297a11be143011ceb75a0720f96855003415a5d",
|
||||
"size": 2097152,
|
||||
"repo_path": "bios/Sega/Triforce/segaboot.gcm",
|
||||
"cores": [
|
||||
"Dolphin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "Wii/shared2/sys/SYSCONF",
|
||||
"sha1": "3256c026284a24fb99d2ec1558d95db3b5dcc2e9",
|
||||
|
||||
+12
-3
@@ -4,7 +4,7 @@
|
||||
"platform": "retroarch",
|
||||
"display_name": "RetroArch",
|
||||
"version": "1.0",
|
||||
"generated": "2026-08-08T09:27:56Z",
|
||||
"generated": "2026-08-08T15:53:41Z",
|
||||
"base_destination": "system",
|
||||
"detect": [
|
||||
{
|
||||
@@ -254,8 +254,8 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"total_files": 1684,
|
||||
"total_size": 4207511677,
|
||||
"total_files": 1685,
|
||||
"total_size": 4209608829,
|
||||
"files": [
|
||||
{
|
||||
"dest": "3do_arcade_saot.bin",
|
||||
@@ -4978,6 +4978,15 @@
|
||||
"DirectXBox"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "Triforce/segaboot.gcm",
|
||||
"sha1": "7297a11be143011ceb75a0720f96855003415a5d",
|
||||
"size": 2097152,
|
||||
"repo_path": "bios/Sega/Triforce/segaboot.gcm",
|
||||
"cores": [
|
||||
"Dolphin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "Wii/shared2/sys/SYSCONF",
|
||||
"sha1": "3256c026284a24fb99d2ec1558d95db3b5dcc2e9",
|
||||
|
||||
+12
-3
@@ -4,7 +4,7 @@
|
||||
"platform": "retrobat",
|
||||
"display_name": "RetroBat",
|
||||
"version": "1.0",
|
||||
"generated": "2026-08-08T08:00:44Z",
|
||||
"generated": "2026-08-08T15:53:55Z",
|
||||
"base_destination": "bios",
|
||||
"detect": [
|
||||
{
|
||||
@@ -235,8 +235,8 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"total_files": 1232,
|
||||
"total_size": 3279906783,
|
||||
"total_files": 1233,
|
||||
"total_size": 3282003935,
|
||||
"files": [
|
||||
{
|
||||
"dest": "panafz1.bin",
|
||||
@@ -8985,6 +8985,15 @@
|
||||
"Dolphin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "Triforce/segaboot.gcm",
|
||||
"sha1": "7297a11be143011ceb75a0720f96855003415a5d",
|
||||
"size": 2097152,
|
||||
"repo_path": "bios/Sega/Triforce/segaboot.gcm",
|
||||
"cores": [
|
||||
"Dolphin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "GBA/gba_bios.bin",
|
||||
"sha1": "300c20df6731a33952ded8c436f7f186d25d3492",
|
||||
|
||||
+12
-3
@@ -4,7 +4,7 @@
|
||||
"platform": "rocknix",
|
||||
"display_name": "ROCKNIX",
|
||||
"version": "1.0",
|
||||
"generated": "2026-08-08T11:34:39Z",
|
||||
"generated": "2026-08-08T15:54:15Z",
|
||||
"base_destination": "bios",
|
||||
"detect": [
|
||||
{
|
||||
@@ -16,8 +16,8 @@
|
||||
}
|
||||
],
|
||||
"standalone_copies": [],
|
||||
"total_files": 1548,
|
||||
"total_size": 4070944579,
|
||||
"total_files": 1549,
|
||||
"total_size": 4073041731,
|
||||
"files": [
|
||||
{
|
||||
"dest": "xemu/bios/mcpx_1.0.bin",
|
||||
@@ -2134,6 +2134,15 @@
|
||||
"Dolphin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "Triforce/segaboot.gcm",
|
||||
"sha1": "7297a11be143011ceb75a0720f96855003415a5d",
|
||||
"size": 2097152,
|
||||
"repo_path": "bios/Sega/Triforce/segaboot.gcm",
|
||||
"cores": [
|
||||
"Dolphin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "Wii/shared2/sys/SYSCONF",
|
||||
"sha1": "3256c026284a24fb99d2ec1558d95db3b5dcc2e9",
|
||||
|
||||
+12
-3
@@ -4,7 +4,7 @@
|
||||
"platform": "romm",
|
||||
"display_name": "RomM",
|
||||
"version": "1.0",
|
||||
"generated": "2026-08-08T09:28:33Z",
|
||||
"generated": "2026-08-08T15:54:17Z",
|
||||
"base_destination": "bios",
|
||||
"detect": [
|
||||
{
|
||||
@@ -14,8 +14,8 @@
|
||||
}
|
||||
],
|
||||
"standalone_copies": [],
|
||||
"total_files": 544,
|
||||
"total_size": 1488406530,
|
||||
"total_files": 545,
|
||||
"total_size": 1490503682,
|
||||
"files": [
|
||||
{
|
||||
"dest": "3do/3do_arcade_saot.bin",
|
||||
@@ -2725,6 +2725,15 @@
|
||||
"Dolphin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "ngc/Triforce/segaboot.gcm",
|
||||
"sha1": "7297a11be143011ceb75a0720f96855003415a5d",
|
||||
"size": 2097152,
|
||||
"repo_path": "bios/Sega/Triforce/segaboot.gcm",
|
||||
"cores": [
|
||||
"Dolphin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "ngc/Wii/shared2/sys/SYSCONF",
|
||||
"sha1": "3256c026284a24fb99d2ec1558d95db3b5dcc2e9",
|
||||
|
||||
+16
-4
@@ -250,7 +250,7 @@ nav:
|
||||
- vitaQuakeII: emulators/vitaquake2.md
|
||||
- yabasanshiro: emulators/yabasanshiro.md
|
||||
- Yuzu: emulators/yuzu.md
|
||||
- Community forks (114):
|
||||
- Community forks (117):
|
||||
- EightyOne: emulators/81.md
|
||||
- a5200: emulators/a5200.md
|
||||
- ACE-DL: emulators/ace-dl.md
|
||||
@@ -335,8 +335,11 @@ nav:
|
||||
- RACE (Neo Geo Pocket): emulators/race.md
|
||||
- REminiscence: emulators/reminiscence.md
|
||||
- RetroDream: emulators/retrodream.md
|
||||
- Rosalie's Mupen GUI: emulators/rosalies-mupen-gui.md
|
||||
- SAME CDi: emulators/same_cdi.md
|
||||
- SDL2TRS: emulators/sdl2trs.md
|
||||
- SimCoupe: emulators/simcp.md
|
||||
- simple64: emulators/simple64.md
|
||||
- Suyu: emulators/suyu.md
|
||||
- swanstation: emulators/swanstation.md
|
||||
- Syobon Action: emulators/syobonaction.md
|
||||
@@ -408,7 +411,7 @@ nav:
|
||||
- MicroW8: emulators/uw8.md
|
||||
- uzem: emulators/uzem.md
|
||||
- VirtualXT: emulators/virtualxt.md
|
||||
- Game engines (27):
|
||||
- Game engines (31):
|
||||
- boom3: emulators/boom3.md
|
||||
- Cannonball: emulators/cannonball.md
|
||||
- ChaiLove: emulators/chailove.md
|
||||
@@ -427,16 +430,20 @@ nav:
|
||||
- OpenLara: emulators/openlara.md
|
||||
- OpenTyrian: emulators/opentyrian.md
|
||||
- PrBoom: emulators/prboom.md
|
||||
- PrBoom+: emulators/prboom-plus.md
|
||||
- PuzzleScript: emulators/puzzlescript.md
|
||||
- Retro8: emulators/retro8.md
|
||||
- ruffle: emulators/ruffle.md
|
||||
- ScummVM: emulators/scummvm.md
|
||||
- solarus: emulators/solarus.md
|
||||
- Super Bros War: emulators/superbroswar.md
|
||||
- TIC-80: emulators/tic80.md
|
||||
- TyrQuake: emulators/tyrquake.md
|
||||
- visual-pinball: emulators/visual-pinball.md
|
||||
- WASM-4: emulators/wasm4.md
|
||||
- XRick: emulators/xrick.md
|
||||
- Zelda Classic v2.10: emulators/zc210.md
|
||||
- Enhanced forks (22):
|
||||
- Enhanced forks (23):
|
||||
- A7800: emulators/a7800.md
|
||||
- AdvanceMAME: emulators/advancemame.md
|
||||
- bsnes-hd beta: emulators/bsnes_hd_beta.md
|
||||
@@ -459,6 +466,7 @@ nav:
|
||||
- PCSX2x6: emulators/pcsx2x6.md
|
||||
- PrimeHack: emulators/primehack.md
|
||||
- SMS Plus GX: emulators/smsplus.md
|
||||
- Triforce: emulators/triforce.md
|
||||
- Frozen snapshots (33):
|
||||
- bnes: emulators/bnes.md
|
||||
- bsnes 2014: emulators/bsnes2014.md
|
||||
@@ -502,7 +510,7 @@ nav:
|
||||
- Launchers (2):
|
||||
- Dolphin Launcher: emulators/dolphin_launcher.md
|
||||
- Parallel Launcher: emulators/parallel-launcher.md
|
||||
- Other (33):
|
||||
- Other (37):
|
||||
- ares: emulators/ares.md
|
||||
- Basilisk II: emulators/basiliskii.md
|
||||
- Beetle GBA (Mednafen): emulators/beetle_gba.md
|
||||
@@ -524,13 +532,17 @@ nav:
|
||||
- Model 2 Emulator: emulators/model2.md
|
||||
- openMSX: emulators/openmsx.md
|
||||
- Oricutron: emulators/oricutron.md
|
||||
- puNES: emulators/punes.md
|
||||
- Redream: emulators/redream.md
|
||||
- Ryujinx: emulators/ryujinx.md
|
||||
- shadps4: emulators/shadps4.md
|
||||
- SheepShaver: emulators/sheepshaver.md
|
||||
- Supermodel: emulators/supermodel.md
|
||||
- SUPER ZSNES: emulators/superzsnes.md
|
||||
- ti99sim: emulators/ti99sim.md
|
||||
- tsugaru: emulators/tsugaru.md
|
||||
- VBA-M: emulators/vba_m.md
|
||||
- veesem: emulators/veesem.md
|
||||
- VICE: emulators/vice.md
|
||||
- Vita3K: emulators/vita3k.md
|
||||
- x16emu: emulators/x16emu.md
|
||||
|
||||
@@ -129,7 +129,23 @@
|
||||
{ "type": "object" }
|
||||
]
|
||||
},
|
||||
"region": { "type": ["string", "array"] },
|
||||
"region": {
|
||||
"description": "Region the emulator code selects this file for, never the region of the dump.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"enum": [
|
||||
"world",
|
||||
"north-america", "canada",
|
||||
"latin-america", "brazil", "mexico", "argentina",
|
||||
"europe", "uk", "france", "germany", "italy", "spain",
|
||||
"netherlands", "portugal", "greece", "poland", "russia",
|
||||
"sweden", "norway", "denmark", "finland",
|
||||
"asia", "japan", "south-korea", "china", "taiwan",
|
||||
"hong-kong", "singapore", "india", "asia-ntsc", "asia-pal",
|
||||
"oceania", "australia", "new-zealand"
|
||||
]
|
||||
}
|
||||
},
|
||||
"contents": { "type": "array", "items": { "type": "object" } },
|
||||
"source_ref": {
|
||||
"description": "A dict splits core from upstream when the two diverge.",
|
||||
|
||||
+12
-12
@@ -164,9 +164,16 @@ def md5_composite(filepath: str | Path) -> str:
|
||||
return result
|
||||
|
||||
|
||||
def parse_md5_list(raw: str) -> list[str]:
|
||||
"""Parse comma-separated MD5 string into normalized lowercase list."""
|
||||
return [m.strip().lower() for m in raw.split(",") if m.strip()] if raw else []
|
||||
def parse_md5_list(raw: str | list | None) -> list[str]:
|
||||
"""Normalize an md5 field into a lowercase list.
|
||||
|
||||
Platform YAMLs carry Recalbox multi-hash as one comma-separated string,
|
||||
emulator profiles carry a YAML list. Both reach here.
|
||||
"""
|
||||
if not raw:
|
||||
return []
|
||||
values = raw if isinstance(raw, list) else str(raw).split(",")
|
||||
return [str(m).strip().lower() for m in values if str(m).strip()]
|
||||
|
||||
|
||||
_shared_yml_cache: dict[str, dict] = {}
|
||||
@@ -436,11 +443,6 @@ def resolve_local_file(
|
||||
exact, zip_exact, hash_mismatch, not_found.
|
||||
"""
|
||||
sha1 = file_entry.get("sha1")
|
||||
md5_field = file_entry.get("md5", "") or ""
|
||||
if isinstance(md5_field, list):
|
||||
md5_raw = ",".join(str(m) for m in md5_field)
|
||||
else:
|
||||
md5_raw = str(md5_field)
|
||||
name = file_entry.get("name", "")
|
||||
zipped_file = file_entry.get("zipped_file")
|
||||
aliases = file_entry.get("aliases", [])
|
||||
@@ -461,9 +463,7 @@ def resolve_local_file(
|
||||
if hint_base and hint_base not in names_to_try:
|
||||
names_to_try.append(hint_base)
|
||||
|
||||
md5_list = (
|
||||
[m.strip().lower() for m in md5_raw.split(",") if m.strip()] if md5_raw else []
|
||||
)
|
||||
md5_list = parse_md5_list(file_entry.get("md5"))
|
||||
files_db = db.get("files", {})
|
||||
by_md5 = db.get("indexes", {}).get("by_md5", {})
|
||||
by_name = db.get("indexes", {}).get("by_name", {})
|
||||
@@ -504,7 +504,7 @@ def resolve_local_file(
|
||||
# declared size must confirm the match.
|
||||
crc_raw = str(file_entry.get("crc32", "") or "").strip().lower()
|
||||
declared_size = file_entry.get("size")
|
||||
if crc_raw and declared_size and not zipped_file and not md5_raw:
|
||||
if crc_raw and declared_size and not zipped_file and not md5_list:
|
||||
by_crc32 = db.get("indexes", {}).get("by_crc32", {})
|
||||
match = by_crc32.get(crc_raw)
|
||||
if match and match in files_db:
|
||||
|
||||
@@ -25,6 +25,7 @@ from common import (
|
||||
load_emulator_profiles,
|
||||
load_platform_config,
|
||||
name_match_size_ok,
|
||||
parse_md5_list,
|
||||
require_yaml,
|
||||
)
|
||||
|
||||
@@ -206,6 +207,7 @@ def cross_reference(
|
||||
by_name = db.get("indexes", {}).get("by_name", {})
|
||||
by_name_lower = {k.lower(): k for k in by_name}
|
||||
by_md5 = db.get("indexes", {}).get("by_md5", {})
|
||||
by_crc32 = db.get("indexes", {}).get("by_crc32", {})
|
||||
by_path_suffix = db.get("indexes", {}).get("by_path_suffix", {})
|
||||
db_files = db.get("files", {})
|
||||
report = {}
|
||||
@@ -331,13 +333,20 @@ def cross_reference(
|
||||
path_field, by_name, by_name_lower,
|
||||
data_names, by_path_suffix, f, db_files,
|
||||
)
|
||||
# Try the alternate names the emulator accepts, like
|
||||
# resolve_local_file does
|
||||
if source is None:
|
||||
for alias in f.get("aliases") or []:
|
||||
source = _resolve_source(
|
||||
alias, by_name, by_name_lower,
|
||||
data_names, by_path_suffix, f, db_files,
|
||||
)
|
||||
if source is not None:
|
||||
break
|
||||
# Try MD5 hash match
|
||||
if source is None:
|
||||
md5_raw = f.get("md5", "")
|
||||
if md5_raw:
|
||||
for md5_val in md5_raw.split(","):
|
||||
md5_val = md5_val.strip().lower()
|
||||
if md5_val and by_md5.get(md5_val):
|
||||
for md5_val in parse_md5_list(f.get("md5")):
|
||||
if by_md5.get(md5_val):
|
||||
source = "bios"
|
||||
break
|
||||
# Try SHA1 hash match
|
||||
@@ -345,6 +354,11 @@ def cross_reference(
|
||||
sha1 = f.get("sha1", "")
|
||||
if sha1 and sha1 in db_files:
|
||||
source = "bios"
|
||||
# Try CRC32 hash match
|
||||
if source is None:
|
||||
crc32 = str(f.get("crc32", "")).lower()
|
||||
if crc32 and by_crc32.get(crc32):
|
||||
source = "bios"
|
||||
if source is None:
|
||||
source = "missing"
|
||||
|
||||
|
||||
+294
-19
@@ -46,9 +46,11 @@ from common import (
|
||||
load_database,
|
||||
load_emulator_profiles,
|
||||
load_platform_config,
|
||||
parse_md5_list,
|
||||
require_yaml,
|
||||
resolve_local_file,
|
||||
)
|
||||
import region as region_mod
|
||||
from deterministic_zip import rebuild_zip_deterministic
|
||||
from validation import (
|
||||
_build_validation_index,
|
||||
@@ -291,15 +293,7 @@ def resolve_file(
|
||||
name = file_entry.get("name", "")
|
||||
sha1 = file_entry.get("sha1")
|
||||
first_sha1 = (sha1[0] if sha1 else "") if isinstance(sha1, list) else (sha1 or "")
|
||||
md5_raw = file_entry.get("md5", "")
|
||||
if isinstance(md5_raw, list):
|
||||
md5_list = [str(m).strip().lower() for m in md5_raw if str(m).strip()]
|
||||
else:
|
||||
md5_list = (
|
||||
[m.strip().lower() for m in md5_raw.split(",") if m.strip()]
|
||||
if md5_raw
|
||||
else []
|
||||
)
|
||||
md5_list = parse_md5_list(file_entry.get("md5"))
|
||||
first_md5 = md5_list[0] if md5_list else ""
|
||||
cached = fetch_large_file(name, expected_sha1=first_sha1, expected_md5=first_md5)
|
||||
if cached:
|
||||
@@ -777,6 +771,8 @@ def _build_readme(
|
||||
num_systems: int,
|
||||
source: str = "full",
|
||||
contributors: list[dict] | None = None,
|
||||
regions: list[str] | None = None,
|
||||
fallback_systems: list[str] | None = None,
|
||||
) -> str:
|
||||
"""Build a personalized step-by-step README for each platform pack."""
|
||||
sep = "=" * 50
|
||||
@@ -952,12 +948,22 @@ def _build_readme(
|
||||
),
|
||||
)
|
||||
|
||||
if regions:
|
||||
region_help = (
|
||||
" - Wrong region? This pack was filtered. Only the\n"
|
||||
" best-matching BIOS was kept per system. Use the\n"
|
||||
" unfiltered pack to play imports.\n"
|
||||
)
|
||||
else:
|
||||
region_help = (
|
||||
" - Wrong region? Some systems have regional BIOS\n"
|
||||
" variants (USA/EUR/JAP). All are included.\n"
|
||||
)
|
||||
footer = (
|
||||
"TROUBLESHOOTING\n\n"
|
||||
" - Core says BIOS missing? Check the exact filename\n"
|
||||
" and make sure it's in the right subfolder.\n"
|
||||
" - Wrong region? Some systems have regional BIOS\n"
|
||||
" variants (USA/EUR/JAP). All are included.\n"
|
||||
f"{region_help}"
|
||||
" - Need help? https://github.com/Abdess/retrobios/issues\n\n"
|
||||
f"{sep}\n"
|
||||
f" https://github.com/Abdess/retrobios\n"
|
||||
@@ -980,6 +986,30 @@ def _build_readme(
|
||||
" Independent of platform scraper accuracy.\n\n"
|
||||
)
|
||||
|
||||
region_info = ""
|
||||
if regions:
|
||||
pretty = ", ".join(
|
||||
" ".join(w.title() for w in slug.split("-")) for slug in regions
|
||||
)
|
||||
region_info = (
|
||||
"PACK TYPE: Region Filtered\n\n"
|
||||
f" Region priority: {pretty}\n\n"
|
||||
" Only the best-matching BIOS was kept for each system.\n"
|
||||
)
|
||||
if fallback_systems:
|
||||
listed = "\n".join(f" {s}" for s in fallback_systems)
|
||||
region_info += (
|
||||
"\n These systems have no BIOS in those regions, so all\n"
|
||||
" of theirs were kept:\n"
|
||||
f"{listed}\n"
|
||||
)
|
||||
region_info += (
|
||||
"\n This shrinks the pack. It does not change how cores pick\n"
|
||||
" a BIOS: most already select per region from fixed filename\n"
|
||||
" lists driven by the game's region. Loading imports from\n"
|
||||
" another region may need the unfiltered pack.\n\n"
|
||||
)
|
||||
|
||||
credits = ""
|
||||
if contributors:
|
||||
credits = "\nCONTRIBUTORS\n\n"
|
||||
@@ -988,7 +1018,7 @@ def _build_readme(
|
||||
credits += f" @{username}\n"
|
||||
credits += "\n"
|
||||
|
||||
return header + source_info + guide + credits + footer
|
||||
return header + source_info + region_info + guide + credits + footer
|
||||
|
||||
|
||||
def _build_agnostic_rename_readme(
|
||||
@@ -1028,6 +1058,7 @@ def generate_pack(
|
||||
precomputed_extras: list[dict] | None = None,
|
||||
source: str = "full",
|
||||
flatten: bool = True,
|
||||
regions: list[str] | None = None,
|
||||
) -> str | None:
|
||||
"""Generate a ZIP pack for a platform.
|
||||
|
||||
@@ -1045,6 +1076,7 @@ def generate_pack(
|
||||
version_tag = f"_{version.replace(' ', '')}" if version else ""
|
||||
req_tag = "_Required" if required_only else ""
|
||||
source_tag = {"platform": "_Platform", "truth": "_Truth"}.get(source, "")
|
||||
region_tag_str = f"_{region_mod.region_tag(regions)}" if regions else ""
|
||||
|
||||
sys_tag = ""
|
||||
if system_filter:
|
||||
@@ -1059,7 +1091,7 @@ def generate_pack(
|
||||
display_parts.append("_".join(p.title() for p in parts if p))
|
||||
sys_tag = "_" + "_".join(display_parts)
|
||||
|
||||
zip_name = f"{platform_display.replace(' ', '_')}{version_tag}{source_tag}{req_tag}_BIOS_Pack{sys_tag}.zip"
|
||||
zip_name = f"{platform_display.replace(' ', '_')}{version_tag}{source_tag}{region_tag_str}{req_tag}_BIOS_Pack{sys_tag}.zip"
|
||||
zip_path = os.path.join(output_dir, zip_name)
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
@@ -1158,6 +1190,55 @@ def generate_pack(
|
||||
if best is not None:
|
||||
preferred_entries[full] = id(best)
|
||||
|
||||
# Region selection is decided once, over both the platform baseline and the
|
||||
# core extras, so the two phases below consult a single set. Grouping uses a
|
||||
# dedicated extras pass with an empty seen set: it only needs group
|
||||
# membership, never destinations, and a superset of members is the safe
|
||||
# direction. Runs only when --region is given.
|
||||
region_drops: set[str] = set()
|
||||
region_fallbacks: list[str] = []
|
||||
if regions:
|
||||
region_index = region_mod.build_region_index(emu_profiles or {})
|
||||
region_groups: dict[str, list[tuple[str, str]]] = {}
|
||||
for sys_id, system in pack_systems.items():
|
||||
members = region_groups.setdefault(sys_id, [])
|
||||
for file_entry in system.get("files", []):
|
||||
dest = _sanitize_path(
|
||||
file_entry.get("destination", file_entry.get("name", ""))
|
||||
)
|
||||
if dest:
|
||||
members.append((dest, file_entry.get("name", "")))
|
||||
if source != "platform":
|
||||
emu_systems = {
|
||||
n: list(p.get("systems", []))
|
||||
for n, p in (emu_profiles or {}).items()
|
||||
}
|
||||
for fe in _collect_emulator_extras(
|
||||
config,
|
||||
emulators_dir,
|
||||
db,
|
||||
set(),
|
||||
base_dest,
|
||||
emu_profiles,
|
||||
target_cores=target_cores,
|
||||
include_all=(source == "truth"),
|
||||
):
|
||||
dest = _sanitize_path(fe.get("destination", fe.get("name", "")))
|
||||
if not dest:
|
||||
continue
|
||||
for sys_id in emu_systems.get(
|
||||
fe.get("source_emulator", ""), ["_extras"]
|
||||
):
|
||||
region_groups.setdefault(sys_id, []).append(
|
||||
(dest, fe.get("name", ""))
|
||||
)
|
||||
region_drops = region_mod.resolve_region_drops(
|
||||
region_groups, region_index, regions
|
||||
)
|
||||
region_fallbacks = region_mod.fallback_groups(
|
||||
region_groups, region_index, regions
|
||||
)
|
||||
|
||||
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
|
||||
if source != "truth":
|
||||
for sys_id, system in sorted(pack_systems.items()):
|
||||
@@ -1165,6 +1246,8 @@ def generate_pack(
|
||||
if required_only and file_entry.get("required") is False:
|
||||
continue
|
||||
dest = _sanitize_path(file_entry.get("destination", file_entry["name"]))
|
||||
if region_drops and dest in region_drops:
|
||||
continue
|
||||
if not dest:
|
||||
# EmuDeck-style entries (system:md5 whitelist, no filename).
|
||||
fkey = f"{sys_id}/{file_entry.get('name', '')}"
|
||||
@@ -1473,6 +1556,8 @@ def generate_pack(
|
||||
if required_only and fe.get("required") is False:
|
||||
continue
|
||||
dest = _sanitize_path(fe.get("destination", fe["name"]))
|
||||
if region_drops and dest in region_drops:
|
||||
continue
|
||||
if not dest:
|
||||
continue
|
||||
# Core extras: _collect_emulator_extras already adjusted
|
||||
@@ -1572,6 +1657,8 @@ def generate_pack(
|
||||
platform_name, platform_display, base_dest, total_files, num_systems,
|
||||
source=source,
|
||||
contributors=_pack_registry.get(platform_name, {}).get("contributed_by", []),
|
||||
regions=regions,
|
||||
fallback_systems=region_fallbacks,
|
||||
)
|
||||
zf.writestr("README.txt", readme_text)
|
||||
|
||||
@@ -1725,6 +1812,7 @@ def generate_emulator_pack(
|
||||
standalone: bool = False,
|
||||
zip_contents: dict | None = None,
|
||||
required_only: bool = False,
|
||||
regions: list[str] | None = None,
|
||||
) -> str | None:
|
||||
"""Generate a ZIP pack for specific emulator profiles."""
|
||||
all_profiles = load_emulator_profiles(emulators_dir, skip_aliases=False)
|
||||
@@ -1768,10 +1856,29 @@ def generate_emulator_pack(
|
||||
|
||||
# ZIP naming
|
||||
display_names = [p.get("emulator", n).replace(" ", "") for n, p in selected]
|
||||
zip_name = "_".join(display_names) + "_BIOS_Pack.zip"
|
||||
region_tag_str = f"_{region_mod.region_tag(regions)}" if regions else ""
|
||||
zip_name = "_".join(display_names) + f"{region_tag_str}_BIOS_Pack.zip"
|
||||
zip_path = os.path.join(output_dir, zip_name)
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
# One group per profile: a core's regional alternatives compete only with
|
||||
# each other. Decided before the ZIP is opened so the loop below reads a
|
||||
# single set.
|
||||
region_drops: set[str] = set()
|
||||
if regions:
|
||||
region_index = region_mod.build_region_index(dict(selected))
|
||||
region_groups: dict[str, list[tuple[str, str]]] = {}
|
||||
for emu_name, profile in sorted(selected):
|
||||
structure = profile.get("pack_structure")
|
||||
members = region_groups.setdefault(emu_name, [])
|
||||
for fe in filter_files_by_mode(profile.get("files", []), standalone):
|
||||
dest = _resolve_destination(fe, structure, standalone)
|
||||
if dest:
|
||||
members.append((dest, fe.get("name", "")))
|
||||
region_drops = region_mod.resolve_region_drops(
|
||||
region_groups, region_index, regions
|
||||
)
|
||||
|
||||
total_files = 0
|
||||
missing_files = []
|
||||
seen_destinations: set[str] = set()
|
||||
@@ -1888,6 +1995,8 @@ def generate_emulator_pack(
|
||||
dest = _resolve_destination(fe, pack_structure, standalone)
|
||||
if not dest:
|
||||
continue
|
||||
if region_drops and dest in region_drops:
|
||||
continue
|
||||
|
||||
if dest.lower() in seen_lower:
|
||||
continue
|
||||
@@ -1989,6 +2098,7 @@ def generate_system_pack(
|
||||
standalone: bool = False,
|
||||
zip_contents: dict | None = None,
|
||||
required_only: bool = False,
|
||||
regions: list[str] | None = None,
|
||||
) -> str | None:
|
||||
"""Generate a ZIP pack for all emulators supporting given system IDs."""
|
||||
profiles = load_emulator_profiles(emulators_dir)
|
||||
@@ -2036,6 +2146,7 @@ def generate_system_pack(
|
||||
standalone,
|
||||
zip_contents,
|
||||
required_only=required_only,
|
||||
regions=regions,
|
||||
)
|
||||
if result:
|
||||
# Rename to system-based name
|
||||
@@ -2092,6 +2203,7 @@ def generate_split_packs(
|
||||
target_cores: set[str] | None = None,
|
||||
required_only: bool = False,
|
||||
source: str = "full",
|
||||
regions: list[str] | None = None,
|
||||
) -> list[str]:
|
||||
"""Generate split packs (one ZIP per system or manufacturer)."""
|
||||
config = load_platform_config(platform_name, platforms_dir)
|
||||
@@ -2162,13 +2274,15 @@ def generate_split_packs(
|
||||
system_filter=group_system_ids,
|
||||
precomputed_extras=group_extras,
|
||||
source=source,
|
||||
regions=regions,
|
||||
)
|
||||
if zip_path:
|
||||
version = config.get("version", config.get("dat_version", ""))
|
||||
ver_tag = f"_{version.replace(' ', '')}" if version else ""
|
||||
req_tag = "_Required" if required_only else ""
|
||||
rgn_tag = f"_{region_mod.region_tag(regions)}" if regions else ""
|
||||
safe_group = group_name.replace(" ", "_")
|
||||
new_name = f"{platform_display.replace(' ', '_')}{ver_tag}{source_tag}{req_tag}_{safe_group}_BIOS_Pack.zip"
|
||||
new_name = f"{platform_display.replace(' ', '_')}{ver_tag}{source_tag}{rgn_tag}{req_tag}_{safe_group}_BIOS_Pack.zip"
|
||||
new_path = os.path.join(split_dir, new_name)
|
||||
if new_path != zip_path:
|
||||
os.rename(zip_path, new_path)
|
||||
@@ -2399,6 +2513,8 @@ def _validate_args(args, parser):
|
||||
parser.error("--manifest is incompatible with --emulator")
|
||||
if args.manifest and args.split:
|
||||
parser.error("--manifest is incompatible with --split")
|
||||
if getattr(args, "region", None) and has_from_md5:
|
||||
parser.error("--region and --from-md5 are mutually exclusive")
|
||||
|
||||
|
||||
def _write_manifest_if_changed(path: str, manifest: dict) -> None:
|
||||
@@ -2456,11 +2572,18 @@ def _run_manifest_mode(
|
||||
emu_profiles=emu_profiles,
|
||||
target_cores=tc,
|
||||
source=source,
|
||||
regions=getattr(args, "regions", None),
|
||||
)
|
||||
source_suffix = {"platform": "_platform", "truth": "_truth"}.get(source, "")
|
||||
req_suffix = "_required" if required_only else ""
|
||||
rgn = getattr(args, "regions", None)
|
||||
region_suffix = (
|
||||
f"_{region_mod.region_tag(rgn).lower()}" if rgn else ""
|
||||
)
|
||||
out_path = os.path.join(
|
||||
args.output_dir, f"{representative}{source_suffix}{req_suffix}.json"
|
||||
args.output_dir,
|
||||
f"{representative}{source_suffix}{region_suffix}"
|
||||
f"{req_suffix}.json",
|
||||
)
|
||||
_write_manifest_if_changed(out_path, manifest)
|
||||
print(
|
||||
@@ -2472,7 +2595,8 @@ def _run_manifest_mode(
|
||||
if alias_plat != representative:
|
||||
alias_path = os.path.join(
|
||||
args.output_dir,
|
||||
f"{alias_plat}{source_suffix}{req_suffix}.json",
|
||||
f"{alias_plat}{source_suffix}{region_suffix}"
|
||||
f"{req_suffix}.json",
|
||||
)
|
||||
alias_manifest = dict(manifest)
|
||||
alias_manifest["platform"] = alias_plat
|
||||
@@ -2518,6 +2642,8 @@ def _run_verify_packs(args):
|
||||
sys.exit(1)
|
||||
|
||||
all_ok = True
|
||||
verify_regions = getattr(args, "regions", None)
|
||||
verify_profiles = load_emulator_profiles(args.emulators_dir)
|
||||
for platform_name in platforms:
|
||||
config = load_platform_config(platform_name, args.platforms_dir)
|
||||
display = config.get("platform", platform_name).replace(" ", "_")
|
||||
@@ -2561,6 +2687,43 @@ def _run_verify_packs(args):
|
||||
os.path.join(extract_dir, base_dest)
|
||||
)
|
||||
|
||||
region_drops: set[str] = set()
|
||||
if verify_regions:
|
||||
region_index = region_mod.build_region_index(verify_profiles)
|
||||
region_groups: dict[str, list[tuple[str, str]]] = {}
|
||||
for sys_id, sys_data in systems.items():
|
||||
members = region_groups.setdefault(sys_id, [])
|
||||
for fe in sys_data.get("files", []):
|
||||
d = _sanitize_path(
|
||||
fe.get("destination", fe.get("name", ""))
|
||||
)
|
||||
if d:
|
||||
members.append((d, fe.get("name", "")))
|
||||
# Core extras join the groups so a platform file dropped in
|
||||
# favour of a better-ranked core file is not reported missing.
|
||||
from verify import find_undeclared_files as _fud
|
||||
|
||||
emu_systems = {
|
||||
n: list(p.get("systems", []))
|
||||
for n, p in verify_profiles.items()
|
||||
}
|
||||
for u in _fud(
|
||||
config, args.emulators_dir, verify_db, verify_profiles
|
||||
):
|
||||
if not u.get("in_repo"):
|
||||
continue
|
||||
raw = u.get("path") or u["name"]
|
||||
d = _sanitize_path(
|
||||
f"{raw}{u['name']}" if raw.endswith("/") else raw
|
||||
)
|
||||
if not d:
|
||||
continue
|
||||
for sid in emu_systems.get(u.get("emulator", ""), ["_extras"]):
|
||||
region_groups.setdefault(sid, []).append((d, u["name"]))
|
||||
region_drops = region_mod.resolve_region_drops(
|
||||
region_groups, region_index, verify_regions
|
||||
)
|
||||
|
||||
missing = []
|
||||
hash_fail = []
|
||||
ok = 0
|
||||
@@ -2569,6 +2732,8 @@ def _run_verify_packs(args):
|
||||
dest = fe.get("destination", fe.get("name", ""))
|
||||
if not dest:
|
||||
continue
|
||||
if region_drops and _sanitize_path(dest) in region_drops:
|
||||
continue
|
||||
fp = (
|
||||
os.path.join(extract_dir, base_dest, dest)
|
||||
if base_dest and not is_flat
|
||||
@@ -2733,6 +2898,7 @@ def _run_platform_packs(
|
||||
target_cores=tc,
|
||||
required_only=required_only,
|
||||
source=source,
|
||||
regions=getattr(args, "regions", None),
|
||||
)
|
||||
print(f" Split into {len(zip_paths)} packs")
|
||||
else:
|
||||
@@ -2751,6 +2917,7 @@ def _run_platform_packs(
|
||||
required_only=required_only,
|
||||
system_filter=system_filter,
|
||||
source=source,
|
||||
regions=getattr(args, "regions", None),
|
||||
)
|
||||
if not args.split and zip_path and aliases:
|
||||
rep_cfg = load_platform_config(representative, args.platforms_dir)
|
||||
@@ -2776,14 +2943,22 @@ def _run_platform_packs(
|
||||
print("\nVerifying packs and generating manifests...")
|
||||
skip_conf = bool(system_filter or args.split)
|
||||
all_ok = verify_and_finalize_packs(
|
||||
args.output_dir, db, skip_conformance=skip_conf, data_registry=data_registry
|
||||
args.output_dir,
|
||||
db,
|
||||
skip_conformance=skip_conf,
|
||||
data_registry=data_registry,
|
||||
regions=getattr(args, "regions", None),
|
||||
)
|
||||
if args.split:
|
||||
for entry in os.listdir(args.output_dir):
|
||||
sub = os.path.join(args.output_dir, entry)
|
||||
if os.path.isdir(sub) and entry.endswith("_Split"):
|
||||
ok = verify_and_finalize_packs(
|
||||
sub, db, skip_conformance=True, data_registry=data_registry
|
||||
sub,
|
||||
db,
|
||||
skip_conformance=True,
|
||||
data_registry=data_registry,
|
||||
regions=getattr(args, "regions", None),
|
||||
)
|
||||
all_ok = all_ok and ok
|
||||
if not all_ok:
|
||||
@@ -2858,6 +3033,10 @@ def main():
|
||||
help="Grouping for --split (default: system)",
|
||||
)
|
||||
parser.add_argument("--target", "-t", help="Hardware target (e.g., switch, rpi4)")
|
||||
parser.add_argument(
|
||||
"--region",
|
||||
help="Region priority list, best first (e.g. us,eu,jp)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--list-targets",
|
||||
action="store_true",
|
||||
@@ -2884,6 +3063,15 @@ def main():
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# Parsed before the quick-exit modes: --verify-packs returns early and
|
||||
# still needs the region priority list to narrow its expectation.
|
||||
args.regions = []
|
||||
if args.region:
|
||||
try:
|
||||
args.regions = region_mod.parse_requested(args.region)
|
||||
except ValueError as exc:
|
||||
parser.error(str(exc))
|
||||
|
||||
# Quick-exit modes: --verify-packs alone = verify existing packs only
|
||||
# Combined with --all-variants, generation runs first then verify
|
||||
if args.verify_packs and not args.all_variants:
|
||||
@@ -2975,6 +3163,7 @@ def main():
|
||||
args.standalone,
|
||||
zip_contents,
|
||||
required_only=args.required_only,
|
||||
regions=getattr(args, "regions", None),
|
||||
):
|
||||
sys.exit(1)
|
||||
return
|
||||
@@ -2991,6 +3180,7 @@ def main():
|
||||
args.standalone,
|
||||
zip_contents,
|
||||
required_only=args.required_only,
|
||||
regions=getattr(args, "regions", None),
|
||||
):
|
||||
sys.exit(1)
|
||||
return
|
||||
@@ -3112,6 +3302,7 @@ def generate_manifest(
|
||||
emu_profiles: dict | None = None,
|
||||
target_cores: set[str] | None = None,
|
||||
source: str = "full",
|
||||
regions: list[str] | None = None,
|
||||
) -> dict:
|
||||
"""Generate a JSON manifest for a platform (same resolution as generate_pack).
|
||||
|
||||
@@ -3157,6 +3348,45 @@ def generate_manifest(
|
||||
manifest_files: list[dict] = []
|
||||
total_size = 0
|
||||
|
||||
region_drops: set[str] = set()
|
||||
if regions:
|
||||
region_index = region_mod.build_region_index(emu_profiles)
|
||||
region_groups: dict[str, list[tuple[str, str]]] = {}
|
||||
for sys_id, system in pack_systems.items():
|
||||
members = region_groups.setdefault(sys_id, [])
|
||||
for file_entry in system.get("files", []):
|
||||
d = _sanitize_path(
|
||||
file_entry.get("destination", file_entry.get("name", ""))
|
||||
)
|
||||
if d:
|
||||
members.append((d, file_entry.get("name", "")))
|
||||
if source != "platform":
|
||||
emu_systems = {
|
||||
n: list(p.get("systems", [])) for n, p in emu_profiles.items()
|
||||
}
|
||||
for fe in _collect_emulator_extras(
|
||||
config,
|
||||
emulators_dir,
|
||||
db,
|
||||
set(),
|
||||
base_dest,
|
||||
emu_profiles,
|
||||
target_cores=target_cores,
|
||||
include_all=(source == "truth"),
|
||||
):
|
||||
d = _sanitize_path(fe.get("destination", fe.get("name", "")))
|
||||
if not d:
|
||||
continue
|
||||
for sid in emu_systems.get(
|
||||
fe.get("source_emulator", ""), ["_extras"]
|
||||
):
|
||||
region_groups.setdefault(sid, []).append(
|
||||
(d, fe.get("name", ""))
|
||||
)
|
||||
region_drops = region_mod.resolve_region_drops(
|
||||
region_groups, region_index, regions
|
||||
)
|
||||
|
||||
# Phase 1: baseline files
|
||||
if source != "truth":
|
||||
for sys_id, system in sorted(pack_systems.items()):
|
||||
@@ -3164,6 +3394,8 @@ def generate_manifest(
|
||||
dest = _sanitize_path(file_entry.get("destination", file_entry["name"]))
|
||||
if not dest:
|
||||
continue
|
||||
if region_drops and dest in region_drops:
|
||||
continue
|
||||
full_dest = f"{base_dest}/{dest}" if base_dest else dest
|
||||
|
||||
dedup_key = full_dest
|
||||
@@ -3234,6 +3466,8 @@ def generate_manifest(
|
||||
dest = _sanitize_path(fe.get("destination", fe["name"]))
|
||||
if not dest:
|
||||
continue
|
||||
if region_drops and dest in region_drops:
|
||||
continue
|
||||
if extras_pfx:
|
||||
if not dest.startswith(f"{extras_pfx}/"):
|
||||
full_dest = f"{extras_pfx}/{dest}"
|
||||
@@ -3703,9 +3937,13 @@ def verify_pack_against_platform(
|
||||
db: dict | None = None,
|
||||
emulators_dir: str = "emulators",
|
||||
emu_profiles: dict | None = None,
|
||||
regions: list[str] | None = None,
|
||||
) -> tuple[bool, int, int, list[str]]:
|
||||
"""Verify a pack ZIP against its platform config and core requirements.
|
||||
|
||||
A region priority list narrows the expectation to what the builder would
|
||||
have packed, using the same selection function.
|
||||
|
||||
Checks:
|
||||
1. Every baseline file declared by the platform exists in the ZIP
|
||||
at the correct destination path
|
||||
@@ -3729,6 +3967,37 @@ def verify_pack_against_platform(
|
||||
if emu_profiles is None:
|
||||
emu_profiles = load_emulator_profiles(emulators_dir)
|
||||
|
||||
region_drops: set[str] = set()
|
||||
if regions:
|
||||
region_index = region_mod.build_region_index(emu_profiles)
|
||||
region_groups: dict[str, list[tuple[str, str]]] = {}
|
||||
for sys_id, system in config.get("systems", {}).items():
|
||||
members = region_groups.setdefault(sys_id, [])
|
||||
for fe in system.get("files", []):
|
||||
d = _sanitize_path(fe.get("destination", fe.get("name", "")))
|
||||
if d:
|
||||
members.append((d, fe.get("name", "")))
|
||||
if db is not None:
|
||||
from verify import find_undeclared_files as _fud
|
||||
|
||||
emu_systems = {
|
||||
n: list(p.get("systems", [])) for n, p in emu_profiles.items()
|
||||
}
|
||||
for u in _fud(config, emulators_dir, db, emu_profiles):
|
||||
if not u.get("in_repo"):
|
||||
continue
|
||||
raw = u.get("path") or u["name"]
|
||||
d = _sanitize_path(
|
||||
f"{raw}{u['name']}" if raw.endswith("/") else raw
|
||||
)
|
||||
if not d:
|
||||
continue
|
||||
for sys_id in emu_systems.get(u.get("emulator", ""), ["_extras"]):
|
||||
region_groups.setdefault(sys_id, []).append((d, u["name"]))
|
||||
region_drops = region_mod.resolve_region_drops(
|
||||
region_groups, region_index, regions
|
||||
)
|
||||
|
||||
with zipfile.ZipFile(zip_path, "r") as zf:
|
||||
zip_set = set(zf.namelist())
|
||||
zip_lower = {n.lower(): n for n in zip_set}
|
||||
@@ -3779,6 +4048,8 @@ def verify_pack_against_platform(
|
||||
dest = fe.get("destination", fe.get("name", ""))
|
||||
if not dest:
|
||||
continue
|
||||
if region_drops and _sanitize_path(dest) in region_drops:
|
||||
continue
|
||||
expected = f"{base_dest}/{dest}" if base_dest and not is_flat else dest
|
||||
baseline_checked += 1
|
||||
|
||||
@@ -3845,6 +4116,8 @@ def verify_pack_against_platform(
|
||||
continue
|
||||
raw_dest = u.get("path") or u["name"]
|
||||
dest = f"{raw_dest}{u['name']}" if raw_dest.endswith("/") else raw_dest
|
||||
if region_drops and _sanitize_path(dest) in region_drops:
|
||||
continue
|
||||
if extras_pfx and not (is_flat and extras_pfx == base_dest):
|
||||
if not dest.startswith(f"{extras_pfx}/"):
|
||||
full = f"{extras_pfx}/{dest}"
|
||||
@@ -3889,6 +4162,7 @@ def verify_and_finalize_packs(
|
||||
platforms_dir: str = "platforms",
|
||||
skip_conformance: bool = False,
|
||||
data_registry: dict | None = None,
|
||||
regions: list[str] | None = None,
|
||||
) -> bool:
|
||||
"""Verify all packs, inject manifests, generate SHA256SUMS.
|
||||
|
||||
@@ -3953,6 +4227,7 @@ def verify_and_finalize_packs(
|
||||
pname,
|
||||
platforms_dir,
|
||||
db=db,
|
||||
regions=regions,
|
||||
)
|
||||
status = "OK" if p_ok else "FAILED"
|
||||
print(
|
||||
|
||||
@@ -31,6 +31,7 @@ from common import (
|
||||
load_database,
|
||||
load_emulator_profiles,
|
||||
load_provenance_snapshots,
|
||||
parse_md5_list,
|
||||
require_yaml,
|
||||
unique_emulator_profiles,
|
||||
write_if_changed,
|
||||
@@ -1479,9 +1480,8 @@ def generate_emulator_page(
|
||||
return True
|
||||
md5_raw = f.get("md5", "")
|
||||
if md5_raw:
|
||||
for md5_val in md5_raw.split(","):
|
||||
md5_val = md5_val.strip().lower()
|
||||
if md5_val and by_md5.get(md5_val):
|
||||
for md5_val in parse_md5_list(md5_raw):
|
||||
if by_md5.get(md5_val):
|
||||
return True
|
||||
sha1 = f.get("sha1", "")
|
||||
if sha1 and sha1 in db_files:
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
"""Region vocabulary and ordered-priority selection for pack generation.
|
||||
|
||||
A file's region: value is the region the emulator code selects it for, not the
|
||||
region of the dump. Signal standard has no field of its own; it survives only
|
||||
inside slugs whose hardware register fuses the two axes (Saturn SMPC areas).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
WORLD = "world"
|
||||
|
||||
# Two levels only: super-region -> member territories.
|
||||
REGION_TREE: dict[str, frozenset[str]] = {
|
||||
"north-america": frozenset({"canada"}),
|
||||
"latin-america": frozenset({"brazil", "mexico", "argentina"}),
|
||||
"europe": frozenset(
|
||||
{
|
||||
"uk",
|
||||
"france",
|
||||
"germany",
|
||||
"italy",
|
||||
"spain",
|
||||
"netherlands",
|
||||
"portugal",
|
||||
"greece",
|
||||
"poland",
|
||||
"russia",
|
||||
"sweden",
|
||||
"norway",
|
||||
"denmark",
|
||||
"finland",
|
||||
}
|
||||
),
|
||||
"asia": frozenset(
|
||||
{
|
||||
"japan",
|
||||
"south-korea",
|
||||
"china",
|
||||
"taiwan",
|
||||
"hong-kong",
|
||||
"singapore",
|
||||
"india",
|
||||
"asia-ntsc",
|
||||
"asia-pal",
|
||||
}
|
||||
),
|
||||
"oceania": frozenset({"australia", "new-zealand"}),
|
||||
}
|
||||
|
||||
_PARENT: dict[str, str] = {
|
||||
member: parent for parent, members in REGION_TREE.items() for member in members
|
||||
}
|
||||
|
||||
REGIONS: frozenset[str] = frozenset({WORLD} | set(REGION_TREE) | set(_PARENT))
|
||||
|
||||
# Accepted on the CLI and for legacy profile values.
|
||||
ALIASES: dict[str, str] = {
|
||||
"jp": "japan",
|
||||
"ntsc-j": "japan",
|
||||
"us": "north-america",
|
||||
"usa": "north-america",
|
||||
"na": "north-america",
|
||||
"ntsc-u": "north-america",
|
||||
"eu": "europe",
|
||||
"pal": "europe",
|
||||
"kr": "south-korea",
|
||||
"korea": "south-korea",
|
||||
"auto": WORLD,
|
||||
"gb": "uk",
|
||||
}
|
||||
|
||||
|
||||
def _canonical(raw: str) -> str:
|
||||
"""Map one token to a canonical region slug."""
|
||||
key = raw.strip().lower()
|
||||
key = ALIASES.get(key, key)
|
||||
if key not in REGIONS:
|
||||
raise ValueError(f"unknown region: {raw!r}")
|
||||
return key
|
||||
|
||||
|
||||
def comparable(a: str, b: str) -> bool:
|
||||
"""True when two regions are equal or in a parent/child relation."""
|
||||
return a == b or _PARENT.get(a) == b or _PARENT.get(b) == a
|
||||
|
||||
|
||||
def normalize_declared(raw: str | list | None) -> set[str]:
|
||||
"""Normalise a profile region: value to canonical slugs."""
|
||||
if raw is None:
|
||||
return set()
|
||||
values = raw if isinstance(raw, list) else [raw]
|
||||
return {_canonical(str(v)) for v in values}
|
||||
|
||||
|
||||
def parse_requested(raw: str) -> list[str]:
|
||||
"""Parse a comma-separated --region value into an ordered priority list."""
|
||||
out: list[str] = []
|
||||
for part in raw.split(","):
|
||||
if not part.strip():
|
||||
continue
|
||||
slug = _canonical(part)
|
||||
if slug not in out:
|
||||
out.append(slug)
|
||||
if not out:
|
||||
raise ValueError("--region requires at least one region")
|
||||
return out
|
||||
|
||||
|
||||
def rank(file_regions: set[str], requested: list[str]) -> int:
|
||||
"""Priority index of a file, lower is better.
|
||||
|
||||
Returns len(requested) when no requested region is comparable, the implicit
|
||||
lowest rank that keeps a group from ever being emptied.
|
||||
"""
|
||||
for i, req in enumerate(requested):
|
||||
if any(comparable(req, fr) for fr in file_regions):
|
||||
return i
|
||||
return len(requested)
|
||||
|
||||
|
||||
def region_tag(requested: list[str]) -> str:
|
||||
"""Pack filename tag for a requested priority list."""
|
||||
return "_".join(
|
||||
"".join(part.title() for part in slug.split("-")) for slug in requested
|
||||
)
|
||||
|
||||
|
||||
def build_region_index(profiles: dict) -> dict[str, dict]:
|
||||
"""Build a region lookup from emulator profiles.
|
||||
|
||||
Keyed by the entry's path when present, by name otherwise. Entries sharing a
|
||||
key have their regions unioned, so an ambiguous lookup keeps more files
|
||||
rather than fewer.
|
||||
"""
|
||||
index: dict[str, dict] = {}
|
||||
for emu_name, profile in sorted(profiles.items()):
|
||||
if profile.get("type") in ("launcher", "alias"):
|
||||
continue
|
||||
for f in profile.get("files") or []:
|
||||
if not isinstance(f, dict):
|
||||
continue
|
||||
try:
|
||||
regions = normalize_declared(f.get("region"))
|
||||
except ValueError as exc:
|
||||
raise ValueError(
|
||||
f"{emu_name}: {f.get('name', '?')}: {exc}"
|
||||
) from exc
|
||||
if not regions:
|
||||
continue
|
||||
name = f.get("name", "")
|
||||
path = f.get("path") or ""
|
||||
# Path-keyed so same-named entries stay separate (Dolphin declares
|
||||
# three IPL.bin), name-keyed so a candidate identified by name alone
|
||||
# sees the union and is never dropped on ambiguity.
|
||||
for key in {path, name} - {""}:
|
||||
entry = index.setdefault(key, {"regions": set(), "emulators": []})
|
||||
entry["regions"] |= regions
|
||||
if emu_name not in entry["emulators"]:
|
||||
entry["emulators"].append(emu_name)
|
||||
return index
|
||||
|
||||
|
||||
def lookup_regions(index: dict[str, dict], destination: str, name: str) -> set[str]:
|
||||
"""Look a candidate file up in the region index.
|
||||
|
||||
Tries the full destination, then progressively shorter path suffixes, then
|
||||
the bare name. Suffix matching bridges a platform entry whose destination
|
||||
carries an extra prefix to the profile entry that declares the region.
|
||||
"""
|
||||
if destination:
|
||||
entry = index.get(destination)
|
||||
if entry:
|
||||
return set(entry["regions"])
|
||||
parts = destination.split("/")
|
||||
for i in range(1, len(parts)):
|
||||
entry = index.get("/".join(parts[i:]))
|
||||
if entry:
|
||||
return set(entry["regions"])
|
||||
entry = index.get(name)
|
||||
return set(entry["regions"]) if entry else set()
|
||||
|
||||
|
||||
def _competing_ranks(
|
||||
members: list[tuple[str, str]],
|
||||
index: dict[str, dict],
|
||||
requested: list[str],
|
||||
) -> list[tuple[int, str]]:
|
||||
"""Rank the members of a group that compete regionally.
|
||||
|
||||
Files with no declared region, and files declared world, are excluded: they
|
||||
never compete and never drop.
|
||||
"""
|
||||
ranked: list[tuple[int, str]] = []
|
||||
for destination, name in members:
|
||||
regions = lookup_regions(index, destination, name)
|
||||
if not regions or WORLD in regions:
|
||||
continue
|
||||
ranked.append((rank(regions, requested), destination))
|
||||
return ranked
|
||||
|
||||
|
||||
def resolve_region_drops(
|
||||
groups: dict[str, list[tuple[str, str]]],
|
||||
index: dict[str, dict],
|
||||
requested: list[str],
|
||||
) -> set[str]:
|
||||
"""Destinations to skip for a requested region priority list.
|
||||
|
||||
Per group, only the best rank actually present survives. A destination kept
|
||||
by any group is kept overall.
|
||||
"""
|
||||
if not requested:
|
||||
return set()
|
||||
|
||||
keep: set[str] = set()
|
||||
drop: set[str] = set()
|
||||
for members in groups.values():
|
||||
ranked = _competing_ranks(members, index, requested)
|
||||
competing = {dest for _r, dest in ranked}
|
||||
keep |= {dest for dest, _name in members if dest not in competing}
|
||||
if not ranked:
|
||||
continue
|
||||
best = min(r for r, _ in ranked)
|
||||
for r, destination in ranked:
|
||||
if r == best:
|
||||
keep.add(destination)
|
||||
else:
|
||||
drop.add(destination)
|
||||
return drop - keep
|
||||
|
||||
|
||||
def fallback_groups(
|
||||
groups: dict[str, list[tuple[str, str]]],
|
||||
index: dict[str, dict],
|
||||
requested: list[str],
|
||||
) -> list[str]:
|
||||
"""Group IDs where no candidate matched, so the whole group was kept."""
|
||||
if not requested:
|
||||
return []
|
||||
out: list[str] = []
|
||||
for group_id, members in groups.items():
|
||||
ranked = _competing_ranks(members, index, requested)
|
||||
if ranked and min(r for r, _ in ranked) == len(requested):
|
||||
out.append(group_id)
|
||||
return sorted(out)
|
||||
@@ -0,0 +1,211 @@
|
||||
"""Cross-check profile region: values against dump-catalog provenance.
|
||||
|
||||
Advisory only. The emulator source decides what a region: value should be; this
|
||||
reports where an independent catalogue disagrees, so a hand-written value gets a
|
||||
second opinion before it is trusted.
|
||||
|
||||
Read the limits printed in the header before acting on anything here.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import collections
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||
|
||||
import region
|
||||
from common import load_emulator_profiles, load_database
|
||||
|
||||
# No-Intro filename tokens: full English territory names.
|
||||
NOINTRO = {
|
||||
"World": "world",
|
||||
"USA": "north-america",
|
||||
"Canada": "canada",
|
||||
"Europe": "europe",
|
||||
"Japan": "japan",
|
||||
"Asia": "asia",
|
||||
"Korea": "south-korea",
|
||||
"Brazil": "brazil",
|
||||
"Taiwan": "taiwan",
|
||||
"China": "china",
|
||||
"Hong Kong": "hong-kong",
|
||||
"France": "france",
|
||||
"Germany": "germany",
|
||||
"Spain": "spain",
|
||||
"Italy": "italy",
|
||||
"Netherlands": "netherlands",
|
||||
"Sweden": "sweden",
|
||||
"Norway": "norway",
|
||||
"Denmark": "denmark",
|
||||
"Finland": "finland",
|
||||
"Australia": "australia",
|
||||
"New Zealand": "new-zealand",
|
||||
"Russia": "russia",
|
||||
"UK": "uk",
|
||||
"Portugal": "portugal",
|
||||
"Greece": "greece",
|
||||
"Poland": "poland",
|
||||
"India": "india",
|
||||
"Mexico": "mexico",
|
||||
"Argentina": "argentina",
|
||||
"Latin America": "latin-america",
|
||||
}
|
||||
|
||||
_PAREN = re.compile(r"\(([^)]+)\)")
|
||||
|
||||
LIMITS = """Limits, read before trusting any line below.
|
||||
|
||||
TOSEC's country field is the country of ORIGIN, not the regional variant
|
||||
(TOSEC Naming Convention v4, p.12). A Korean MSX ROM published by Microsoft
|
||||
US is tagged (US). TOSEC names are therefore NOT read here.
|
||||
|
||||
Redump DATs carry no region at all: it exists in the redump.org database but
|
||||
is never exported, so a Redump entry contributes nothing.
|
||||
|
||||
Only No-Intro filename tokens are usable, and only for the fraction of repo
|
||||
files that carry one. Everything else is silent, not clean.
|
||||
|
||||
The emulator source decides. A disagreement is a prompt to reread the code,
|
||||
never a correction to apply on its own.
|
||||
"""
|
||||
|
||||
|
||||
def tokens_from_name(name: str) -> set[str]:
|
||||
"""Extract geography slugs from a No-Intro style DAT entry name."""
|
||||
found: set[str] = set()
|
||||
for group in _PAREN.findall(name):
|
||||
for part in (p.strip() for p in group.split(",")):
|
||||
slug = NOINTRO.get(part)
|
||||
if slug:
|
||||
found.add(slug)
|
||||
return found
|
||||
|
||||
|
||||
def catalog_regions(db: dict) -> dict[str, tuple[set[str], str]]:
|
||||
"""Map SHA1 to (geography slugs, source label) from No-Intro provenance."""
|
||||
out: dict[str, tuple[set[str], str]] = {}
|
||||
for sha1, record in db["files"].items():
|
||||
entry = (record.get("provenance") or {}).get("no-intro")
|
||||
if not entry:
|
||||
continue
|
||||
name = entry.get("name", "")
|
||||
found = tokens_from_name(name)
|
||||
if found:
|
||||
out[sha1] = (found, name)
|
||||
return out
|
||||
|
||||
|
||||
def resolve_sha1(file_entry: dict, db: dict) -> str | None:
|
||||
"""Resolve a profile file entry to a repo SHA1, or None when ambiguous."""
|
||||
files = db["files"]
|
||||
indexes = db["indexes"]
|
||||
declared = str(file_entry.get("sha1") or "").lower()
|
||||
if declared and declared in files:
|
||||
return declared
|
||||
md5 = str(file_entry.get("md5") or "").lower()
|
||||
if md5 and md5 in indexes["by_md5"]:
|
||||
return indexes["by_md5"][md5]
|
||||
hits = indexes["by_name"].get(file_entry.get("name", ""), [])
|
||||
if isinstance(hits, str):
|
||||
hits = [hits]
|
||||
return hits[0] if len(hits) == 1 else None
|
||||
|
||||
|
||||
def build_report(profiles: dict, db: dict) -> dict:
|
||||
"""Compare every declared region: against the No-Intro catalogue."""
|
||||
catalog = catalog_regions(db)
|
||||
agree = 0
|
||||
unchecked = 0
|
||||
conflicts: list[dict] = []
|
||||
vocabulary: collections.Counter = collections.Counter()
|
||||
for slugs, _name in catalog.values():
|
||||
vocabulary.update(slugs)
|
||||
|
||||
for emu_name, profile in sorted(profiles.items()):
|
||||
if profile.get("type") in ("launcher", "alias"):
|
||||
continue
|
||||
for f in profile.get("files") or []:
|
||||
if not isinstance(f, dict) or not f.get("region"):
|
||||
continue
|
||||
declared = region.normalize_declared(f["region"])
|
||||
sha1 = resolve_sha1(f, db)
|
||||
if not sha1 or sha1 not in catalog:
|
||||
unchecked += 1
|
||||
continue
|
||||
found, name = catalog[sha1]
|
||||
if region.WORLD in declared or any(
|
||||
region.comparable(d, c) for d in declared for c in found
|
||||
):
|
||||
agree += 1
|
||||
continue
|
||||
conflicts.append(
|
||||
{
|
||||
"emulator": emu_name,
|
||||
"file": f.get("name", ""),
|
||||
"profile": sorted(declared),
|
||||
"catalog": sorted(found),
|
||||
"dat_name": name,
|
||||
}
|
||||
)
|
||||
return {
|
||||
"agree": agree,
|
||||
"disagree": len(conflicts),
|
||||
"unchecked": unchecked,
|
||||
"catalog_files": len(catalog),
|
||||
"repo_files": len(db["files"]),
|
||||
"vocabulary": dict(vocabulary.most_common()),
|
||||
"conflicts": conflicts,
|
||||
}
|
||||
|
||||
|
||||
def print_report(report: dict) -> None:
|
||||
print(LIMITS)
|
||||
print(
|
||||
f"repo files: {report['repo_files']} "
|
||||
f"with a usable No-Intro region token: {report['catalog_files']}"
|
||||
)
|
||||
print(
|
||||
f"declared region: values -> agree {report['agree']}, "
|
||||
f"disagree {report['disagree']}, no catalogue entry {report['unchecked']}"
|
||||
)
|
||||
if report["vocabulary"]:
|
||||
print("\ncatalogue vocabulary present in this collection:")
|
||||
for slug, count in report["vocabulary"].items():
|
||||
print(f" {count:5d} {slug}")
|
||||
if not report["conflicts"]:
|
||||
print("\nNo disagreement.")
|
||||
return
|
||||
print("\nDISAGREEMENTS (reread the emulator source, do not apply blindly):\n")
|
||||
for c in report["conflicts"]:
|
||||
print(f" {c['emulator']}: {c['file']}")
|
||||
print(f" profile: {c['profile']}")
|
||||
print(f" catalogue: {c['catalog']} ({c['dat_name']})")
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Cross-check profile region: values against No-Intro provenance"
|
||||
)
|
||||
parser.add_argument("--db", default="database.json", help="Path to database.json")
|
||||
parser.add_argument("--emulators-dir", default="emulators")
|
||||
parser.add_argument("--json", action="store_true", help="Machine-readable output")
|
||||
args = parser.parse_args()
|
||||
|
||||
db = load_database(args.db)
|
||||
profiles = load_emulator_profiles(args.emulators_dir)
|
||||
report = build_report(profiles, db)
|
||||
|
||||
if args.json:
|
||||
print(json.dumps(report, indent=2, sort_keys=True))
|
||||
else:
|
||||
print_report(report)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
+1
-1
@@ -1495,7 +1495,7 @@ def print_emulator_result(result: dict, verbose: bool = False) -> None:
|
||||
f" Ground truth: {gt_cov['with_validation']}/{gt_cov['total']} files have emulator validation ({pct}%)"
|
||||
)
|
||||
if gt_cov["platform_only"]:
|
||||
print(f" {gt_cov['platform_only']} platform-only (no emulator profile)")
|
||||
print(f" {gt_cov['platform_only']} without declared validation")
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -5335,6 +5335,324 @@ struct BurnDriver BurnDrvneogeo = {
|
||||
self.assertLess(sizes[0], len(raw))
|
||||
self.assertEqual(sizes[0], sizes[1])
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# Region filter
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
def _region_profiles(self) -> dict:
|
||||
return {
|
||||
"rgncore": {
|
||||
"emulator": "RegionCore",
|
||||
"type": "libretro",
|
||||
"systems": ["region-console"],
|
||||
"files": [
|
||||
{"name": "rgn_us.bin", "region": ["north-america"]},
|
||||
{"name": "rgn_jp.bin", "region": ["japan"]},
|
||||
{"name": "rgn_eu.bin", "region": ["europe"]},
|
||||
{"name": "rgn_world.bin", "region": ["world"]},
|
||||
{"name": "rgn_plain.bin"},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
def _create_region_fixtures(self) -> None:
|
||||
names = (
|
||||
"rgn_us.bin",
|
||||
"rgn_jp.bin",
|
||||
"rgn_eu.bin",
|
||||
"rgn_world.bin",
|
||||
"rgn_plain.bin",
|
||||
)
|
||||
for name in names:
|
||||
self._make_file(name, name.encode())
|
||||
self.db = self._build_db()
|
||||
config = {
|
||||
"platform": "RegionPlat",
|
||||
"verification_mode": "existence",
|
||||
"systems": {
|
||||
"region-console": {
|
||||
"files": [
|
||||
{
|
||||
"name": n,
|
||||
"destination": n,
|
||||
"required": n
|
||||
in ("rgn_us.bin", "rgn_jp.bin", "rgn_eu.bin"),
|
||||
}
|
||||
for n in names
|
||||
]
|
||||
}
|
||||
},
|
||||
}
|
||||
with open(
|
||||
os.path.join(self.platforms_dir, "regionplat.yml"), "w", encoding="utf-8"
|
||||
) as fh:
|
||||
yaml.safe_dump(config, fh)
|
||||
|
||||
def _region_pack(self, **kwargs) -> str:
|
||||
from generate_pack import generate_pack
|
||||
|
||||
self._create_region_fixtures()
|
||||
out = generate_pack(
|
||||
"regionplat",
|
||||
self.platforms_dir,
|
||||
self.db,
|
||||
self.bios_dir,
|
||||
self.root,
|
||||
emu_profiles=self._region_profiles(),
|
||||
**kwargs,
|
||||
)
|
||||
self.assertIsNotNone(out)
|
||||
return out
|
||||
|
||||
@staticmethod
|
||||
def _names_in(zip_path: str) -> set[str]:
|
||||
with zipfile.ZipFile(zip_path) as zf:
|
||||
return {os.path.basename(n) for n in zf.namelist()}
|
||||
|
||||
def test_region_filter_drops_other_regions(self):
|
||||
names = self._names_in(self._region_pack(regions=["north-america"]))
|
||||
self.assertIn("rgn_us.bin", names)
|
||||
self.assertNotIn("rgn_jp.bin", names)
|
||||
self.assertNotIn("rgn_eu.bin", names)
|
||||
|
||||
def test_region_priority_selects_second_choice(self):
|
||||
names = self._names_in(self._region_pack(regions=["oceania", "europe"]))
|
||||
self.assertIn("rgn_eu.bin", names)
|
||||
self.assertNotIn("rgn_us.bin", names)
|
||||
self.assertNotIn("rgn_jp.bin", names)
|
||||
|
||||
def test_region_filter_keeps_group_with_no_match(self):
|
||||
names = self._names_in(self._region_pack(regions=["oceania"]))
|
||||
for expected in ("rgn_us.bin", "rgn_jp.bin", "rgn_eu.bin"):
|
||||
self.assertIn(expected, names)
|
||||
|
||||
def test_region_filter_keeps_untagged_and_world(self):
|
||||
names = self._names_in(self._region_pack(regions=["north-america"]))
|
||||
self.assertIn("rgn_plain.bin", names)
|
||||
self.assertIn("rgn_world.bin", names)
|
||||
|
||||
def test_region_tag_in_pack_name(self):
|
||||
out = self._region_pack(regions=["north-america", "japan"])
|
||||
self.assertIn("NorthAmerica_Japan", os.path.basename(out))
|
||||
|
||||
def test_no_region_argument_changes_nothing(self):
|
||||
names = self._names_in(self._region_pack())
|
||||
for expected in (
|
||||
"rgn_us.bin",
|
||||
"rgn_jp.bin",
|
||||
"rgn_eu.bin",
|
||||
"rgn_plain.bin",
|
||||
"rgn_world.bin",
|
||||
):
|
||||
self.assertIn(expected, names)
|
||||
|
||||
def test_region_composes_with_required_only(self):
|
||||
out = self._region_pack(regions=["north-america"], required_only=True)
|
||||
names = self._names_in(out)
|
||||
self.assertIn("rgn_us.bin", names)
|
||||
self.assertNotIn("rgn_jp.bin", names)
|
||||
self.assertNotIn("rgn_eu.bin", names)
|
||||
self.assertIn("NorthAmerica", os.path.basename(out))
|
||||
self.assertIn("Required", os.path.basename(out))
|
||||
|
||||
def test_region_composes_with_source_platform(self):
|
||||
names = self._names_in(
|
||||
self._region_pack(regions=["north-america"], source="platform")
|
||||
)
|
||||
self.assertIn("rgn_us.bin", names)
|
||||
self.assertNotIn("rgn_jp.bin", names)
|
||||
self.assertNotIn("rgn_eu.bin", names)
|
||||
self.assertIn("rgn_world.bin", names)
|
||||
self.assertIn("rgn_plain.bin", names)
|
||||
|
||||
def test_region_composes_with_split(self):
|
||||
from generate_pack import generate_split_packs
|
||||
|
||||
self._create_region_fixtures()
|
||||
paths = generate_split_packs(
|
||||
"regionplat",
|
||||
self.platforms_dir,
|
||||
self.db,
|
||||
self.bios_dir,
|
||||
self.root,
|
||||
emu_profiles=self._region_profiles(),
|
||||
regions=["north-america"],
|
||||
)
|
||||
self.assertTrue(paths)
|
||||
for p in paths:
|
||||
self.assertIn("NorthAmerica", os.path.basename(p))
|
||||
names = self._names_in(p)
|
||||
self.assertNotIn("rgn_jp.bin", names)
|
||||
self.assertNotIn("rgn_eu.bin", names)
|
||||
|
||||
def test_region_composes_with_manifest(self):
|
||||
from generate_pack import generate_manifest
|
||||
|
||||
self._create_region_fixtures()
|
||||
registry_path = os.path.join(self.platforms_dir, "_registry.yml")
|
||||
if not os.path.exists(registry_path):
|
||||
with open(registry_path, "w", encoding="utf-8") as fh:
|
||||
yaml.safe_dump({"platforms": {}}, fh)
|
||||
manifest = generate_manifest(
|
||||
"regionplat",
|
||||
self.platforms_dir,
|
||||
self.db,
|
||||
self.bios_dir,
|
||||
registry_path,
|
||||
emu_profiles=self._region_profiles(),
|
||||
regions=["north-america"],
|
||||
)
|
||||
packed = {os.path.basename(f["dest"]) for f in manifest["files"]}
|
||||
self.assertIn("rgn_us.bin", packed)
|
||||
self.assertNotIn("rgn_jp.bin", packed)
|
||||
self.assertNotIn("rgn_eu.bin", packed)
|
||||
|
||||
def test_manifest_without_region_keeps_every_variant(self):
|
||||
from generate_pack import generate_manifest
|
||||
|
||||
self._create_region_fixtures()
|
||||
registry_path = os.path.join(self.platforms_dir, "_registry.yml")
|
||||
if not os.path.exists(registry_path):
|
||||
with open(registry_path, "w", encoding="utf-8") as fh:
|
||||
yaml.safe_dump({"platforms": {}}, fh)
|
||||
manifest = generate_manifest(
|
||||
"regionplat",
|
||||
self.platforms_dir,
|
||||
self.db,
|
||||
self.bios_dir,
|
||||
registry_path,
|
||||
emu_profiles=self._region_profiles(),
|
||||
)
|
||||
packed = {os.path.basename(f["dest"]) for f in manifest["files"]}
|
||||
for expected in ("rgn_us.bin", "rgn_jp.bin", "rgn_eu.bin"):
|
||||
self.assertIn(expected, packed)
|
||||
|
||||
def test_region_composes_with_system_pack(self):
|
||||
from generate_pack import generate_system_pack
|
||||
|
||||
self._create_region_fixtures()
|
||||
os.makedirs(self.emulators_dir, exist_ok=True)
|
||||
with open(
|
||||
os.path.join(self.emulators_dir, "rgncore.yml"), "w", encoding="utf-8"
|
||||
) as fh:
|
||||
yaml.safe_dump(self._region_profiles()["rgncore"], fh)
|
||||
out = generate_system_pack(
|
||||
["region-console"],
|
||||
self.emulators_dir,
|
||||
self.db,
|
||||
self.bios_dir,
|
||||
self.root,
|
||||
regions=["north-america"],
|
||||
)
|
||||
self.assertIsNotNone(out)
|
||||
names = self._names_in(out)
|
||||
self.assertIn("rgn_us.bin", names)
|
||||
self.assertNotIn("rgn_jp.bin", names)
|
||||
|
||||
def test_readme_region_section_lists_fallback_systems(self):
|
||||
from generate_pack import _build_readme
|
||||
|
||||
text = _build_readme(
|
||||
"regionplat",
|
||||
"RegionPlat",
|
||||
"",
|
||||
10,
|
||||
2,
|
||||
regions=["north-america"],
|
||||
fallback_systems=["nintendo-fds"],
|
||||
)
|
||||
self.assertIn("PACK TYPE: Region Filtered", text)
|
||||
self.assertIn("Region priority: North America", text)
|
||||
self.assertIn("nintendo-fds", text)
|
||||
self.assertIn("This pack was filtered", text)
|
||||
self.assertNotIn("All are included", text)
|
||||
|
||||
def test_readme_without_region_keeps_the_regional_note(self):
|
||||
from generate_pack import _build_readme
|
||||
|
||||
text = _build_readme("regionplat", "RegionPlat", "", 10, 2)
|
||||
self.assertNotIn("PACK TYPE: Region Filtered", text)
|
||||
self.assertIn("All are included", text)
|
||||
|
||||
def test_verify_agrees_with_region_filtered_pack(self):
|
||||
from generate_pack import verify_pack_against_platform
|
||||
|
||||
out = self._region_pack(regions=["north-america"])
|
||||
result = verify_pack_against_platform(
|
||||
out,
|
||||
"regionplat",
|
||||
self.platforms_dir,
|
||||
db=self.db,
|
||||
emu_profiles=self._region_profiles(),
|
||||
regions=["north-america"],
|
||||
)
|
||||
ok, _checked, _present, errors = result[:4]
|
||||
self.assertTrue(ok, errors)
|
||||
|
||||
def test_verify_reports_the_gap_without_the_region_filter(self):
|
||||
from generate_pack import verify_pack_against_platform
|
||||
|
||||
out = self._region_pack(regions=["north-america"])
|
||||
result = verify_pack_against_platform(
|
||||
out,
|
||||
"regionplat",
|
||||
self.platforms_dir,
|
||||
db=self.db,
|
||||
emu_profiles=self._region_profiles(),
|
||||
)
|
||||
ok, _checked, _present, errors = result[:4]
|
||||
self.assertFalse(ok)
|
||||
self.assertTrue(any("rgn_jp.bin" in e for e in errors), errors)
|
||||
|
||||
def test_region_composes_with_source_truth(self):
|
||||
names = self._names_in(
|
||||
self._region_pack(regions=["north-america"], source="truth")
|
||||
)
|
||||
self.assertIn("rgn_us.bin", names)
|
||||
self.assertNotIn("rgn_jp.bin", names)
|
||||
|
||||
# ---- gap analysis resolves files the pack can already ship ----
|
||||
|
||||
@staticmethod
|
||||
def _gap_db(repo_name: str, sha1: str, crc32: str) -> dict:
|
||||
return {
|
||||
"files": {sha1: {"path": f"bios/{repo_name}", "size": 8192}},
|
||||
"indexes": {
|
||||
"by_name": {repo_name: [sha1]},
|
||||
"by_md5": {},
|
||||
"by_crc32": {crc32: sha1},
|
||||
"by_sha256": {},
|
||||
"by_path_suffix": {},
|
||||
},
|
||||
}
|
||||
|
||||
def _gap_source(self, entry: dict, db: dict) -> str:
|
||||
from cross_reference import cross_reference
|
||||
|
||||
report = cross_reference(
|
||||
{"gapemu": {"emulator": "GapEmu", "systems": ["s"], "files": [entry]}},
|
||||
{"s": set()},
|
||||
db,
|
||||
)
|
||||
return report["gapemu"]["gap_details"][0]["source"]
|
||||
|
||||
def test_gap_analysis_resolves_by_alias(self):
|
||||
"""A file stored under an alias is in the repo, not a sourcing target."""
|
||||
db = self._gap_db("REPO_chip.rom", "a" * 40, "465c4e1c")
|
||||
entry = {"name": "chip.rom", "aliases": ["REPO_chip.rom"], "required": True}
|
||||
self.assertEqual(self._gap_source(entry, db), "bios")
|
||||
|
||||
def test_gap_analysis_resolves_by_crc32(self):
|
||||
"""crc32 is the only hash some emulators declare."""
|
||||
db = self._gap_db("REPO_chip.rom", "a" * 40, "465c4e1c")
|
||||
entry = {"name": "chip.rom", "crc32": "465c4e1c", "required": True}
|
||||
self.assertEqual(self._gap_source(entry, db), "bios")
|
||||
|
||||
def test_gap_analysis_reports_unknown_file_missing(self):
|
||||
db = self._gap_db("REPO_chip.rom", "a" * 40, "465c4e1c")
|
||||
entry = {"name": "chip.rom", "aliases": ["OTHER.rom"], "crc32": "deadbeef"}
|
||||
self.assertEqual(self._gap_source(entry, db), "missing")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,423 @@
|
||||
"""Region vocabulary, hierarchy, and rank resolution."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "scripts"))
|
||||
|
||||
import region
|
||||
|
||||
|
||||
class TestVocabulary(unittest.TestCase):
|
||||
def test_world_is_a_region(self):
|
||||
self.assertIn(region.WORLD, region.REGIONS)
|
||||
|
||||
def test_super_regions_and_members_are_all_regions(self):
|
||||
for parent, members in region.REGION_TREE.items():
|
||||
self.assertIn(parent, region.REGIONS)
|
||||
for member in members:
|
||||
self.assertIn(member, region.REGIONS)
|
||||
|
||||
def test_hierarchy_is_two_levels(self):
|
||||
for members in region.REGION_TREE.values():
|
||||
for member in members:
|
||||
self.assertNotIn(member, region.REGION_TREE)
|
||||
|
||||
def test_no_member_has_two_parents(self):
|
||||
seen: set[str] = set()
|
||||
for members in region.REGION_TREE.values():
|
||||
for member in members:
|
||||
self.assertNotIn(member, seen)
|
||||
seen.add(member)
|
||||
|
||||
|
||||
class TestComparable(unittest.TestCase):
|
||||
def test_equal(self):
|
||||
self.assertTrue(region.comparable("japan", "japan"))
|
||||
|
||||
def test_parent_matches_child(self):
|
||||
self.assertTrue(region.comparable("europe", "france"))
|
||||
|
||||
def test_child_matches_parent(self):
|
||||
self.assertTrue(region.comparable("france", "europe"))
|
||||
|
||||
def test_siblings_do_not_match(self):
|
||||
self.assertFalse(region.comparable("france", "germany"))
|
||||
|
||||
def test_disjoint_super_regions_do_not_match(self):
|
||||
self.assertFalse(region.comparable("europe", "asia"))
|
||||
|
||||
def test_saturn_fused_slug_is_under_asia(self):
|
||||
self.assertTrue(region.comparable("asia", "asia-ntsc"))
|
||||
|
||||
|
||||
class TestNormalizeDeclared(unittest.TestCase):
|
||||
def test_none_is_empty(self):
|
||||
self.assertEqual(region.normalize_declared(None), set())
|
||||
|
||||
def test_list_passes_through(self):
|
||||
self.assertEqual(region.normalize_declared(["japan"]), {"japan"})
|
||||
|
||||
def test_legacy_signal_string_maps_to_geography(self):
|
||||
self.assertEqual(region.normalize_declared("NTSC-J"), {"japan"})
|
||||
self.assertEqual(region.normalize_declared("NTSC-U"), {"north-america"})
|
||||
self.assertEqual(region.normalize_declared("PAL"), {"europe"})
|
||||
|
||||
def test_legacy_auto_and_world_map_to_world(self):
|
||||
self.assertEqual(region.normalize_declared("Auto"), {region.WORLD})
|
||||
self.assertEqual(region.normalize_declared("World"), {region.WORLD})
|
||||
|
||||
def test_unknown_value_raises(self):
|
||||
with self.assertRaises(ValueError):
|
||||
region.normalize_declared("atlantis")
|
||||
|
||||
|
||||
class TestParseRequested(unittest.TestCase):
|
||||
def test_single_alias(self):
|
||||
self.assertEqual(region.parse_requested("us"), ["north-america"])
|
||||
|
||||
def test_order_is_preserved(self):
|
||||
self.assertEqual(
|
||||
region.parse_requested("us,eu,jp"),
|
||||
["north-america", "europe", "japan"],
|
||||
)
|
||||
|
||||
def test_duplicates_collapse_keeping_first_position(self):
|
||||
self.assertEqual(
|
||||
region.parse_requested("us,usa,eu"), ["north-america", "europe"]
|
||||
)
|
||||
|
||||
def test_whitespace_and_case_tolerated(self):
|
||||
self.assertEqual(
|
||||
region.parse_requested(" US , Eu "), ["north-america", "europe"]
|
||||
)
|
||||
|
||||
def test_unknown_region_raises(self):
|
||||
with self.assertRaises(ValueError):
|
||||
region.parse_requested("us,atlantis")
|
||||
|
||||
def test_empty_raises(self):
|
||||
with self.assertRaises(ValueError):
|
||||
region.parse_requested(" ")
|
||||
|
||||
|
||||
class TestRank(unittest.TestCase):
|
||||
def test_exact_match_is_zero(self):
|
||||
self.assertEqual(region.rank({"north-america"}, ["north-america"]), 0)
|
||||
|
||||
def test_second_choice_is_one(self):
|
||||
self.assertEqual(region.rank({"europe"}, ["north-america", "europe"]), 1)
|
||||
|
||||
def test_no_match_is_lowest(self):
|
||||
self.assertEqual(region.rank({"japan"}, ["north-america", "europe"]), 2)
|
||||
|
||||
def test_child_matches_requested_parent(self):
|
||||
self.assertEqual(region.rank({"france"}, ["europe"]), 0)
|
||||
|
||||
def test_parent_matches_requested_child(self):
|
||||
self.assertEqual(region.rank({"europe"}, ["france"]), 0)
|
||||
|
||||
def test_best_of_several_declared_regions_wins(self):
|
||||
self.assertEqual(
|
||||
region.rank({"japan", "north-america"}, ["north-america", "japan"]), 0
|
||||
)
|
||||
|
||||
def test_empty_declared_is_lowest(self):
|
||||
self.assertEqual(region.rank(set(), ["north-america"]), 1)
|
||||
|
||||
|
||||
class TestRegionTag(unittest.TestCase):
|
||||
def test_single(self):
|
||||
self.assertEqual(region.region_tag(["north-america"]), "NorthAmerica")
|
||||
|
||||
def test_multiple_joined(self):
|
||||
self.assertEqual(
|
||||
region.region_tag(["north-america", "japan"]), "NorthAmerica_Japan"
|
||||
)
|
||||
|
||||
|
||||
class TestRegionIndex(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.profiles = {
|
||||
"dolphin": {
|
||||
"files": [
|
||||
{
|
||||
"name": "IPL.bin",
|
||||
"path": "GC/USA/IPL.bin",
|
||||
"region": ["north-america"],
|
||||
},
|
||||
{
|
||||
"name": "IPL.bin",
|
||||
"path": "GC/EUR/IPL.bin",
|
||||
"region": ["europe"],
|
||||
},
|
||||
{
|
||||
"name": "IPL.bin",
|
||||
"path": "GC/JAP/IPL.bin",
|
||||
"region": ["japan"],
|
||||
},
|
||||
{"name": "dsp_rom.bin"},
|
||||
]
|
||||
},
|
||||
"beetle_psx": {
|
||||
"files": [
|
||||
{"name": "scph5501.bin", "region": "NTSC-U"},
|
||||
{"name": "psxonpsp660.bin", "region": "Auto"},
|
||||
]
|
||||
},
|
||||
"swanstation": {
|
||||
"files": [{"name": "scph5501.bin", "region": "NTSC-U"}]
|
||||
},
|
||||
"launcher_profile": {
|
||||
"type": "launcher",
|
||||
"files": [{"name": "ignored.bin", "region": ["japan"]}],
|
||||
},
|
||||
}
|
||||
self.index = region.build_region_index(self.profiles)
|
||||
|
||||
def test_path_keyed_entries_stay_separate(self):
|
||||
self.assertEqual(
|
||||
region.lookup_regions(self.index, "GC/USA/IPL.bin", "IPL.bin"),
|
||||
{"north-america"},
|
||||
)
|
||||
self.assertEqual(
|
||||
region.lookup_regions(self.index, "GC/JAP/IPL.bin", "IPL.bin"),
|
||||
{"japan"},
|
||||
)
|
||||
|
||||
def test_bare_name_lookup_unions_ambiguous_entries(self):
|
||||
self.assertEqual(
|
||||
region.lookup_regions(self.index, "", "IPL.bin"),
|
||||
{"north-america", "europe", "japan"},
|
||||
)
|
||||
|
||||
def test_destination_suffix_bridges_platform_entry(self):
|
||||
self.assertEqual(
|
||||
region.lookup_regions(
|
||||
self.index, "dolphin-emu/GC/EUR/IPL.bin", "IPL.bin"
|
||||
),
|
||||
{"europe"},
|
||||
)
|
||||
|
||||
def test_untagged_file_has_no_regions(self):
|
||||
self.assertEqual(
|
||||
region.lookup_regions(self.index, "dsp_rom.bin", "dsp_rom.bin"), set()
|
||||
)
|
||||
|
||||
def test_unknown_file_has_no_regions(self):
|
||||
self.assertEqual(
|
||||
region.lookup_regions(self.index, "nope.bin", "nope.bin"), set()
|
||||
)
|
||||
|
||||
def test_legacy_string_values_are_normalised(self):
|
||||
self.assertEqual(
|
||||
region.lookup_regions(self.index, "scph5501.bin", "scph5501.bin"),
|
||||
{"north-america"},
|
||||
)
|
||||
self.assertEqual(
|
||||
region.lookup_regions(self.index, "psxonpsp660.bin", "psxonpsp660.bin"),
|
||||
{region.WORLD},
|
||||
)
|
||||
|
||||
def test_two_profiles_agreeing_merge(self):
|
||||
entry = self.index["scph5501.bin"]
|
||||
self.assertEqual(entry["regions"], {"north-america"})
|
||||
self.assertEqual(entry["emulators"], ["beetle_psx", "swanstation"])
|
||||
|
||||
def test_launcher_profiles_are_skipped(self):
|
||||
self.assertEqual(
|
||||
region.lookup_regions(self.index, "ignored.bin", "ignored.bin"), set()
|
||||
)
|
||||
|
||||
def test_unknown_value_names_the_profile_and_file(self):
|
||||
with self.assertRaises(ValueError) as ctx:
|
||||
region.build_region_index(
|
||||
{"badcore": {"files": [{"name": "x.bin", "region": ["atlantis"]}]}}
|
||||
)
|
||||
self.assertIn("badcore", str(ctx.exception))
|
||||
self.assertIn("x.bin", str(ctx.exception))
|
||||
|
||||
|
||||
class TestResolveRegionDrops(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.index = region.build_region_index(
|
||||
{
|
||||
"psx": {
|
||||
"files": [
|
||||
{"name": "scph5501.bin", "region": ["north-america"]},
|
||||
{"name": "scph5500.bin", "region": ["japan"]},
|
||||
{"name": "scph5502.bin", "region": ["europe"]},
|
||||
{"name": "psxonpsp660.bin", "region": ["world"]},
|
||||
{"name": "gamepad.cfg"},
|
||||
]
|
||||
},
|
||||
"fds": {"files": [{"name": "disksys.rom", "region": ["japan"]}]},
|
||||
"o2em": {"files": [{"name": "c52.bin", "region": ["france"]}]},
|
||||
}
|
||||
)
|
||||
|
||||
def _drops(self, groups, requested):
|
||||
return region.resolve_region_drops(groups, self.index, requested)
|
||||
|
||||
def test_best_rank_wins_and_siblings_drop(self):
|
||||
groups = {
|
||||
"psx": [
|
||||
("scph5501.bin", "scph5501.bin"),
|
||||
("scph5500.bin", "scph5500.bin"),
|
||||
("scph5502.bin", "scph5502.bin"),
|
||||
]
|
||||
}
|
||||
self.assertEqual(
|
||||
self._drops(groups, ["north-america"]),
|
||||
{"scph5500.bin", "scph5502.bin"},
|
||||
)
|
||||
|
||||
def test_priority_order_selects_second_choice(self):
|
||||
groups = {
|
||||
"psx": [
|
||||
("scph5500.bin", "scph5500.bin"),
|
||||
("scph5502.bin", "scph5502.bin"),
|
||||
]
|
||||
}
|
||||
self.assertEqual(
|
||||
self._drops(groups, ["north-america", "europe", "japan"]),
|
||||
{"scph5500.bin"},
|
||||
)
|
||||
|
||||
def test_group_with_no_match_is_kept_whole(self):
|
||||
groups = {"fds": [("disksys.rom", "disksys.rom")]}
|
||||
self.assertEqual(self._drops(groups, ["north-america"]), set())
|
||||
|
||||
def test_world_never_competes(self):
|
||||
groups = {
|
||||
"psx": [
|
||||
("scph5501.bin", "scph5501.bin"),
|
||||
("scph5500.bin", "scph5500.bin"),
|
||||
("psxonpsp660.bin", "psxonpsp660.bin"),
|
||||
]
|
||||
}
|
||||
self.assertEqual(self._drops(groups, ["north-america"]), {"scph5500.bin"})
|
||||
|
||||
def test_untagged_files_never_drop(self):
|
||||
groups = {
|
||||
"psx": [
|
||||
("scph5501.bin", "scph5501.bin"),
|
||||
("gamepad.cfg", "gamepad.cfg"),
|
||||
("scph5500.bin", "scph5500.bin"),
|
||||
]
|
||||
}
|
||||
self.assertEqual(self._drops(groups, ["north-america"]), {"scph5500.bin"})
|
||||
|
||||
def test_child_region_satisfies_requested_parent(self):
|
||||
groups = {"o2em": [("c52.bin", "c52.bin")]}
|
||||
self.assertEqual(self._drops(groups, ["europe"]), set())
|
||||
|
||||
def test_destination_kept_by_any_group_is_not_dropped(self):
|
||||
groups = {
|
||||
"psx": [
|
||||
("scph5501.bin", "scph5501.bin"),
|
||||
("scph5500.bin", "scph5500.bin"),
|
||||
],
|
||||
"psx-import": [("scph5500.bin", "scph5500.bin")],
|
||||
}
|
||||
self.assertEqual(self._drops(groups, ["north-america"]), set())
|
||||
|
||||
def test_no_requested_regions_drops_nothing(self):
|
||||
groups = {"psx": [("scph5500.bin", "scph5500.bin")]}
|
||||
self.assertEqual(self._drops(groups, []), set())
|
||||
|
||||
|
||||
class TestFallbackGroups(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.index = region.build_region_index(
|
||||
{
|
||||
"psx": {
|
||||
"files": [
|
||||
{"name": "scph5501.bin", "region": ["north-america"]}
|
||||
]
|
||||
},
|
||||
"fds": {"files": [{"name": "disksys.rom", "region": ["japan"]}]},
|
||||
}
|
||||
)
|
||||
|
||||
def test_only_unmatched_groups_are_reported(self):
|
||||
groups = {
|
||||
"psx": [("scph5501.bin", "scph5501.bin")],
|
||||
"fds": [("disksys.rom", "disksys.rom")],
|
||||
}
|
||||
self.assertEqual(
|
||||
region.fallback_groups(groups, self.index, ["north-america"]), ["fds"]
|
||||
)
|
||||
|
||||
def test_no_requested_regions_reports_nothing(self):
|
||||
groups = {"fds": [("disksys.rom", "disksys.rom")]}
|
||||
self.assertEqual(region.fallback_groups(groups, self.index, []), [])
|
||||
|
||||
|
||||
class TestProfileConformance(unittest.TestCase):
|
||||
"""Every region: value in the repo is a list of canonical slugs."""
|
||||
|
||||
def setUp(self):
|
||||
import glob
|
||||
|
||||
import yaml
|
||||
|
||||
self.repo_profiles = sorted(
|
||||
glob.glob(
|
||||
os.path.join(os.path.dirname(__file__), "..", "emulators", "*.yml")
|
||||
)
|
||||
)
|
||||
if not self.repo_profiles:
|
||||
self.skipTest("emulators/ not present")
|
||||
self.yaml = yaml
|
||||
|
||||
def test_all_region_values_are_canonical_lists(self):
|
||||
offenders: list[str] = []
|
||||
for path in self.repo_profiles:
|
||||
with open(path, encoding="utf-8") as fh:
|
||||
profile = self.yaml.safe_load(fh) or {}
|
||||
for f in profile.get("files") or []:
|
||||
if not isinstance(f, dict) or "region" not in f:
|
||||
continue
|
||||
raw = f["region"]
|
||||
if not isinstance(raw, list):
|
||||
offenders.append(
|
||||
f"{os.path.basename(path)}: {f.get('name')} not a list"
|
||||
)
|
||||
continue
|
||||
for value in raw:
|
||||
if value not in region.REGIONS:
|
||||
offenders.append(
|
||||
f"{os.path.basename(path)}: {f.get('name')} -> {value!r}"
|
||||
)
|
||||
self.assertEqual(offenders, [], "\n".join(offenders))
|
||||
|
||||
def test_index_builds_over_every_repo_profile(self):
|
||||
profiles = {}
|
||||
for path in self.repo_profiles:
|
||||
with open(path, encoding="utf-8") as fh:
|
||||
profiles[os.path.basename(path)[:-4]] = self.yaml.safe_load(fh) or {}
|
||||
index = region.build_region_index(profiles)
|
||||
self.assertGreater(len(index), 0)
|
||||
|
||||
|
||||
class TestSchemaMatchesModule(unittest.TestCase):
|
||||
def test_schema_enum_equals_module_regions(self):
|
||||
import json
|
||||
|
||||
path = os.path.join(
|
||||
os.path.dirname(__file__), "..", "schemas", "emulator.schema.json"
|
||||
)
|
||||
if not os.path.exists(path):
|
||||
self.skipTest("schema not present")
|
||||
with open(path, encoding="utf-8") as fh:
|
||||
schema = json.load(fh)
|
||||
node = schema["properties"]["files"]["items"]["properties"]["region"]
|
||||
self.assertEqual(set(node["items"]["enum"]), set(region.REGIONS))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -56,6 +56,56 @@ What counts as "required" depends on the platform YAML. For existence-mode platf
|
||||
For MD5-mode platforms (Batocera), all declared files are treated as required unless
|
||||
explicitly marked optional.
|
||||
|
||||
### Region filtering
|
||||
|
||||
Keep one regional BIOS per system instead of all of them:
|
||||
|
||||
```bash
|
||||
python scripts/generate_pack.py --platform retroarch --region us
|
||||
python scripts/generate_pack.py --platform retroarch --region us,eu,jp
|
||||
```
|
||||
|
||||
The list is an **ordered priority**, best first, the same model ROM managers use
|
||||
for 1G1R. `--region us,eu,jp` means prefer North America, fall back to Europe,
|
||||
fall back to Japan. It does not mean "keep all three": when a US BIOS exists for
|
||||
a system, the European and Japanese ones are dropped.
|
||||
|
||||
Accepted names are territory slugs (`japan`, `north-america`, `europe`,
|
||||
`south-korea`, `taiwan`, `brazil`, `france`...) plus short aliases (`jp`, `us`,
|
||||
`eu`, `kr`) and the signal spellings (`ntsc-j`, `ntsc-u`, `pal`). Territories
|
||||
nest, so `--region europe` keeps a BIOS declared `[france]`, and `--region france`
|
||||
keeps one declared `[europe]`.
|
||||
|
||||
Three rules keep a filtered pack usable:
|
||||
|
||||
- **No system is ever emptied.** If nothing matches, the whole group is kept.
|
||||
`--region us` still ships `disksys.rom`, because the Famicom Disk System has no
|
||||
American BIOS.
|
||||
- **Region-free files always survive.** `psxonpsp660.bin`, `ps1_rom.bin` and
|
||||
`openbios.bin` are declared `[world]`; they add a capability rather than
|
||||
competing regionally.
|
||||
- **Untagged files always survive.** Most BIOS are not regional and carry no
|
||||
`region:` at all.
|
||||
|
||||
What this does and does not do: it shrinks the pack. It does not change how cores
|
||||
pick a BIOS. Most cores already select per region from fixed filename lists driven
|
||||
by the game's region (PicoDrive in `libretro.c`, Genesis Plus GX in `loadrom.c`),
|
||||
so a USA game already gets the US BIOS when it is present. Only cores that load a
|
||||
single unqualified file change behaviour. Loading imports from another region may
|
||||
need the unfiltered pack.
|
||||
|
||||
Verification takes the same flag, and needs it: a filtered pack checked without it
|
||||
reports every dropped file as missing.
|
||||
|
||||
```bash
|
||||
python scripts/generate_pack.py --platform recalbox --region us --verify-packs
|
||||
```
|
||||
|
||||
`--region` composes with `--split`, `--target`, `--required-only`, `--source`,
|
||||
`--emulator` and `--system`. It is mutually exclusive with `--from-md5`, which
|
||||
selects by hash. `pipeline.py` never passes it, so the released packs stay
|
||||
complete.
|
||||
|
||||
|
||||
## Pack Source Variants
|
||||
|
||||
|
||||
Reference in new issue
Block a user