20 Commits

Author SHA1 Message Date
Abdessamad Derraz
caf6285a04 fix: skip entries without md5 in batocera and retrobat exports 2026-03-30 17:46:48 +02:00
Abdessamad Derraz
529cb8a915 fix: recalbox paths from scrape, batocera md5 fallback from scrape 2026-03-30 17:35:39 +02:00
Abdessamad Derraz
1146fdf177 fix: rewrite emudeck exporter to match exact checkBIOS.sh format 2026-03-30 17:21:56 +02:00
Abdessamad Derraz
4fbb3571f8 fix: exporters use _dest fallback, merge colliding systems, per-platform subdirs 2026-03-30 17:15:44 +02:00
Abdessamad Derraz
0be68edad0 feat: add exporters for lakka, retropie, emudeck, retrodeck, romm 2026-03-30 17:07:08 +02:00
Abdessamad Derraz
1ffc4f89ca refactor: registry merge is fully flexible, no hardcoded lists 2026-03-30 16:38:13 +02:00
Abdessamad Derraz
f1ebfff5bd refactor: registry merge uses exclusion list instead of hardcoded fields 2026-03-30 16:36:40 +02:00
Abdessamad Derraz
425ea064ae fix: scrapers merge into existing YAML instead of overwriting 2026-03-30 16:31:40 +02:00
Abdessamad Derraz
6818a18a42 feat: load_platform_config merges all metadata from registry 2026-03-30 16:24:40 +02:00
Abdessamad Derraz
c11de6dba6 fix: restore retroarch.yml fields lost by scraper regeneration 2026-03-30 16:22:16 +02:00
Abdessamad Derraz
c4f3192020 fix: system.dat rom quoting, native_ids, acronym display names 2026-03-30 16:17:50 +02:00
Abdessamad Derraz
e2d0510f4e fix: exporters match exact native formats with display names 2026-03-30 16:09:02 +02:00
Abdessamad Derraz
74269bab84 fix: rewrite exporters to match exact native formats 2026-03-30 15:49:33 +02:00
Abdessamad Derraz
1e6b499602 feat: add batocera, recalbox, retrobat native exporters 2026-03-30 15:31:44 +02:00
Abdessamad Derraz
9b785ec785 feat: add missing laseractive sega pac bios files
v1.05 japan and v1.01 japan from archive.org.
v1.04 us variant alias for pioneer-named file.
resolves retrobat laseractive missing files.
2026-03-30 15:16:23 +02:00
Abdessamad Derraz
d415777f2c feat: add PS3UPDAT.PUP to rpcs3 profile
storage: large_file, validated via SCEUF magic + HMAC-SHA1
per entry (PUP.cpp:23-77). also adds missing standard fields
(cores, core_classification, upstream), removes non-standard
firmware_file/validation/firmware_version fields.
2026-03-30 15:06:51 +02:00
Abdessamad Derraz
eafabd20f3 refactor: skip writing generated files when content unchanged
write_if_changed in common.py compares content after stripping
timestamps (generated_at, Auto-generated on, Generated on).
applied to generate_db, generate_readme, generate_site.
eliminates timestamp-only diffs in database.json, README.md,
mkdocs.yml, and 423 docs pages.
2026-03-30 14:33:44 +02:00
Abdessamad Derraz
2aca4927c0 chore: regenerate database, readme, manifests, site 2026-03-30 14:19:00 +02:00
Abdessamad Derraz
17777f315b feat: agnostic bios mode for filename-agnostic emulators
bios_mode: agnostic (profile) and agnostic: true (file) for
emulators that accept any valid BIOS without specific filename.
find_undeclared_files skips agnostic entries, pack extras scan
includes all matching DB files by path prefix + size criteria,
resolve_local_file has agnostic fallback with rename README.
applied to pcsx2, lrps2 (bios_mode), melonds dsi_nand (file).
2026-03-30 14:18:54 +02:00
Abdessamad Derraz
692484d32d refactor: remove false aliases from pcsx2 and melonds profiles
aliases must be same-SHA1 alternative names, not distinct files.
pcsx2: 164 different BIOS dumps are separate DB entries, not aliases.
melonds: 6 regional NAND dumps are separate DB entries, not aliases.
also cleans pcsx2 non-standard fields, fixes display_name.
2026-03-30 12:37:32 +02:00
43 changed files with 1887 additions and 339 deletions

View File

@@ -130,4 +130,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
This repository provides BIOS files for personal backup and archival purposes. This repository provides BIOS files for personal backup and archival purposes.
*Auto-generated on 2026-03-30T07:40:45Z* *Auto-generated on 2026-03-30T12:09:51Z*

View File

@@ -1,7 +1,7 @@
{ {
"generated_at": "2026-03-30T07:24:43Z", "generated_at": "2026-03-30T13:15:58Z",
"total_files": 7239, "total_files": 7241,
"total_size": 8539795099, "total_size": 8540057243,
"files": { "files": {
"520d3d1b5897800af47f92efd2444a26b7a7dead": { "520d3d1b5897800af47f92efd2444a26b7a7dead": {
"path": "bios/3DO Company/3DO/3do_arcade_saot.bin", "path": "bios/3DO Company/3DO/3do_arcade_saot.bin",
@@ -40843,16 +40843,6 @@
"crc32": "11647ca5", "crc32": "11647ca5",
"adler32": "1817f6f4" "adler32": "1817f6f4"
}, },
"26237b333db4a4c6770297fa5e655ea95840d5d9": {
"path": "bios/Pioneer/LaserActive/Pioneer LaserActive Sega PAC Boot ROM v1.02 (1993)(Pioneer - Sega)(JP)(en-ja).bin",
"name": "Pioneer LaserActive Sega PAC Boot ROM v1.02 (1993)(Pioneer - Sega)(JP)(en-ja).bin",
"size": 131072,
"sha1": "26237b333db4a4c6770297fa5e655ea95840d5d9",
"md5": "a5a2f9aae57d464bc66b80ee79c3da6e",
"sha256": "dca942d977217f703d8d1c6eb1aeb6b32c78ecc421486bbb46c459d385161c94",
"crc32": "00eedb3a",
"adler32": "94a45a21"
},
"aa811861f8874775075bd3f53008c8aaf59b07db": { "aa811861f8874775075bd3f53008c8aaf59b07db": {
"path": "bios/Pioneer/LaserActive/Pioneer LaserActive Sega PAC Boot ROM v1.04 (1993)(Pioneer - Sega)(US).bin", "path": "bios/Pioneer/LaserActive/Pioneer LaserActive Sega PAC Boot ROM v1.04 (1993)(Pioneer - Sega)(US).bin",
"name": "Pioneer LaserActive Sega PAC Boot ROM v1.04 (1993)(Pioneer - Sega)(US).bin", "name": "Pioneer LaserActive Sega PAC Boot ROM v1.04 (1993)(Pioneer - Sega)(US).bin",
@@ -40863,6 +40853,16 @@
"crc32": "50cd3d23", "crc32": "50cd3d23",
"adler32": "72a13133" "adler32": "72a13133"
}, },
"26237b333db4a4c6770297fa5e655ea95840d5d9": {
"path": "bios/Pioneer/LaserActive/Pioneer LaserActive Sega PAC Boot ROM v1.02 (1993)(Pioneer - Sega)(JP)(en-ja).bin",
"name": "Pioneer LaserActive Sega PAC Boot ROM v1.02 (1993)(Pioneer - Sega)(JP)(en-ja).bin",
"size": 131072,
"sha1": "26237b333db4a4c6770297fa5e655ea95840d5d9",
"md5": "a5a2f9aae57d464bc66b80ee79c3da6e",
"sha256": "dca942d977217f703d8d1c6eb1aeb6b32c78ecc421486bbb46c459d385161c94",
"crc32": "00eedb3a",
"adler32": "94a45a21"
},
"6973e2593e66fd21627fedccec98d4a364afaaff": { "6973e2593e66fd21627fedccec98d4a364afaaff": {
"path": "bios/Pioneer/LaserActive/[BIOS] LaserActive PAC-N1 (Japan) (v1.02).bin", "path": "bios/Pioneer/LaserActive/[BIOS] LaserActive PAC-N1 (Japan) (v1.02).bin",
"name": "[BIOS] LaserActive PAC-N1 (Japan) (v1.02).bin", "name": "[BIOS] LaserActive PAC-N1 (Japan) (v1.02).bin",
@@ -40883,6 +40883,26 @@
"crc32": "01223dd5", "crc32": "01223dd5",
"adler32": "74e98625" "adler32": "74e98625"
}, },
"bc746df0c5d2b779ca41a94954b60d6ac6a7c2a3": {
"path": "bios/Pioneer/LaserActive/[BIOS] LaserActive PAC-S1 (Japan) (v1.01).bin",
"name": "[BIOS] LaserActive PAC-S1 (Japan) (v1.01).bin",
"size": 131072,
"sha1": "bc746df0c5d2b779ca41a94954b60d6ac6a7c2a3",
"md5": "219dc2aa7cb8d1ad5142c86d50c2ffa5",
"sha256": "3300e3771b468b41818b90c2358ff288da69bada92b8247acd793a437b30731c",
"crc32": "24ad336e",
"adler32": "6993a1dd"
},
"f7101b40cae0484a0f43f3bbee2d55033ff1e3ec": {
"path": "bios/Pioneer/LaserActive/[BIOS] LaserActive PAC-S1 (Japan) (v1.05).bin",
"name": "[BIOS] LaserActive PAC-S1 (Japan) (v1.05).bin",
"size": 131072,
"sha1": "f7101b40cae0484a0f43f3bbee2d55033ff1e3ec",
"md5": "515636778430c8e01027234c38c4ddf8",
"sha256": "369c1e699abbaecd231a3ab7b98443d28097366ff3cdd978698417195d54ca3c",
"crc32": "1493522c",
"adler32": "9e972082"
},
"c1b9202cbe072db12114b223a9ba5374b30718fb": { "c1b9202cbe072db12114b223a9ba5374b30718fb": {
"path": "bios/Pioneer/LaserActive/[BIOS] LaserActive PCE-LP1 (Japan) (v1.02).bin", "path": "bios/Pioneer/LaserActive/[BIOS] LaserActive PCE-LP1 (Japan) (v1.02).bin",
"name": "[BIOS] LaserActive PCE-LP1 (Japan) (v1.02).bin", "name": "[BIOS] LaserActive PCE-LP1 (Japan) (v1.02).bin",
@@ -76480,10 +76500,12 @@
"c500ff71236068e0dc0d0603d265ae76": "5130243429b40b01a14e1304d0394b8459a6fbae", "c500ff71236068e0dc0d0603d265ae76": "5130243429b40b01a14e1304d0394b8459a6fbae",
"f1071cdb0b6b10dde94d3bc8a6146387": "a6120aed50831c9c0d95dbdf707820f601d9452e", "f1071cdb0b6b10dde94d3bc8a6146387": "a6120aed50831c9c0d95dbdf707820f601d9452e",
"279008e4a0db2dc5f1c048853b033828": "54b8d2c1317628de51a85fc1c424423a986775e4", "279008e4a0db2dc5f1c048853b033828": "54b8d2c1317628de51a85fc1c424423a986775e4",
"a5a2f9aae57d464bc66b80ee79c3da6e": "26237b333db4a4c6770297fa5e655ea95840d5d9",
"0e7393cd0951d6dde818fcd4cd819466": "aa811861f8874775075bd3f53008c8aaf59b07db", "0e7393cd0951d6dde818fcd4cd819466": "aa811861f8874775075bd3f53008c8aaf59b07db",
"a5a2f9aae57d464bc66b80ee79c3da6e": "26237b333db4a4c6770297fa5e655ea95840d5d9",
"f69f173b251d8bf7649b10a9167a10bf": "6973e2593e66fd21627fedccec98d4a364afaaff", "f69f173b251d8bf7649b10a9167a10bf": "6973e2593e66fd21627fedccec98d4a364afaaff",
"f0fb8a4605ac7eefbafd4f2d5a793cc8": "f7412aa822d70a55b2ff3d7095137263dc54f6b6", "f0fb8a4605ac7eefbafd4f2d5a793cc8": "f7412aa822d70a55b2ff3d7095137263dc54f6b6",
"219dc2aa7cb8d1ad5142c86d50c2ffa5": "bc746df0c5d2b779ca41a94954b60d6ac6a7c2a3",
"515636778430c8e01027234c38c4ddf8": "f7101b40cae0484a0f43f3bbee2d55033ff1e3ec",
"761fea207d0eafd4cfd78da7c44cac88": "c1b9202cbe072db12114b223a9ba5374b30718fb", "761fea207d0eafd4cfd78da7c44cac88": "c1b9202cbe072db12114b223a9ba5374b30718fb",
"69489153dde910a69d5ae6de5dd65323": "f2a9ce387019bf272c6e3459d961b30f28942ac5", "69489153dde910a69d5ae6de5dd65323": "f2a9ce387019bf272c6e3459d961b30f28942ac5",
"3fd0d13282b031f4c017cd6bf6597183": "9dda4cbcc1d3f6d38c10fee3de53b9abd5e47ec0", "3fd0d13282b031f4c017cd6bf6597183": "9dda4cbcc1d3f6d38c10fee3de53b9abd5e47ec0",
@@ -91151,18 +91173,24 @@
"jopac.bin": [ "jopac.bin": [
"54b8d2c1317628de51a85fc1c424423a986775e4" "54b8d2c1317628de51a85fc1c424423a986775e4"
], ],
"Pioneer LaserActive Sega PAC Boot ROM v1.02 (1993)(Pioneer - Sega)(JP)(en-ja).bin": [
"26237b333db4a4c6770297fa5e655ea95840d5d9"
],
"Pioneer LaserActive Sega PAC Boot ROM v1.04 (1993)(Pioneer - Sega)(US).bin": [ "Pioneer LaserActive Sega PAC Boot ROM v1.04 (1993)(Pioneer - Sega)(US).bin": [
"aa811861f8874775075bd3f53008c8aaf59b07db" "aa811861f8874775075bd3f53008c8aaf59b07db"
], ],
"Pioneer LaserActive Sega PAC Boot ROM v1.02 (1993)(Pioneer - Sega)(JP)(en-ja).bin": [
"26237b333db4a4c6770297fa5e655ea95840d5d9"
],
"[BIOS] LaserActive PAC-N1 (Japan) (v1.02).bin": [ "[BIOS] LaserActive PAC-N1 (Japan) (v1.02).bin": [
"6973e2593e66fd21627fedccec98d4a364afaaff" "6973e2593e66fd21627fedccec98d4a364afaaff"
], ],
"[BIOS] LaserActive PAC-N10 (US) (v1.02).bin": [ "[BIOS] LaserActive PAC-N10 (US) (v1.02).bin": [
"f7412aa822d70a55b2ff3d7095137263dc54f6b6" "f7412aa822d70a55b2ff3d7095137263dc54f6b6"
], ],
"[BIOS] LaserActive PAC-S1 (Japan) (v1.01).bin": [
"bc746df0c5d2b779ca41a94954b60d6ac6a7c2a3"
],
"[BIOS] LaserActive PAC-S1 (Japan) (v1.05).bin": [
"f7101b40cae0484a0f43f3bbee2d55033ff1e3ec"
],
"[BIOS] LaserActive PCE-LP1 (Japan) (v1.02).bin": [ "[BIOS] LaserActive PCE-LP1 (Japan) (v1.02).bin": [
"c1b9202cbe072db12114b223a9ba5374b30718fb" "c1b9202cbe072db12114b223a9ba5374b30718fb"
], ],
@@ -101218,6 +101246,9 @@
"g7400.bin": [ "g7400.bin": [
"5130243429b40b01a14e1304d0394b8459a6fbae" "5130243429b40b01a14e1304d0394b8459a6fbae"
], ],
"[BIOS] LaserActive PAC-S10 (US) (v1.04).bin": [
"aa811861f8874775075bd3f53008c8aaf59b07db"
],
"Battle 1.mid": [ "Battle 1.mid": [
"dc5cc32fafa442b09ab2d814ed32074d02597234" "dc5cc32fafa442b09ab2d814ed32074d02597234"
], ],
@@ -107490,10 +107521,12 @@
"e20a9f41": "5130243429b40b01a14e1304d0394b8459a6fbae", "e20a9f41": "5130243429b40b01a14e1304d0394b8459a6fbae",
"a318e8d6": "a6120aed50831c9c0d95dbdf707820f601d9452e", "a318e8d6": "a6120aed50831c9c0d95dbdf707820f601d9452e",
"11647ca5": "54b8d2c1317628de51a85fc1c424423a986775e4", "11647ca5": "54b8d2c1317628de51a85fc1c424423a986775e4",
"00eedb3a": "26237b333db4a4c6770297fa5e655ea95840d5d9",
"50cd3d23": "aa811861f8874775075bd3f53008c8aaf59b07db", "50cd3d23": "aa811861f8874775075bd3f53008c8aaf59b07db",
"00eedb3a": "26237b333db4a4c6770297fa5e655ea95840d5d9",
"a8cb694c": "6973e2593e66fd21627fedccec98d4a364afaaff", "a8cb694c": "6973e2593e66fd21627fedccec98d4a364afaaff",
"01223dd5": "f7412aa822d70a55b2ff3d7095137263dc54f6b6", "01223dd5": "f7412aa822d70a55b2ff3d7095137263dc54f6b6",
"24ad336e": "bc746df0c5d2b779ca41a94954b60d6ac6a7c2a3",
"1493522c": "f7101b40cae0484a0f43f3bbee2d55033ff1e3ec",
"76116a02": "c1b9202cbe072db12114b223a9ba5374b30718fb", "76116a02": "c1b9202cbe072db12114b223a9ba5374b30718fb",
"4e70e3c0": "f2a9ce387019bf272c6e3459d961b30f28942ac5", "4e70e3c0": "f2a9ce387019bf272c6e3459d961b30f28942ac5",
"92bcc762": "9dda4cbcc1d3f6d38c10fee3de53b9abd5e47ec0", "92bcc762": "9dda4cbcc1d3f6d38c10fee3de53b9abd5e47ec0",
@@ -124965,6 +124998,9 @@
"roms/win486/ALI1429G.AMW": [ "roms/win486/ALI1429G.AMW": [
"72c60172fb1ba77c9b24b06b7755f0a16f0b3a13" "72c60172fb1ba77c9b24b06b7755f0a16f0b3a13"
], ],
".variants/[BIOS] LaserActive PAC-S10 (US) (v1.04).bin": [
"aa811861f8874775075bd3f53008c8aaf59b07db"
],
"rtp/2003/Battle/Arrow.png": [ "rtp/2003/Battle/Arrow.png": [
"7aa8d4c377efcea1c9fad01924da1ba7b8575e1e" "7aa8d4c377efcea1c9fad01924da1ba7b8575e1e"
], ],

View File

@@ -5,6 +5,7 @@
emulator: LRPS2 emulator: LRPS2
type: libretro type: libretro
core_classification: community_fork core_classification: community_fork
bios_mode: agnostic
source: "https://github.com/libretro/ps2" source: "https://github.com/libretro/ps2"
upstream: "https://github.com/PCSX2/pcsx2" upstream: "https://github.com/PCSX2/pcsx2"
profiled_date: "2026-03-25" profiled_date: "2026-03-25"

View File

@@ -77,7 +77,7 @@ files:
source_ref: "src/SPI.cpp:197-211, src/frontend/Util_ROM.cpp:201-217" source_ref: "src/SPI.cpp:197-211, src/frontend/Util_ROM.cpp:201-217"
- name: dsi_nand.bin - name: dsi_nand.bin
aliases: [DSi_Nand_USA.bin, DSi_Nand_EUR.bin, DSi_Nand_JPN.bin, DSi_Nand_AUS.bin, DSi_Nand_CHN.bin, DSi_Nand_KOR.bin] agnostic: true
system: nintendo-dsi system: nintendo-dsi
description: "DSi NAND dump" description: "DSi NAND dump"
required: true required: true

View File

@@ -1,219 +1,74 @@
# PCSX2 emulator BIOS profile
# Generated from source analysis of https://github.com/PCSX2/pcsx2
# Commit analyzed: HEAD as of 2026-03-17
emulator: PCSX2 emulator: PCSX2
type: standalone type: standalone
core_classification: official_port
bios_mode: agnostic
source: "https://github.com/PCSX2/pcsx2" source: "https://github.com/PCSX2/pcsx2"
logo: "https://raw.githubusercontent.com/PCSX2/pcsx2/master/pcsx2-qt/resources/icons/PCSX2logo.svg" upstream: "https://github.com/PCSX2/pcsx2"
profiled_date: "2026-03-18" cores:
- pcsx2
profiled_date: "2026-03-30"
core_version: "Git" core_version: "Git"
display_name: "Sony - PlayStation 2 (LRPS2)" display_name: "Sony - PlayStation 2 (PCSX2)"
systems: [sony-playstation-2] systems: [sony-playstation-2]
bios_directory: "bios/" notes: |
bios_detection: "romdir" # scans romdir structure inside binary, looks for RESET/ROMVER/EXTINFO entries Filename-agnostic BIOS detection. Scans bios/ for any file between 4-8 MB
bios_selection: "automatic" # scans all files in bios dir matching 4-8 MB size, validates via romdir with valid romdir structure (RESET + ROMVER entries). No hash validation.
Companion files (.rom1, .rom2, .nvm, .mec) derive paths from selected BIOS.
validation: ROM1 (DVD player) and ROM2 (Chinese extension) silently skipped if missing.
method: "romdir_parse" NVM and MEC auto-created with defaults if missing.
min_size: 4194304 # 4 MB (MIN_BIOS_SIZE = 4 * _1mb)
max_size: 8388608 # 8 MB (MAX_BIOS_SIZE = 8 * _1mb)
required_entries: ["RESET", "ROMVER"]
optional_entries: ["EXTINFO"]
note: "Any file in bios/ between 4-8 MB with valid romdir containing RESET+ROMVER is accepted"
regions:
J: {zone: "Japan", id: 0}
A: {zone: "USA", id: 1}
E: {zone: "Europe", id: 2}
H: {zone: "Asia", id: 4}
C: {zone: "China", id: 6}
T: {zone: "T10K/COH-H", id: 8}
X: {zone: "Test", id: 9}
P: {zone: "Free", id: 10}
files: files:
# -- Main BIOS binary (required) -- - name: ps2-0230a-20080220.bin
- name: "<user-selected>.bin"
pattern: "*"
required: true required: true
size_range: "4MB-8MB" min_size: 4194304
source_ref: "pcsx2/ps2/BiosTools.cpp:258-282" max_size: 8388608
note: > validation: [size]
PCSX2 does not mandate a specific filename. It scans the entire bios/ directory source_ref: "pcsx2/ps2/BiosTools.cpp:258-362"
for any file between 4-8 MB that contains a valid romdir structure (RESET + ROMVER entries). note: "Accepts any file 4-8 MB with valid romdir (RESET + ROMVER). Naming convention ps2-VVVVr-YYYYMMDD.bin (version, region, date)."
Common filenames follow the SCPH-XXXXX_BIOS_VYYY_REGION_ZZZ.BIN convention but this is
not enforced. The file is loaded into the 4 MB ROM region of EE memory.
# -- ROM1 (optional, DVD player) -- - name: rom1.bin
- name: "<biosname>.rom1"
pattern: "{biosname}.rom1 or {biosbase}.rom1"
required: false required: false
max_size: 4194304 # 4 MB (Ps2MemSize::Rom1) max_size: 4194304
source_ref: "pcsx2/ps2/BiosTools.cpp:214-241" source_ref: "pcsx2/ps2/BiosTools.cpp:214-241,366"
note: > note: "DVD player ROM. Tries {biospath}.rom1 then {biosbase}.rom1. Silently skipped if missing."
DVD player ROM. Loaded via LoadExtraRom("rom1"). PCSX2 tries two naming patterns:
1) Full bios path + ".rom1" appended (e.g. scph70004.bin.rom1)
2) Bios path with extension replaced (e.g. scph70004.rom1)
Mapped to EE memory at ROM1 region (0x1FC00000 + 4MB offset).
Contains DVD player and region detection data (DVDID).
# -- ROM2 (optional, Chinese ROM extension) -- - name: ROM2.BIN
- name: "<biosname>.rom2"
pattern: "{biosname}.rom2 or {biosbase}.rom2"
required: false required: false
max_size: 4194304 # 4 MB (Ps2MemSize::Rom2) max_size: 4194304
source_ref: "pcsx2/ps2/BiosTools.cpp:214-241" source_ref: "pcsx2/ps2/BiosTools.cpp:214-241,367"
note: > note: "Chinese ROM extension. Same naming convention as rom1. Only present on Chinese region consoles."
Chinese ROM extension. Loaded via LoadExtraRom("rom2"). Same naming convention
as rom1: tries appended extension first, then replaced extension.
Only present on Chinese region consoles.
# -- NVM / NVRAM (optional, auto-created) -- - name: EROM.BIN
- name: "<biosname>.nvm" required: false
pattern: "{biosbase}.nvm" source_ref: "pcsx2/ps2/BiosTools.cpp"
note: "Extended ROM. Present in some BIOS dumps but not loaded by PCSX2 code via LoadExtraRom."
path: null
- name: eeprom.dat
required: false required: false
hle_fallback: true hle_fallback: true
size: 1024 # NVRAM_SIZE = 1024 bytes size: 64
source_ref: "pcsx2/CDVD/CDVD.cpp:160-238"
note: >
EEPROM / NVRAM data. Path derived from BiosPath with extension replaced to ".nvm"
(cdvdGetNVRAMPath). Contains console configuration: language, timezone, iLink ID,
region parameters, OSD settings. Auto-created with defaults if missing.
Two NVM layouts exist: v0.00+ (biosVer 0x000) and v1.70+ (biosVer 0x146).
# -- MEC file (optional, auto-created) --
- name: "<biosname>.mec"
pattern: "{biosbase}.mec"
required: false
hle_fallback: true
size: 4 # u32 s_mecha_version
source_ref: "pcsx2/CDVD/CDVD.cpp:190-204"
note: >
Mechacon (mechanism controller) version file. 4 bytes containing the mecha version
as a u32 value. Auto-created with DEFAULT_MECHA_VERSION (0x00020603) if missing.
Path derived from BiosPath with extension replaced to ".mec".
# -- IRX override (optional, advanced) --
- name: "<custom>.irx"
pattern: "*.irx"
required: false
source_ref: "pcsx2/ps2/BiosTools.cpp:243-256,384-385"
note: >
Custom IOP Reboot eXecutable module. Loaded into ROM at offset 0x3C0000 if
EmuConfig.CurrentIRX is set (path length > 3). Injected at IOP reset (PC=0x1630).
Used for debugging/development, not needed for normal operation.
# -- DEV9 EEPROM (optional, network adapter) --
- name: "eeprom.dat"
required: false
hle_fallback: true
size: 64 # 64 bytes, mmap'd
source_ref: "pcsx2/DEV9/DEV9.cpp:110-160" source_ref: "pcsx2/DEV9/DEV9.cpp:110-160"
note: > note: "DEV9 network adapter EEPROM. Falls back to built-in defaults if missing."
DEV9 (network adapter / HDD expansion bay) EEPROM data. Fixed filename "eeprom.dat"
opened from working directory. Contains network adapter configuration.
Falls back to built-in defaults if file not found. Only relevant when using
DEV9 features (online play, HDD).
common_bios_filenames: - name: GameIndex.yaml
# Japan path: pcsx2/resources/GameIndex.yaml
- "SCPH-10000_BIOS_V1_JAP_100.BIN" required: false
- "SCPH-15000_BIOS_V3_JAP_120.BIN" mode: libretro
- "SCPH-30000_BIOS_V4_JAP_150.BIN" source_ref: "pcsx2/GameDatabase.cpp:48,880"
- "SCPH-30001R_BIOS_V7_JAP_160.BIN" note: "Game compatibility database. OSD warning if missing."
- "SCPH-30004R_BIOS_V7_JAP_160.BIN"
- "SCPH-35000_BIOS_V5_JAP_160.BIN"
- "SCPH-50000_BIOS_V9_JAP_170.BIN"
- "SCPH-50004_BIOS_V9_JAP_170.BIN"
- "SCPH-70000_BIOS_V12_JAP_200.BIN"
- "SCPH-75000_BIOS_V14_JAP_220.BIN"
- "SCPH-77000_BIOS_V14_JAP_220.BIN"
- "SCPH-90000_BIOS_V18_JAP_230.BIN"
# USA
- "SCPH-30001_BIOS_V4_USA_150.BIN"
- "SCPH-39001_BIOS_V6_USA_160.BIN"
- "SCPH-50001_BIOS_V9_USA_170.BIN"
- "SCPH-50003_BIOS_V9_USA_170.BIN"
- "SCPH-70002_BIOS_V12_USA_200.BIN"
- "SCPH-70004_BIOS_V12_USA_200.BIN"
- "SCPH-70012_BIOS_V12_USA_200.BIN"
- "SCPH-75001_BIOS_V14_USA_220.BIN"
- "SCPH-77001_BIOS_V14_USA_220.BIN"
- "SCPH-90001_BIOS_V18_USA_230.BIN"
# Europe
- "SCPH-30002_BIOS_V4_EUR_150.BIN"
- "SCPH-30003_BIOS_V4_EUR_150.BIN"
- "SCPH-30004_BIOS_V4_EUR_150.BIN"
- "SCPH-39002_BIOS_V6_EUR_160.BIN"
- "SCPH-39003_BIOS_V6_EUR_160.BIN"
- "SCPH-39004_BIOS_V6_EUR_160.BIN"
- "SCPH-50002_BIOS_V9_EUR_170.BIN"
- "SCPH-50004_BIOS_V9_EUR_170.BIN"
- "SCPH-70002_BIOS_V12_EUR_200.BIN"
- "SCPH-70003_BIOS_V12_EUR_200.BIN"
- "SCPH-70004_BIOS_V12_EUR_200.BIN"
- "SCPH-70008_BIOS_V12_EUR_200.BIN"
- "SCPH-75002_BIOS_V14_EUR_220.BIN"
- "SCPH-75003_BIOS_V14_EUR_220.BIN"
- "SCPH-75004_BIOS_V14_EUR_220.BIN"
- "SCPH-77002_BIOS_V14_EUR_220.BIN"
- "SCPH-77003_BIOS_V14_EUR_220.BIN"
- "SCPH-77004_BIOS_V14_EUR_220.BIN"
- "SCPH-90002_BIOS_V18_EUR_230.BIN"
- "SCPH-90003_BIOS_V18_EUR_230.BIN"
- "SCPH-90004_BIOS_V18_EUR_230.BIN"
# Asia
- "SCPH-50009_BIOS_V9_HK_170.BIN"
- "SCPH-70005_BIOS_V12_HK_200.BIN"
- "SCPH-70006_BIOS_V12_HK_200.BIN"
- "SCPH-70008_BIOS_V12_HK_200.BIN"
# China
- "SCPH-50009_BIOS_V9_CHN_170.BIN"
- "SCPH-70006_BIOS_V12_CHN_200.BIN"
memory_layout: - name: cheats_ws.zip
ROM: {offset: "0x1FC00000", size: "4 MB", purpose: "Main BIOS binary"} path: pcsx2/resources/cheats_ws.zip
ROM1: {offset: "ROM + 4MB", size: "4 MB", purpose: "DVD player"} required: false
ROM2: {offset: "ROM + 8MB", size: "4 MB", purpose: "Chinese ROM extension"} mode: libretro
source_ref: "pcsx2/VMManager.cpp:340-353"
note: "Widescreen patches archive."
nvm_layout: - name: cheats_ni.zip
format_0: path: pcsx2/resources/cheats_ni.zip
applies_to: "BIOS v0.00+" required: false
biosVer: 0x000 mode: libretro
config0: 0x280 source_ref: "pcsx2/VMManager.cpp:375-388"
config1: 0x300 note: "No-interlacing patches archive."
config2: 0x200
consoleId: 0x1C8
ilinkId: 0x1C0
modelNum: 0x1A0
regparams: 0x180
mac: 0x198
format_1:
applies_to: "BIOS v1.70+"
biosVer: 0x146
config0: 0x270
config1: 0x2B0
config2: 0x200
consoleId: 0x1F0
ilinkId: 0x1E0
modelNum: 0x1B0
regparams: 0x180
mac: 0x198
notes: |
PCSX2 is filename-agnostic for the main BIOS. Detection relies on romdir structure
parsing inside the binary itself, not on filename or extension. Any file between 4-8 MB
with a valid romdir (containing at least RESET and ROMVER entries) is accepted.
The ROMVER entry encodes: version (2+2 digits), region letter, console/devel flag,
build date (YYYYMMDD), and is used to determine the BIOS description and region.
Companion files (.nvm, .mec) are auto-created with sane defaults if missing.
ROM1/ROM2 are silently skipped if not found - only the main BIOS binary is strictly required.
PCSX2 no longer ships as a libretro core in official builds. The standalone emulator
is the primary distribution channel.
Devel console BIOSes (< ~2.3 MB) lack the OSD and are handled with NoOSD=true flag.

View File

@@ -1,32 +1,21 @@
# RPCS3 emulator firmware profile
# Generated from source analysis of https://github.com/RPCS3/rpcs3
# Commit analyzed: HEAD as of 2026-03-17
emulator: RPCS3 emulator: RPCS3
type: standalone type: standalone
core_classification: official_port
source: "https://github.com/RPCS3/rpcs3" source: "https://github.com/RPCS3/rpcs3"
logo: "https://raw.githubusercontent.com/RPCS3/rpcs3/master/rpcs3/rpcs3.svg" upstream: "https://github.com/RPCS3/rpcs3"
profiled_date: "2026-03-18" cores:
- rpcs3
profiled_date: "2026-03-30"
core_version: "0.0.35" core_version: "0.0.35"
display_name: "RPCS3 (PS3)" display_name: "Sony - PlayStation 3 (RPCS3)"
systems: [sony-playstation-3] systems: [sony-playstation-3]
firmware_file: "PS3UPDAT.PUP" files:
firmware_source: "https://www.playstation.com/en-us/support/hardware/ps3/system-software/" - name: PS3UPDAT.PUP
firmware_detection: "pup_header" # validates PUP magic bytes, HMAC-SHA1 hash per entry required: true
firmware_install: "extracts dev_flash_* TAR packages from PUP into dev_flash/" storage: large_file
source_ref: "rpcs3/Loader/PUP.cpp:23-77"
validation: note: "PUP firmware package. Validated via SCEUF magic + HMAC-SHA1 per entry. Extracted to dev_flash/ at install time."
method: "pup_object"
magic: "SCEUF"
hash_algo: "HMAC-SHA1"
source_ref: "rpcs3/Loader/PUP.cpp:8-114"
note: "PUP file is validated by magic header, file count, HMAC-SHA1 per entry against PUP_KEY"
firmware_version:
path: "dev_flash/vsh/etc/version.txt"
source_ref: "rpcs3/util/sysinfo.cpp:686"
note: "Read at startup, displayed as 'Firmware version: X.XX'. Missing = 'Missing Firmware'"
# dev_flash filesystem layout extracted from PUP # dev_flash filesystem layout extracted from PUP
dev_flash: dev_flash:

View File

@@ -3,7 +3,7 @@
"platform": "batocera", "platform": "batocera",
"display_name": "Batocera", "display_name": "Batocera",
"version": "1.0", "version": "1.0",
"generated": "2026-03-30T07:37:55Z", "generated": "2026-03-30T09:46:19Z",
"base_destination": "bios", "base_destination": "bios",
"detect": [ "detect": [
{ {

View File

@@ -3,7 +3,7 @@
"platform": "bizhawk", "platform": "bizhawk",
"display_name": "BizHawk", "display_name": "BizHawk",
"version": "1.0", "version": "1.0",
"generated": "2026-03-30T07:37:59Z", "generated": "2026-03-30T09:46:23Z",
"base_destination": "Firmware", "base_destination": "Firmware",
"detect": [ "detect": [
{ {

View File

@@ -3,7 +3,7 @@
"platform": "emudeck", "platform": "emudeck",
"display_name": "EmuDeck", "display_name": "EmuDeck",
"version": "1.0", "version": "1.0",
"generated": "2026-03-30T07:38:01Z", "generated": "2026-03-30T09:46:25Z",
"base_destination": "bios", "base_destination": "bios",
"detect": [ "detect": [
{ {

View File

@@ -3,7 +3,7 @@
"platform": "lakka", "platform": "lakka",
"display_name": "Lakka", "display_name": "Lakka",
"version": "1.0", "version": "1.0",
"generated": "2026-03-30T07:38:59Z", "generated": "2026-03-30T09:47:34Z",
"base_destination": "system", "base_destination": "system",
"detect": [ "detect": [
{ {

View File

@@ -3,7 +3,7 @@
"platform": "recalbox", "platform": "recalbox",
"display_name": "Recalbox", "display_name": "Recalbox",
"version": "1.0", "version": "1.0",
"generated": "2026-03-30T07:39:27Z", "generated": "2026-03-30T09:48:01Z",
"base_destination": "bios", "base_destination": "bios",
"detect": [ "detect": [
{ {

View File

@@ -3,7 +3,7 @@
"platform": "retroarch", "platform": "retroarch",
"display_name": "RetroArch", "display_name": "RetroArch",
"version": "1.0", "version": "1.0",
"generated": "2026-03-30T07:38:59Z", "generated": "2026-03-30T09:47:34Z",
"base_destination": "system", "base_destination": "system",
"detect": [ "detect": [
{ {

View File

@@ -3,7 +3,7 @@
"platform": "retrobat", "platform": "retrobat",
"display_name": "RetroBat", "display_name": "RetroBat",
"version": "1.0", "version": "1.0",
"generated": "2026-03-30T07:39:32Z", "generated": "2026-03-30T09:48:07Z",
"base_destination": "bios", "base_destination": "bios",
"detect": [ "detect": [
{ {

View File

@@ -3,7 +3,7 @@
"platform": "retrodeck", "platform": "retrodeck",
"display_name": "RetroDECK", "display_name": "RetroDECK",
"version": "1.0", "version": "1.0",
"generated": "2026-03-30T07:40:43Z", "generated": "2026-03-30T09:49:19Z",
"base_destination": "", "base_destination": "",
"detect": [ "detect": [
{ {

View File

@@ -3,7 +3,7 @@
"platform": "romm", "platform": "romm",
"display_name": "RomM", "display_name": "RomM",
"version": "1.0", "version": "1.0",
"generated": "2026-03-30T07:40:45Z", "generated": "2026-03-30T09:49:20Z",
"base_destination": "bios", "base_destination": "bios",
"detect": [ "detect": [
{ {

View File

@@ -132,7 +132,7 @@ nav:
- ZC: systems/zc.md - ZC: systems/zc.md
- Emulators: - Emulators:
- Overview: emulators/index.md - Overview: emulators/index.md
- Official ports (61): - Official ports (62):
- amiarcadia: emulators/amiarcadia.md - amiarcadia: emulators/amiarcadia.md
- Amiberry: emulators/amiberry.md - Amiberry: emulators/amiberry.md
- Ardens: emulators/ardens.md - Ardens: emulators/ardens.md
@@ -176,6 +176,7 @@ nav:
- mGBA: emulators/mgba.md - mGBA: emulators/mgba.md
- Mr.Boom: emulators/mrboom.md - Mr.Boom: emulators/mrboom.md
- Panda3DS: emulators/panda3ds.md - Panda3DS: emulators/panda3ds.md
- PCSX2: emulators/pcsx2.md
- PicoDrive: emulators/picodrive.md - PicoDrive: emulators/picodrive.md
- play: emulators/play.md - play: emulators/play.md
- PPSSPP: emulators/ppsspp.md - PPSSPP: emulators/ppsspp.md
@@ -428,7 +429,7 @@ nav:
- PCSX-ReARMed: emulators/pcsx_rearmed.md - PCSX-ReARMed: emulators/pcsx_rearmed.md
- Launchers (1): - Launchers (1):
- Dolphin Launcher: emulators/dolphin_launcher.md - Dolphin Launcher: emulators/dolphin_launcher.md
- Other (25): - Other (24):
- ares: emulators/ares.md - ares: emulators/ares.md
- Beetle GBA (Mednafen): emulators/beetle_gba.md - Beetle GBA (Mednafen): emulators/beetle_gba.md
- BigPEmu: emulators/bigpemu.md - BigPEmu: emulators/bigpemu.md
@@ -440,7 +441,6 @@ nav:
- Lexaloffle: emulators/lexaloffle.md - Lexaloffle: emulators/lexaloffle.md
- Model 2 Emulator: emulators/model2.md - Model 2 Emulator: emulators/model2.md
- openMSX: emulators/openmsx.md - openMSX: emulators/openmsx.md
- PCSX2: emulators/pcsx2.md
- Redream: emulators/redream.md - Redream: emulators/redream.md
- RPCS3: emulators/rpcs3.md - RPCS3: emulators/rpcs3.md
- Ryujinx: emulators/ryujinx.md - Ryujinx: emulators/ryujinx.md

View File

@@ -7,6 +7,9 @@ platforms:
source_url: https://raw.githubusercontent.com/libretro/libretro-database/master/dat/System.dat source_url: https://raw.githubusercontent.com/libretro/libretro-database/master/dat/System.dat
source_format: clrmamepro_dat source_format: clrmamepro_dat
hash_type: sha1 hash_type: sha1
verification_mode: existence
base_destination: system
case_insensitive_fs: true
schedule: weekly schedule: weekly
cores: all_libretro cores: all_libretro
target_scraper: retroarch_targets target_scraper: retroarch_targets

View File

@@ -183,6 +183,7 @@ systems:
required: true required: true
md5: 0bac0c6a50104045d902df4503a4c30b md5: 0bac0c6a50104045d902df4503a4c30b
native_id: atari800 native_id: atari800
name: Atari 800
atari-5200: atari-5200:
files: files:
- name: 5200.rom - name: 5200.rom
@@ -190,6 +191,7 @@ systems:
required: true required: true
md5: 281f20ea4320404ec820fb7ec0693b38 md5: 281f20ea4320404ec820fb7ec0693b38
native_id: atari5200 native_id: atari5200
name: Atari 5200
atari-st: atari-st:
files: files:
- name: tos.img - name: tos.img
@@ -337,6 +339,7 @@ systems:
required: true required: true
md5: e5ea0f216fb446f1c4a4f476bc5f03d4 md5: e5ea0f216fb446f1c4a4f476bc5f03d4
native_id: atarist native_id: atarist
name: Atari ST
atari-lynx: atari-lynx:
files: files:
- name: lynxboot.img - name: lynxboot.img
@@ -344,6 +347,7 @@ systems:
required: true required: true
md5: fcd403db69f54290b51035d82f835e7b md5: fcd403db69f54290b51035d82f835e7b
native_id: lynx native_id: lynx
name: Lynx
magnavox-odyssey2: magnavox-odyssey2:
files: files:
- name: o2rom.bin - name: o2rom.bin
@@ -355,6 +359,7 @@ systems:
required: true required: true
md5: f1071cdb0b6b10dde94d3bc8a6146387 md5: f1071cdb0b6b10dde94d3bc8a6146387
native_id: odyssey2 native_id: odyssey2
name: Odyssey 2
videopacplus: videopacplus:
files: files:
- name: g7400.bin - name: g7400.bin
@@ -366,6 +371,7 @@ systems:
required: true required: true
md5: 279008e4a0db2dc5f1c048853b033828 md5: 279008e4a0db2dc5f1c048853b033828
native_id: videopacplus native_id: videopacplus
name: Videopac+ G7400
mattel-intellivision: mattel-intellivision:
files: files:
- name: exec.bin - name: exec.bin
@@ -377,6 +383,7 @@ systems:
required: true required: true
md5: 0cd5946c6473e42e8e4c2137785e427f md5: 0cd5946c6473e42e8e4c2137785e427f
native_id: intellivision native_id: intellivision
name: Mattel Intellivision
nec-pc-engine: nec-pc-engine:
files: files:
- name: syscard3.pce - name: syscard3.pce
@@ -388,6 +395,7 @@ systems:
required: true required: true
md5: 38179df8f4ac870017db21ebcbf53114 md5: 38179df8f4ac870017db21ebcbf53114
native_id: pcengine native_id: pcengine
name: Supergrafx
nec-pc-fx: nec-pc-fx:
files: files:
- name: pcfx.rom - name: pcfx.rom
@@ -395,6 +403,7 @@ systems:
required: true required: true
md5: 08e36edbea28a017f79f8d4f7ff9b6d7 md5: 08e36edbea28a017f79f8d4f7ff9b6d7
native_id: pcfx native_id: pcfx
name: PC-FX
snk-neogeo: snk-neogeo:
files: files:
- name: neogeo.zip - name: neogeo.zip
@@ -402,6 +411,7 @@ systems:
required: true required: true
md5: dffb72f116d36d025068b23970a4f6df md5: dffb72f116d36d025068b23970a4f6df
native_id: neogeo native_id: neogeo
name: NeoGeo
snk-neogeo-cd: snk-neogeo-cd:
files: files:
- name: neocd_f.rom - name: neocd_f.rom
@@ -445,6 +455,7 @@ systems:
required: true required: true
md5: 08ca8b2dba6662e8024f9e789711c6fc md5: 08ca8b2dba6662e8024f9e789711c6fc
native_id: neogeocd native_id: neogeocd
name: NeoGeo CD
sharp-x68000: sharp-x68000:
files: files:
- name: iplrom.dat - name: iplrom.dat
@@ -456,6 +467,7 @@ systems:
required: true required: true
md5: cb0a5cfcf7247a7eab74bb2716260269 md5: cb0a5cfcf7247a7eab74bb2716260269
native_id: x68000 native_id: x68000
name: Sharp x68000
3do: 3do:
files: files:
- name: panafz1.bin - name: panafz1.bin
@@ -471,6 +483,7 @@ systems:
required: true required: true
md5: 8639fd5e549bd6238cfee79e3e749114 md5: 8639fd5e549bd6238cfee79e3e749114
native_id: 3do native_id: 3do
name: 3DO
sega-dreamcast: sega-dreamcast:
files: files:
- name: dc_boot.bin - name: dc_boot.bin
@@ -478,6 +491,7 @@ systems:
required: true required: true
md5: e10c53c2f8b90bab96ead2d368858623 md5: e10c53c2f8b90bab96ead2d368858623
native_id: dreamcast native_id: dreamcast
name: Dreamcast
sega-dreamcast-arcade: sega-dreamcast-arcade:
files: files:
- name: naomi.zip - name: naomi.zip
@@ -597,6 +611,7 @@ systems:
md5: 960ece0dc22a7c5ff81c812a2993e7cc md5: 960ece0dc22a7c5ff81c812a2993e7cc
zipped_file: x76f100_eeprom.bin zipped_file: x76f100_eeprom.bin
native_id: naomi native_id: naomi
name: Naomi 2
sega-mega-cd: sega-mega-cd:
files: files:
- name: bios_CD_E.bin - name: bios_CD_E.bin
@@ -612,6 +627,7 @@ systems:
required: true required: true
md5: 278a9397d192149e84e820ac621a8edd md5: 278a9397d192149e84e820ac621a8edd
native_id: segacd native_id: segacd
name: Sega CD
megadrive-msu: megadrive-msu:
files: files:
- name: bios_CD_E.bin - name: bios_CD_E.bin
@@ -627,6 +643,7 @@ systems:
required: true required: true
md5: 278a9397d192149e84e820ac621a8edd md5: 278a9397d192149e84e820ac621a8edd
native_id: megadrive-msu native_id: megadrive-msu
name: MSU-MD
sega-saturn: sega-saturn:
files: files:
- name: sega_101.bin - name: sega_101.bin
@@ -650,6 +667,7 @@ systems:
required: true required: true
md5: af5828fdff51384f99b3c4926be27762 md5: af5828fdff51384f99b3c4926be27762
native_id: saturn native_id: saturn
name: Sega Saturn
sony-playstation: sony-playstation:
files: files:
- name: psxonpsp660.bin - name: psxonpsp660.bin
@@ -681,6 +699,7 @@ systems:
required: true required: true
md5: 1e68c231d0896b7eadcad1d7d8e76129 md5: 1e68c231d0896b7eadcad1d7d8e76129
native_id: psx native_id: psx
name: PSX
sony-playstation-2: sony-playstation-2:
files: files:
- name: ps2-0230a-20080220.bin - name: ps2-0230a-20080220.bin
@@ -688,6 +707,7 @@ systems:
required: true required: true
md5: 21038400dc633070a78ad53090c53017 md5: 21038400dc633070a78ad53090c53017
native_id: ps2 native_id: ps2
name: PS2
sony-playstation-3: sony-playstation-3:
files: files:
- name: PS3UPDAT.PUP - name: PS3UPDAT.PUP
@@ -695,6 +715,7 @@ systems:
required: true required: true
md5: 05fe32f5dc8c78acbcd84d36ee7fdc5b md5: 05fe32f5dc8c78acbcd84d36ee7fdc5b
native_id: ps3 native_id: ps3
name: PS3
nintendo-fds: nintendo-fds:
files: files:
- name: disksys.rom - name: disksys.rom
@@ -702,6 +723,7 @@ systems:
required: true required: true
md5: ca30b50f880eb660a320674ed365ef7a md5: ca30b50f880eb660a320674ed365ef7a
native_id: fds native_id: fds
name: Nintendo Family Computer Disk System
nintendo-ds: nintendo-ds:
files: files:
- name: firmware.bin - name: firmware.bin
@@ -730,6 +752,7 @@ systems:
destination: dsi_nand.bin destination: dsi_nand.bin
required: true required: true
native_id: nds native_id: nds
name: Nintendo DS
nintendo-gba: nintendo-gba:
files: files:
- name: gba_bios.bin - name: gba_bios.bin
@@ -749,6 +772,7 @@ systems:
required: true required: true
md5: d574d4f9c12f305074798f54c091a8b4 md5: d574d4f9c12f305074798f54c091a8b4
native_id: gba native_id: gba
name: Nintendo Gameboy Advance
nintendo-satellaview: nintendo-satellaview:
files: files:
- name: BS-X.bin - name: BS-X.bin
@@ -756,6 +780,7 @@ systems:
required: true required: true
md5: 96cf17bf589fcbfa6f8de2dc84f19fa2 md5: 96cf17bf589fcbfa6f8de2dc84f19fa2
native_id: satellaview native_id: satellaview
name: Satellaview
sufami: sufami:
files: files:
- name: STBIOS.bin - name: STBIOS.bin
@@ -763,6 +788,7 @@ systems:
required: true required: true
md5: d3a44ba7d42a74d3ac58cb9c14c6a5ca md5: d3a44ba7d42a74d3ac58cb9c14c6a5ca
native_id: sufami native_id: sufami
name: Sufami
nintendo-sgb: nintendo-sgb:
files: files:
- name: sgb_boot.bin - name: sgb_boot.bin
@@ -782,6 +808,7 @@ systems:
required: true required: true
md5: 8ecd73eb4edf7ed7e81aef1be80031d5 md5: 8ecd73eb4edf7ed7e81aef1be80031d5
native_id: sgb native_id: sgb
name: Super Game Boy
microsoft-msx: microsoft-msx:
files: files:
- name: MSX.ROM - name: MSX.ROM
@@ -861,6 +888,7 @@ systems:
required: true required: true
md5: 42af93619160ef2116416f74a6cb12f2 md5: 42af93619160ef2116416f74a6cb12f2
native_id: msx native_id: msx
name: MSX-Turbo
xbox: xbox:
files: files:
- name: mcpx_1.0.bin - name: mcpx_1.0.bin
@@ -872,6 +900,7 @@ systems:
required: true required: true
md5: 39cee882148a87f93cb440b99dde3ceb md5: 39cee882148a87f93cb440b99dde3ceb
native_id: xbox native_id: xbox
name: Xbox
amiga500: amiga500:
files: files:
- name: kick33180.A500 - name: kick33180.A500
@@ -915,6 +944,7 @@ systems:
required: true required: true
md5: e40a5dfb3d017ba8779faba30cbd1c8e md5: e40a5dfb3d017ba8779faba30cbd1c8e
native_id: amiga500 native_id: amiga500
name: Amiga 500
commodore-amiga: commodore-amiga:
files: files:
- name: kick34005.A500 - name: kick34005.A500
@@ -982,6 +1012,7 @@ systems:
required: true required: true
md5: bb72565701b1b6faece07d68ea5da639 md5: bb72565701b1b6faece07d68ea5da639
native_id: amigacdtv native_id: amigacdtv
name: Amiga CD32
pc60: pc60:
files: files:
- name: pc6001.zip - name: pc6001.zip
@@ -1050,6 +1081,7 @@ systems:
md5: 9d61f3cbd47c4a281c5241c4f1d80919 md5: 9d61f3cbd47c4a281c5241c4f1d80919
zipped_file: cgrom68.64 zipped_file: cgrom68.64
native_id: pc60 native_id: pc60
name: NEC PC-6000
nec-pc-88: nec-pc-88:
files: files:
- name: N88.ROM - name: N88.ROM
@@ -1085,6 +1117,7 @@ systems:
required: true required: true
md5: fc4b76a402ba501e6ba6de4b3e8b4273 md5: fc4b76a402ba501e6ba6de4b3e8b4273
native_id: pc88 native_id: pc88
name: NEC PC-8800
nec-pc-98: nec-pc-98:
files: files:
- name: BIOS.ROM - name: BIOS.ROM
@@ -1108,6 +1141,7 @@ systems:
required: true required: true
md5: 7da1e5b7c482d4108d22a5b09631d967 md5: 7da1e5b7c482d4108d22a5b09631d967
native_id: pc98 native_id: pc98
name: NEC PC-9800
loopy: loopy:
files: files:
- name: casloopy.zip - name: casloopy.zip
@@ -1125,6 +1159,7 @@ systems:
md5: c0f1c899c9ca098663d046d60779711d md5: c0f1c899c9ca098663d046d60779711d
zipped_file: hn62434fa.lsi352 zipped_file: hn62434fa.lsi352
native_id: loopy native_id: loopy
name: Casio Loopy
fairchild-channel-f: fairchild-channel-f:
files: files:
- name: sl31253.bin - name: sl31253.bin
@@ -1140,6 +1175,7 @@ systems:
required: true required: true
md5: 95d339631d867c8f1d15a5f2ec26069d md5: 95d339631d867c8f1d15a5f2ec26069d
native_id: channelf native_id: channelf
name: Fairchild ChannelF
sharp-x1: sharp-x1:
files: files:
- name: IPLROM.X1 - name: IPLROM.X1
@@ -1151,6 +1187,7 @@ systems:
required: true required: true
md5: 56c28adcf1f3a2f87cf3d57c378013f5 md5: 56c28adcf1f3a2f87cf3d57c378013f5
native_id: x1 native_id: x1
name: Sharp X1
fmtowns: fmtowns:
files: files:
- name: FMT_DIC.ROM - name: FMT_DIC.ROM
@@ -1238,6 +1275,7 @@ systems:
md5: 1a15f6c1b58ec7e5f850118610a787a7 md5: 1a15f6c1b58ec7e5f850118610a787a7
zipped_file: mytownsux.rom zipped_file: mytownsux.rom
native_id: fmtowns native_id: fmtowns
name: Fujistu FM-Towns
gp32: gp32:
files: files:
- name: gp32.zip - name: gp32.zip
@@ -1274,6 +1312,7 @@ systems:
md5: d4af2bc352bdaf4972ea40902feda114 md5: d4af2bc352bdaf4972ea40902feda114
zipped_file: gp32mfv2.bin zipped_file: gp32mfv2.bin
native_id: gp32 native_id: gp32
name: GamePark GP32
laser310: laser310:
files: files:
- name: laser310.zip - name: laser310.zip
@@ -1290,6 +1329,7 @@ systems:
md5: f7e5d9a3eb2b57bf5f4e2a4565318a8f md5: f7e5d9a3eb2b57bf5f4e2a4565318a8f
zipped_file: vtechv21.u12 zipped_file: vtechv21.u12
native_id: laser310 native_id: laser310
name: VTech Laser 310
scv: scv:
files: files:
- name: upd7801g.s01 - name: upd7801g.s01
@@ -1297,6 +1337,7 @@ systems:
required: true required: true
md5: 635a978fd40db9a18ee44eff449fc126 md5: 635a978fd40db9a18ee44eff449fc126
native_id: scv native_id: scv
name: Super Cassette Vision
apple-iigs: apple-iigs:
files: files:
- name: ROM1 - name: ROM1
@@ -1332,6 +1373,7 @@ systems:
md5: 68ff96a624237d233e8d4c701f660dd1 md5: 68ff96a624237d233e8d4c701f660dd1
zipped_file: apple2gs.chr zipped_file: apple2gs.chr
native_id: gsplus native_id: gsplus
name: Apple IIgs
zc210: zc210:
files: files:
- name: zcdata.dat - name: zcdata.dat
@@ -1387,6 +1429,7 @@ systems:
required: true required: true
md5: e0ba7a8634b12cfee4b6760a6f89051a md5: e0ba7a8634b12cfee4b6760a6f89051a
native_id: zc210 native_id: zc210
name: Zelda Classic
apple-macintosh-ii: apple-macintosh-ii:
files: files:
- name: MacII.ROM - name: MacII.ROM
@@ -1594,6 +1637,7 @@ systems:
destination: mac755.chd destination: mac755.chd
required: true required: true
native_id: macintosh native_id: macintosh
name: Apple Macintosh
sc3000: sc3000:
files: files:
- name: sc3000.zip - name: sc3000.zip
@@ -1605,18 +1649,21 @@ systems:
md5: a6a47eae38600e41cc67e887e36e70b7 md5: a6a47eae38600e41cc67e887e36e70b7
zipped_file: sc3000.rom zipped_file: sc3000.rom
native_id: sc3000 native_id: sc3000
name: Sega SC-3000
segaai: segaai:
files: files:
- name: segaai.zip - name: segaai.zip
destination: segaai.zip destination: segaai.zip
required: true required: true
native_id: segaai native_id: segaai
name: Sega AI Computer
beena: beena:
files: files:
- name: beena.zip - name: beena.zip
destination: beena.zip destination: beena.zip
required: true required: true
native_id: beena native_id: beena
name: Advanced Pico Beena
coco: coco:
files: files:
- name: coco.zip - name: coco.zip
@@ -1678,12 +1725,14 @@ systems:
md5: 8cab28f4b7311b8df63c07bb3b59bfd5 md5: 8cab28f4b7311b8df63c07bb3b59bfd5
zipped_file: disk11.rom zipped_file: disk11.rom
native_id: coco native_id: coco
name: Tandy Color Computer
cgenie: cgenie:
files: files:
- name: cgenie.zip - name: cgenie.zip
destination: cgenie.zip destination: cgenie.zip
required: true required: true
native_id: cgenie native_id: cgenie
name: Colour Genie
dragon64: dragon64:
files: files:
- name: dragon64.zip - name: dragon64.zip
@@ -1711,6 +1760,7 @@ systems:
required: true required: true
zipped_file: d32.rom zipped_file: d32.rom
native_id: dragon64 native_id: dragon64
name: Dragon 64
mc10: mc10:
files: files:
- name: mc10.zip - name: mc10.zip
@@ -1730,6 +1780,7 @@ systems:
md5: 78af465c2f31cf4e05dec1efda77da01 md5: 78af465c2f31cf4e05dec1efda77da01
zipped_file: alice.rom zipped_file: alice.rom
native_id: mc10 native_id: mc10
name: Tandy MC-10
trs80: trs80:
files: files:
- name: trs80.zip - name: trs80.zip
@@ -1761,6 +1812,7 @@ systems:
destination: trs80m4p.zip destination: trs80m4p.zip
required: true required: true
native_id: trs80 native_id: trs80
name: TRS-80
tutor: tutor:
files: files:
- name: tutor.zip - name: tutor.zip
@@ -1777,6 +1829,7 @@ systems:
md5: 5770834c10946ac2c3617504ba530884 md5: 5770834c10946ac2c3617504ba530884
zipped_file: tutor2.bin zipped_file: tutor2.bin
native_id: tutor native_id: tutor
name: Tomy Tutor
ti99: ti99:
files: files:
- name: ti99_4a.zip - name: ti99_4a.zip
@@ -1821,6 +1874,7 @@ systems:
md5: 206daf498ac5d0141de1d47d38afd899 md5: 206daf498ac5d0141de1d47d38afd899
zipped_file: cd2326a.u2b zipped_file: cd2326a.u2b
native_id: ti99 native_id: ti99
name: Texas Instruments TI-99
astrocade: astrocade:
files: files:
- name: astrocde.zip - name: astrocde.zip
@@ -1832,6 +1886,7 @@ systems:
md5: 7d25a26e5c4841b364cfe6b1735eaf03 md5: 7d25a26e5c4841b364cfe6b1735eaf03
zipped_file: astro.bin zipped_file: astro.bin
native_id: astrocade native_id: astrocade
name: Bally Astrocade
gmaster: gmaster:
files: files:
- name: gmaster.zip - name: gmaster.zip
@@ -1843,6 +1898,7 @@ systems:
md5: 6bff08b5e5f96de405cd56d5f04a08f8 md5: 6bff08b5e5f96de405cd56d5f04a08f8
zipped_file: d78c11agf_e19.u1 zipped_file: d78c11agf_e19.u1
native_id: gmaster native_id: gmaster
name: Hartung Game Master
adam: adam:
files: files:
- name: adam.zip - name: adam.zip
@@ -1951,6 +2007,7 @@ systems:
md5: 3cdf2fe48ac4224b56f26c03f6c68982 md5: 3cdf2fe48ac4224b56f26c03f6c68982
zipped_file: printer.u2 zipped_file: printer.u2
native_id: adam native_id: adam
name: Coleco Adam
coleco-colecovision: coleco-colecovision:
files: files:
- name: colecovision.rom - name: colecovision.rom
@@ -1958,6 +2015,7 @@ systems:
required: true required: true
md5: 2c66f5911e5b42b8ebe113403548eee7 md5: 2c66f5911e5b42b8ebe113403548eee7
native_id: colecovision native_id: colecovision
name: ColecoVision
bbc: bbc:
files: files:
- name: bbcb.zip - name: bbcb.zip
@@ -2150,6 +2208,7 @@ systems:
required: true required: true
md5: f083f49d6fe66344c650d7e74249cb96 md5: f083f49d6fe66344c650d7e74249cb96
native_id: bbc native_id: bbc
name: BBC Micro
pcw: pcw:
files: files:
- name: pcw8256.zip - name: pcw8256.zip
@@ -2174,6 +2233,7 @@ systems:
md5: b664af93987d575b0248832832c61505 md5: b664af93987d575b0248832832c61505
zipped_file: 40103.ic109 zipped_file: 40103.ic109
native_id: pcw native_id: pcw
name: Amstrad PCW
apfm1000: apfm1000:
files: files:
- name: apfm1000.zip - name: apfm1000.zip
@@ -2195,6 +2255,7 @@ systems:
md5: 89a7cfa5469ce24773721d65b28f8544 md5: 89a7cfa5469ce24773721d65b28f8544
zipped_file: trash-ii.bin zipped_file: trash-ii.bin
native_id: apfm1000 native_id: apfm1000
name: APF M-1000
fm7: fm7:
files: files:
- name: fm7.zip - name: fm7.zip
@@ -2254,6 +2315,7 @@ systems:
md5: 7db27dede3e358017d518101850bccfa md5: 7db27dede3e358017d518101850bccfa
zipped_file: subsyscg.rom zipped_file: subsyscg.rom
native_id: fm7 native_id: fm7
name: Fujitsu FM-7
archimedes: archimedes:
files: files:
- name: aa310.zip - name: aa310.zip
@@ -2463,6 +2525,7 @@ systems:
md5: 1a8617c1abe3e0729d20ce844e1e12a8 md5: 1a8617c1abe3e0729d20ce844e1e12a8
zipped_file: acorn_0280,022-01_philips_8051ah-2.bin zipped_file: acorn_0280,022-01_philips_8051ah-2.bin
native_id: archimedes native_id: archimedes
name: Acorn Archimedes
atom: atom:
files: files:
- name: atom.zip - name: atom.zip
@@ -2484,6 +2547,7 @@ systems:
md5: 9627dfb5f8302db8dd5702dbf7c09f72 md5: 9627dfb5f8302db8dd5702dbf7c09f72
zipped_file: dosrom.u15 zipped_file: dosrom.u15
native_id: atom native_id: atom
name: Acorn Atom
electron: electron:
files: files:
- name: electron.zip - name: electron.zip
@@ -2544,6 +2608,7 @@ systems:
md5: 5c39baa89fe8a40a5167a53cc5ae7791 md5: 5c39baa89fe8a40a5167a53cc5ae7791
zipped_file: pres_adfs_115.rom zipped_file: pres_adfs_115.rom
native_id: electron native_id: electron
name: Acorn Electron
apple-ii: apple-ii:
files: files:
- name: apple2e.zip - name: apple2e.zip
@@ -2671,6 +2736,7 @@ systems:
md5: 5f1be0c1cdff26f5956eef9643911886 md5: 5f1be0c1cdff26f5956eef9643911886
zipped_file: 341-0028-a.rom zipped_file: 341-0028-a.rom
native_id: apple2 native_id: apple2
name: Apple II
camplynx: camplynx:
files: files:
- name: lynx48k.zip - name: lynx48k.zip
@@ -2753,6 +2819,7 @@ systems:
md5: f9f54913cdedb22bb8f0c549ad121379 md5: f9f54913cdedb22bb8f0c549ad121379
zipped_file: lynx128-3.ic3 zipped_file: lynx128-3.ic3
native_id: camplynx native_id: camplynx
name: Camputers Lynx
mz80k: mz80k:
files: files:
- name: mz80k.zip - name: mz80k.zip
@@ -2782,6 +2849,7 @@ systems:
md5: 4138784bdd2e2cbacd6c55eff195b2ac md5: 4138784bdd2e2cbacd6c55eff195b2ac
zipped_file: mz80kfdif.rom zipped_file: mz80kfdif.rom
native_id: mz80k native_id: mz80k
name: Sharp MZ-80K
mz700: mz700:
files: files:
- name: mz700.zip - name: mz700.zip
@@ -2798,6 +2866,7 @@ systems:
md5: e9045d57e8574f3eb3f775c02369fbfe md5: e9045d57e8574f3eb3f775c02369fbfe
zipped_file: mz700fon.int zipped_file: mz700fon.int
native_id: mz700 native_id: mz700
name: Sharp MZ-700
mz800: mz800:
files: files:
- name: mz800.zip - name: mz800.zip
@@ -2809,6 +2878,7 @@ systems:
md5: 7d3909267b3f3e6ce7aa999de0ded226 md5: 7d3909267b3f3e6ce7aa999de0ded226
zipped_file: mz800.rom zipped_file: mz800.rom
native_id: mz800 native_id: mz800
name: Sharp MZ-800
mz2000: mz2000:
files: files:
- name: mz2000.zip - name: mz2000.zip
@@ -2825,6 +2895,7 @@ systems:
md5: dea49b39998885631db16de1176e71b9 md5: dea49b39998885631db16de1176e71b9
zipped_file: font.bin zipped_file: font.bin
native_id: mz2000 native_id: mz2000
name: Sharp MZ-2000
mz2500: mz2500:
files: files:
- name: mz2500.zip - name: mz2500.zip
@@ -2861,6 +2932,7 @@ systems:
md5: 04843d14a8e2f69a7fcaff394a8cc012 md5: 04843d14a8e2f69a7fcaff394a8cc012
zipped_file: phone.rom zipped_file: phone.rom
native_id: mz2500 native_id: mz2500
name: Sharp MZ-2500
vgmplay: vgmplay:
files: files:
- name: qsound.zip - name: qsound.zip
@@ -2888,6 +2960,7 @@ systems:
md5: 8740932cda05e518a9955f1d08d6786f md5: 8740932cda05e518a9955f1d08d6786f
zipped_file: ym2608_adpcm_rom.bin zipped_file: ym2608_adpcm_rom.bin
native_id: vgmplay native_id: vgmplay
name: Video Game Music Player
gamepock: gamepock:
files: files:
- name: gamepock.zip - name: gamepock.zip
@@ -2899,6 +2972,7 @@ systems:
md5: a0dd595eafb407a6a4b4ed800005a394 md5: a0dd595eafb407a6a4b4ed800005a394
zipped_file: egpcboot.bin zipped_file: egpcboot.bin
native_id: gamepock native_id: gamepock
name: Epoch Game Pocket Computer
gamecom: gamecom:
files: files:
- name: gamecom.zip - name: gamecom.zip
@@ -2915,6 +2989,7 @@ systems:
md5: f7bcefb6daf923c8e5ea2eb69f619efe md5: f7bcefb6daf923c8e5ea2eb69f619efe
zipped_file: internal.bin zipped_file: internal.bin
native_id: gamecom native_id: gamecom
name: Tiger Game.com
xegs: xegs:
files: files:
- name: xegs.zip - name: xegs.zip
@@ -2926,6 +3001,7 @@ systems:
md5: 42cbd989802c17d0ac3731d33270d835 md5: 42cbd989802c17d0ac3731d33270d835
zipped_file: c101687.rom zipped_file: c101687.rom
native_id: xegs native_id: xegs
name: Atari XE Game System
crvision: crvision:
files: files:
- name: crvision.zip - name: crvision.zip
@@ -2937,6 +3013,7 @@ systems:
md5: 3b1ef759d8e3fb4071582efd33dd05f9 md5: 3b1ef759d8e3fb4071582efd33dd05f9
zipped_file: crvision.u20 zipped_file: crvision.u20
native_id: crvision native_id: crvision
name: VTech CreatiVision
vsmile: vsmile:
files: files:
- name: vsmile.zip - name: vsmile.zip
@@ -2958,6 +3035,7 @@ systems:
md5: 11e59253c578c8f16ea2375ec398e4e9 md5: 11e59253c578c8f16ea2375ec398e4e9
zipped_file: vsmile_v103.bin zipped_file: vsmile_v103.bin
native_id: vsmile native_id: vsmile
name: VTech V.Smile
socrates: socrates:
files: files:
- name: socrates.zip - name: socrates.zip
@@ -2989,6 +3067,7 @@ systems:
md5: 31c29c57e3d3e6788ba5817eaaa8b17a md5: 31c29c57e3d3e6788ba5817eaaa8b17a
zipped_file: speech_eng_vsm3.bin zipped_file: speech_eng_vsm3.bin
native_id: socrates native_id: socrates
name: VTech Socrates
rx78: rx78:
files: files:
- name: rx78.zip - name: rx78.zip
@@ -3000,6 +3079,7 @@ systems:
md5: f613b15c4b965013e4827d2ad79c7080 md5: f613b15c4b965013e4827d2ad79c7080
zipped_file: ipl.rom zipped_file: ipl.rom
native_id: rx78 native_id: rx78
name: Bandai RX-78
advision: advision:
files: files:
- name: advision.zip - name: advision.zip
@@ -3016,6 +3096,7 @@ systems:
md5: fc5e71445e4947a9d00eedbc66b13a8f md5: fc5e71445e4947a9d00eedbc66b13a8f
zipped_file: b8223__cop411l-kcn_n.u8 zipped_file: b8223__cop411l-kcn_n.u8
native_id: advision native_id: advision
name: Entex Adventure Vision
gamate: gamate:
files: files:
- name: gamate.zip - name: gamate.zip
@@ -3032,12 +3113,14 @@ systems:
md5: ef67993a94503c4b7798b5901c7dda52 md5: ef67993a94503c4b7798b5901c7dda52
zipped_file: gamate_bios_umc.bin zipped_file: gamate_bios_umc.bin
native_id: gamate native_id: gamate
name: Bitcorp Gamate
pv2000: pv2000:
files: files:
- name: pv2000.zip - name: pv2000.zip
destination: pv2000.zip destination: pv2000.zip
required: true required: true
native_id: pv2000 native_id: pv2000
name: Casio PV-2000
pc80: pc80:
files: files:
- name: pc8001.zip - name: pc8001.zip
@@ -3092,6 +3175,7 @@ systems:
md5: d81c6d5d7ad1a4bbbd6ae22a01257603 md5: d81c6d5d7ad1a4bbbd6ae22a01257603
zipped_file: kanji1.rom zipped_file: kanji1.rom
native_id: pc80 native_id: pc80
name: NEC PC-8001
cdi: cdi:
files: files:
- name: cdimono1.zip - name: cdimono1.zip
@@ -3123,6 +3207,7 @@ systems:
md5: 3d20cf7550f1b723158b42a1fd5bac62 md5: 3d20cf7550f1b723158b42a1fd5bac62
zipped_file: zx405042p__cdi_slave_2.0__b43t__zzmk9213.mc68hc705c8a_withtestrom.7206 zipped_file: zx405042p__cdi_slave_2.0__b43t__zzmk9213.mc68hc705c8a_withtestrom.7206
native_id: cdi native_id: cdi
name: Phillips CD-i
hikaru: hikaru:
files: files:
- name: hikaru.zip - name: hikaru.zip
@@ -3138,6 +3223,7 @@ systems:
required: true required: true
md5: ee643e8c7369fe7feca610d4daa4a57c md5: ee643e8c7369fe7feca610d4daa4a57c
native_id: hikaru native_id: hikaru
name: Hikaru
sony-playstation-vita: sony-playstation-vita:
files: files:
- name: PSP2UPDAT.PUP - name: PSP2UPDAT.PUP
@@ -3149,6 +3235,7 @@ systems:
required: true required: true
md5: f2c7b12fe85496ec88a0391b514d6e3b md5: f2c7b12fe85496ec88a0391b514d6e3b
native_id: psvita native_id: psvita
name: PS Vita
vectrex: vectrex:
files: files:
- name: vectrex.zip - name: vectrex.zip
@@ -3165,6 +3252,7 @@ systems:
md5: a9c238473229912eb757ff3dfe6f4631 md5: a9c238473229912eb757ff3dfe6f4631
zipped_file: exec_rom_intl_284001-1.bin zipped_file: exec_rom_intl_284001-1.bin
native_id: vectrex native_id: vectrex
name: GCE Vectrex
scummvm: scummvm:
files: files:
- name: MT32_PCM.ROM - name: MT32_PCM.ROM
@@ -3176,6 +3264,7 @@ systems:
required: true required: true
md5: 5626206284b22c2734f3e9efefcd2675 md5: 5626206284b22c2734f3e9efefcd2675
native_id: scummvm native_id: scummvm
name: ScummVM
supracan: supracan:
files: files:
- name: supracan.zip - name: supracan.zip
@@ -3187,6 +3276,7 @@ systems:
md5: 53c4343e062bb3b337370bbb58e64d16 md5: 53c4343e062bb3b337370bbb58e64d16
zipped_file: internal_68k.bin zipped_file: internal_68k.bin
native_id: supracan native_id: supracan
name: Supr'A'can
tandy-vis: tandy-vis:
files: files:
- name: vis.zip - name: vis.zip
@@ -3203,6 +3293,7 @@ systems:
md5: 758f8fec271fbf526bb22b36e88f154b md5: 758f8fec271fbf526bb22b36e88f154b
zipped_file: p513bk1b.bin zipped_file: p513bk1b.bin
native_id: vis native_id: vis
name: Tandy Video Information System
nintendo-64dd: nintendo-64dd:
files: files:
- name: 64DD_IPL.bin - name: 64DD_IPL.bin
@@ -3210,6 +3301,7 @@ systems:
required: true required: true
md5: 8d3d9f294b6e174bc7b1d2fd1c727530 md5: 8d3d9f294b6e174bc7b1d2fd1c727530
native_id: n64dd native_id: n64dd
name: Nintendo 64DD
nintendo-gamecube: nintendo-gamecube:
files: files:
- name: IPL.bin - name: IPL.bin
@@ -3225,6 +3317,7 @@ systems:
required: true required: true
md5: b17148254a5799684c7d783206504926 md5: b17148254a5799684c7d783206504926
native_id: gamecube native_id: gamecube
name: Nintendo GameCube
chihiro: chihiro:
files: files:
- name: mcpx_1.0.bin - name: mcpx_1.0.bin
@@ -3236,6 +3329,7 @@ systems:
required: true required: true
md5: f23d7e00ae8fbf88908ed1f9165f35eb md5: f23d7e00ae8fbf88908ed1f9165f35eb
native_id: chihiro native_id: chihiro
name: Sega Chihiro
oricatmos: oricatmos:
files: files:
- name: basic11.rom - name: basic11.rom
@@ -3247,6 +3341,7 @@ systems:
required: true required: true
md5: c888b36bf0fc222c3585c3fabe556d21 md5: c888b36bf0fc222c3585c3fabe556d21
native_id: oricatmos native_id: oricatmos
name: Oric Atmos
enterprise-64-128: enterprise-64-128:
files: files:
- name: basic21.bin - name: basic21.bin
@@ -3361,6 +3456,7 @@ systems:
md5: 47258d03aec37bc24df5dbc5d50fc9f8 md5: 47258d03aec37bc24df5dbc5d50fc9f8
zipped_file: exdos13.rom zipped_file: exdos13.rom
native_id: enterprise native_id: enterprise
name: Enterprise
videoton-tvc: videoton-tvc:
files: files:
- name: tvc22_sys.rom - name: tvc22_sys.rom
@@ -3413,6 +3509,7 @@ systems:
md5: 5ce95a26ceed5bec73995d83568da9cf md5: 5ce95a26ceed5bec73995d83568da9cf
zipped_file: tvc22_d7.64k zipped_file: tvc22_d7.64k
native_id: tvc native_id: tvc
name: Videoton TVC
triforce: triforce:
files: files:
- name: segaboot.gcm - name: segaboot.gcm
@@ -3420,6 +3517,7 @@ systems:
required: true required: true
md5: 2ef01c0e93f7ee3a0a4b139aa14728b9 md5: 2ef01c0e93f7ee3a0a4b139aa14728b9
native_id: triforce native_id: triforce
name: Triforce
bk: bk:
files: files:
- name: B11M_BOS.ROM - name: B11M_BOS.ROM
@@ -3513,6 +3611,7 @@ systems:
md5: fe4627d1e3a1535874085050733263e7 md5: fe4627d1e3a1535874085050733263e7
zipped_file: bk11m_324_bos.rom zipped_file: bk11m_324_bos.rom
native_id: bk native_id: bk
name: Elektronika BK
standalone_cores: standalone_cores:
- abuse - abuse
- azahar - azahar

View File

@@ -4,10 +4,8 @@ dat_version: v1.19.0
homepage: https://www.retroarch.com homepage: https://www.retroarch.com
source: https://github.com/libretro/libretro-database/blob/master/dat/System.dat source: https://github.com/libretro/libretro-database/blob/master/dat/System.dat
base_destination: system base_destination: system
cores: all_libretro
hash_type: sha1 hash_type: sha1
verification_mode: existence verification_mode: existence
case_insensitive_fs: true
systems: systems:
3do: 3do:
files: files:
@@ -102,6 +100,7 @@ systems:
md5: 35fa1a1ebaaeea286dc5cd15487c13ea md5: 35fa1a1ebaaeea286dc5cd15487c13ea
crc32: d5cbc509 crc32: d5cbc509
size: 1048576 size: 1048576
native_id: 3DO Company, The - 3DO
core: opera core: opera
manufacturer: Panasonic|GoldStar|Sanyo manufacturer: Panasonic|GoldStar|Sanyo
docs: https://docs.libretro.com/library/opera/ docs: https://docs.libretro.com/library/opera/
@@ -135,6 +134,7 @@ systems:
md5: 25629dfe870d097469c217b95fdc1c95 md5: 25629dfe870d097469c217b95fdc1c95
crc32: 1fe22ecd crc32: 1fe22ecd
size: 16384 size: 16384
native_id: Amstrad - CPC
arcade: arcade:
files: files:
- name: bubsys.zip - name: bubsys.zip
@@ -249,6 +249,10 @@ systems:
- name: aes.zip - name: aes.zip
destination: aes.zip destination: aes.zip
required: true required: true
native_id: Arcade
core: fbneo
manufacturer: Various
docs: https://docs.libretro.com/library/fbneo/
data_directories: data_directories:
- ref: fbneo-hiscore - ref: fbneo-hiscore
destination: '' destination: ''
@@ -256,9 +260,6 @@ systems:
destination: fbneo destination: fbneo
- ref: fbneo-samples - ref: fbneo-samples
destination: fbneo destination: fbneo
core: fbneo
manufacturer: Various
docs: https://docs.libretro.com/library/fbneo/
atari-400-800: atari-400-800:
files: files:
- name: ATARIBAS.ROM - name: ATARIBAS.ROM
@@ -303,6 +304,7 @@ systems:
md5: d7eb37aec6960cba36bc500e0e5d00bc md5: d7eb37aec6960cba36bc500e0e5d00bc
crc32: bdca01fb crc32: bdca01fb
size: 8192 size: 8192
native_id: Atari - 400-800
atari-5200: atari-5200:
files: files:
- name: 5200.rom - name: 5200.rom
@@ -312,6 +314,7 @@ systems:
md5: 281f20ea4320404ec820fb7ec0693b38 md5: 281f20ea4320404ec820fb7ec0693b38
crc32: 4248d3e3 crc32: 4248d3e3
size: 2048 size: 2048
native_id: Atari - 5200
core: a5200 core: a5200
manufacturer: Atari manufacturer: Atari
docs: https://docs.libretro.com/library/a5200/ docs: https://docs.libretro.com/library/a5200/
@@ -331,6 +334,7 @@ systems:
md5: 0763f1ffb006ddbe32e52d497ee848ae md5: 0763f1ffb006ddbe32e52d497ee848ae
crc32: 5d13730c crc32: 5d13730c
size: 4096 size: 4096
native_id: Atari - 7800
core: prosystem core: prosystem
manufacturer: Atari manufacturer: Atari
docs: https://docs.libretro.com/library/prosystem/ docs: https://docs.libretro.com/library/prosystem/
@@ -343,6 +347,7 @@ systems:
md5: fcd403db69f54290b51035d82f835e7b md5: fcd403db69f54290b51035d82f835e7b
crc32: 0d973c9d crc32: 0d973c9d
size: 512 size: 512
native_id: Atari - Lynx
core: handy core: handy
manufacturer: Atari manufacturer: Atari
docs: https://docs.libretro.com/library/handy/ docs: https://docs.libretro.com/library/handy/
@@ -355,6 +360,7 @@ systems:
md5: c1c57ce48e8ee4135885cee9e63a68a2 md5: c1c57ce48e8ee4135885cee9e63a68a2
crc32: d3c32283 crc32: d3c32283
size: 196608 size: 196608
native_id: Atari - ST
core: hatari core: hatari
manufacturer: Atari manufacturer: Atari
docs: https://docs.libretro.com/library/hatari/ docs: https://docs.libretro.com/library/hatari/
@@ -376,6 +382,7 @@ systems:
- name: bioscv.rom - name: bioscv.rom
destination: bioscv.rom destination: bioscv.rom
required: true required: true
native_id: Coleco - ColecoVision
commodore-amiga: commodore-amiga:
files: files:
- name: kick33180.A500 - name: kick33180.A500
@@ -462,6 +469,7 @@ systems:
md5: bb72565701b1b6faece07d68ea5da639 md5: bb72565701b1b6faece07d68ea5da639
crc32: 87746be2 crc32: 87746be2
size: 524288 size: 524288
native_id: Commodore - Amiga
core: puae core: puae
manufacturer: Commodore manufacturer: Commodore
docs: https://docs.libretro.com/library/puae/ docs: https://docs.libretro.com/library/puae/
@@ -510,6 +518,7 @@ systems:
destination: scpu-dos-2.04.bin destination: scpu-dos-2.04.bin
required: true required: true
md5: b2869f8678b8b274227f35aad26ba509 md5: b2869f8678b8b274227f35aad26ba509
native_id: Commodore - C128
core: vice_x128 core: vice_x128
manufacturer: Commodore manufacturer: Commodore
docs: https://docs.libretro.com/library/vice_x128/ docs: https://docs.libretro.com/library/vice_x128/
@@ -522,6 +531,7 @@ systems:
md5: a2e891e330d146c4046c2b622fc31462 md5: a2e891e330d146c4046c2b622fc31462
crc32: 683ed4ad crc32: 683ed4ad
size: 5763199 size: 5763199
native_id: Dinothawr
dos: dos:
files: files:
- name: MT32_CONTROL.ROM - name: MT32_CONTROL.ROM
@@ -552,6 +562,7 @@ systems:
md5: 08cdcfa0ed93e9cb16afa76e6ac5f0a4 md5: 08cdcfa0ed93e9cb16afa76e6ac5f0a4
crc32: 4b961eba crc32: 4b961eba
size: 1048576 size: 1048576
native_id: DOS
elektronika-bk: elektronika-bk:
files: files:
- name: B11M_BOS.ROM - name: B11M_BOS.ROM
@@ -610,6 +621,7 @@ systems:
md5: 95f8c41c6abf7640e35a6a03cecebd01 md5: 95f8c41c6abf7640e35a6a03cecebd01
crc32: 26c6e8a0 crc32: 26c6e8a0
size: 8192 size: 8192
native_id: Elektronika - BK-0010/BK-0011(M)
enterprise-64-128: enterprise-64-128:
files: files:
- name: hun.rom - name: hun.rom
@@ -696,6 +708,7 @@ systems:
md5: 55af78f877a21ca45eb2df68a74fcc60 md5: 55af78f877a21ca45eb2df68a74fcc60
crc32: c099a5e3 crc32: c099a5e3
size: 65536 size: 65536
native_id: Enterprise - 64/128
includes: includes:
- ep128emu - ep128emu
epoch-scv: epoch-scv:
@@ -707,6 +720,7 @@ systems:
md5: 635a978fd40db9a18ee44eff449fc126 md5: 635a978fd40db9a18ee44eff449fc126
crc32: 7ac06182 crc32: 7ac06182
size: 4096 size: 4096
native_id: EPOCH/YENO Super Cassette Vision
fairchild-channel-f: fairchild-channel-f:
files: files:
- name: sl31253.bin - name: sl31253.bin
@@ -730,6 +744,7 @@ systems:
md5: 95d339631d867c8f1d15a5f2ec26069d md5: 95d339631d867c8f1d15a5f2ec26069d
crc32: 015c1e38 crc32: 015c1e38
size: 1024 size: 1024
native_id: Fairchild Channel F
doom: doom:
files: files:
- name: prboom.wad - name: prboom.wad
@@ -739,6 +754,7 @@ systems:
md5: 72ae1b47820fcc93cc0df9c428d0face md5: 72ae1b47820fcc93cc0df9c428d0face
crc32: a5751b99 crc32: a5751b99
size: 143312 size: 143312
native_id: Id Software - Doom
j2me: j2me:
files: files:
- name: freej2me-lr.jar - name: freej2me-lr.jar
@@ -762,6 +778,7 @@ systems:
md5: 29a92d0867da2917275b7c6c805d256f md5: 29a92d0867da2917275b7c6c805d256f
crc32: ffb98ffa crc32: ffb98ffa
size: 552039 size: 552039
native_id: J2ME
core: freej2me core: freej2me
manufacturer: Java manufacturer: Java
docs: https://docs.libretro.com/library/freej2me/ docs: https://docs.libretro.com/library/freej2me/
@@ -774,6 +791,7 @@ systems:
md5: 66223be1497460f1e60885eeb35e03cc md5: 66223be1497460f1e60885eeb35e03cc
crc32: 4df6d054 crc32: 4df6d054
size: 262144 size: 262144
native_id: MacII
magnavox-odyssey2: magnavox-odyssey2:
files: files:
- name: o2rom.bin - name: o2rom.bin
@@ -804,6 +822,7 @@ systems:
md5: 279008e4a0db2dc5f1c048853b033828 md5: 279008e4a0db2dc5f1c048853b033828
crc32: 11647ca5 crc32: 11647ca5
size: 1024 size: 1024
native_id: Magnavox - Odyssey2
core: o2em core: o2em
manufacturer: Magnavox|Philips manufacturer: Magnavox|Philips
docs: https://docs.libretro.com/library/o2em/ docs: https://docs.libretro.com/library/o2em/
@@ -823,6 +842,7 @@ systems:
md5: 0cd5946c6473e42e8e4c2137785e427f md5: 0cd5946c6473e42e8e4c2137785e427f
crc32: 683a4158 crc32: 683a4158
size: 2048 size: 2048
native_id: Mattel - Intellivision
data_directories: data_directories:
- ref: freeintv-overlays - ref: freeintv-overlays
destination: freeintv_overlays destination: freeintv_overlays
@@ -933,6 +953,7 @@ systems:
md5: 279efd1eae0d358eecd4edc7d9adedf3 md5: 279efd1eae0d358eecd4edc7d9adedf3
crc32: ab6874f8 crc32: ab6874f8
size: 16640 size: 16640
native_id: Microsoft - MSX
core: bluemsx core: bluemsx
manufacturer: Spectravideo|Philips|Al Alamiah|Sony|Sanyo|Mitsubishi|Toshiba|Hitachi|Panasonic|Canon|Casio|Pioneer|Fujitsu|Yamaha|JVC|Kyocera|GoldStar|Samsung|Daewoo|Gradiente|Sharp|Talent|NTT|ACVS/CIEL|DDX|AGE manufacturer: Spectravideo|Philips|Al Alamiah|Sony|Sanyo|Mitsubishi|Toshiba|Hitachi|Panasonic|Canon|Casio|Pioneer|Fujitsu|Yamaha|JVC|Kyocera|GoldStar|Samsung|Daewoo|Gradiente|Sharp|Talent|NTT|ACVS/CIEL|DDX|AGE
Labs Labs
@@ -991,6 +1012,7 @@ systems:
md5: 0754f903b52e3b3342202bdafb13efa5 md5: 0754f903b52e3b3342202bdafb13efa5
crc32: 2b5b75fe crc32: 2b5b75fe
size: 262144 size: 262144
native_id: NEC - PC Engine - TurboGrafx 16 - SuperGrafx
core: mednafen_pce_fast core: mednafen_pce_fast
manufacturer: NEC manufacturer: NEC
docs: https://docs.libretro.com/library/mednafen_pce_fast/ docs: https://docs.libretro.com/library/mednafen_pce_fast/
@@ -1073,6 +1095,7 @@ systems:
md5: 524473c1a5a03b17e21d86a0408ff827 md5: 524473c1a5a03b17e21d86a0408ff827
crc32: fe9f57f2 crc32: fe9f57f2
size: 16384 size: 16384
native_id: NEC - PC-98
core: np2kai core: np2kai
manufacturer: NEC manufacturer: NEC
docs: https://docs.libretro.com/library/np2kai/ docs: https://docs.libretro.com/library/np2kai/
@@ -1115,6 +1138,7 @@ systems:
md5: e2fb7c7220e3a7838c2dd7e401a7f3d8 md5: e2fb7c7220e3a7838c2dd7e401a7f3d8
crc32: 236102c9 crc32: 236102c9
size: 1048576 size: 1048576
native_id: NEC - PC-FX
core: mednafen_pcfx core: mednafen_pcfx
manufacturer: NEC manufacturer: NEC
docs: https://docs.libretro.com/library/mednafen_pcfx/ docs: https://docs.libretro.com/library/mednafen_pcfx/
@@ -1134,6 +1158,7 @@ systems:
md5: 7f98d77d7a094ad7d069b74bd553ec98 md5: 7f98d77d7a094ad7d069b74bd553ec98
crc32: 4c514089 crc32: 4c514089
size: 24592 size: 24592
native_id: Nintendo - Famicom Disk System
nintendo-gb: nintendo-gb:
files: files:
- name: dmg_boot.bin - name: dmg_boot.bin
@@ -1150,6 +1175,7 @@ systems:
md5: 32fbbd84168d3482956eb3c5051637f5 md5: 32fbbd84168d3482956eb3c5051637f5
crc32: 59c8598e crc32: 59c8598e
size: 256 size: 256
native_id: Nintendo - Gameboy
core: gambatte core: gambatte
manufacturer: Nintendo manufacturer: Nintendo
docs: https://docs.libretro.com/library/gambatte/ docs: https://docs.libretro.com/library/gambatte/
@@ -1162,6 +1188,7 @@ systems:
md5: a860e8c0b6d573d191e4ec7db1b1e4f6 md5: a860e8c0b6d573d191e4ec7db1b1e4f6
crc32: '81977335' crc32: '81977335'
size: 16384 size: 16384
native_id: Nintendo - Game Boy Advance
core: gpsp core: gpsp
manufacturer: Nintendo manufacturer: Nintendo
docs: https://docs.libretro.com/library/gpsp/ docs: https://docs.libretro.com/library/gpsp/
@@ -1181,6 +1208,7 @@ systems:
md5: dbfce9db9deaa2567f6a84fde55f9680 md5: dbfce9db9deaa2567f6a84fde55f9680
crc32: 41884e46 crc32: 41884e46
size: 2304 size: 2304
native_id: Nintendo - Gameboy Color
nintendo-gamecube: nintendo-gamecube:
files: files:
- name: gc-dvd-20010608.bin - name: gc-dvd-20010608.bin
@@ -1274,6 +1302,7 @@ systems:
- name: font_japanese.bin - name: font_japanese.bin
destination: dolphin-emu/Sys/GC/font_japanese.bin destination: dolphin-emu/Sys/GC/font_japanese.bin
required: false required: false
native_id: Nintendo - GameCube
core: dolphin core: dolphin
manufacturer: Nintendo manufacturer: Nintendo
docs: https://docs.libretro.com/library/dolphin/ docs: https://docs.libretro.com/library/dolphin/
@@ -1289,6 +1318,7 @@ systems:
md5: 8d3d9f294b6e174bc7b1d2fd1c727530 md5: 8d3d9f294b6e174bc7b1d2fd1c727530
crc32: 7f933ce2 crc32: 7f933ce2
size: 4194304 size: 4194304
native_id: Nintendo - Nintendo 64DD
nintendo-ds: nintendo-ds:
files: files:
- name: bios7.bin - name: bios7.bin
@@ -1324,6 +1354,7 @@ systems:
- name: dsi_nand.bin - name: dsi_nand.bin
destination: dsi_nand.bin destination: dsi_nand.bin
required: true required: true
native_id: Nintendo - Nintendo DS
core: desmume core: desmume
manufacturer: Nintendo manufacturer: Nintendo
docs: https://docs.libretro.com/library/desmume/ docs: https://docs.libretro.com/library/desmume/
@@ -1332,10 +1363,11 @@ systems:
- name: NstDatabase.xml - name: NstDatabase.xml
destination: NstDatabase.xml destination: NstDatabase.xml
required: true required: true
sha1: f92312bae56e29c5bf00a5103105fce78472bf5c sha1: 26322f182540211e9b5e3647675b7c593706ae2b
md5: 0ee6cbdc6f5c96ce9c8aa5edb59066f4 md5: 7bfe8c0540ed4bd6a0f1e2a0f0118ced
crc32: 0e4d552b crc32: ebb2196c
size: 1009534 size: 1009534
native_id: Nintendo - Nintendo Entertainment System
core: fceumm core: fceumm
manufacturer: Nintendo manufacturer: Nintendo
docs: https://docs.libretro.com/library/fceumm/ docs: https://docs.libretro.com/library/fceumm/
@@ -1348,6 +1380,7 @@ systems:
md5: 1e4fb124a3a886865acb574f388c803d md5: 1e4fb124a3a886865acb574f388c803d
crc32: aed3c14d crc32: aed3c14d
size: 4096 size: 4096
native_id: Nintendo - Pokemon Mini
nintendo-satellaview: nintendo-satellaview:
files: files:
- name: BS-X.bin - name: BS-X.bin
@@ -1371,6 +1404,7 @@ systems:
md5: 4ed9648505ab33a4daec93707b16caba md5: 4ed9648505ab33a4daec93707b16caba
crc32: 8c573c7e crc32: 8c573c7e
size: 1048576 size: 1048576
native_id: Nintendo - Satellaview
nintendo-sufami-turbo: nintendo-sufami-turbo:
files: files:
- name: STBIOS.bin - name: STBIOS.bin
@@ -1380,6 +1414,7 @@ systems:
md5: d3a44ba7d42a74d3ac58cb9c14c6a5ca md5: d3a44ba7d42a74d3ac58cb9c14c6a5ca
crc32: 9b4ca911 crc32: 9b4ca911
size: 262144 size: 262144
native_id: Nintendo - SuFami Turbo
nintendo-sgb: nintendo-sgb:
files: files:
- name: SGB1.sfc - name: SGB1.sfc
@@ -1441,6 +1476,7 @@ systems:
- name: sgb.boot.rom - name: sgb.boot.rom
destination: sgb.boot.rom destination: sgb.boot.rom
required: false required: false
native_id: Nintendo - Super Game Boy
nintendo-snes: nintendo-snes:
files: files:
- name: cx4.data.rom - name: cx4.data.rom
@@ -1562,6 +1598,7 @@ systems:
md5: dda40ccd57390c96e49d30a041f9a9e7 md5: dda40ccd57390c96e49d30a041f9a9e7
crc32: f73d5e10 crc32: f73d5e10
size: 131072 size: 131072
native_id: Nintendo - Super Nintendo Entertainment System
core: bsnes core: bsnes
manufacturer: Nintendo manufacturer: Nintendo
docs: https://docs.libretro.com/library/bsnes/ docs: https://docs.libretro.com/library/bsnes/
@@ -1588,6 +1625,7 @@ systems:
md5: 279008e4a0db2dc5f1c048853b033828 md5: 279008e4a0db2dc5f1c048853b033828
crc32: 11647ca5 crc32: 11647ca5
size: 1024 size: 1024
native_id: Phillips - Videopac+
sega-dreamcast: sega-dreamcast:
files: files:
- name: dc_boot.bin - name: dc_boot.bin
@@ -1611,6 +1649,7 @@ systems:
md5: 0a93f7940c455905bea6e392dfde92a4 md5: 0a93f7940c455905bea6e392dfde92a4
crc32: c611b498 crc32: c611b498
size: 131072 size: 131072
native_id: Sega - Dreamcast
core: flycast core: flycast
manufacturer: Sega manufacturer: Sega
docs: https://docs.libretro.com/library/flycast/ docs: https://docs.libretro.com/library/flycast/
@@ -1668,6 +1707,7 @@ systems:
- name: segasp.zip - name: segasp.zip
destination: dc/segasp.zip destination: dc/segasp.zip
required: true required: true
native_id: Sega - Dreamcast-based Arcade
sega-game-gear: sega-game-gear:
files: files:
- name: bios.gg - name: bios.gg
@@ -1677,6 +1717,7 @@ systems:
md5: 672e104c3be3a238301aceffc3b23fd6 md5: 672e104c3be3a238301aceffc3b23fd6
crc32: 0ebea9d4 crc32: 0ebea9d4
size: 1024 size: 1024
native_id: Sega - Game Gear
sega-master-system: sega-master-system:
files: files:
- name: bios.sms - name: bios.sms
@@ -1707,6 +1748,7 @@ systems:
md5: 840481177270d5642a14ca71ee72844c md5: 840481177270d5642a14ca71ee72844c
crc32: 0072ed54 crc32: 0072ed54
size: 8192 size: 8192
native_id: Sega - Master System - Mark III
sega-mega-cd: sega-mega-cd:
files: files:
- name: bios_CD_E.bin - name: bios_CD_E.bin
@@ -1730,6 +1772,7 @@ systems:
md5: 2efd74e3232ff260e371b99f84024f7f md5: 2efd74e3232ff260e371b99f84024f7f
crc32: c6d10268 crc32: c6d10268
size: 131072 size: 131072
native_id: Sega - Mega CD - Sega CD
sega-mega-drive: sega-mega-drive:
files: files:
- name: areplay.bin - name: areplay.bin
@@ -1774,6 +1817,7 @@ systems:
md5: b4e76e416b887f4e7413ba76fa735f16 md5: b4e76e416b887f4e7413ba76fa735f16
crc32: 4dcfd55c crc32: 4dcfd55c
size: 262144 size: 262144
native_id: Sega - Mega Drive - Genesis
core: genesis_plus_gx core: genesis_plus_gx
manufacturer: Sega manufacturer: Sega
docs: https://docs.libretro.com/library/genesis_plus_gx/ docs: https://docs.libretro.com/library/genesis_plus_gx/
@@ -1859,6 +1903,7 @@ systems:
- name: stvbios.zip - name: stvbios.zip
destination: kronos/stvbios.zip destination: kronos/stvbios.zip
required: true required: true
native_id: Sega - Saturn
core: kronos core: kronos
manufacturer: Sega manufacturer: Sega
docs: https://docs.libretro.com/library/kronos/ docs: https://docs.libretro.com/library/kronos/
@@ -1880,6 +1925,7 @@ systems:
md5: 851e4a5936f17d13f8c39a980cf00d77 md5: 851e4a5936f17d13f8c39a980cf00d77
crc32: e3995a57 crc32: e3995a57
size: 2048 size: 2048
native_id: Sharp - X1
core: x1 core: x1
manufacturer: Sharp manufacturer: Sharp
docs: https://docs.libretro.com/library/x1/ docs: https://docs.libretro.com/library/x1/
@@ -1920,6 +1966,7 @@ systems:
md5: 0617321daa182c3f3d6f41fd02fb3275 md5: 0617321daa182c3f3d6f41fd02fb3275
crc32: 00eeb408 crc32: 00eeb408
size: 131072 size: 131072
native_id: Sharp - X68000
core: px68k core: px68k
manufacturer: Sharp manufacturer: Sharp
docs: https://docs.libretro.com/library/px68k/ docs: https://docs.libretro.com/library/px68k/
@@ -2270,6 +2317,7 @@ systems:
md5: 85fede415f4294cc777517d7eada482e md5: 85fede415f4294cc777517d7eada482e
crc32: 2cbe8995 crc32: 2cbe8995
size: 32768 size: 32768
native_id: Sinclair - ZX Spectrum
core: fuse core: fuse
manufacturer: Sinclair|Amstrad manufacturer: Sinclair|Amstrad
docs: https://docs.libretro.com/library/fuse/ docs: https://docs.libretro.com/library/fuse/
@@ -2352,6 +2400,7 @@ systems:
md5: 08ca8b2dba6662e8024f9e789711c6fc md5: 08ca8b2dba6662e8024f9e789711c6fc
crc32: ff3abc59 crc32: ff3abc59
size: 524288 size: 524288
native_id: SNK - NeoGeo CD
core: neocd core: neocd
manufacturer: SNK manufacturer: SNK
docs: https://docs.libretro.com/library/neocd/ docs: https://docs.libretro.com/library/neocd/
@@ -2511,6 +2560,7 @@ systems:
md5: 81bbe60ba7a3d1cea1d48c14cbcc647b md5: 81bbe60ba7a3d1cea1d48c14cbcc647b
crc32: 2f53b852 crc32: 2f53b852
size: 524288 size: 524288
native_id: Sony - PlayStation
core: duckstation core: duckstation
manufacturer: Sony manufacturer: Sony
docs: https://docs.libretro.com/library/duckstation/ docs: https://docs.libretro.com/library/duckstation/
@@ -3027,6 +3077,7 @@ systems:
md5: d3e81e95db25f5a86a7b7474550a2155 md5: d3e81e95db25f5a86a7b7474550a2155
crc32: 4e8c160c crc32: 4e8c160c
size: 4194304 size: 4194304
native_id: Sony - PlayStation 2
sony-psp: sony-psp:
files: files:
- name: ppge_atlas.zim - name: ppge_atlas.zim
@@ -3036,6 +3087,7 @@ systems:
md5: 866855cc330b9b95cc69135fb7b41d38 md5: 866855cc330b9b95cc69135fb7b41d38
crc32: 7b57fa78 crc32: 7b57fa78
size: 666530 size: 666530
native_id: Sony - PlayStation Portable
core: ppsspp core: ppsspp
manufacturer: Sony manufacturer: Sony
docs: https://docs.libretro.com/library/ppsspp/ docs: https://docs.libretro.com/library/ppsspp/
@@ -3065,6 +3117,7 @@ systems:
md5: d4448d09bbfde687c04f9e3310e023ab md5: d4448d09bbfde687c04f9e3310e023ab
crc32: 4bf05697 crc32: 4bf05697
size: 262144 size: 262144
native_id: Texas Instruments TI-83
core: numero core: numero
manufacturer: Texas Instruments manufacturer: Texas Instruments
docs: https://docs.libretro.com/library/numero/ docs: https://docs.libretro.com/library/numero/
@@ -3098,6 +3151,7 @@ systems:
md5: 88dc7876d584f90e4106f91444ab23b7 md5: 88dc7876d584f90e4106f91444ab23b7
crc32: 1466aed4 crc32: 1466aed4
size: 16384 size: 16384
native_id: Videoton - TV Computer
wolfenstein-3d: wolfenstein-3d:
files: files:
- name: ecwolf.pk3 - name: ecwolf.pk3
@@ -3107,6 +3161,7 @@ systems:
md5: c011b428819eea4a80b455c245a5a04d md5: c011b428819eea4a80b455c245a5a04d
crc32: 26dc3fba crc32: 26dc3fba
size: 178755 size: 178755
native_id: Wolfenstein 3D
scummvm: scummvm:
files: files:
- name: scummvm.zip - name: scummvm.zip
@@ -3116,6 +3171,7 @@ systems:
md5: a17e0e0150155400d8cced329563d9c8 md5: a17e0e0150155400d8cced329563d9c8
crc32: a93f1c4b crc32: a93f1c4b
size: 9523360 size: 9523360
native_id: ScummVM
core: scummvm core: scummvm
manufacturer: Various manufacturer: Various
docs: https://docs.libretro.com/library/scummvm/ docs: https://docs.libretro.com/library/scummvm/
@@ -3141,3 +3197,5 @@ systems:
core: xrick core: xrick
manufacturer: Other manufacturer: Other
docs: https://docs.libretro.com/library/xrick/ docs: https://docs.libretro.com/library/xrick/
case_insensitive_fs: true
cores: all_libretro

View File

@@ -191,10 +191,9 @@ def load_platform_config(platform_name: str, platforms_dir: str = "platforms") -
system.setdefault("files", []).append(gf) system.setdefault("files", []).append(gf)
existing.add(key) existing.add(key)
# Merge cores from _registry.yml if the registry has a broader list. # Merge metadata from _registry.yml. The registry is our curated source;
# The scraped YAML may have an incomplete cores list; the registry is # the scraped YAML may be incomplete (missing cores, metadata fields).
# our curated source and supplements it. This affects all consumers: # Registry fields supplement (not replace) the scraped config.
# generate_pack, verify, generate_truth, diff_truth.
registry_path = os.path.join(platforms_dir, "_registry.yml") registry_path = os.path.join(platforms_dir, "_registry.yml")
if os.path.exists(registry_path): if os.path.exists(registry_path):
reg_real = os.path.realpath(registry_path) reg_real = os.path.realpath(registry_path)
@@ -203,18 +202,26 @@ def load_platform_config(platform_name: str, platforms_dir: str = "platforms") -
_shared_yml_cache[reg_real] = yaml.safe_load(f) or {} _shared_yml_cache[reg_real] = yaml.safe_load(f) or {}
reg = _shared_yml_cache[reg_real] reg = _shared_yml_cache[reg_real]
reg_entry = reg.get("platforms", {}).get(platform_name, {}) reg_entry = reg.get("platforms", {}).get(platform_name, {})
# Merge cores (union for lists, override for all_libretro)
reg_cores = reg_entry.get("cores") reg_cores = reg_entry.get("cores")
if reg_cores is not None: if reg_cores is not None:
cfg_cores = config.get("cores") cfg_cores = config.get("cores")
if reg_cores == "all_libretro": if reg_cores == "all_libretro":
config["cores"] = "all_libretro" config["cores"] = "all_libretro"
elif isinstance(reg_cores, list) and isinstance(cfg_cores, list): elif isinstance(reg_cores, list) and isinstance(cfg_cores, list):
# Union: registry supplements scraped cores
merged_set = {str(c) for c in cfg_cores} | {str(c) for c in reg_cores} merged_set = {str(c) for c in cfg_cores} | {str(c) for c in reg_cores}
config["cores"] = sorted(merged_set) config["cores"] = sorted(merged_set)
elif isinstance(reg_cores, list) and cfg_cores is None: elif isinstance(reg_cores, list) and cfg_cores is None:
config["cores"] = reg_cores config["cores"] = reg_cores
# Merge all registry fields absent from config (except cores,
# handled above with union logic). No hardcoded list — any field
# added to the registry is automatically available in the config.
for key, val in reg_entry.items():
if key != "cores" and key not in config:
config[key] = val
_platform_config_cache[cache_key] = config _platform_config_cache[cache_key] = config
return config return config
@@ -546,6 +553,25 @@ def resolve_local_file(
if fn.casefold() in basename_targets: if fn.casefold() in basename_targets:
return os.path.join(root, fn), "data_dir" return os.path.join(root, fn), "data_dir"
# Agnostic fallback: for filename-agnostic files, find any DB file
# matching the system path prefix and size criteria
if file_entry.get("agnostic"):
agnostic_prefix = file_entry.get("agnostic_path_prefix", "")
min_size = file_entry.get("min_size", 0)
max_size = file_entry.get("max_size", float("inf"))
exact_size = file_entry.get("size")
if exact_size and not min_size:
min_size = exact_size
max_size = exact_size
if agnostic_prefix:
for _sha1, entry in files_db.items():
path = entry.get("path", "")
if not path.startswith(agnostic_prefix):
continue
size = entry.get("size", 0)
if min_size <= size <= max_size and os.path.exists(path):
return path, "agnostic_fallback"
return None, "not_found" return None, "not_found"
@@ -949,6 +975,39 @@ def expand_platform_declared_names(config: dict, db: dict) -> set[str]:
return declared return declared
import re
_TIMESTAMP_PATTERNS = [
re.compile(r'"generated_at":\s*"[^"]*"'), # database.json
re.compile(r'\*Auto-generated on [^*]*\*'), # README.md
re.compile(r'\*Generated on [^*]*\*'), # docs site pages
]
def write_if_changed(path: str, content: str) -> bool:
"""Write content to path only if the non-timestamp content differs.
Compares new and existing content after stripping timestamp lines.
Returns True if the file was written, False if skipped (unchanged).
"""
if os.path.exists(path):
with open(path) as f:
existing = f.read()
if _strip_timestamps(existing) == _strip_timestamps(content):
return False
with open(path, "w") as f:
f.write(content)
return True
def _strip_timestamps(text: str) -> str:
"""Remove known timestamp patterns for content comparison."""
result = text
for pattern in _TIMESTAMP_PATTERNS:
result = pattern.sub("", result)
return result
# Validation and mode filtering -extracted to validation.py for SoC. # Validation and mode filtering -extracted to validation.py for SoC.
# Re-exported below for backward compatibility. # Re-exported below for backward compatibility.

View File

@@ -16,15 +16,27 @@ from exporter import discover_exporters
OUTPUT_FILENAMES: dict[str, str] = { OUTPUT_FILENAMES: dict[str, str] = {
"retroarch": "System.dat", "retroarch": "System.dat",
"lakka": "System.dat",
"retropie": "System.dat",
"batocera": "batocera-systems", "batocera": "batocera-systems",
"recalbox": "es_bios.xml", "recalbox": "es_bios.xml",
"retrobat": "batocera-systems.json", "retrobat": "batocera-systems.json",
"emudeck": "checkBIOS.sh",
"retrodeck": "component_manifest.json",
"romm": "known_bios_files.json",
} }
def output_filename(platform: str) -> str: def output_path(platform: str, output_dir: str) -> str:
"""Return the native output filename for a platform.""" """Return the full output path for a platform's native export.
return OUTPUT_FILENAMES.get(platform, f"{platform}_bios.dat")
Each platform gets its own subdirectory to avoid filename collisions
(e.g. retroarch, lakka, retropie all produce System.dat).
"""
filename = OUTPUT_FILENAMES.get(platform, f"{platform}_bios.dat")
plat_dir = Path(output_dir) / platform
plat_dir.mkdir(parents=True, exist_ok=True)
return str(plat_dir / filename)
def run( def run(
@@ -35,8 +47,6 @@ def run(
) -> int: ) -> int:
"""Export truth to native formats, return exit code.""" """Export truth to native formats, return exit code."""
exporters = discover_exporters() exporters = discover_exporters()
output_path = Path(output_dir)
output_path.mkdir(parents=True, exist_ok=True)
errors = 0 errors = 0
@@ -60,7 +70,7 @@ def run(
except (FileNotFoundError, OSError): except (FileNotFoundError, OSError):
pass pass
dest = str(output_path / output_filename(platform)) dest = output_path(platform, output_dir)
exporter = exporter_cls() exporter = exporter_cls()
exporter.export(truth_data, dest, scraped_data=scraped) exporter.export(truth_data, dest, scraped_data=scraped)

View File

@@ -25,3 +25,37 @@ class BaseExporter(ABC):
@abstractmethod @abstractmethod
def validate(self, truth_data: dict, output_path: str) -> list[str]: def validate(self, truth_data: dict, output_path: str) -> list[str]:
"""Validate exported file against truth data, return list of issues.""" """Validate exported file against truth data, return list of issues."""
@staticmethod
def _is_pattern(name: str) -> bool:
"""Check if a filename is a placeholder pattern (not a real file)."""
return "<" in name or ">" in name or "*" in name
@staticmethod
def _dest(fe: dict) -> str:
"""Get destination path for a file entry, falling back to name."""
return fe.get("path") or fe.get("destination") or fe.get("name", "")
@staticmethod
def _display_name(
sys_id: str, scraped_sys: dict | None = None,
) -> str:
"""Get display name for a system from scraped data or slug."""
if scraped_sys:
name = scraped_sys.get("name")
if name:
return name
# Fallback: convert slug to display name with acronym handling
_UPPER = {
"3do", "cdi", "cpc", "cps1", "cps2", "cps3", "dos", "gba",
"gbc", "hle", "msx", "nes", "nds", "ngp", "psp", "psx",
"sms", "snes", "stv", "tvc", "vb", "zx",
}
parts = sys_id.replace("-", " ").split()
result = []
for p in parts:
if p.lower() in _UPPER:
result.append(p.upper())
else:
result.append(p.capitalize())
return " ".join(result)

View File

@@ -0,0 +1,111 @@
"""Exporter for Batocera batocera-systems format.
Produces a Python dict matching the exact format of
batocera-linux/batocera-scripts/scripts/batocera-systems.
"""
from __future__ import annotations
from pathlib import Path
from .base_exporter import BaseExporter
class Exporter(BaseExporter):
"""Export truth data to Batocera batocera-systems format."""
@staticmethod
def platform_name() -> str:
return "batocera"
def export(
self,
truth_data: dict,
output_path: str,
scraped_data: dict | None = None,
) -> None:
# Build native_id and display name maps from scraped data
native_map: dict[str, str] = {}
if scraped_data:
for sys_id, sys_data in scraped_data.get("systems", {}).items():
nid = sys_data.get("native_id")
if nid:
native_map[sys_id] = nid
lines: list[str] = ["systems = {", ""]
systems = truth_data.get("systems", {})
for sys_id in sorted(systems):
sys_data = systems[sys_id]
files = sys_data.get("files", [])
if not files:
continue
native_id = native_map.get(sys_id, sys_id)
scraped_sys = scraped_data.get("systems", {}).get(sys_id) if scraped_data else None
display_name = self._display_name(sys_id, scraped_sys)
# Build md5 lookup from scraped data for this system
scraped_md5: dict[str, str] = {}
if scraped_data:
s_sys = scraped_data.get("systems", {}).get(sys_id, {})
for sf in s_sys.get("files", []):
sname = sf.get("name", "").lower()
smd5 = sf.get("md5", "")
if sname and smd5:
scraped_md5[sname] = smd5
# Build biosFiles entries as compact single-line dicts
# Original format ALWAYS has md5 — use scraped md5 as fallback
bios_parts: list[str] = []
for fe in files:
name = fe.get("name", "")
if name.startswith("_") or self._is_pattern(name):
continue
dest = self._dest(fe)
md5 = fe.get("md5", "")
if isinstance(md5, list):
md5 = md5[0] if md5 else ""
if not md5:
md5 = scraped_md5.get(name.lower(), "")
# Original format requires md5 for every entry — skip without
if not md5:
continue
bios_parts.append(
f'{{ "md5": "{md5}", "file": "bios/{dest}" }}'
)
bios_str = ", ".join(bios_parts)
line = (
f' "{native_id}": '
f'{{ "name": "{display_name}", '
f'"biosFiles": [ {bios_str} ] }},'
)
lines.append(line)
lines.append("")
lines.append("}")
lines.append("")
Path(output_path).write_text("\n".join(lines), encoding="utf-8")
def validate(self, truth_data: dict, output_path: str) -> list[str]:
content = Path(output_path).read_text(encoding="utf-8")
issues: list[str] = []
for sys_data in truth_data.get("systems", {}).values():
for fe in sys_data.get("files", []):
name = fe.get("name", "")
if name.startswith("_") or self._is_pattern(name):
continue
# Skip entries without md5 (not exportable in this format)
md5 = fe.get("md5", "")
if isinstance(md5, list):
md5 = md5[0] if md5 else ""
if not md5:
continue
dest = self._dest(fe)
if dest not in content and name not in content:
issues.append(f"missing: {name}")
return issues

View File

@@ -0,0 +1,207 @@
"""Exporter for EmuDeck checkBIOS.sh format.
Produces a bash script matching the exact pattern of EmuDeck's
functions/checkBIOS.sh: per-system check functions with MD5 arrays
inside the function body, iterating over $biosPath/* files.
Two patterns:
- MD5 pattern: systems with known hashes, loop $biosPath/*, md5sum each, match
- File-exists pattern: systems with specific paths, check -f
"""
from __future__ import annotations
import re
from pathlib import Path
from .base_exporter import BaseExporter
# Map our system IDs to EmuDeck function naming conventions
_SYSTEM_CONFIG: dict[str, dict] = {
"sony-playstation": {
"func": "checkPS1BIOS",
"var": "PSXBIOS",
"array": "PSBios",
"pattern": "md5",
},
"sony-playstation-2": {
"func": "checkPS2BIOS",
"var": "PS2BIOS",
"array": "PS2Bios",
"pattern": "md5",
},
"sega-mega-cd": {
"func": "checkSegaCDBios",
"var": "SEGACDBIOS",
"array": "CDBios",
"pattern": "md5",
},
"sega-saturn": {
"func": "checkSaturnBios",
"var": "SATURNBIOS",
"array": "SaturnBios",
"pattern": "md5",
},
"sega-dreamcast": {
"func": "checkDreamcastBios",
"var": "BIOS",
"array": "hashes",
"pattern": "md5",
},
"nintendo-ds": {
"func": "checkDSBios",
"var": "BIOS",
"array": "hashes",
"pattern": "md5",
},
"nintendo-switch": {
"func": "checkCitronBios",
"pattern": "file-exists",
"firmware_path": "$biosPath/citron/firmware",
"keys_path": "$biosPath/citron/keys/prod.keys",
},
}
def _make_md5_function(cfg: dict, md5s: list[str]) -> list[str]:
"""Generate a MD5-checking function matching EmuDeck's exact pattern."""
func = cfg["func"]
var = cfg["var"]
array = cfg["array"]
md5_str = " ".join(md5s)
return [
f"{func}(){{",
"",
f'\t{var}="NULL"',
"",
'\tfor entry in "$biosPath/"*',
"\tdo",
'\t\tif [ -f "$entry" ]; then',
'\t\t\tmd5=($(md5sum "$entry"))',
f'\t\t\tif [[ "${var}" != true ]]; then',
f"\t\t\t\t{array}=({md5_str})",
f'\t\t\t\tfor i in "${{{array}[@]}}"',
"\t\t\t\tdo",
'\t\t\t\tif [[ "$md5" == *"${i}"* ]]; then',
f"\t\t\t\t\t{var}=true",
"\t\t\t\t\tbreak",
"\t\t\t\telse",
f"\t\t\t\t\t{var}=false",
"\t\t\t\tfi",
"\t\t\t\tdone",
"\t\t\tfi",
"\t\tfi",
"\tdone",
"",
"",
f"\tif [ ${var} == true ]; then",
'\t\techo "$entry true";',
"\telse",
'\t\techo "false";',
"\tfi",
"}",
]
def _make_file_exists_function(cfg: dict) -> list[str]:
"""Generate a file-exists function matching EmuDeck's pattern."""
func = cfg["func"]
firmware = cfg.get("firmware_path", "")
keys = cfg.get("keys_path", "")
return [
f"{func}(){{",
"",
f'\tlocal FIRMWARE="{firmware}"',
f'\tlocal KEYS="{keys}"',
'\tif [[ -f "$KEYS" ]] && [[ "$( ls -A "$FIRMWARE")" ]]; then',
'\t\t\techo "true";',
"\telse",
'\t\t\techo "false";',
"\tfi",
"}",
]
class Exporter(BaseExporter):
"""Export truth data to EmuDeck checkBIOS.sh format."""
@staticmethod
def platform_name() -> str:
return "emudeck"
def export(
self,
truth_data: dict,
output_path: str,
scraped_data: dict | None = None,
) -> None:
lines: list[str] = ["#!/bin/bash"]
systems = truth_data.get("systems", {})
for sys_id, cfg in sorted(_SYSTEM_CONFIG.items(), key=lambda x: x[1]["func"]):
sys_data = systems.get(sys_id)
if not sys_data:
continue
lines.append("")
if cfg["pattern"] == "md5":
md5s: list[str] = []
for fe in sys_data.get("files", []):
name = fe.get("name", "")
if self._is_pattern(name) or name.startswith("_"):
continue
md5 = fe.get("md5", "")
if isinstance(md5, list):
md5s.extend(m for m in md5 if m and re.fullmatch(r"[a-f0-9]{32}", m))
elif md5 and re.fullmatch(r"[a-f0-9]{32}", md5):
md5s.append(md5)
if md5s:
lines.extend(_make_md5_function(cfg, md5s))
elif cfg["pattern"] == "file-exists":
lines.extend(_make_file_exists_function(cfg))
lines.append("")
Path(output_path).write_text("\n".join(lines), encoding="utf-8")
def validate(self, truth_data: dict, output_path: str) -> list[str]:
content = Path(output_path).read_text(encoding="utf-8")
issues: list[str] = []
systems = truth_data.get("systems", {})
for sys_id, cfg in _SYSTEM_CONFIG.items():
if cfg["pattern"] != "md5":
continue
sys_data = systems.get(sys_id)
if not sys_data:
continue
for fe in sys_data.get("files", []):
md5 = fe.get("md5", "")
if isinstance(md5, list):
md5 = md5[0] if md5 else ""
if md5 and re.fullmatch(r"[a-f0-9]{32}", md5) and md5 not in content:
issues.append(f"missing md5: {md5} ({fe.get('name', '')})")
for sys_id, cfg in _SYSTEM_CONFIG.items():
func = cfg["func"]
if func in content:
continue
sys_data = systems.get(sys_id)
if not sys_data or not sys_data.get("files"):
continue
# Only flag if the system has usable data for the function type
if cfg["pattern"] == "md5":
has_md5 = any(
fe.get("md5") and isinstance(fe.get("md5"), str)
and re.fullmatch(r"[a-f0-9]{32}", fe["md5"])
for fe in sys_data["files"]
)
if has_md5:
issues.append(f"missing function: {func}")
elif cfg["pattern"] == "file-exists":
issues.append(f"missing function: {func}")
return issues

View File

@@ -0,0 +1,17 @@
"""Exporter for Lakka (System.dat format, same as RetroArch).
Lakka inherits RetroArch cores and uses the same System.dat format.
Delegates to systemdat_exporter for export and validation.
"""
from __future__ import annotations
from .systemdat_exporter import Exporter as SystemDatExporter
class Exporter(SystemDatExporter):
"""Export truth data to Lakka System.dat format."""
@staticmethod
def platform_name() -> str:
return "lakka"

View File

@@ -0,0 +1,130 @@
"""Exporter for Recalbox es_bios.xml format.
Produces XML matching the exact format of recalbox's es_bios.xml:
- XML namespace declaration
- <system fullname="..." platform="...">
- <bios path="system/file" md5="..." core="..." /> with optional mandatory, hashMatchMandatory, note
- mandatory absent = true (only explicit when false)
- 2-space indentation
"""
from __future__ import annotations
from pathlib import Path
from .base_exporter import BaseExporter
class Exporter(BaseExporter):
"""Export truth data to Recalbox es_bios.xml format."""
@staticmethod
def platform_name() -> str:
return "recalbox"
def export(
self,
truth_data: dict,
output_path: str,
scraped_data: dict | None = None,
) -> None:
native_map: dict[str, str] = {}
if scraped_data:
for sys_id, sys_data in scraped_data.get("systems", {}).items():
nid = sys_data.get("native_id")
if nid:
native_map[sys_id] = nid
lines: list[str] = [
'<?xml version="1.0" encoding="UTF-8"?>',
'<biosList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"'
' xsi:noNamespaceSchemaLocation="es_bios.xsd">',
]
systems = truth_data.get("systems", {})
for sys_id in sorted(systems):
sys_data = systems[sys_id]
files = sys_data.get("files", [])
if not files:
continue
native_id = native_map.get(sys_id, sys_id)
scraped_sys = scraped_data.get("systems", {}).get(sys_id) if scraped_data else None
display_name = self._display_name(sys_id, scraped_sys)
lines.append(f' <system fullname="{display_name}" platform="{native_id}">')
# Build path lookup from scraped data for this system
scraped_paths: dict[str, str] = {}
if scraped_data:
s_sys = scraped_data.get("systems", {}).get(sys_id, {})
for sf in s_sys.get("files", []):
sname = sf.get("name", "").lower()
spath = sf.get("destination", sf.get("name", ""))
if sname and spath:
scraped_paths[sname] = spath
for fe in files:
name = fe.get("name", "")
if name.startswith("_") or self._is_pattern(name):
continue
# Use scraped path when available (preserves original format)
path = scraped_paths.get(name.lower())
if not path:
dest = self._dest(fe)
path = f"{native_id}/{dest}" if "/" not in dest else dest
md5 = fe.get("md5", "")
if isinstance(md5, list):
md5 = ",".join(md5)
required = fe.get("required", True)
# Build cores string from _cores
cores_list = fe.get("_cores", [])
core_str = ",".join(f"libretro/{c}" for c in cores_list) if cores_list else ""
attrs = [f'path="{path}"']
if md5:
attrs.append(f'md5="{md5}"')
if not required:
attrs.append('mandatory="false"')
if not required:
attrs.append('hashMatchMandatory="true"')
if core_str:
attrs.append(f'core="{core_str}"')
lines.append(f' <bios {" ".join(attrs)} />')
lines.append(" </system>")
lines.append("</biosList>")
lines.append("")
Path(output_path).write_text("\n".join(lines), encoding="utf-8")
def validate(self, truth_data: dict, output_path: str) -> list[str]:
from xml.etree.ElementTree import parse as xml_parse
tree = xml_parse(output_path)
root = tree.getroot()
exported_paths: set[str] = set()
for bios_el in root.iter("bios"):
path = bios_el.get("path", "")
if path:
exported_paths.add(path.lower())
exported_paths.add(path.split("/")[-1].lower())
issues: list[str] = []
for sys_data in truth_data.get("systems", {}).values():
for fe in sys_data.get("files", []):
name = fe.get("name", "")
if name.startswith("_") or self._is_pattern(name):
continue
dest = self._dest(fe)
if name.lower() not in exported_paths and dest.lower() not in exported_paths:
issues.append(f"missing: {name}")
return issues

View File

@@ -0,0 +1,114 @@
"""Exporter for RetroBat batocera-systems.json format.
Produces JSON matching the exact format of
RetroBat-Official/emulatorlauncher/batocera-systems/Resources/batocera-systems.json:
- System keys with "name" and "biosFiles" fields
- Each biosFile has "md5" before "file" (matching original key order)
"""
from __future__ import annotations
import json
from collections import OrderedDict
from pathlib import Path
from .base_exporter import BaseExporter
class Exporter(BaseExporter):
"""Export truth data to RetroBat batocera-systems.json format."""
@staticmethod
def platform_name() -> str:
return "retrobat"
def export(
self,
truth_data: dict,
output_path: str,
scraped_data: dict | None = None,
) -> None:
native_map: dict[str, str] = {}
if scraped_data:
for sys_id, sys_data in scraped_data.get("systems", {}).items():
nid = sys_data.get("native_id")
if nid:
native_map[sys_id] = nid
output: OrderedDict[str, dict] = OrderedDict()
systems = truth_data.get("systems", {})
for sys_id in sorted(systems):
sys_data = systems[sys_id]
files = sys_data.get("files", [])
if not files:
continue
native_id = native_map.get(sys_id, sys_id)
scraped_sys = scraped_data.get("systems", {}).get(sys_id) if scraped_data else None
display_name = self._display_name(sys_id, scraped_sys)
bios_files: list[OrderedDict] = []
for fe in files:
name = fe.get("name", "")
if name.startswith("_") or self._is_pattern(name):
continue
dest = self._dest(fe)
md5 = fe.get("md5", "")
if isinstance(md5, list):
md5 = md5[0] if md5 else ""
# Original format requires md5 for every entry
if not md5:
continue
entry: OrderedDict[str, str] = OrderedDict()
entry["md5"] = md5
entry["file"] = f"bios/{dest}"
bios_files.append(entry)
if bios_files:
if native_id in output:
existing_files = {e.get("file") for e in output[native_id]["biosFiles"]}
for entry in bios_files:
if entry.get("file") not in existing_files:
output[native_id]["biosFiles"].append(entry)
else:
sys_entry: OrderedDict[str, object] = OrderedDict()
sys_entry["name"] = display_name
sys_entry["biosFiles"] = bios_files
output[native_id] = sys_entry
Path(output_path).write_text(
json.dumps(output, indent=2, ensure_ascii=False) + "\n",
encoding="utf-8",
)
def validate(self, truth_data: dict, output_path: str) -> list[str]:
data = json.loads(Path(output_path).read_text(encoding="utf-8"))
exported_files: set[str] = set()
for sys_data in data.values():
for bf in sys_data.get("biosFiles", []):
path = bf.get("file", "")
stripped = path.removeprefix("bios/")
exported_files.add(stripped)
basename = path.split("/")[-1] if "/" in path else path
exported_files.add(basename)
issues: list[str] = []
for sys_data in truth_data.get("systems", {}).values():
for fe in sys_data.get("files", []):
name = fe.get("name", "")
if name.startswith("_") or self._is_pattern(name):
continue
md5 = fe.get("md5", "")
if isinstance(md5, list):
md5 = md5[0] if md5 else ""
if not md5:
continue
dest = self._dest(fe)
if name not in exported_files and dest not in exported_files:
issues.append(f"missing: {name}")
return issues

View File

@@ -0,0 +1,208 @@
"""Exporter for RetroDECK component_manifest.json format.
Produces a JSON file compatible with RetroDECK's component manifests.
Each system maps to a component with BIOS entries containing filename,
md5 (comma-separated if multiple), paths ($bios_path default), and
required status.
Path tokens: $bios_path for bios/, $roms_path for roms/.
Entries without an explicit path default to $bios_path.
"""
from __future__ import annotations
import json
import re
from collections import OrderedDict
from pathlib import Path
from .base_exporter import BaseExporter
# retrobios slug -> RetroDECK system ID (reverse of scraper SYSTEM_SLUG_MAP)
_REVERSE_SLUG: dict[str, str] = {
"nintendo-nes": "nes",
"nintendo-snes": "snes",
"nintendo-64": "n64",
"nintendo-64dd": "n64dd",
"nintendo-gamecube": "gc",
"nintendo-wii": "wii",
"nintendo-wii-u": "wiiu",
"nintendo-switch": "switch",
"nintendo-gb": "gb",
"nintendo-gbc": "gbc",
"nintendo-gba": "gba",
"nintendo-ds": "nds",
"nintendo-3ds": "3ds",
"nintendo-fds": "fds",
"nintendo-sgb": "sgb",
"nintendo-virtual-boy": "virtualboy",
"nintendo-pokemon-mini": "pokemini",
"sony-playstation": "psx",
"sony-playstation-2": "ps2",
"sony-playstation-3": "ps3",
"sony-psp": "psp",
"sony-psvita": "psvita",
"sega-mega-drive": "megadrive",
"sega-mega-cd": "megacd",
"sega-saturn": "saturn",
"sega-dreamcast": "dreamcast",
"sega-dreamcast-arcade": "naomi",
"sega-game-gear": "gamegear",
"sega-master-system": "mastersystem",
"nec-pc-engine": "pcengine",
"nec-pc-fx": "pcfx",
"nec-pc-98": "pc98",
"nec-pc-88": "pc88",
"3do": "3do",
"amstrad-cpc": "amstradcpc",
"arcade": "arcade",
"atari-400-800": "atari800",
"atari-5200": "atari5200",
"atari-7800": "atari7800",
"atari-jaguar": "atarijaguar",
"atari-lynx": "atarilynx",
"atari-st": "atarist",
"commodore-c64": "c64",
"commodore-amiga": "amiga",
"philips-cdi": "cdimono1",
"fairchild-channel-f": "channelf",
"coleco-colecovision": "colecovision",
"mattel-intellivision": "intellivision",
"microsoft-msx": "msx",
"microsoft-xbox": "xbox",
"doom": "doom",
"j2me": "j2me",
"apple-macintosh-ii": "macintosh",
"apple-ii": "apple2",
"apple-iigs": "apple2gs",
"enterprise-64-128": "enterprise",
"tiger-game-com": "gamecom",
"hartung-game-master": "gmaster",
"epoch-scv": "scv",
"watara-supervision": "supervision",
"bandai-wonderswan": "wonderswan",
"snk-neogeo-cd": "neogeocd",
"tandy-coco": "coco",
"tandy-trs-80": "trs80",
"dragon-32-64": "dragon",
"pico8": "pico8",
"wolfenstein-3d": "wolfenstein",
"sinclair-zx-spectrum": "zxspectrum",
}
def _dest_to_path_token(destination: str) -> str:
"""Convert a truth destination path to a RetroDECK path token."""
if destination.startswith("roms/"):
return "$roms_path/" + destination.removeprefix("roms/")
if destination.startswith("bios/"):
return "$bios_path/" + destination.removeprefix("bios/")
# Default: bios path
return "$bios_path/" + destination
class Exporter(BaseExporter):
"""Export truth data to RetroDECK component_manifest.json format."""
@staticmethod
def platform_name() -> str:
return "retrodeck"
def export(
self,
truth_data: dict,
output_path: str,
scraped_data: dict | None = None,
) -> None:
native_map: dict[str, str] = {}
if scraped_data:
for sys_id, sys_data in scraped_data.get("systems", {}).items():
nid = sys_data.get("native_id")
if nid:
native_map[sys_id] = nid
manifest: OrderedDict[str, dict] = OrderedDict()
systems = truth_data.get("systems", {})
for sys_id in sorted(systems):
sys_data = systems[sys_id]
files = sys_data.get("files", [])
if not files:
continue
native_id = native_map.get(sys_id, _REVERSE_SLUG.get(sys_id, sys_id))
bios_entries: list[OrderedDict] = []
for fe in files:
name = fe.get("name", "")
if name.startswith("_") or self._is_pattern(name):
continue
dest = self._dest(fe)
path_token = _dest_to_path_token(dest)
md5 = fe.get("md5", "")
if isinstance(md5, list):
md5 = ",".join(m for m in md5 if m)
required = fe.get("required", True)
entry: OrderedDict[str, object] = OrderedDict()
entry["filename"] = name
if md5:
# Validate MD5 entries
parts = [
m.strip().lower()
for m in str(md5).split(",")
if re.fullmatch(r"[0-9a-f]{32}", m.strip())
]
if parts:
entry["md5"] = ",".join(parts) if len(parts) > 1 else parts[0]
entry["paths"] = path_token
entry["required"] = required
system_val = native_id
entry["system"] = system_val
bios_entries.append(entry)
if bios_entries:
if native_id in manifest:
# Merge into existing component (multiple truth systems
# may map to the same native ID)
existing_names = {e["filename"] for e in manifest[native_id]["bios"]}
for entry in bios_entries:
if entry["filename"] not in existing_names:
manifest[native_id]["bios"].append(entry)
else:
component = OrderedDict()
component["system"] = native_id
component["bios"] = bios_entries
manifest[native_id] = component
Path(output_path).write_text(
json.dumps(manifest, indent=2, ensure_ascii=False) + "\n",
encoding="utf-8",
)
def validate(self, truth_data: dict, output_path: str) -> list[str]:
data = json.loads(Path(output_path).read_text(encoding="utf-8"))
exported_names: set[str] = set()
for comp_data in data.values():
bios = comp_data.get("bios", [])
if isinstance(bios, list):
for entry in bios:
fn = entry.get("filename", "")
if fn:
exported_names.add(fn)
issues: list[str] = []
for sys_data in truth_data.get("systems", {}).values():
for fe in sys_data.get("files", []):
name = fe.get("name", "")
if name.startswith("_") or self._is_pattern(name):
continue
if name not in exported_names:
issues.append(f"missing: {name}")
return issues

View File

@@ -0,0 +1,17 @@
"""Exporter for RetroPie (System.dat format, same as RetroArch).
RetroPie inherits RetroArch cores and uses the same System.dat format.
Delegates to systemdat_exporter for export and validation.
"""
from __future__ import annotations
from .systemdat_exporter import Exporter as SystemDatExporter
class Exporter(SystemDatExporter):
"""Export truth data to RetroPie System.dat format."""
@staticmethod
def platform_name() -> str:
return "retropie"

View File

@@ -0,0 +1,160 @@
"""Exporter for RomM known_bios_files.json format.
Produces JSON matching the exact format of
rommapp/romm/backend/models/fixtures/known_bios_files.json:
- Keys are "igdb_slug:filename"
- Values contain size, crc, md5, sha1 (all optional but at least one hash)
- Hashes are lowercase hex strings
- Size is an integer
"""
from __future__ import annotations
import json
from collections import OrderedDict
from pathlib import Path
from .base_exporter import BaseExporter
# retrobios slug -> IGDB slug (reverse of scraper SLUG_MAP)
_REVERSE_SLUG: dict[str, str] = {
"3do": "3do",
"nintendo-64dd": "64dd",
"amstrad-cpc": "acpc",
"commodore-amiga": "amiga",
"arcade": "arcade",
"atari-st": "atari-st",
"atari-5200": "atari5200",
"atari-7800": "atari7800",
"atari-400-800": "atari8bit",
"coleco-colecovision": "colecovision",
"sega-dreamcast": "dc",
"doom": "doom",
"enterprise-64-128": "enterprise",
"fairchild-channel-f": "fairchild-channel-f",
"nintendo-fds": "fds",
"sega-game-gear": "gamegear",
"nintendo-gb": "gb",
"nintendo-gba": "gba",
"nintendo-gbc": "gbc",
"sega-mega-drive": "genesis",
"mattel-intellivision": "intellivision",
"j2me": "j2me",
"atari-lynx": "lynx",
"apple-macintosh-ii": "mac",
"microsoft-msx": "msx",
"nintendo-ds": "nds",
"snk-neogeo-cd": "neo-geo-cd",
"nintendo-nes": "nes",
"nintendo-gamecube": "ngc",
"magnavox-odyssey2": "odyssey-2-slash-videopac-g7000",
"nec-pc-98": "pc-9800-series",
"nec-pc-fx": "pc-fx",
"nintendo-pokemon-mini": "pokemon-mini",
"sony-playstation-2": "ps2",
"sony-psp": "psp",
"sony-playstation": "psx",
"nintendo-satellaview": "satellaview",
"sega-saturn": "saturn",
"scummvm": "scummvm",
"sega-mega-cd": "segacd",
"sharp-x68000": "sharp-x68000",
"sega-master-system": "sms",
"nintendo-snes": "snes",
"nintendo-sufami-turbo": "sufami-turbo",
"nintendo-sgb": "super-gb",
"nec-pc-engine": "tg16",
"videoton-tvc": "tvc",
"philips-videopac": "videopac-g7400",
"wolfenstein-3d": "wolfenstein",
"sharp-x1": "x1",
"microsoft-xbox": "xbox",
"sinclair-zx-spectrum": "zxs",
}
class Exporter(BaseExporter):
"""Export truth data to RomM known_bios_files.json format."""
@staticmethod
def platform_name() -> str:
return "romm"
def export(
self,
truth_data: dict,
output_path: str,
scraped_data: dict | None = None,
) -> None:
native_map: dict[str, str] = {}
if scraped_data:
for sys_id, sys_data in scraped_data.get("systems", {}).items():
nid = sys_data.get("native_id")
if nid:
native_map[sys_id] = nid
output: OrderedDict[str, dict] = OrderedDict()
systems = truth_data.get("systems", {})
for sys_id in sorted(systems):
sys_data = systems[sys_id]
files = sys_data.get("files", [])
if not files:
continue
igdb_slug = native_map.get(sys_id, _REVERSE_SLUG.get(sys_id, sys_id))
for fe in files:
name = fe.get("name", "")
if name.startswith("_") or self._is_pattern(name):
continue
key = f"{igdb_slug}:{name}"
entry: OrderedDict[str, object] = OrderedDict()
size = fe.get("size")
if size is not None:
entry["size"] = int(size)
crc = fe.get("crc32", "")
if crc:
entry["crc"] = str(crc).strip().lower()
md5 = fe.get("md5", "")
if isinstance(md5, list):
md5 = md5[0] if md5 else ""
if md5:
entry["md5"] = str(md5).strip().lower()
sha1 = fe.get("sha1", "")
if isinstance(sha1, list):
sha1 = sha1[0] if sha1 else ""
if sha1:
entry["sha1"] = str(sha1).strip().lower()
output[key] = entry
Path(output_path).write_text(
json.dumps(output, indent=2, ensure_ascii=False) + "\n",
encoding="utf-8",
)
def validate(self, truth_data: dict, output_path: str) -> list[str]:
data = json.loads(Path(output_path).read_text(encoding="utf-8"))
exported_names: set[str] = set()
for key in data:
if ":" in key:
_, filename = key.split(":", 1)
exported_names.add(filename)
issues: list[str] = []
for sys_data in truth_data.get("systems", {}).values():
for fe in sys_data.get("files", []):
name = fe.get("name", "")
if name.startswith("_") or self._is_pattern(name):
continue
if name not in exported_names:
issues.append(f"missing: {name}")
return issues

View File

@@ -1,4 +1,8 @@
"""Exporter for libretro System.dat (clrmamepro DAT format).""" """Exporter for libretro System.dat (clrmamepro DAT format).
Produces a single 'game' block with all ROMs grouped by system,
matching the exact format of libretro-database/dat/System.dat.
"""
from __future__ import annotations from __future__ import annotations
@@ -13,7 +17,7 @@ from .base_exporter import BaseExporter
def _slug_to_native(slug: str) -> str: def _slug_to_native(slug: str) -> str:
"""Convert a system slug to a native 'Manufacturer - Console' name.""" """Convert a system slug to 'Manufacturer - Console' format."""
parts = slug.split("-", 1) parts = slug.split("-", 1)
if len(parts) == 1: if len(parts) == 1:
return parts[0].title() return parts[0].title()
@@ -42,45 +46,69 @@ class Exporter(BaseExporter):
if nid: if nid:
native_map[sys_id] = nid native_map[sys_id] = nid
lines: list[str] = [] # Match exact header format of libretro-database/dat/System.dat
lines.append('clrmamepro (') version = ""
lines.append('\tname "System.dat"') if scraped_data:
lines.append(')') version = scraped_data.get("dat_version", scraped_data.get("version", ""))
lines: list[str] = [
"clrmamepro (",
'\tname "System"',
'\tdescription "System"',
'\tcomment "System, firmware, and BIOS files used by libretro cores."',
]
if version:
lines.append(f"\tversion {version}")
lines.extend([
'\tauthor "libretro"',
'\thomepage "https://github.com/libretro/libretro-database/blob/master/dat/System.dat"',
'\turl "https://raw.githubusercontent.com/libretro/libretro-database/master/dat/System.dat"',
")",
"",
"game (",
'\tname "System"',
'\tcomment "System"',
])
systems = truth_data.get("systems", {}) systems = truth_data.get("systems", {})
for sys_id in sorted(systems): for sys_id in sorted(systems):
sys_data = systems[sys_id] sys_data = systems[sys_id]
native_name = native_map.get(sys_id, _slug_to_native(sys_id)) files = sys_data.get("files", [])
if not files:
continue
for fe in sys_data.get("files", []): native_name = native_map.get(sys_id, _slug_to_native(sys_id))
lines.append("")
lines.append(f'\tcomment "{native_name}"')
for fe in files:
name = fe.get("name", "") name = fe.get("name", "")
if name.startswith("_"): if name.startswith("_") or self._is_pattern(name):
continue continue
dest = fe.get("path", name) # Quote names with spaces or special chars (matching original format)
size = fe.get("size", 0) needs_quote = " " in name or "(" in name or ")" in name
name_str = f'"{name}"' if needs_quote else name
rom_parts = [f"name {name_str}"]
size = fe.get("size")
if size:
rom_parts.append(f"size {size}")
crc = fe.get("crc32", "") crc = fe.get("crc32", "")
md5 = fe.get("md5", "")
sha1 = fe.get("sha1", "")
rom_parts = [f'name "{name}"']
rom_parts.append(f"size {size}")
if crc: if crc:
rom_parts.append(f"crc {crc}") rom_parts.append(f"crc {crc.upper()}")
md5 = fe.get("md5", "")
if isinstance(md5, list):
md5 = md5[0] if md5 else ""
if md5: if md5:
rom_parts.append(f"md5 {md5}") rom_parts.append(f"md5 {md5}")
sha1 = fe.get("sha1", "")
if isinstance(sha1, list):
sha1 = sha1[0] if sha1 else ""
if sha1: if sha1:
rom_parts.append(f"sha1 {sha1}") rom_parts.append(f"sha1 {sha1}")
rom_str = " ".join(rom_parts)
game_name = f"{native_name}/{dest}" lines.append(f"\trom ( {' '.join(rom_parts)} )")
lines.append("")
lines.append("game (")
lines.append(f'\tname "{game_name}"')
lines.append(f'\tdescription "{name}"')
lines.append(f"\trom ( {rom_str} )")
lines.append(")")
lines.append(")")
lines.append("") lines.append("")
Path(output_path).write_text("\n".join(lines), encoding="utf-8") Path(output_path).write_text("\n".join(lines), encoding="utf-8")
@@ -93,12 +121,11 @@ class Exporter(BaseExporter):
exported_names.add(rom.name) exported_names.add(rom.name)
issues: list[str] = [] issues: list[str] = []
for sys_id, sys_data in truth_data.get("systems", {}).items(): for sys_data in truth_data.get("systems", {}).values():
for fe in sys_data.get("files", []): for fe in sys_data.get("files", []):
name = fe.get("name", "") name = fe.get("name", "")
if name.startswith("_"): if name.startswith("_") or self._is_pattern(name):
continue continue
if name not in exported_names: if name not in exported_names:
issues.append(f"missing: {name} (system {sys_id})") issues.append(f"missing: {name}")
return issues return issues

View File

@@ -18,7 +18,7 @@ from datetime import datetime, timezone
from pathlib import Path from pathlib import Path
sys.path.insert(0, os.path.dirname(__file__)) sys.path.insert(0, os.path.dirname(__file__))
from common import compute_hashes, list_registered_platforms from common import compute_hashes, list_registered_platforms, write_if_changed
CACHE_DIR = ".cache" CACHE_DIR = ".cache"
CACHE_FILE = os.path.join(CACHE_DIR, "db_cache.json") CACHE_FILE = os.path.join(CACHE_DIR, "db_cache.json")
@@ -315,14 +315,15 @@ def main():
"indexes": indexes, "indexes": indexes,
} }
with open(args.output, "w") as f: new_content = json.dumps(database, indent=2)
json.dump(database, f, indent=2) written = write_if_changed(args.output, new_content)
save_cache(CACHE_FILE, new_cache) save_cache(CACHE_FILE, new_cache)
alias_count = sum(len(v) for v in aliases.values()) alias_count = sum(len(v) for v in aliases.values())
name_count = len(indexes["by_name"]) name_count = len(indexes["by_name"])
print(f"Generated {args.output}: {len(files)} files, {total_size:,} bytes total") status = "Generated" if written else "Unchanged"
print(f"{status} {args.output}: {len(files)} files, {total_size:,} bytes total")
print(f" Name index: {name_count} names ({alias_count} aliases)") print(f" Name index: {name_count} names ({alias_count} aliases)")
return 0 return 0

View File

@@ -422,6 +422,91 @@ def _collect_emulator_extras(
"source_emulator": profile.get("emulator", emu_name), "source_emulator": profile.get("emulator", emu_name),
}) })
# Third pass: agnostic scan — for filename-agnostic cores, include all
# DB files matching the system path prefix and size criteria.
files_db = db.get("files", {})
for emu_name, profile in sorted(profiles.items()):
if profile.get("type") in ("launcher", "alias"):
continue
if emu_name not in relevant:
continue
is_profile_agnostic = profile.get("bios_mode") == "agnostic"
if not is_profile_agnostic:
if not any(f.get("agnostic") for f in profile.get("files", [])):
continue
for f in profile.get("files", []):
if not is_profile_agnostic and not f.get("agnostic"):
continue
fname = f.get("name", "")
if not fname:
continue
# Derive path prefix from the representative file in the DB
path_prefix = None
sha1_list = by_name.get(fname, [])
for sha1 in sha1_list:
entry = files_db.get(sha1, {})
path = entry.get("path", "")
if path:
parts = path.rsplit("/", 1)
if len(parts) == 2:
path_prefix = parts[0] + "/"
break
if not path_prefix:
# Fallback: try other files in the profile for the same system
for other_f in profile.get("files", []):
if other_f is f:
continue
other_name = other_f.get("name", "")
for sha1 in by_name.get(other_name, []):
entry = files_db.get(sha1, {})
path = entry.get("path", "")
if path:
parts = path.rsplit("/", 1)
if len(parts) == 2:
path_prefix = parts[0] + "/"
break
if path_prefix:
break
if not path_prefix:
continue
# Size criteria from the file entry
min_size = f.get("min_size", 0)
max_size = f.get("max_size", float("inf"))
exact_size = f.get("size")
if exact_size and not min_size:
min_size = exact_size
max_size = exact_size
# Scan DB for all files under this prefix matching size
for sha1, entry in files_db.items():
path = entry.get("path", "")
if not path.startswith(path_prefix):
continue
size = entry.get("size", 0)
if not (min_size <= size <= max_size):
continue
scan_name = entry.get("name", "")
if not scan_name:
continue
dest = scan_name
full_dest = f"{base_dest}/{dest}" if base_dest else dest
if full_dest in seen_dests:
continue
seen_dests.add(full_dest)
extras.append({
"name": scan_name,
"destination": dest,
"required": False,
"hle_fallback": False,
"source_emulator": profile.get("emulator", emu_name),
"agnostic_scan": True,
})
return extras return extras
@@ -621,6 +706,24 @@ def _build_readme(platform_name: str, platform_display: str,
return header + guide + footer return header + guide + footer
def _build_agnostic_rename_readme(
destination: str, original: str, alternatives: list[str],
) -> str:
"""Build a README explaining an agnostic file rename."""
lines = [
"This file was renamed for compatibility:",
f" {destination} <- {original}",
"",
]
if alternatives:
lines.append("All variants included in this pack:")
for alt in sorted(alternatives):
lines.append(f" {alt}")
lines.append("")
lines.append(f"To use a different variant, rename it to: {destination}")
return "\n".join(lines) + "\n"
def generate_pack( def generate_pack(
platform_name: str, platform_name: str,
platforms_dir: str, platforms_dir: str,
@@ -788,10 +891,71 @@ def generate_pack(
continue continue
if status == "not_found": if status == "not_found":
if not already_packed: # Agnostic fallback: if an agnostic core covers this system,
missing_files.append(file_entry["name"]) # find any matching file in the DB
file_status[dedup_key] = "missing" by_name = db.get("indexes", {}).get("by_name", {})
continue files_db = db.get("files", {})
agnostic_path = None
agnostic_resolved = False
if emu_profiles:
for _emu_key, _emu_prof in emu_profiles.items():
if _emu_prof.get("bios_mode") != "agnostic":
continue
if sys_id not in set(_emu_prof.get("systems", [])):
continue
for _ef in _emu_prof.get("files", []):
ef_name = _ef.get("name", "")
for _sha1 in by_name.get(ef_name, []):
_entry = files_db.get(_sha1, {})
_path = _entry.get("path", "")
if _path:
_prefix = _path.rsplit("/", 1)[0] + "/"
_min = _ef.get("min_size", 0)
_max = _ef.get("max_size", float("inf"))
if _ef.get("size") and not _min:
_min = _ef["size"]
_max = _ef["size"]
for _s, _e in files_db.items():
if _e.get("path", "").startswith(_prefix):
if _min <= _e.get("size", 0) <= _max:
if os.path.exists(_e["path"]):
local_path = _e["path"]
agnostic_path = _prefix
agnostic_resolved = True
break
break
if agnostic_resolved:
break
if agnostic_resolved:
break
if agnostic_resolved and local_path:
# Write rename README
original_name = os.path.basename(local_path)
dest_name = file_entry.get("name", "")
if original_name != dest_name and agnostic_path:
alt_names = []
for _s, _e in files_db.items():
_p = _e.get("path", "")
if _p.startswith(agnostic_path):
_n = _e.get("name", "")
if _n and _n != original_name:
alt_names.append(_n)
readme_text = _build_agnostic_rename_readme(
dest_name, original_name, alt_names,
)
readme_name = f"RENAMED_{dest_name}.txt"
readme_full = f"{base_dest}/{readme_name}" if base_dest else readme_name
if readme_full not in seen_destinations:
zf.writestr(readme_full, readme_text)
seen_destinations.add(readme_full)
status = "agnostic_fallback"
# Fall through to normal packing below
else:
if not already_packed:
missing_files.append(file_entry["name"])
file_status[dedup_key] = "missing"
continue
if status == "hash_mismatch" and verification_mode != "existence": if status == "hash_mismatch" and verification_mode != "existence":
zf_name = file_entry.get("zipped_file") zf_name = file_entry.get("zipped_file")

View File

@@ -18,7 +18,7 @@ from datetime import datetime, timezone
from pathlib import Path from pathlib import Path
sys.path.insert(0, os.path.dirname(__file__)) sys.path.insert(0, os.path.dirname(__file__))
from common import list_registered_platforms, load_database, load_platform_config from common import list_registered_platforms, load_database, load_platform_config, write_if_changed
from verify import verify_platform from verify import verify_platform
def compute_coverage(platform_name: str, platforms_dir: str, db: dict) -> dict: def compute_coverage(platform_name: str, platforms_dir: str, db: dict) -> dict:
@@ -316,14 +316,12 @@ def main():
db = load_database(args.db) db = load_database(args.db)
readme = generate_readme(db, args.platforms_dir) readme = generate_readme(db, args.platforms_dir)
with open("README.md", "w") as f: status = "Generated" if write_if_changed("README.md", readme) else "Unchanged"
f.write(readme) print(f"{status} ./README.md")
print(f"Generated ./README.md")
contributing = generate_contributing() contributing = generate_contributing()
with open("CONTRIBUTING.md", "w") as f: status = "Generated" if write_if_changed("CONTRIBUTING.md", contributing) else "Unchanged"
f.write(contributing) print(f"{status} ./CONTRIBUTING.md")
print(f"Generated ./CONTRIBUTING.md")
if __name__ == "__main__": if __name__ == "__main__":

View File

@@ -20,7 +20,7 @@ from datetime import datetime, timezone
from pathlib import Path from pathlib import Path
sys.path.insert(0, os.path.dirname(__file__)) sys.path.insert(0, os.path.dirname(__file__))
from common import list_registered_platforms, load_database, load_emulator_profiles, load_platform_config, require_yaml from common import list_registered_platforms, load_database, load_emulator_profiles, load_platform_config, require_yaml, write_if_changed
yaml = require_yaml() yaml = require_yaml()
from generate_readme import compute_coverage from generate_readme import compute_coverage
@@ -2064,7 +2064,7 @@ def main():
# Generate home # Generate home
print("Generating home page...") print("Generating home page...")
(docs / "index.md").write_text(generate_home(db, coverages, profiles, registry)) write_if_changed(str(docs / "index.md"), generate_home(db, coverages, profiles, registry))
# Build system_id -> manufacturer page map (needed by all generators) # Build system_id -> manufacturer page map (needed by all generators)
print("Building system cross-reference map...") print("Building system cross-reference map...")
@@ -2074,37 +2074,35 @@ def main():
# Generate platform pages # Generate platform pages
print("Generating platform pages...") print("Generating platform pages...")
(docs / "platforms" / "index.md").write_text(generate_platform_index(coverages)) write_if_changed(str(docs / "platforms" / "index.md"), generate_platform_index(coverages))
for name, cov in coverages.items(): for name, cov in coverages.items():
(docs / "platforms" / f"{name}.md").write_text(generate_platform_page(name, cov, registry, emulator_files)) write_if_changed(str(docs / "platforms" / f"{name}.md"), generate_platform_page(name, cov, registry, emulator_files))
# Generate system pages # Generate system pages
print("Generating system pages...") print("Generating system pages...")
(docs / "systems" / "index.md").write_text(generate_systems_index(manufacturers)) write_if_changed(str(docs / "systems" / "index.md"), generate_systems_index(manufacturers))
for mfr, consoles in manufacturers.items(): for mfr, consoles in manufacturers.items():
slug = mfr.lower().replace(" ", "-") slug = mfr.lower().replace(" ", "-")
page = generate_system_page(mfr, consoles, platform_files, emulator_files) page = generate_system_page(mfr, consoles, platform_files, emulator_files)
(docs / "systems" / f"{slug}.md").write_text(page) write_if_changed(str(docs / "systems" / f"{slug}.md"), page)
# Generate emulator pages # Generate emulator pages
print("Generating emulator pages...") print("Generating emulator pages...")
(docs / "emulators" / "index.md").write_text(generate_emulators_index(profiles)) write_if_changed(str(docs / "emulators" / "index.md"), generate_emulators_index(profiles))
for name, profile in profiles.items(): for name, profile in profiles.items():
page = generate_emulator_page(name, profile, db, platform_files) page = generate_emulator_page(name, profile, db, platform_files)
(docs / "emulators" / f"{name}.md").write_text(page) write_if_changed(str(docs / "emulators" / f"{name}.md"), page)
# Generate cross-reference page # Generate cross-reference page
print("Generating cross-reference page...") print("Generating cross-reference page...")
(docs / "cross-reference.md").write_text( write_if_changed(str(docs / "cross-reference.md"),
generate_cross_reference(coverages, profiles) generate_cross_reference(coverages, profiles))
)
# Generate gap analysis page # Generate gap analysis page
print("Generating gap analysis page...") print("Generating gap analysis page...")
(docs / "gaps.md").write_text( write_if_changed(str(docs / "gaps.md"),
generate_gap_analysis(profiles, coverages, db) generate_gap_analysis(profiles, coverages, db))
)
# Wiki pages: copy manually maintained sources + generate dynamic ones # Wiki pages: copy manually maintained sources + generate dynamic ones
print("Generating wiki pages...") print("Generating wiki pages...")
@@ -2115,11 +2113,11 @@ def main():
for src_file in wiki_src.glob("*.md"): for src_file in wiki_src.glob("*.md"):
shutil.copy2(src_file, wiki_dest / src_file.name) shutil.copy2(src_file, wiki_dest / src_file.name)
# data-model.md is generated (contains live DB stats) # data-model.md is generated (contains live DB stats)
(wiki_dest / "data-model.md").write_text(generate_wiki_data_model(db, profiles)) write_if_changed(str(wiki_dest / "data-model.md"), generate_wiki_data_model(db, profiles))
# Generate contributing # Generate contributing
print("Generating contributing page...") print("Generating contributing page...")
(docs / "contributing.md").write_text(generate_contributing()) write_if_changed(str(docs / "contributing.md"), generate_contributing())
# Update mkdocs.yml nav section only (avoid yaml.dump round-trip mangling quotes) # Update mkdocs.yml nav section only (avoid yaml.dump round-trip mangling quotes)
print("Updating mkdocs.yml nav...") print("Updating mkdocs.yml nav...")
@@ -2173,9 +2171,7 @@ markdown_extensions:
plugins: plugins:
- search - search
""" """
with open("mkdocs.yml", "w") as f: write_if_changed("mkdocs.yml", mkdocs_static + nav_yaml)
f.write(mkdocs_static)
f.write(nav_yaml)
total_pages = ( total_pages = (
1 # home 1 # home

View File

@@ -8,6 +8,7 @@ import urllib.request
import urllib.error import urllib.error
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from dataclasses import dataclass, field from dataclasses import dataclass, field
from pathlib import Path
@dataclass @dataclass
@@ -231,7 +232,28 @@ def scraper_cli(scraper_class: type, description: str = "Scrape BIOS requirement
if req.zipped_file: if req.zipped_file:
entry["zipped_file"] = req.zipped_file entry["zipped_file"] = req.zipped_file
config["systems"][sys_id]["files"].append(entry) config["systems"][sys_id]["files"].append(entry)
with open(args.output, "w") as f: # Merge into existing YAML: preserve fields the scraper doesn't generate
# (data_directories, case_insensitive_fs, manually added metadata).
# The scraper replaces systems + files; everything else is preserved.
output_path = Path(args.output)
if output_path.exists():
with open(output_path) as f:
existing = yaml.safe_load(f) or {}
# Preserve existing keys not generated by the scraper.
# Only keys present in the NEW config are considered scraper-generated.
# Everything else in the existing file is preserved.
for key, val in existing.items():
if key not in config:
config[key] = val
# Preserve per-system fields not generated by the scraper
# (data_directories, native_id from manual additions, etc.)
existing_systems = existing.get("systems", {})
for sys_id, sys_data in config.get("systems", {}).items():
old_sys = existing_systems.get(sys_id, {})
for field in ("data_directories",):
if field in old_sys and field not in sys_data:
sys_data[field] = old_sys[field]
with open(output_path, "w") as f:
yaml.dump(config, f, default_flow_style=False, sort_keys=False) yaml.dump(config, f, default_flow_style=False, sort_keys=False)
print(f"Written {len(reqs)} entries to {args.output}") print(f"Written {len(reqs)} entries to {args.output}")
return return

View File

@@ -303,12 +303,25 @@ class Scraper(BaseScraper):
"""Generate a platform YAML config dict from scraped data.""" """Generate a platform YAML config dict from scraped data."""
requirements = self.fetch_requirements() requirements = self.fetch_requirements()
# Parse source to extract display names per system
raw = self._fetch_raw()
source_dict = self._extract_systems_dict(raw)
display_names: dict[str, str] = {}
for sys_key, sys_data in source_dict.items():
dname = sys_data.get("name", "")
if dname:
slug = SYSTEM_SLUG_MAP.get(sys_key, sys_key)
display_names[slug] = dname
systems = {} systems = {}
for req in requirements: for req in requirements:
if req.system not in systems: if req.system not in systems:
sys_entry: dict = {"files": []} sys_entry: dict = {"files": []}
if req.native_id: if req.native_id:
sys_entry["native_id"] = req.native_id sys_entry["native_id"] = req.native_id
dname = display_names.get(req.system)
if dname:
sys_entry["name"] = dname
systems[req.system] = sys_entry systems[req.system] = sys_entry
entry = { entry = {

View File

@@ -121,10 +121,25 @@ class Scraper(BaseScraper):
"""Generate a platform YAML config dict from scraped data.""" """Generate a platform YAML config dict from scraped data."""
requirements = self.fetch_requirements() requirements = self.fetch_requirements()
# Parse source to extract display names per system
raw = self._fetch_raw()
source_data = json.loads(raw)
display_names: dict[str, str] = {}
for sys_key, sys_data in source_data.items():
if isinstance(sys_data, dict):
dname = sys_data.get("name", "")
if dname:
slug = SYSTEM_SLUG_MAP.get(sys_key, sys_key)
display_names[slug] = dname
systems = {} systems = {}
for req in requirements: for req in requirements:
if req.system not in systems: if req.system not in systems:
systems[req.system] = {"files": []} sys_entry: dict = {"files": []}
dname = display_names.get(req.system)
if dname:
sys_entry["name"] = dname
systems[req.system] = sys_entry
entry = { entry = {
"name": req.name, "name": req.name,

View File

@@ -291,6 +291,10 @@ def find_undeclared_files(
if emu_name not in relevant: if emu_name not in relevant:
continue continue
# Skip agnostic profiles entirely (filename-agnostic BIOS detection)
if profile.get("bios_mode") == "agnostic":
continue
# Check if this profile is standalone: match profile name or any cores: alias # Check if this profile is standalone: match profile name or any cores: alias
is_standalone = emu_name in standalone_set or bool( is_standalone = emu_name in standalone_set or bool(
standalone_set & {str(c) for c in profile.get("cores", [])} standalone_set & {str(c) for c in profile.get("cores", [])}
@@ -317,6 +321,10 @@ def find_undeclared_files(
if load_from and load_from != "system_dir": if load_from and load_from != "system_dir":
continue continue
# Skip agnostic files (filename-agnostic, handled by agnostic scan)
if f.get("agnostic"):
continue
archive = f.get("archive") archive = f.get("archive")
# Skip files declared by the platform (by name or archive) # Skip files declared by the platform (by name or archive)

View File

@@ -170,6 +170,7 @@ class TestE2E(unittest.TestCase):
"md5": info["md5"], "md5": info["md5"],
"name": name, "name": name,
"crc32": info.get("crc32", ""), "crc32": info.get("crc32", ""),
"size": len(info["data"]),
} }
by_md5[info["md5"]] = sha1 by_md5[info["md5"]] = sha1
by_name.setdefault(name, []).append(sha1) by_name.setdefault(name, []).append(sha1)
@@ -510,6 +511,33 @@ class TestE2E(unittest.TestCase):
with open(os.path.join(self.emulators_dir, "test_renamed.yml"), "w") as fh: with open(os.path.join(self.emulators_dir, "test_renamed.yml"), "w") as fh:
yaml.dump(emu_renamed, fh) yaml.dump(emu_renamed, fh)
# Agnostic profile (bios_mode: agnostic) — skipped by find_undeclared_files
emu_agnostic = {
"emulator": "TestAgnostic",
"type": "standalone",
"bios_mode": "agnostic",
"systems": ["console-a"],
"files": [
{"name": "correct_hash.bin", "required": True,
"min_size": 1, "max_size": 999999},
],
}
with open(os.path.join(self.emulators_dir, "test_agnostic.yml"), "w") as fh:
yaml.dump(emu_agnostic, fh)
# Mixed profile with per-file agnostic
emu_mixed_agnostic = {
"emulator": "TestMixedAgnostic",
"type": "libretro",
"systems": ["console-a"],
"files": [
{"name": "undeclared_req.bin", "required": True},
{"name": "agnostic_file.bin", "required": True, "agnostic": True},
],
}
with open(os.path.join(self.emulators_dir, "test_mixed_agnostic.yml"), "w") as fh:
yaml.dump(emu_mixed_agnostic, fh)
# --------------------------------------------------------------- # ---------------------------------------------------------------
# THE TEST -one method per feature area, all using same fixtures # THE TEST -one method per feature area, all using same fixtures
# --------------------------------------------------------------- # ---------------------------------------------------------------
@@ -3142,7 +3170,8 @@ class TestE2E(unittest.TestCase):
self.assertIn("Sony - PlayStation", content) self.assertIn("Sony - PlayStation", content)
self.assertIn("scph5501.bin", content) self.assertIn("scph5501.bin", content)
self.assertIn("b056ee5a4d65937e1a3a17e1e78f3258ea49c38e", content) self.assertIn("b056ee5a4d65937e1a3a17e1e78f3258ea49c38e", content)
self.assertIn('name "System.dat"', content) self.assertIn('name "System"', content)
self.assertIn("71AF80B4", content) # CRC uppercase
issues = exporter.validate(truth, out_path) issues = exporter.validate(truth, out_path)
self.assertEqual(issues, []) self.assertEqual(issues, [])
@@ -3411,6 +3440,173 @@ class TestE2E(unittest.TestCase):
self.assertEqual(result["summary"]["total_missing"], 0) self.assertEqual(result["summary"]["total_missing"], 0)
self.assertEqual(result["summary"]["systems_compared"], 1) self.assertEqual(result["summary"]["systems_compared"], 1)
def test_179_agnostic_profile_skipped_in_undeclared(self):
"""bios_mode: agnostic profiles are skipped entirely by find_undeclared_files."""
config = load_platform_config("test_existence", self.platforms_dir)
profiles = load_emulator_profiles(self.emulators_dir)
undeclared = find_undeclared_files(config, self.emulators_dir, self.db, profiles)
emulators = {u["emulator"] for u in undeclared}
# TestAgnostic should NOT appear in undeclared (bios_mode: agnostic)
self.assertNotIn("TestAgnostic", emulators)
def test_180_agnostic_file_skipped_in_undeclared(self):
"""Files with agnostic: true are skipped, others in same profile are not."""
config = load_platform_config("test_existence", self.platforms_dir)
profiles = load_emulator_profiles(self.emulators_dir)
undeclared = find_undeclared_files(config, self.emulators_dir, self.db, profiles)
names = {u["name"] for u in undeclared}
# agnostic_file.bin should NOT be in undeclared (agnostic: true)
self.assertNotIn("agnostic_file.bin", names)
# undeclared_req.bin should still be in undeclared (not agnostic)
self.assertIn("undeclared_req.bin", names)
def test_181_agnostic_extras_scan(self):
"""Agnostic profiles add all matching DB files as extras."""
from generate_pack import _collect_emulator_extras
config = load_platform_config("test_existence", self.platforms_dir)
profiles = load_emulator_profiles(self.emulators_dir)
extras = _collect_emulator_extras(
config, self.emulators_dir, self.db, set(), "system", profiles,
)
agnostic_extras = [e for e in extras if e.get("source_emulator") == "TestAgnostic"]
# Agnostic scan should find files in the same directory as correct_hash.bin
self.assertTrue(len(agnostic_extras) > 0, "Agnostic scan should produce extras")
# All agnostic extras should have agnostic_scan flag
for e in agnostic_extras:
self.assertTrue(e.get("agnostic_scan", False))
def test_182_agnostic_rename_readme(self):
"""_build_agnostic_rename_readme generates correct text."""
from generate_pack import _build_agnostic_rename_readme
result = _build_agnostic_rename_readme(
"dsi_nand.bin", "DSi_Nand_AUS.bin",
["DSi_Nand_EUR.bin", "DSi_Nand_USA.bin"],
)
self.assertIn("dsi_nand.bin <- DSi_Nand_AUS.bin", result)
self.assertIn("DSi_Nand_EUR.bin", result)
self.assertIn("DSi_Nand_USA.bin", result)
self.assertIn("rename it to: dsi_nand.bin", result)
def test_183_agnostic_resolve_fallback(self):
"""resolve_local_file with agnostic fallback finds a system file."""
file_entry = {
"name": "nonexistent_agnostic.bin",
"agnostic": True,
"min_size": 1,
"max_size": 999999,
"agnostic_path_prefix": self.bios_dir + "/",
}
path, status = resolve_local_file(file_entry, self.db)
self.assertIsNotNone(path)
self.assertEqual(status, "agnostic_fallback")
def test_179_batocera_exporter_round_trip(self):
"""Batocera exporter produces valid Python dict format."""
from exporter.batocera_exporter import Exporter
truth = {
"systems": {
"sony-playstation": {
"_coverage": {"cores_profiled": ["c"]},
"files": [
{"name": "scph5501.bin", "destination": "scph5501.bin",
"required": True, "md5": "b" * 32,
"_cores": ["c"], "_source_refs": []},
],
}
}
}
scraped = {
"systems": {
"sony-playstation": {"native_id": "psx", "files": []},
}
}
out = os.path.join(self.root, "batocera-systems")
exp = Exporter()
exp.export(truth, out, scraped_data=scraped)
content = open(out).read()
self.assertIn('"psx"', content)
self.assertIn("scph5501.bin", content)
self.assertIn("b" * 32, content)
self.assertEqual(exp.validate(truth, out), [])
def test_180_recalbox_exporter_round_trip(self):
"""Recalbox exporter produces valid es_bios.xml."""
from exporter.recalbox_exporter import Exporter
truth = {
"systems": {
"sony-playstation": {
"_coverage": {"cores_profiled": ["c"]},
"files": [
{"name": "scph5501.bin", "destination": "scph5501.bin",
"required": True, "md5": "b" * 32,
"_cores": ["c"], "_source_refs": []},
],
}
}
}
scraped = {
"systems": {
"sony-playstation": {"native_id": "psx", "files": []},
}
}
out = os.path.join(self.root, "es_bios.xml")
exp = Exporter()
exp.export(truth, out, scraped_data=scraped)
content = open(out).read()
self.assertIn("<biosList", content)
self.assertIn('platform="psx"', content)
self.assertIn('fullname=', content)
self.assertIn("scph5501.bin", content)
# mandatory="true" is the default, not emitted (matching Recalbox format)
self.assertNotIn('mandatory="false"', content)
self.assertIn('core="libretro/c"', content)
self.assertEqual(exp.validate(truth, out), [])
def test_181_retrobat_exporter_round_trip(self):
"""RetroBat exporter produces valid JSON."""
import json as _json
from exporter.retrobat_exporter import Exporter
truth = {
"systems": {
"sony-playstation": {
"_coverage": {"cores_profiled": ["c"]},
"files": [
{"name": "scph5501.bin", "destination": "scph5501.bin",
"required": True, "md5": "b" * 32,
"_cores": ["c"], "_source_refs": []},
],
}
}
}
scraped = {
"systems": {
"sony-playstation": {"native_id": "psx", "files": []},
}
}
out = os.path.join(self.root, "batocera-systems.json")
exp = Exporter()
exp.export(truth, out, scraped_data=scraped)
data = _json.loads(open(out).read())
self.assertIn("psx", data)
self.assertTrue(any("scph5501" in bf["file"] for bf in data["psx"]["biosFiles"]))
self.assertEqual(exp.validate(truth, out), [])
def test_182_exporter_discovery(self):
"""All exporters are discovered by the plugin system."""
from exporter import discover_exporters
exporters = discover_exporters()
self.assertIn("retroarch", exporters)
self.assertIn("batocera", exporters)
self.assertIn("recalbox", exporters)
self.assertIn("retrobat", exporters)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()