64 Commits
Author SHA1 Message Date
Abdessamad Derraz c64e146a43 fix: refuse standalone copies install can't run 2026-10-10 12:13:18 +02:00
Abdessamad Derraz 61cba2d3c1 fix: hold the installer to python 3.8 for real 2026-10-10 12:12:32 +02:00
Abdessamad Derraz 8380f63f97 fix: write one romm entry per key 2026-10-10 12:11:16 +02:00
Abdessamad Derraz c38c790945 fix: never file a multi-system rom by guess 2026-10-10 12:10:32 +02:00
Abdessamad Derraz 88d7ca4814 fix: stop a retrodeck scrape on a failed fetch 2026-10-10 12:08:52 +02:00
Abdessamad Derraz 14b32b38ba fix: verify data-dir pack members by content 2026-10-10 12:08:13 +02:00
Abdessamad Derraz 3d0170264a fix: place slot claims where the pack puts extras 2026-10-10 12:07:00 +02:00
Abdessamad Derraz 2c2f284414 fix: file a slug-platform bios under its own system 2026-10-10 11:43:08 +02:00
Abdessamad Derraz 315549a5b0 fix: decide destination winners without data dirs 2026-10-10 11:20:33 +02:00
Abdessamad Derraz 26ea5336a7 fix: list a target's cores after its overrides 2026-10-10 11:17:53 +02:00
Abdessamad Derraz de795262d7 fix: count an alias as declared in the gap report 2026-10-10 11:16:59 +02:00
Abdessamad Derraz b00352754c fix: keep retropie's list idiom when extending 2026-10-10 11:16:21 +02:00
Abdessamad Derraz e26ee44229 fix: export only what the platform declares 2026-10-10 11:15:25 +02:00
Abdessamad Derraz b5f9095633 fix: write retrodeck additions beside homonyms 2026-10-10 10:57:57 +02:00
Abdessamad Derraz 0057a82fe1 fix: write recalbox additions at the core path 2026-10-10 10:56:46 +02:00
Abdessamad Derraz 1cedc9930b fix: let search order guard regional files 2026-10-10 10:56:09 +02:00
Abdessamad Derraz c2bdef4db4 fix: pair a bare truth name by name, not suffix 2026-10-10 10:04:35 +02:00
Abdessamad Derraz 556d4a45aa fix: carry an archive in the truth, not its roms 2026-10-10 10:04:35 +02:00
Abdessamad Derraz befb23b492 fix: tag only territory-switched ace-dl roms 2026-10-10 10:00:30 +02:00
Abdessamad Derraz 65d396e996 fix: drop territory tags from msx font roms 2026-10-10 09:58:05 +02:00
Abdessamad Derraz 2a049dc9c9 fix: let a shared path only keep a system 2026-10-10 09:49:56 +02:00
Abdessamad Derraz ffa6516e09 fix: let an alias settle only where the core reads 2026-10-10 09:47:50 +02:00
Abdessamad Derraz 836c56b3fa fix: pin the jaguar model-m boot rom by hash 2026-10-10 09:17:46 +02:00
Abdessamad Derraz bbce3902dd fix: keep a system an on-target core reads 2026-10-10 09:15:22 +02:00
Abdessamad Derraz f6da2cff3a fix: settle a core file only by its size or slot 2026-10-10 09:15:11 +02:00
Abdessamad Derraz be45eda62d fix: expect the renamed system pack in its test 2026-10-10 08:48:09 +02:00
Abdessamad Derraz c3f3d16999 chore: regenerate install manifests 2026-10-10 08:30:07 +02:00
Abdessamad Derraz d7cc757d22 fix: re-profile eka2l1 on its rewritten branch 2026-10-10 08:21:26 +02:00
Abdessamad Derraz e0dab891ee fix: let a system-less extra compete with nothing 2026-10-10 08:14:57 +02:00
Abdessamad Derraz 707c81d2cf fix: name a target by its canonical name 2026-10-10 08:09:00 +02:00
Abdessamad Derraz e6905f48e7 fix: name a system pack apart from an emulator one 2026-10-10 08:04:04 +02:00
Abdessamad Derraz 9002a4a4bb fix: lay a hash pack out as its emulator does 2026-10-10 07:59:18 +02:00
Abdessamad Derraz e8bdcb405d fix: refuse an unknown emulator for a hash pack 2026-10-10 07:54:56 +02:00
Abdessamad Derraz 1cd743242f fix: report a pin the tracked history lost 2026-10-10 07:53:06 +02:00
Abdessamad Derraz e8bac256e7 chore: re-pin advancemame on its rewritten master 2026-10-10 07:53:06 +02:00
Abdessamad Derraz 884c43c109 fix: a citation in an unmoved repo needs no move 2026-10-10 07:51:09 +02:00
Abdessamad Derraz a213898c6d fix: follow dosbox-pure to codeberg 2026-10-10 07:50:07 +02:00
Abdessamad Derraz 203e75c1f4 fix: let the live, owning repository judge a ref 2026-10-10 07:49:14 +02:00
Abdessamad Derraz 7e079e789e fix: point twice-shifted refs at their code 2026-10-10 07:46:12 +02:00
Abdessamad Derraz af7b3133a4 fix: resolve profile entries with their owner 2026-10-10 07:38:33 +02:00
Abdessamad Derraz 8a661932f2 chore: regenerate database, manifests and readme 2026-10-10 07:33:39 +02:00
Abdessamad Derraz 06ad1d10d3 fix: serve armsx2 the vulkan shader it reads 2026-10-10 07:32:21 +02:00
Abdessamad Derraz c38b8f980f fix: identify each zesarux rom by its bytes 2026-10-10 07:32:21 +02:00
Abdessamad Derraz 81a989c56c fix: give the azahar key slot its own key file 2026-10-10 07:32:21 +02:00
Abdessamad Derraz 1a182a1e33 fix: write each profile path with its file name 2026-10-10 07:16:03 +02:00
Abdessamad Derraz 31e2d4235a fix: say what the core refuses in emulator mode 2026-10-10 07:11:08 +02:00
Abdessamad Derraz 22cad6ee01 fix: a core copy does not pass a hash declaration 2026-10-10 07:04:22 +02:00
Abdessamad Derraz 748562e71d fix: count a conflicting declaration by its file 2026-10-10 07:02:38 +02:00
Abdessamad Derraz d2a61b4a89 refactor: name the new exceptions as errors 2026-10-10 05:29:38 +02:00
Abdessamad Derraz 4de21e1157 chore: deploy the site when manifest fields change 2026-10-10 05:29:38 +02:00
Abdessamad Derraz 20ba21c710 fix: keep the git probe out of the repository 2026-10-10 05:29:38 +02:00
Abdessamad Derraz 8c324706c5 chore: record systems and regions in the manifests 2026-10-10 04:56:18 +02:00
Abdessamad Derraz a493111820 fix: update the native source index under a lock 2026-10-10 04:48:30 +02:00
Abdessamad Derraz 77b9abfa49 fix: read a data directory tag before its download 2026-10-10 04:45:44 +02:00
Abdessamad Derraz a00f57ad37 fix: write scraped files whole or not at all 2026-10-10 04:45:10 +02:00
Abdessamad Derraz 170b7d93ae fix: record a release only from finished packs 2026-10-10 04:43:42 +02:00
Abdessamad Derraz 6c75f5ae15 fix: keep the cache when an archive yields nothing 2026-10-10 04:42:58 +02:00
Abdessamad Derraz 6fca4149f5 feat: offer the files no platform system owns 2026-10-10 04:42:11 +02:00
Abdessamad Derraz 788a6dd4df fix: read the upstream cache from one name 2026-10-10 04:41:39 +02:00
Abdessamad Derraz 537600730c chore: refresh mame refs at the 0.289 tag 2026-10-10 04:41:39 +02:00
Abdessamad Derraz a116743471 fix: scrape the mame release that labels the data 2026-10-10 04:40:27 +02:00
Abdessamad Derraz ece2c06aaf feat: choose systems, cores and regions to install 2026-10-10 04:31:57 +02:00
Abdessamad Derraz b0bd20f403 refactor: build every manifest entry in one place 2026-10-10 04:07:04 +02:00
Abdessamad Derraz bfa741a9c9 fix: accept the slug omission in the installer 2026-10-10 03:59:23 +02:00
133 changed files with 72471 additions and 7046 deletions

No files matched your search

+1
View File
@@ -22,6 +22,7 @@ on:
- "scripts/hashing.py" - "scripts/hashing.py"
- "scripts/largefiles.py" - "scripts/largefiles.py"
- "scripts/list_platforms.py" - "scripts/list_platforms.py"
- "scripts/manifestselect.py"
- "scripts/nativemode.py" - "scripts/nativemode.py"
- "scripts/packextras.py" - "scripts/packextras.py"
- "scripts/packpaths.py" - "scripts/packpaths.py"
+15 -15
View File
@@ -32,7 +32,7 @@ The script auto-detects your platform, downloads only missing files, and verifie
## Download BIOS packs ## Download BIOS packs
One pack per platform, and it holds everything the platform runs: its own BIOS list plus every file its emulator cores load. Pick your platform, download the ZIP, extract to the BIOS path. The installer above does the same file by file, and `--target` narrows it to one machine; for a region or a bare minimum, build your own pack below. One pack per platform, and it holds everything the platform runs: its own BIOS list plus every file its emulator cores load. Pick your platform, download the ZIP, extract to the BIOS path. The installer above does the same file by file. At a terminal it asks whether to install everything or to choose systems, cores and regions, and `--system`, `--core`, `--region` and `--target` make the same choice without questions; for a bare minimum, build your own pack below.
Files is how many the pack holds once extracted, the figure a file manager shows for the folder. It differs from one platform to the next because a pack carries only what that platform's emulators load. The size is what the files occupy once extracted; the ZIP itself downloads smaller. Files is how many the pack holds once extracted, the figure a file manager shows for the folder. It differs from one platform to the next because a pack carries only what that platform's emulators load. The size is what the files occupy once extracted; the ZIP itself downloads smaller.
A pack over 2 GB comes in several parts, and every part is needed. How to open them depends on their name: A pack over 2 GB comes in several parts, and every part is needed. How to open them depends on their name:
@@ -71,14 +71,14 @@ Each file is checked the way your platform checks it. Most compare a checksum, t
- **392 files** the platforms' emulators load are still to be found, and 51 more cannot be sourced at all (per-user keys, user-filled slots, dumps nobody has made); both are named in the [gap analysis](https://abdess.github.io/retrobios/gaps/) - **392 files** the platforms' emulators load are still to be found, and 51 more cannot be sourced at all (per-user keys, user-filled slots, dumps nobody has made); both are named in the [gap analysis](https://abdess.github.io/retrobios/gaps/)
- **12 platforms** supported with platform-specific verification - **12 platforms** supported with platform-specific verification
- **506 emulators** profiled from source (RetroArch cores + standalone) - **506 emulators** profiled from source (RetroArch cores + standalone)
- **546 systems** handled by those emulators (NES, SNES, PlayStation, Saturn, Dreamcast, ...) - **544 systems** handled by those emulators (NES, SNES, PlayStation, Saturn, Dreamcast, ...)
- **19,002 files in the collection**, each with its SHA1, MD5, SHA256, CRC32 and Adler-32 fingerprints: 5,266 system files, 2,808 arcade ROM sets, 10,928 game and engine data files. That is every platform and emulator together, so no pack holds them all: each carries what its own emulators load, counted in the download table - **19,005 files in the collection**, each with its SHA1, MD5, SHA256, CRC32 and Adler-32 fingerprints: 5,269 system files, 2,808 arcade ROM sets, 10,928 game and engine data files. That is every platform and emulator together, so no pack holds them all: each carries what its own emulators load, counted in the download table
- **562 of 5,266 system files** matched to dump-preservation catalogs (No-Intro, Redump, TOSEC); arcade sets and engine data fall outside what those catalogs index - **562 of 5,269 system files** matched to dump-preservation catalogs (No-Intro, Redump, TOSEC); arcade sets and engine data fall outside what those catalogs index
- **16687 MB** total collection size - **16687 MB** total collection size
## Supported systems ## Supported systems
NES, SNES, Nintendo 64, GameCube, Wii, Game Boy, Game Boy Advance, Nintendo DS, Nintendo 3DS, Switch, PlayStation, PlayStation 2, PlayStation 3, PSP, PS Vita, Mega Drive, Saturn, Dreamcast, Game Gear, Master System, Neo Geo, Atari 2600, Atari 7800, Atari Lynx, Atari ST, MSX, PC Engine, TurboGrafx-16, ColecoVision, Intellivision, Commodore 64, Amiga, ZX Spectrum, Arcade (MAME), and 512+ more. NES, SNES, Nintendo 64, GameCube, Wii, Game Boy, Game Boy Advance, Nintendo DS, Nintendo 3DS, Switch, PlayStation, PlayStation 2, PlayStation 3, PSP, PS Vita, Mega Drive, Saturn, Dreamcast, Game Gear, Master System, Neo Geo, Atari 2600, Atari 7800, Atari Lynx, Atari ST, MSX, PC Engine, TurboGrafx-16, ColecoVision, Intellivision, Commodore 64, Amiga, ZX Spectrum, Arcade (MAME), and 510+ more.
Full list with per-file details: **[https://abdess.github.io/retrobios/](https://abdess.github.io/retrobios/)** Full list with per-file details: **[https://abdess.github.io/retrobios/](https://abdess.github.io/retrobios/)**
@@ -86,18 +86,18 @@ Full list with per-file details: **[https://abdess.github.io/retrobios/](https:/
| Platform | On its BIOS list | Files its emulators load | Checked by | | Platform | On its BIOS list | Files its emulators load | Checked by |
|----------|-----------------:|-------------------------:|------------| |----------|-----------------:|-------------------------:|------------|
| Batocera | 353/353 | 12,287/12,718 | MD5 hash | | Batocera | 353/353 | 12,295/12,726 | MD5 hash |
| BizHawk | 118/118 | 800/801 | SHA1 hash | | BizHawk | 118/118 | 800/801 | SHA1 hash |
| EmuDeck | 168/168 | 430/432 | MD5 hash | | EmuDeck | 168/168 | 430/432 | MD5 hash |
| Lakka | 526/526 | 5,256/5,286 | file presence | | Lakka | 526/526 | 5,260/5,290 | file presence |
| MiSTer FPGA | 81/81 | - | MD5 hash | | MiSTer FPGA | 81/81 | - | MD5 hash |
| ROCKNIX | 38/38 | 5,695/5,725 | MD5 hash | | ROCKNIX | 38/38 | 5,697/5,727 | MD5 hash |
| Recalbox | 351/351 | 2,721/2,729 | MD5 hash | | Recalbox | 351/351 | 2,731/2,739 | MD5 hash |
| RetroArch | 526/526 | 5,256/5,286 | file presence | | RetroArch | 526/526 | 5,260/5,290 | file presence |
| RetroBat | 348/348 | 11,875/12,305 | MD5 hash | | RetroBat | 348/348 | 11,880/12,310 | MD5 hash |
| RetroDECK | 2,008/2,008 | 5,387/5,437 | MD5 hash | | RetroDECK | 2,008/2,008 | 5,400/5,450 | MD5 hash |
| RetroPie * | 526/526 | 9,171/9,313 | file presence | | RetroPie * | 526/526 | 9,181/9,323 | file presence |
| RomM | 381/381 | 746/752 | MD5 hash | | RomM | 381/381 | 748/754 | MD5 hash |
Each fraction is what the pack has over what is needed, counting required and optional files alike since both ship. The first column is the BIOS list the platform publishes. The second counts files its emulators load that this list never mentions, found by reading their source code, and it is routinely several times larger. A short fraction means files are still missing, and they are named in the [gap analysis](https://abdess.github.io/retrobios/gaps/). Each fraction is what the pack has over what is needed, counting required and optional files alike since both ship. The first column is the BIOS list the platform publishes. The second counts files its emulators load that this list never mentions, found by reading their source code, and it is routinely several times larger. A short fraction means files are still missing, and they are named in the [gap analysis](https://abdess.github.io/retrobios/gaps/).
That second number is a floor, not a ceiling: an emulator that accepts any file handed to it names none in its code, so nothing there can be counted. That second number is a floor, not a ceiling: an emulator that accepts any file handed to it names none in its code, so nothing there can be counted.
@@ -180,4 +180,4 @@ The scripts and tooling are released under the [MIT License](LICENSE).
The BIOS and firmware files are not covered by that license: they are third-party system software, preserved and provided for personal backup, archival, and interoperability with emulation software. [NOTICE](NOTICE) sets out their status and how to ask for a file to be removed. The BIOS and firmware files are not covered by that license: they are third-party system software, preserved and provided for personal backup, archival, and interoperability with emulation software. [NOTICE](NOTICE) sets out their status and how to ask for a file to be removed.
The reasoning, and where it is weakest, is in the [FAQ](https://abdess.github.io/retrobios/wiki/faq/#is-this-legal). The reasoning, and where it is weakest, is in the [FAQ](https://abdess.github.io/retrobios/wiki/faq/#is-this-legal).
*Auto-generated on 2026-10-09T23:59:06Z* *Auto-generated on 2026-10-10T09:28:18Z*
+150
View File
@@ -0,0 +1,150 @@
# Default keys file bundled with Azahar.
# DISCLAIMER: The keys present in this document are used exclusively for interoperability
# with the user's legally obtained software, as well as to perform cryptographic operations
# on other 3DS-related data of non creative nature (data structures, numbers, etc).
:AES
# Generator constant
generatorConstant=1ff9e9aac5fe0408024591dc5d52768a
# OTP
otpKey=06457901d485a367ac4f2ad01c53cf74
otpIV=ba4f599b0ae1122c80e13f6865c4fa49
# Movable
movableKeyY=a717802dea9776137ba16cd389141cf0
movableCmacY=6e1e6bcb9ee098dd67ae771ad73b2cc9
# KeyX
slot0x18KeyX=82e9c9bebfb8bdb875ecc0a07d474374
slot0x19KeyX=f5367fce73142e66ed13917914b7f2ef
slot0x1AKeyX=eaba984c9cb766d4a3a7e974e2e713a3
slot0x1BKeyX=45ad04953992c7c893724a9a7bce6182
slot0x1CKeyX=c3830f8156e3543b723f0bc046741e8f
slot0x1DKeyX=d6b38bc759417596d619d6029d13e0d8
slot0x1EKeyX=bb623a97ddd793d757c4104b8d9fb969
slot0x1FKeyX=4c28ec6effa3c23646078bba350c7995
slot0x25KeyX=cee7d8ab30c00dae850ef5e382ac5af3
slot0x2CKeyX=b98e95ceca3e4d171f76a94de934c053
slot0x2DKeyX=b98e95ceca3e4d171f76a94de934c053
slot0x2EKeyX=b98e95ceca3e4d171f76a94de934c053
slot0x2FKeyX=b98e95ceca3e4d171f76a94de934c053
slot0x30KeyX=c66e23128f289133f04cdb877a3749f2
slot0x31KeyX=c66e23128f289133f04cdb877a3749f2
slot0x32KeyX=c66e23128f289133f04cdb877a3749f2
slot0x33KeyX=c66e23128f289133f04cdb877a3749f2
slot0x34KeyX=6fbb01f872caf9c01834eec04065ee53
slot0x35KeyX=6fbb01f872caf9c01834eec04065ee53
slot0x36KeyX=6fbb01f872caf9c01834eec04065ee53
slot0x37KeyX=6fbb01f872caf9c01834eec04065ee53
slot0x38KeyX=b529221cddb5db5a1bf26eff2041e875
slot0x39KeyX=b529221cddb5db5a1bf26eff2041e875
slot0x3AKeyX=b529221cddb5db5a1bf26eff2041e875
slot0x3BKeyX=b529221cddb5db5a1bf26eff2041e875
slot0x3CKeyX=c35d6d15680b1ad4e912a341836121b3
slot0x3DKeyX=617085719b7cfb316df4df2e8362c6e2
slot0x3EKeyX=24baf628d06889bf282d0aa35dc55650
slot0x3FKeyX=a31233280bb4daa7761393f78c424952
# KeyY
slot0x04KeyY=ff3388ecd21705bb339e967986dc4907
slot0x05KeyY=54ef035f30260e0e9b5e004fc985dc22
slot0x06KeyY=24b05aaaac0b099252030c02d1040317
slot0x07KeyY=e9acc5abd4ad3f0660c83c8934882f3f
slot0x08KeyY=4803050106d482dcd75f85c5aadff9b3
slot0x09KeyY=af6346efdddfa9806e3c6b6855b78930
slot0x0AKeyY=0a870a2c4b2fc3172e5f0335d8c5085d
slot0x0BKeyY=fda0152fcd6ddb3133b887ba727c0ada
slot0x24KeyY=74ca074884f4228deb2a1ca72d287762
slot0x2EKeyY=7462553f9e5a7904b8647cca736da1f5
slot0x2FKeyY=c369baa21e188a88a9aa94e5506a9f16
slot0x31KeyY=7462553f9e5a7904b8647cca736da1f5
# DLP/NFC KeyY (slot 0x39)
dlpKeyY=7462553f9e5a7904b8647cca736da1f5
nfcKeyY=ed7858a8bba7eed7fc970c5979bc0af2
# Ticket Common KeyY (slot 0x3D)
common0=d07b337f9ca4385932a2e25723232eb9
common1=0c767230f0998f1c46828202faacbe4c
common2=c475cb3ab8c788bb575e12a10907b8a4
common3=e486eee3d0c09c902f6686d4c06f649f
common4=ed31ba9c04b067506c4497a35b7804fc
common5=5e66998ab4e8931606850fd7a16dd755
# KeyN
slot0x0CKeyN=e7c9ff9d4f5b6f4dc5e2f50e856f0ab2
slot0x0DKeyN=e7c9ff9d4f5b6f4dc5e2f50e856f0ab2
slot0x0EKeyN=e7c9ff9d4f5b6f4dc5e2f50e856f0ab2
slot0x0FKeyN=e7c9ff9d4f5b6f4dc5e2f50e856f0ab2
slot0x10KeyN=285713db53051c089bdfb3b6aa638fda
slot0x11KeyN=285713db53051c089bdfb3b6aa638fda
slot0x12KeyN=285713db53051c089bdfb3b6aa638fda
slot0x13KeyN=285713db53051c089bdfb3b6aa638fda
slot0x14KeyN=2af3bbd32cd59c06fd4abe58651987ad
slot0x15KeyN=be2836751c734ba8da18e1887f888bd6
slot0x16KeyN=e3a18eb1c1dc8a3d27c3967e6e362de3
slot0x17KeyN=d0294cfb7be0b4fb7324d986fd3993bb
slot0x20KeyN=7c92f6272551c4614db0b345edd2e869
slot0x21KeyN=7c92f6272551c4614db0b345edd2e869
slot0x22KeyN=7c92f6272551c4614db0b345edd2e869
slot0x23KeyN=7c92f6272551c4614db0b345edd2e869
slot0x26KeyN=bbe8b4e09d0937816b234d8eb3cd3ca2
slot0x27KeyN=bbe8b4e09d0937816b234d8eb3cd3ca2
slot0x28KeyN=bbe8b4e09d0937816b234d8eb3cd3ca2
slot0x29KeyN=5218127e133ce3b85bb8c018ce76b7e2
slot0x2AKeyN=4a4264cf32e84170666f29ac88ef3f7e
slot0x2BKeyN=51af6c4c8b13da3228bd29b371cf84e1
slot0x2DKeyN=3ed6f5cf2cc37c54655000b7c8b52e0d
slot0x32KeyN=b87e64018b190ffe048a8124c6454196
slot0x36KeyN=28c0d59b736657bcdf50ff174979958a
slot0x38KeyN=6e78a3be9bddda09bfd569483f24fce0
# NFC Secrets
nfcSecret0Phrase=756e666978656420696e666f7300
nfcSecret0Seed=db4b9e3f45278f397eff9b4fb993
nfcSecret0HmacKey=1d164b375b72a55728b91d64b6a3c205
nfcSecret1Phrase=6c6f636b65642073656372657400
nfcSecret1Seed=fdc8a07694b89e4c47d37de8ce5c74c1
nfcSecret1HmacKey=7f752d2873a20017fef85c0575904b6d
nfcIv=4fd39a6e79fceaad99904db8ee38e9db
:RSA
# Slots
slot0x00X=c034829a11c7116a08633e89a78ca0919779da8cc967077afc60e1786247e5068b9a76588a15f0304b3887b5147c259f7a2e6e480aceb0be35f0c5885ea2d8838fd6aafe45ac58fc08d4d0569d3cd3b09f9c6985fcd5c7152ebc54a885d6b11129b503611510bbfa0b07552d5800ecf636ef90d101cd475a3f4274e0c1e828b2bb516c8cd80f95f5db6a239083435509c0190e5d218b19c3bebc2a3aa73cebf53e7e4998d9be01a440f3f1c0f3157ba6f65a7be9e059d4d26f9de997141979df2ceaa175d91db2079051978f805d6f97741b6db4e96e3305611392b4b7ca76f0b45387c40d5879cde6b1509062216cb9ac21f3510ccd6df4da8f581ff86d2f31
slot0x00M=c12e4877ff0fedb98afa6dbe5d7ac86489a4e0901dba22d2e7ba945c830126ca71976f284a5e57e386b4778fbb78e3f1750ecd8b6ed3984f3cd363f801dd9ed5271ea859f49ff4da02d88445efba774b3a46279167cb905300d0903ecb71772b78cfe918921e2bef699f87d9cbef273fbc0e320d177e925021e869bb827f227f17da206ae9ce5de262566c4fab0be86866f7218fd48c248c0d8cbdeb3adbc423a6cd1b572f82c0172d23e32b1b4f0c30b04c20749126851dba39b31a2918c23f91034969e601c0a1099742e8fd31cfe4a02cd9e6bd7b646c52465567c7463b2a9e7e9fa53a0e260d4daee68091ee7adfa1c1491538276161e1c10293f4592a33
# Ticket wrap
ticketWrapExp=010001
ticketWrapMod=d24cb2e48feaf004d4bb08f8f3defcbb0c934a146b15366c9ddc1eb1649b9feb964b569c2283954d3d2b8a1ab21dc1159c2e6cb4cdd4c0bb96dbad4f02d31f45573892af855273aca20c459b9bd3126425c05d766bfd2fad87986c08416aea8d4266cd9d4ffc3f20f7b5672b686793141edde1b11689aca2f6469c9b0ea4577150235185ed4e7e4f2f9036c165a20c73e160c644a47303d2ed9bb0ba2fc90989bd87eb4563d8f7a61d889a7807b155e7f2107d048d828fcba23090839341385614fde4fabe84f2f0532da34750f32ab11fbe084c0083edbf0b50e96a49dd9d1e293e2249ee954db8afd139461ead4f11a2d06836446473140ed3867e4e5ead3b
# Secure info
secureInfoExp=010001
secureInfoMod=b1791a6d1eadd429ba89a1cd433630174bc68730c5e70560197b50d8c4546710a6e8a101bc2ceb0376f005c70ce0b6d6dffd26df33468bdbb2391e7ec01aa1a5a091e807da378676ba390a25429d5961e161d40485a74bb20186beb11a3572c1c2ea28ab7a1015325c9e712b7df965eae6c6fb8baed76c2a94a6c5ece40eaf987e06f20f884fd20635a476e9f70aba5c5b1461520054044593e468270435355aad5809d1193f5a0728d6db6b551f77945dc3be6fae5bcc0863e476dfa29b36ea853403e616eaa905e07f3a3e7e7077cf166a61d17e4d354c744485d4f67b0eee32f1c2d5790248e9621a33baa39b02b02294057ff6b43888e301e55a237c9c0b
# Local friend code seed
lfcsExp=010001
lfcsMod=a3759a3546cfa7fe30ec55a1b64e08e9449d0c72fcd191fd610a288975bce6a9b21556e9c7670255adfc3cee5edb78259a4b221b71e7e9515b2a6793b21868ce5e5e12ffd86806af318d56f9549902346a17e7837496a05aaf6efde6bed686aafd7a65a8ebe11c983a15c17ab540c23d9b7cfdd463c5e6deb77824c629473335b2e937e054ee9fa53dd793ca3eae4db60f5a11e70cdfba03b21e2b31b65906db5f940bf76e74cad4ab55d940058f10fe06050c81bb422190ba4f5c5382e1e10fbc949f60695d1303aae2e0c108424c200b9baa552d55276e24e5d60457588ff75f0cec819f6d2d28f31055f83b7662d4e4a69369b5da6b4023af07eb9cbfa9c9
:ECC
# Root public
rootPublicXY=004e3bb74d5d959e68ce900434fe9e4a3f094a33771fa7c0e4b023264d98014ca1fc799d3fa52171d5f9bd5b1777ec0fef7a38d1669bbf830325843a
# Padding......
+154
View File
@@ -0,0 +1,154 @@
# Default keys file bundled with Azahar.
# DISCLAIMER: The keys present in this document are used exclusively for interoperability
# with the user's legally obtained software, as well as to perform cryptographic operations
# on other 3DS-related data of non creative nature (data structures, numbers, etc).
:AES
# Generator constant
generatorConstant=1ff9e9aac5fe0408024591dc5d52768a
# OTP
otpKey=06457901d485a367ac4f2ad01c53cf74
otpIV=ba4f599b0ae1122c80e13f6865c4fa49
# Movable
movableKeyY=a717802dea9776137ba16cd389141cf0
movableCmacY=6e1e6bcb9ee098dd67ae771ad73b2cc9
# KeyX
slot0x18KeyX=82e9c9bebfb8bdb875ecc0a07d474374
slot0x19KeyX=f5367fce73142e66ed13917914b7f2ef
slot0x1AKeyX=eaba984c9cb766d4a3a7e974e2e713a3
slot0x1BKeyX=45ad04953992c7c893724a9a7bce6182
slot0x1CKeyX=c3830f8156e3543b723f0bc046741e8f
slot0x1DKeyX=d6b38bc759417596d619d6029d13e0d8
slot0x1EKeyX=bb623a97ddd793d757c4104b8d9fb969
slot0x1FKeyX=4c28ec6effa3c23646078bba350c7995
slot0x25KeyX=cee7d8ab30c00dae850ef5e382ac5af3
slot0x2CKeyX=b98e95ceca3e4d171f76a94de934c053
slot0x2DKeyX=b98e95ceca3e4d171f76a94de934c053
slot0x2EKeyX=b98e95ceca3e4d171f76a94de934c053
slot0x2FKeyX=b98e95ceca3e4d171f76a94de934c053
slot0x30KeyX=c66e23128f289133f04cdb877a3749f2
slot0x31KeyX=c66e23128f289133f04cdb877a3749f2
slot0x32KeyX=c66e23128f289133f04cdb877a3749f2
slot0x33KeyX=c66e23128f289133f04cdb877a3749f2
slot0x34KeyX=6fbb01f872caf9c01834eec04065ee53
slot0x35KeyX=6fbb01f872caf9c01834eec04065ee53
slot0x36KeyX=6fbb01f872caf9c01834eec04065ee53
slot0x37KeyX=6fbb01f872caf9c01834eec04065ee53
slot0x38KeyX=b529221cddb5db5a1bf26eff2041e875
slot0x39KeyX=b529221cddb5db5a1bf26eff2041e875
slot0x3AKeyX=b529221cddb5db5a1bf26eff2041e875
slot0x3BKeyX=b529221cddb5db5a1bf26eff2041e875
slot0x3CKeyX=c35d6d15680b1ad4e912a341836121b3
slot0x3DKeyX=617085719b7cfb316df4df2e8362c6e2
slot0x3EKeyX=24baf628d06889bf282d0aa35dc55650
slot0x3FKeyX=a31233280bb4daa7761393f78c424952
# KeyY
slot0x04KeyY=ff3388ecd21705bb339e967986dc4907
slot0x05KeyY=54ef035f30260e0e9b5e004fc985dc22
slot0x06KeyY=24b05aaaac0b099252030c02d1040317
slot0x07KeyY=e9acc5abd4ad3f0660c83c8934882f3f
slot0x08KeyY=4803050106d482dcd75f85c5aadff9b3
slot0x09KeyY=af6346efdddfa9806e3c6b6855b78930
slot0x0AKeyY=0a870a2c4b2fc3172e5f0335d8c5085d
slot0x0BKeyY=fda0152fcd6ddb3133b887ba727c0ada
slot0x24KeyY=74ca074884f4228deb2a1ca72d287762
slot0x2EKeyY=7462553f9e5a7904b8647cca736da1f5
slot0x2FKeyY=c369baa21e188a88a9aa94e5506a9f16
slot0x31KeyY=7462553f9e5a7904b8647cca736da1f5
# DLP/NFC KeyY (slot 0x39)
dlpKeyY=7462553f9e5a7904b8647cca736da1f5
nfcKeyY=ed7858a8bba7eed7fc970c5979bc0af2
# Ticket Common KeyY (slot 0x3D)
common0=d07b337f9ca4385932a2e25723232eb9
common1=0c767230f0998f1c46828202faacbe4c
common2=c475cb3ab8c788bb575e12a10907b8a4
common3=e486eee3d0c09c902f6686d4c06f649f
common4=ed31ba9c04b067506c4497a35b7804fc
common5=5e66998ab4e8931606850fd7a16dd755
# KeyN
slot0x0CKeyN=e7c9ff9d4f5b6f4dc5e2f50e856f0ab2
slot0x0DKeyN=e7c9ff9d4f5b6f4dc5e2f50e856f0ab2
slot0x0EKeyN=e7c9ff9d4f5b6f4dc5e2f50e856f0ab2
slot0x0FKeyN=e7c9ff9d4f5b6f4dc5e2f50e856f0ab2
slot0x10KeyN=285713db53051c089bdfb3b6aa638fda
slot0x11KeyN=285713db53051c089bdfb3b6aa638fda
slot0x12KeyN=285713db53051c089bdfb3b6aa638fda
slot0x13KeyN=285713db53051c089bdfb3b6aa638fda
slot0x14KeyN=2af3bbd32cd59c06fd4abe58651987ad
slot0x15KeyN=be2836751c734ba8da18e1887f888bd6
slot0x16KeyN=e3a18eb1c1dc8a3d27c3967e6e362de3
slot0x17KeyN=d0294cfb7be0b4fb7324d986fd3993bb
slot0x20KeyN=7c92f6272551c4614db0b345edd2e869
slot0x21KeyN=7c92f6272551c4614db0b345edd2e869
slot0x22KeyN=7c92f6272551c4614db0b345edd2e869
slot0x23KeyN=7c92f6272551c4614db0b345edd2e869
slot0x26KeyN=bbe8b4e09d0937816b234d8eb3cd3ca2
slot0x27KeyN=bbe8b4e09d0937816b234d8eb3cd3ca2
slot0x28KeyN=bbe8b4e09d0937816b234d8eb3cd3ca2
slot0x29KeyN=5218127e133ce3b85bb8c018ce76b7e2
slot0x2AKeyN=4a4264cf32e84170666f29ac88ef3f7e
slot0x2BKeyN=51af6c4c8b13da3228bd29b371cf84e1
slot0x2DKeyN=3ed6f5cf2cc37c54655000b7c8b52e0d
slot0x32KeyN=b87e64018b190ffe048a8124c6454196
slot0x36KeyN=28c0d59b736657bcdf50ff174979958a
slot0x38KeyN=6e78a3be9bddda09bfd569483f24fce0
# NFC Secrets
nfcSecret0Phrase=756e666978656420696e666f7300
nfcSecret0Seed=db4b9e3f45278f397eff9b4fb993
nfcSecret0HmacKey=1d164b375b72a55728b91d64b6a3c205
nfcSecret1Phrase=6c6f636b65642073656372657400
nfcSecret1Seed=fdc8a07694b89e4c47d37de8ce5c74c1
nfcSecret1HmacKey=7f752d2873a20017fef85c0575904b6d
nfcIv=4fd39a6e79fceaad99904db8ee38e9db
# DLP Checksum
dlpChecksumModIv=fe449ac13ae3b4095011d18944107833
:RSA
# Slots
slot0x00X=c034829a11c7116a08633e89a78ca0919779da8cc967077afc60e1786247e5068b9a76588a15f0304b3887b5147c259f7a2e6e480aceb0be35f0c5885ea2d8838fd6aafe45ac58fc08d4d0569d3cd3b09f9c6985fcd5c7152ebc54a885d6b11129b503611510bbfa0b07552d5800ecf636ef90d101cd475a3f4274e0c1e828b2bb516c8cd80f95f5db6a239083435509c0190e5d218b19c3bebc2a3aa73cebf53e7e4998d9be01a440f3f1c0f3157ba6f65a7be9e059d4d26f9de997141979df2ceaa175d91db2079051978f805d6f97741b6db4e96e3305611392b4b7ca76f0b45387c40d5879cde6b1509062216cb9ac21f3510ccd6df4da8f581ff86d2f31
slot0x00M=c12e4877ff0fedb98afa6dbe5d7ac86489a4e0901dba22d2e7ba945c830126ca71976f284a5e57e386b4778fbb78e3f1750ecd8b6ed3984f3cd363f801dd9ed5271ea859f49ff4da02d88445efba774b3a46279167cb905300d0903ecb71772b78cfe918921e2bef699f87d9cbef273fbc0e320d177e925021e869bb827f227f17da206ae9ce5de262566c4fab0be86866f7218fd48c248c0d8cbdeb3adbc423a6cd1b572f82c0172d23e32b1b4f0c30b04c20749126851dba39b31a2918c23f91034969e601c0a1099742e8fd31cfe4a02cd9e6bd7b646c52465567c7463b2a9e7e9fa53a0e260d4daee68091ee7adfa1c1491538276161e1c10293f4592a33
# Ticket wrap
ticketWrapExp=010001
ticketWrapMod=d24cb2e48feaf004d4bb08f8f3defcbb0c934a146b15366c9ddc1eb1649b9feb964b569c2283954d3d2b8a1ab21dc1159c2e6cb4cdd4c0bb96dbad4f02d31f45573892af855273aca20c459b9bd3126425c05d766bfd2fad87986c08416aea8d4266cd9d4ffc3f20f7b5672b686793141edde1b11689aca2f6469c9b0ea4577150235185ed4e7e4f2f9036c165a20c73e160c644a47303d2ed9bb0ba2fc90989bd87eb4563d8f7a61d889a7807b155e7f2107d048d828fcba23090839341385614fde4fabe84f2f0532da34750f32ab11fbe084c0083edbf0b50e96a49dd9d1e293e2249ee954db8afd139461ead4f11a2d06836446473140ed3867e4e5ead3b
# Secure info
secureInfoExp=010001
secureInfoMod=b1791a6d1eadd429ba89a1cd433630174bc68730c5e70560197b50d8c4546710a6e8a101bc2ceb0376f005c70ce0b6d6dffd26df33468bdbb2391e7ec01aa1a5a091e807da378676ba390a25429d5961e161d40485a74bb20186beb11a3572c1c2ea28ab7a1015325c9e712b7df965eae6c6fb8baed76c2a94a6c5ece40eaf987e06f20f884fd20635a476e9f70aba5c5b1461520054044593e468270435355aad5809d1193f5a0728d6db6b551f77945dc3be6fae5bcc0863e476dfa29b36ea853403e616eaa905e07f3a3e7e7077cf166a61d17e4d354c744485d4f67b0eee32f1c2d5790248e9621a33baa39b02b02294057ff6b43888e301e55a237c9c0b
# Local friend code seed
lfcsExp=010001
lfcsMod=a3759a3546cfa7fe30ec55a1b64e08e9449d0c72fcd191fd610a288975bce6a9b21556e9c7670255adfc3cee5edb78259a4b221b71e7e9515b2a6793b21868ce5e5e12ffd86806af318d56f9549902346a17e7837496a05aaf6efde6bed686aafd7a65a8ebe11c983a15c17ab540c23d9b7cfdd463c5e6deb77824c629473335b2e937e054ee9fa53dd793ca3eae4db60f5a11e70cdfba03b21e2b31b65906db5f940bf76e74cad4ab55d940058f10fe06050c81bb422190ba4f5c5382e1e10fbc949f60695d1303aae2e0c108424c200b9baa552d55276e24e5d60457588ff75f0cec819f6d2d28f31055f83b7662d4e4a69369b5da6b4023af07eb9cbfa9c9
:ECC
# Root public
rootPublicXY=004e3bb74d5d959e68ce900434fe9e4a3f094a33771fa7c0e4b023264d98014ca1fc799d3fa52171d5f9bd5b1777ec0fef7a38d1669bbf830325843a
# Padding...................
File renamed without changes.
Binary file not shown.
+82 -14
View File
@@ -1,8 +1,8 @@
{ {
"schema_version": 1, "schema_version": 1,
"generated_at": "2026-10-09T20:41:14Z", "generated_at": "2026-10-10T05:24:26Z",
"total_files": 19002, "total_files": 19005,
"total_size": 17497698358, "total_size": 17497729510,
"bios_dir": "bios", "bios_dir": "bios",
"files": { "files": {
"738c7f5fd0c8b57ee2e87ae7a97bf8e21a821d07": { "738c7f5fd0c8b57ee2e87ae7a97bf8e21a821d07": {
@@ -133360,6 +133360,26 @@
"crc32": "1e65e2e2", "crc32": "1e65e2e2",
"adler32": "765cc424" "adler32": "765cc424"
}, },
"5fab786695311433602028cd89898689c5df0898": {
"path": "bios/Other/azahar/sysdata/keys.txt",
"name": "keys.txt",
"size": 7344,
"sha1": "5fab786695311433602028cd89898689c5df0898",
"md5": "cb52d323b13129a2f0bc3e3e911e567f",
"sha256": "dee8cbca96b74e81d19b0292f8c6255f0e2b23d8551f82cacd086c2eead35ea4",
"crc32": "352ceb91",
"adler32": "5d463cad"
},
"5288772be438b86978a801e6dcb14228ad3a20af": {
"path": "bios/Other/azaharplus/sysdata/keys.txt",
"name": "keys.txt",
"size": 7424,
"sha1": "5288772be438b86978a801e6dcb14228ad3a20af",
"md5": "4812e96097b7fc1bdd7b40366208b7a7",
"sha256": "ea8b3d7c752d85f2326b9bfa01615414ab34ba052f87481b0ff4003fd887e50a",
"crc32": "d5b60ff6",
"adler32": "13b65227"
},
"efd505f459448c5741a8ca249089f068b72cb751": { "efd505f459448c5741a8ca249089f068b72cb751": {
"path": "bios/Other/beebem/AUNMap", "path": "bios/Other/beebem/AUNMap",
"name": "AUNMap", "name": "AUNMap",
@@ -156214,7 +156234,7 @@
"adler32": "b5608705" "adler32": "b5608705"
}, },
"6e5c9416a1cc89fe4012627f496f71ff2bb07b81": { "6e5c9416a1cc89fe4012627f496f71ff2bb07b81": {
"path": "bios/Other/zesarux/ace.rom", "path": "bios/Other/zesarux/alternate_roms/ace.rom",
"name": "ace.rom", "name": "ace.rom",
"size": 8192, "size": 8192,
"sha1": "6e5c9416a1cc89fe4012627f496f71ff2bb07b81", "sha1": "6e5c9416a1cc89fe4012627f496f71ff2bb07b81",
@@ -157054,6 +157074,16 @@
"crc32": "340a3285", "crc32": "340a3285",
"adler32": "bc8e2965" "adler32": "bc8e2965"
}, },
"0a74bd34538a03d0e1d214b425d95c14ad10c8c4": {
"path": "bios/Other/zesarux/trdos.rom",
"name": "trdos.rom",
"size": 16384,
"sha1": "0a74bd34538a03d0e1d214b425d95c14ad10c8c4",
"md5": "0c42e3b9ab8dd91ea096f1d0c07c55e5",
"sha256": "9ee624153f6ff198267fa57dd7568933a36e2003fbe9dd17799b6d21652a37bb",
"crc32": "c43d717f",
"adler32": "1d25c092"
},
"cdb90bd05b073ec7a5f875444e29ab0f6bcee9ac": { "cdb90bd05b073ec7a5f875444e29ab0f6bcee9ac": {
"path": "bios/Other/zesarux/unodos3.rom", "path": "bios/Other/zesarux/unodos3.rom",
"name": "unodos3.rom", "name": "unodos3.rom",
@@ -208287,6 +208317,8 @@
"1d361b7dc10f35866f2e9e713acaf0ee": "f8200a0f4bd284670f0f0191fa9340684e94f66d", "1d361b7dc10f35866f2e9e713acaf0ee": "f8200a0f4bd284670f0f0191fa9340684e94f66d",
"d5a884f20ade826001ea9fd93be86345": "da328a0a3074ec86fee2d6741bfc2e5a1f68d063", "d5a884f20ade826001ea9fd93be86345": "da328a0a3074ec86fee2d6741bfc2e5a1f68d063",
"3a1f6bebe84204bc6fe507cdb2f80ee6": "99fc2a4d8107c36ee3075c9cff24872d2a98e37a", "3a1f6bebe84204bc6fe507cdb2f80ee6": "99fc2a4d8107c36ee3075c9cff24872d2a98e37a",
"cb52d323b13129a2f0bc3e3e911e567f": "5fab786695311433602028cd89898689c5df0898",
"4812e96097b7fc1bdd7b40366208b7a7": "5288772be438b86978a801e6dcb14228ad3a20af",
"d2e4b22f9433cf16a3dcb320dc2cf1a2": "efd505f459448c5741a8ca249089f068b72cb751", "d2e4b22f9433cf16a3dcb320dc2cf1a2": "efd505f459448c5741a8ca249089f068b72cb751",
"cd6ba85e22adec70b6d863de4c053db7": "05980c296e1a7800c4aa21d6bed16323b6d859d5", "cd6ba85e22adec70b6d863de4c053db7": "05980c296e1a7800c4aa21d6bed16323b6d859d5",
"751f615ccbefa312915d1be8aa5617f5": "ba33eaf1a23cfef6fc1b88aa516ca2b3693e69d9", "751f615ccbefa312915d1be8aa5617f5": "ba33eaf1a23cfef6fc1b88aa516ca2b3693e69d9",
@@ -210637,6 +210669,7 @@
"3e77d3685c3e5ba755ab3d34c820b488": "c627527551e441a000184565a72df8ff37fce891", "3e77d3685c3e5ba755ab3d34c820b488": "c627527551e441a000184565a72df8ff37fce891",
"9deb72f8785888a80d97d9ed60300c7e": "82bf848bac698d1ffd9ca809c4293f004152645a", "9deb72f8785888a80d97d9ed60300c7e": "82bf848bac698d1ffd9ca809c4293f004152645a",
"133ec2129ebf57555662d899d55fe374": "b7ec72fa0a55243a53c55d649e0555acea25d0c1", "133ec2129ebf57555662d899d55fe374": "b7ec72fa0a55243a53c55d649e0555acea25d0c1",
"0c42e3b9ab8dd91ea096f1d0c07c55e5": "0a74bd34538a03d0e1d214b425d95c14ad10c8c4",
"6132dc41594d166a86a9067622b8cb90": "cdb90bd05b073ec7a5f875444e29ab0f6bcee9ac", "6132dc41594d166a86a9067622b8cb90": "cdb90bd05b073ec7a5f875444e29ab0f6bcee9ac",
"d9cdd73ad75df44489b01550af6e4cbd": "1570048704f11dc239060ee4f63190d199c94987", "d9cdd73ad75df44489b01550af6e4cbd": "1570048704f11dc239060ee4f63190d199c94987",
"a406f2d22ee445b2ddd061e0f5bea549": "2551026b7d63a2627f79701e26688a7d715dc96a", "a406f2d22ee445b2ddd061e0f5bea549": "2551026b7d63a2627f79701e26688a7d715dc96a",
@@ -246739,7 +246772,9 @@
"4d88ddd169da56de721e4aa9e213a421f7221b4b" "4d88ddd169da56de721e4aa9e213a421f7221b4b"
], ],
"keys.txt": [ "keys.txt": [
"9edc52be45201ec99f6e4ceb5dc6abfc633c4eae" "9edc52be45201ec99f6e4ceb5dc6abfc633c4eae",
"5fab786695311433602028cd89898689c5df0898",
"5288772be438b86978a801e6dcb14228ad3a20af"
], ],
"CafeCn.ttf": [ "CafeCn.ttf": [
"23abcb52bc8bd580e9f58556d220f541dccafd15", "23abcb52bc8bd580e9f58556d220f541dccafd15",
@@ -254477,6 +254512,13 @@
"transtape3-vC.rom": [ "transtape3-vC.rom": [
"b7ec72fa0a55243a53c55d649e0555acea25d0c1" "b7ec72fa0a55243a53c55d649e0555acea25d0c1"
], ],
"trdos.rom": [
"0a74bd34538a03d0e1d214b425d95c14ad10c8c4",
"0de79c33e378c601e238ee37c219f8aa8442787a",
"56b8959de51919e55f1f691def6d7c6dc402bcd0",
"cd046dd7e71d8605a9323e9889d222817d77400f",
"21695e3f2a8f796386ce66eea8a246b0ac44810c"
],
"unodos3.rom": [ "unodos3.rom": [
"cdb90bd05b073ec7a5f875444e29ab0f6bcee9ac" "cdb90bd05b073ec7a5f875444e29ab0f6bcee9ac"
], ],
@@ -262735,12 +262777,6 @@
"0a747cc0b827a94b4fd74cfd818ca792437a38f7", "0a747cc0b827a94b4fd74cfd818ca792437a38f7",
"22e50c6ba4157a3f6a821bd9937cd26e292775c6" "22e50c6ba4157a3f6a821bd9937cd26e292775c6"
], ],
"trdos.rom": [
"0de79c33e378c601e238ee37c219f8aa8442787a",
"56b8959de51919e55f1f691def6d7c6dc402bcd0",
"cd046dd7e71d8605a9323e9889d222817d77400f",
"21695e3f2a8f796386ce66eea8a246b0ac44810c"
],
"128-spanish-0.rom": [ "128-spanish-0.rom": [
"968937b1c750f0ef6205f01c6db4148da4cca4e3" "968937b1c750f0ef6205f01c6db4148da4cca4e3"
], ],
@@ -264145,7 +264181,8 @@
"45bedc4cbdeac66c7df59e9e599195c778d86a92" "45bedc4cbdeac66c7df59e9e599195c778d86a92"
], ],
"coleco.rom": [ "coleco.rom": [
"45bedc4cbdeac66c7df59e9e599195c778d86a92" "45bedc4cbdeac66c7df59e9e599195c778d86a92",
"99ba9be24ada3e86e5c17aeecb7a2d68c5edfe59"
], ],
"colecovision.rom": [ "colecovision.rom": [
"45bedc4cbdeac66c7df59e9e599195c778d86a92" "45bedc4cbdeac66c7df59e9e599195c778d86a92"
@@ -284101,6 +284138,8 @@
"7d7e5f43": "f8200a0f4bd284670f0f0191fa9340684e94f66d", "7d7e5f43": "f8200a0f4bd284670f0f0191fa9340684e94f66d",
"d1a532a3": "da328a0a3074ec86fee2d6741bfc2e5a1f68d063", "d1a532a3": "da328a0a3074ec86fee2d6741bfc2e5a1f68d063",
"1e65e2e2": "99fc2a4d8107c36ee3075c9cff24872d2a98e37a", "1e65e2e2": "99fc2a4d8107c36ee3075c9cff24872d2a98e37a",
"352ceb91": "5fab786695311433602028cd89898689c5df0898",
"d5b60ff6": "5288772be438b86978a801e6dcb14228ad3a20af",
"71ddc671": "efd505f459448c5741a8ca249089f068b72cb751", "71ddc671": "efd505f459448c5741a8ca249089f068b72cb751",
"56b44837": "05980c296e1a7800c4aa21d6bed16323b6d859d5", "56b44837": "05980c296e1a7800c4aa21d6bed16323b6d859d5",
"ed80462a": "ba33eaf1a23cfef6fc1b88aa516ca2b3693e69d9", "ed80462a": "ba33eaf1a23cfef6fc1b88aa516ca2b3693e69d9",
@@ -286451,6 +286490,7 @@
"dc68673c": "c627527551e441a000184565a72df8ff37fce891", "dc68673c": "c627527551e441a000184565a72df8ff37fce891",
"0aed1b3f": "82bf848bac698d1ffd9ca809c4293f004152645a", "0aed1b3f": "82bf848bac698d1ffd9ca809c4293f004152645a",
"340a3285": "b7ec72fa0a55243a53c55d649e0555acea25d0c1", "340a3285": "b7ec72fa0a55243a53c55d649e0555acea25d0c1",
"c43d717f": "0a74bd34538a03d0e1d214b425d95c14ad10c8c4",
"4cdff071": "cdb90bd05b073ec7a5f875444e29ab0f6bcee9ac", "4cdff071": "cdb90bd05b073ec7a5f875444e29ab0f6bcee9ac",
"cb7b98f7": "1570048704f11dc239060ee4f63190d199c94987", "cb7b98f7": "1570048704f11dc239060ee4f63190d199c94987",
"4c7e9e47": "2551026b7d63a2627f79701e26688a7d715dc96a", "4c7e9e47": "2551026b7d63a2627f79701e26688a7d715dc96a",
@@ -303105,6 +303145,8 @@
"e4cbe52fae4f8bd59c0b616907fcf06cd10c8ce8f681cac8664af6f323795679": "f8200a0f4bd284670f0f0191fa9340684e94f66d", "e4cbe52fae4f8bd59c0b616907fcf06cd10c8ce8f681cac8664af6f323795679": "f8200a0f4bd284670f0f0191fa9340684e94f66d",
"d9b8d3b5e4ae0a6ed336e769301546735ff34f29c14be6e1232da6b7096c0988": "da328a0a3074ec86fee2d6741bfc2e5a1f68d063", "d9b8d3b5e4ae0a6ed336e769301546735ff34f29c14be6e1232da6b7096c0988": "da328a0a3074ec86fee2d6741bfc2e5a1f68d063",
"f7261790bd5aaaef104ffa7173182a3847de9e21ef444033555c3caed83bff10": "99fc2a4d8107c36ee3075c9cff24872d2a98e37a", "f7261790bd5aaaef104ffa7173182a3847de9e21ef444033555c3caed83bff10": "99fc2a4d8107c36ee3075c9cff24872d2a98e37a",
"dee8cbca96b74e81d19b0292f8c6255f0e2b23d8551f82cacd086c2eead35ea4": "5fab786695311433602028cd89898689c5df0898",
"ea8b3d7c752d85f2326b9bfa01615414ab34ba052f87481b0ff4003fd887e50a": "5288772be438b86978a801e6dcb14228ad3a20af",
"7edd55c0dc5e520987a92a4b41f3f8baed9167d68697afd25f28c733244cb275": "efd505f459448c5741a8ca249089f068b72cb751", "7edd55c0dc5e520987a92a4b41f3f8baed9167d68697afd25f28c733244cb275": "efd505f459448c5741a8ca249089f068b72cb751",
"e67d1afdd4ed545a6c544cbdf50d3d32dbffdd94d97f3b822d2fa08a5e6b0aed": "05980c296e1a7800c4aa21d6bed16323b6d859d5", "e67d1afdd4ed545a6c544cbdf50d3d32dbffdd94d97f3b822d2fa08a5e6b0aed": "05980c296e1a7800c4aa21d6bed16323b6d859d5",
"103fd16b0dab2c3267e8689ee01497fa9568935be87b253dbf22cd8c8417e57f": "ba33eaf1a23cfef6fc1b88aa516ca2b3693e69d9", "103fd16b0dab2c3267e8689ee01497fa9568935be87b253dbf22cd8c8417e57f": "ba33eaf1a23cfef6fc1b88aa516ca2b3693e69d9",
@@ -305455,6 +305497,7 @@
"5b84f59e701dc9ace29be5a3f93d00680d37f883623d871dd43e82ec5da10026": "c627527551e441a000184565a72df8ff37fce891", "5b84f59e701dc9ace29be5a3f93d00680d37f883623d871dd43e82ec5da10026": "c627527551e441a000184565a72df8ff37fce891",
"53af928797a17cde8bd0f551f0d2bc522c5a82117470ad9493c25d3a103de995": "82bf848bac698d1ffd9ca809c4293f004152645a", "53af928797a17cde8bd0f551f0d2bc522c5a82117470ad9493c25d3a103de995": "82bf848bac698d1ffd9ca809c4293f004152645a",
"e3efe8d378a77aa09abe08fb2b4083941737bef21ba947d7c634b378c25e7768": "b7ec72fa0a55243a53c55d649e0555acea25d0c1", "e3efe8d378a77aa09abe08fb2b4083941737bef21ba947d7c634b378c25e7768": "b7ec72fa0a55243a53c55d649e0555acea25d0c1",
"9ee624153f6ff198267fa57dd7568933a36e2003fbe9dd17799b6d21652a37bb": "0a74bd34538a03d0e1d214b425d95c14ad10c8c4",
"a802e4f07234bae69a985496bb2ee1a5669a7f5a6f9a351eee59aad37004eb5b": "cdb90bd05b073ec7a5f875444e29ab0f6bcee9ac", "a802e4f07234bae69a985496bb2ee1a5669a7f5a6f9a351eee59aad37004eb5b": "cdb90bd05b073ec7a5f875444e29ab0f6bcee9ac",
"409844660a4fb101f0846e058d30c2b6457fb0616b69e4428dec23111a1982a4": "1570048704f11dc239060ee4f63190d199c94987", "409844660a4fb101f0846e058d30c2b6457fb0616b69e4428dec23111a1982a4": "1570048704f11dc239060ee4f63190d199c94987",
"3acae3f2e61c9ed92ff8669af242cc5dd1d1b7d5edbc31dd8847e019a74f8fe8": "2551026b7d63a2627f79701e26688a7d715dc96a", "3acae3f2e61c9ed92ff8669af242cc5dd1d1b7d5edbc31dd8847e019a74f8fe8": "2551026b7d63a2627f79701e26688a7d715dc96a",
@@ -471011,6 +471054,22 @@
"ax360e/Account": [ "ax360e/Account": [
"99fc2a4d8107c36ee3075c9cff24872d2a98e37a" "99fc2a4d8107c36ee3075c9cff24872d2a98e37a"
], ],
"Other/azahar/sysdata/keys.txt": [
"5fab786695311433602028cd89898689c5df0898"
],
"azahar/sysdata/keys.txt": [
"5fab786695311433602028cd89898689c5df0898"
],
"sysdata/keys.txt": [
"5fab786695311433602028cd89898689c5df0898",
"5288772be438b86978a801e6dcb14228ad3a20af"
],
"Other/azaharplus/sysdata/keys.txt": [
"5288772be438b86978a801e6dcb14228ad3a20af"
],
"azaharplus/sysdata/keys.txt": [
"5288772be438b86978a801e6dcb14228ad3a20af"
],
"Other/beebem/AUNMap": [ "Other/beebem/AUNMap": [
"efd505f459448c5741a8ca249089f068b72cb751" "efd505f459448c5741a8ca249089f068b72cb751"
], ],
@@ -495921,10 +495980,13 @@
"zesarux/Z88UK400.rom": [ "zesarux/Z88UK400.rom": [
"23c63ceced72d0a9031cba08d2ebc72ca336921d" "23c63ceced72d0a9031cba08d2ebc72ca336921d"
], ],
"Other/zesarux/ace.rom": [ "Other/zesarux/alternate_roms/ace.rom": [
"6e5c9416a1cc89fe4012627f496f71ff2bb07b81" "6e5c9416a1cc89fe4012627f496f71ff2bb07b81"
], ],
"zesarux/ace.rom": [ "zesarux/alternate_roms/ace.rom": [
"6e5c9416a1cc89fe4012627f496f71ff2bb07b81"
],
"alternate_roms/ace.rom": [
"6e5c9416a1cc89fe4012627f496f71ff2bb07b81" "6e5c9416a1cc89fe4012627f496f71ff2bb07b81"
], ],
"Other/zesarux/aszmice07.rom": [ "Other/zesarux/aszmice07.rom": [
@@ -496347,6 +496409,12 @@
"zesarux/transtape3-vC.rom": [ "zesarux/transtape3-vC.rom": [
"b7ec72fa0a55243a53c55d649e0555acea25d0c1" "b7ec72fa0a55243a53c55d649e0555acea25d0c1"
], ],
"Other/zesarux/trdos.rom": [
"0a74bd34538a03d0e1d214b425d95c14ad10c8c4"
],
"zesarux/trdos.rom": [
"0a74bd34538a03d0e1d214b425d95c14ad10c8c4"
],
"Other/zesarux/unodos3.rom": [ "Other/zesarux/unodos3.rom": [
"cdb90bd05b073ec7a5f875444e29ab0f6bcee9ac" "cdb90bd05b073ec7a5f875444e29ab0f6bcee9ac"
], ],
-10
View File
@@ -88,7 +88,6 @@ files:
- name: OS6128_UK.rom - name: OS6128_UK.rom
path: "private/firmware/OS6128_UK.rom" path: "private/firmware/OS6128_UK.rom"
system: amstrad-cpc system: amstrad-cpc
region: [uk]
required: true required: true
min_size: 32768 min_size: 32768
validation: [size] validation: [size]
@@ -138,7 +137,6 @@ files:
- name: OS464_UK.rom - name: OS464_UK.rom
path: "private/firmware/OS464_UK.rom" path: "private/firmware/OS464_UK.rom"
system: amstrad-cpc system: amstrad-cpc
region: [uk]
required: false required: false
min_size: 32768 min_size: 32768
validation: [size] validation: [size]
@@ -150,7 +148,6 @@ files:
- name: OS464_FR.rom - name: OS464_FR.rom
path: "private/firmware/OS464_FR.rom" path: "private/firmware/OS464_FR.rom"
system: amstrad-cpc system: amstrad-cpc
region: [france]
required: false required: false
min_size: 32768 min_size: 32768
validation: [size] validation: [size]
@@ -164,7 +161,6 @@ files:
- name: OS664_UK.rom - name: OS664_UK.rom
path: "private/firmware/OS664_UK.rom" path: "private/firmware/OS664_UK.rom"
system: amstrad-cpc system: amstrad-cpc
region: [uk]
required: false required: false
min_size: 32768 min_size: 32768
validation: [size] validation: [size]
@@ -176,7 +172,6 @@ files:
- name: XMEM_FW_3_15_UK.rom - name: XMEM_FW_3_15_UK.rom
path: "private/firmware/XMEM_FW_3_15_UK.rom" path: "private/firmware/XMEM_FW_3_15_UK.rom"
system: amstrad-cpc system: amstrad-cpc
region: [uk]
required: false required: false
min_size: 32768 min_size: 32768
validation: [size] validation: [size]
@@ -188,7 +183,6 @@ files:
- name: XMEM_FW_3_15_FR.rom - name: XMEM_FW_3_15_FR.rom
path: "private/firmware/XMEM_FW_3_15_FR.rom" path: "private/firmware/XMEM_FW_3_15_FR.rom"
system: amstrad-cpc system: amstrad-cpc
region: [france]
required: false required: false
min_size: 32768 min_size: 32768
validation: [size] validation: [size]
@@ -224,7 +218,6 @@ files:
- name: Amstrad_f4.cpr - name: Amstrad_f4.cpr
path: "private/firmware/Amstrad_f4.cpr" path: "private/firmware/Amstrad_f4.cpr"
system: amstrad-cpc system: amstrad-cpc
region: [france]
required: false required: false
size: 131156 size: 131156
md5: e7159fd674022ba05f9b347ae3cdafc6 md5: e7159fd674022ba05f9b347ae3cdafc6
@@ -235,7 +228,6 @@ files:
- name: Amstrad_s4.cpr - name: Amstrad_s4.cpr
path: "private/firmware/Amstrad_s4.cpr" path: "private/firmware/Amstrad_s4.cpr"
system: amstrad-cpc system: amstrad-cpc
region: [spain]
required: false required: false
size: 131156 size: 131156
md5: 6eeed3fdd4abc98a2a932da810410128 md5: 6eeed3fdd4abc98a2a932da810410128
@@ -257,7 +249,6 @@ files:
- name: hackit400uk.rom - name: hackit400uk.rom
path: "private/hacker/hackit400uk.rom" path: "private/hacker/hackit400uk.rom"
system: amstrad-cpc system: amstrad-cpc
region: [uk]
required: false required: false
size: 16384 size: 16384
md5: 9a293eaaebca601f671070ad56cb81b6 md5: 9a293eaaebca601f671070ad56cb81b6
@@ -269,7 +260,6 @@ files:
- name: hacker481fr.rom - name: hacker481fr.rom
path: "private/hacker/hacker481fr.rom" path: "private/hacker/hacker481fr.rom"
system: amstrad-cpc system: amstrad-cpc
region: [france]
required: false required: false
size: 16384 size: 16384
md5: dfdd9924295f491d54f30569874287f8 md5: dfdd9924295f491d54f30569874287f8
+1 -1
View File
@@ -4,7 +4,7 @@ core_classification: enhanced_fork
source: "https://github.com/amadvance/advancemame" source: "https://github.com/amadvance/advancemame"
upstream: "https://www.mamedev.org" upstream: "https://www.mamedev.org"
profiled_date: "2026-08-08" profiled_date: "2026-08-08"
source_commit: "0f6a7677ad42b4dfdfa36b046f74bef5238d5837" source_commit: "21f8f6f79d8a9abe9f23b1a070841963774cf65d"
core_version: "5.0" core_version: "5.0"
mame_version: "0.106" mame_version: "0.106"
display_name: "Arcade (AdvanceMAME)" display_name: "Arcade (AdvanceMAME)"
+1 -1
View File
@@ -61,7 +61,7 @@ notes: |
Firmware path resolution: settings.paths.firmware (user override) or Firmware path resolution: settings.paths.firmware (user override) or
{userData}/ares/Firmware/ (default). Per-system firmware assigned in {userData}/ares/Firmware/ (default). Per-system firmware assigned in
Settings > Firmware dialog or via scan-by-hash. Settings > Firmware dialog or via scan-by-hash.
ref: desktop-ui/settings/settings.hpp:89-101, desktop-ui/desktop-ui.cpp:68-75 ref: desktop-ui/settings/settings.hpp:87-99, desktop-ui/desktop-ui.cpp:68-75
Systems with fully embedded firmware (no user files needed): Systems with fully embedded firmware (no user files needed):
Game Boy (boot.rom), Game Boy Color (boot.rom), Mega Drive (TMSS), Game Boy (boot.rom), Game Boy Color (boot.rom), Mega Drive (TMSS),
+1
View File
@@ -506,6 +506,7 @@ files:
- name: cas.glsl - name: cas.glsl
path: pcsx2/resources/shaders/vulkan/cas.glsl path: pcsx2/resources/shaders/vulkan/cas.glsl
standalone_path: resources/shaders/vulkan/cas.glsl standalone_path: resources/shaders/vulkan/cas.glsl
sha1: "005cecf1a5e20b05407d55d0aad22cf81ad66c31"
required: false required: false
bundled: true bundled: true
description: "Vulkan contrast adaptive sharpening shader" description: "Vulkan contrast adaptive sharpening shader"
+4 -2
View File
@@ -38,8 +38,10 @@ files:
system: nintendo-3ds system: nintendo-3ds
required: false required: false
hle_fallback: true hle_fallback: true
note: "embedded encrypted fallback if missing" sha1: "5fab786695311433602028cd89898689c5df0898"
source_ref: "src/core/hw/aes/key.cpp:283, src/common/common_paths.h:83" md5: "cb52d323b13129a2f0bc3e3e911e567f"
note: "embedded encrypted fallback if missing: default_keys_enc, AES-CBC with a zero key and IV; the hashes are those of the decrypted text without its PKCS#7 block"
source_ref: "src/core/hw/aes/key.cpp:283-300, src/common/common_paths.h:83, src/core/hw/default_keys.h:4-5"
- name: shared_font.bin - name: shared_font.bin
path: sysdata/shared_font.bin path: sysdata/shared_font.bin
+4 -2
View File
@@ -56,8 +56,10 @@ files:
system: nintendo-3ds system: nintendo-3ds
required: false required: false
hle_fallback: true hle_fallback: true
note: "AES key slots, common keys and NFC secrets; embedded encrypted blob used if missing" sha1: "5288772be438b86978a801e6dcb14228ad3a20af"
source_ref: "src/core/hw/aes/key.cpp:225-364, src/core/hw/aes/key.cpp:473-613" md5: "4812e96097b7fc1bdd7b40366208b7a7"
note: "AES key slots, common keys and NFC secrets; embedded encrypted blob used if missing: default_keys_enc, AES-CBC with a zero key and IV, the hashes are those of the decrypted text"
source_ref: "src/core/hw/aes/key.cpp:225-364, src/core/hw/aes/key.cpp:473-613, src/core/hw/default_keys.h:11"
- name: aes_keys.txt - name: aes_keys.txt
path: sysdata/aes_keys.txt path: sysdata/aes_keys.txt
+1 -1
View File
@@ -14,7 +14,7 @@ notes: |
Libretro port of Mednafen's Virtual Boy emulation module. Libretro port of Mednafen's Virtual Boy emulation module.
No external BIOS or firmware files. The Virtual Boy has no boot No external BIOS or firmware files. The Virtual Boy has no boot
ROM; the V810 CPU executes directly from the game ROM mapped at ROM; the V810 CPU executes directly from the game ROM mapped at
0x07000000. Load() in libretro.cpp:1833 takes only ROM data. 0x07000000. Load() in libretro.cpp:1810 takes only ROM data.
Upstream Mednafen supports an optional vb.pal palette file Upstream Mednafen supports an optional vb.pal palette file
(Documentation/vb.html); the libretro port handles palette via (Documentation/vb.html); the libretro port handles palette via
core options instead. core options instead.
-3
View File
@@ -406,7 +406,6 @@ files:
- name: KANJI.rom - name: KANJI.rom
path: "Machines/Shared Roms/KANJI.rom" path: "Machines/Shared Roms/KANJI.rom"
region: [japan]
size: 262144 size: 262144
required: false required: false
note: "Kanji font ROM (256 KB). Used by MSX2+, turboR, and Japanese MSX2 machines." note: "Kanji font ROM (256 KB). Used by MSX2+, turboR, and Japanese MSX2 machines."
@@ -414,7 +413,6 @@ files:
- name: MSXKANJI.rom - name: MSXKANJI.rom
path: "Machines/Shared Roms/MSXKANJI.rom" path: "Machines/Shared Roms/MSXKANJI.rom"
region: [japan]
size: 32768 size: 32768
required: false required: false
note: "MSX-Kanji driver ROM. Used by MSX2+, turboR, and Japanese MSX2 for kanji display." note: "MSX-Kanji driver ROM. Used by MSX2+, turboR, and Japanese MSX2 for kanji display."
@@ -456,7 +454,6 @@ files:
- name: HANGUL.rom - name: HANGUL.rom
path: "Machines/Shared Roms/HANGUL.rom" path: "Machines/Shared Roms/HANGUL.rom"
region: [south-korea]
size: 131072 size: 131072
required: false required: false
note: "Korean Hangul font ROM (128 KB)." note: "Korean Hangul font ROM (128 KB)."
+5 -5
View File
@@ -36,7 +36,7 @@ files:
mode: standalone mode: standalone
size: 256 size: 256
note: "Original Nintendo DMG boot ROM" note: "Original Nintendo DMG boot ROM"
source_ref: "src/gb.rs:1516, src/data.rs:14" source_ref: "src/gb.rs:1416, src/data.rs:14"
- name: sgb_boot.bin - name: sgb_boot.bin
system: nintendo-gb system: nintendo-gb
@@ -45,7 +45,7 @@ files:
mode: standalone mode: standalone
size: 256 size: 256
note: "Super Game Boy boot ROM" note: "Super Game Boy boot ROM"
source_ref: "src/gb.rs:1517, src/data.rs:29" source_ref: "src/gb.rs:1417, src/data.rs:29"
- name: dmg_bootix.bin - name: dmg_bootix.bin
system: nintendo-gb system: nintendo-gb
@@ -54,7 +54,7 @@ files:
mode: standalone mode: standalone
size: 256 size: 256
note: "Open-source DMG boot ROM from Bootix (default DMG)" note: "Open-source DMG boot ROM from Bootix (default DMG)"
source_ref: "src/gb.rs:1518, src/data.rs:44" source_ref: "src/gb.rs:1418, src/data.rs:44"
- name: mgb_bootix.bin - name: mgb_bootix.bin
system: nintendo-gb system: nintendo-gb
@@ -63,7 +63,7 @@ files:
mode: standalone mode: standalone
size: 256 size: 256
note: "Open-source MGB (Pocket) boot ROM from Bootix" note: "Open-source MGB (Pocket) boot ROM from Bootix"
source_ref: "src/gb.rs:1519, src/data.rs:59" source_ref: "src/gb.rs:1419, src/data.rs:59"
- name: cgb_boot.bin - name: cgb_boot.bin
system: nintendo-gbc system: nintendo-gbc
@@ -81,4 +81,4 @@ files:
mode: standalone mode: standalone
size: 2304 size: 2304
note: "Custom open-source CGB boot ROM (default CGB)" note: "Custom open-source CGB boot ROM (default CGB)"
source_ref: "src/gb.rs:1484, src/data.rs:177" source_ref: "src/gb.rs:1421, src/data.rs:177"
+4 -4
View File
@@ -24,25 +24,25 @@ mode: standalone
files: files:
- name: "prod.keys" - name: "prod.keys"
required: true required: true
path: "switch/" path: "switch/prod.keys"
note: "Production keys for NCA decryption (master, key area, header, titlekek)" note: "Production keys for NCA decryption (master, key area, header, titlekek)"
source_ref: "src/core/crypto/key_manager.cpp:655-656" source_ref: "src/core/crypto/key_manager.cpp:655-656"
- name: "title.keys" - name: "title.keys"
required: false required: false
path: "switch/" path: "switch/title.keys"
note: "Per-title encryption keys (rights_id to titlekey mappings)" note: "Per-title encryption keys (rights_id to titlekey mappings)"
source_ref: "src/core/crypto/key_manager.cpp:659-660" source_ref: "src/core/crypto/key_manager.cpp:659-660"
- name: "console.keys" - name: "console.keys"
required: false required: false
path: "switch/" path: "switch/console.keys"
note: "Console-specific keys (BIS, SD seed)" note: "Console-specific keys (BIS, SD seed)"
source_ref: "src/core/crypto/key_manager.cpp:661-662" source_ref: "src/core/crypto/key_manager.cpp:661-662"
- name: "key_retail.bin" - name: "key_retail.bin"
required: false required: false
path: "switch/" path: "switch/key_retail.bin"
size: 160 size: 160
note: "Amiibo decryption keys (two InternalKey structs, 0x50 bytes each)" note: "Amiibo decryption keys (two InternalKey structs, 0x50 bytes each)"
source_ref: "src/core/hle/service/nfc/common/amiibo_crypto.cpp:274-292" source_ref: "src/core/hle/service/nfc/common/amiibo_crypto.cpp:274-292"
+2 -2
View File
@@ -1,8 +1,8 @@
emulator: DOSBox Pure Unleashed emulator: DOSBox Pure Unleashed
type: standalone type: standalone
core_classification: official_port core_classification: official_port
source: "https://github.com/schellingb/dosbox-pure-unleashed" source: "https://codeberg.org/schelling/dosbox-pure-unleashed"
upstream: "https://github.com/schellingb/dosbox-pure" upstream: "https://codeberg.org/schelling/dosbox-pure"
source_commit: "4a11412248ca4c862751a7d9e6818023795031e9" source_commit: "4a11412248ca4c862751a7d9e6818023795031e9"
upstream_commit: "7f6e8fb7385fa446d1444d671063268520bf9b54" upstream_commit: "7f6e8fb7385fa446d1444d671063268520bf9b54"
logo: "https://raw.githubusercontent.com/schellingb/dosbox-pure-unleashed/main/images/logo.png" logo: "https://raw.githubusercontent.com/schellingb/dosbox-pure-unleashed/main/images/logo.png"
+1 -1
View File
@@ -16,7 +16,7 @@ notes: |
ref: src/gui/midi_mt32.cpp:35,51-66 ref: src/gui/midi_mt32.cpp:35,51-66
MUNT identifies ROMs by SHA1+size (libretro/deps/munt/mt32emu/src/ROMInfo.cpp:171-180). MUNT identifies ROMs by SHA1+size (libretro/deps/munt/mt32emu/src/ROMInfo.cpp:171-180).
FluidSynth reads .sf2/.sf3/.dls/.gig from system/soundfonts/. FluidSynth reads .sf2/.sf3/.dls/.gig from system/soundfonts/.
ref: libretro/src/libretro.cpp:1112-1123 ref: libretro/src/libretro.cpp:1099-1110
BASSMIDI reads .sf2/.sfz from system/soundfonts/, requires BASS + BASSMIDI reads .sf2/.sfz from system/soundfonts/, requires BASS +
BASSMIDI shared libs in system dir (dlopen'd at runtime). BASSMIDI shared libs in system dir (dlopen'd at runtime).
ref: libretro/src/midi_bassmidi.cpp:232-254 ref: libretro/src/midi_bassmidi.cpp:232-254
+1 -1
View File
@@ -2,7 +2,7 @@ emulator: DOSBox Pure
type: standalone + libretro type: standalone + libretro
core_classification: enhanced_fork core_classification: enhanced_fork
source: "https://github.com/libretro/dosbox-pure" source: "https://github.com/libretro/dosbox-pure"
upstream: "https://github.com/schellingb/dosbox-pure" upstream: "https://codeberg.org/schelling/dosbox-pure"
logo: "https://raw.githubusercontent.com/schellingb/dosbox-pure/main/images/logo.png" logo: "https://raw.githubusercontent.com/schellingb/dosbox-pure/main/images/logo.png"
profiled_date: "2026-03-23" profiled_date: "2026-03-23"
source_commit: "a4a0bab7f8931433588f2fcad9045c85b277373d" source_commit: "a4a0bab7f8931433588f2fcad9045c85b277373d"
+1 -1
View File
@@ -30,7 +30,7 @@ notes: |
only by the two settings screens, which fall back to the real filename when it only by the two settings screens, which fall back to the real filename when it
is null. is null.
ref: src/core/bios.h:41-43 (field declarations) ref: src/core/bios.h:41-43 (field declarations)
ref: src/duckstation-qt/biossettingswidget.cpp:149, src/core/fullscreenui_settings.cpp:3301 ref: src/duckstation-qt/biossettingswidget.cpp:149, src/core/fullscreenui_settings.cpp:3281
Lower priority = preferred. PS2 deprioritized (>= 100) for dynamic fast boot. Lower priority = preferred. PS2 deprioritized (>= 100) for dynamic fast boot.
Late PAL deprioritized (>= 150) due to additional regional checks. Late PAL deprioritized (>= 150) due to additional regional checks.
OpenBIOS detected by signature at offset 0x78, not by hash. OpenBIOS detected by signature at offset 0x78, not by hash.
+1 -1
View File
@@ -24,7 +24,7 @@ notes: |
"embedded" when that fails. The system directory is still passed to SelectGame, "embedded" when that fails. The system directory is still passed to SelectGame,
but for the game data search, not for the engine package. but for the game data search, not for the engine package.
ref: src/libretro/ecwolf_pk3_data.c, src/wl_iwad.cpp:664-672, ref: src/libretro/ecwolf_pk3_data.c, src/wl_iwad.cpp:664-672,
src/libretro/libretro.cpp:1028-1030 src/libretro/libretro.cpp:1009-1011
Standalone: ecwolf.pk3 searched in progdir (binary directory). Standalone: ecwolf.pk3 searched in progdir (binary directory).
ref: src/libretro/wl_main.cpp:1247 (progdir passed to SelectGame) ref: src/libretro/wl_main.cpp:1247 (progdir passed to SelectGame)
+5 -5
View File
@@ -27,31 +27,31 @@ mode: standalone
files: files:
- name: "prod.keys" - name: "prod.keys"
required: true required: true
path: "switch/" path: "switch/prod.keys"
note: "Production keys for NCA decryption (master, key area, header, titlekek)" note: "Production keys for NCA decryption (master, key area, header, titlekek)"
source_ref: "src/core/crypto/key_manager.cpp:574-575, 853" source_ref: "src/core/crypto/key_manager.cpp:574-575, 853"
- name: "dev.keys" - name: "dev.keys"
required: false required: false
path: "switch/" path: "switch/dev.keys"
note: "Development-unit keys, read instead of prod.keys when use_dev_keys is set" note: "Development-unit keys, read instead of prod.keys when use_dev_keys is set"
source_ref: "src/core/crypto/key_manager.cpp:570-571, 852" source_ref: "src/core/crypto/key_manager.cpp:570-571, 852"
- name: "title.keys" - name: "title.keys"
required: false required: false
path: "switch/" path: "switch/title.keys"
note: "Per-title encryption keys (rights_id to titlekey mappings)" note: "Per-title encryption keys (rights_id to titlekey mappings)"
source_ref: "src/core/crypto/key_manager.cpp:577-578, 850" source_ref: "src/core/crypto/key_manager.cpp:577-578, 850"
- name: "console.keys" - name: "console.keys"
required: false required: false
path: "switch/" path: "switch/console.keys"
note: "Console-specific keys (BIS, SD seed)" note: "Console-specific keys (BIS, SD seed)"
source_ref: "src/core/crypto/key_manager.cpp:579-580" source_ref: "src/core/crypto/key_manager.cpp:579-580"
- name: "key_retail.bin" - name: "key_retail.bin"
required: false required: false
path: "switch/" path: "switch/key_retail.bin"
size: 160 size: 160
note: "Amiibo decryption keys (two InternalKey structs, 0x50 bytes each)" note: "Amiibo decryption keys (two InternalKey structs, 0x50 bytes each)"
source_ref: "src/core/hle/service/nfc/common/amiibo_crypto.cpp:281-302" source_ref: "src/core/hle/service/nfc/common/amiibo_crypto.cpp:281-302"
+23 -23
View File
@@ -8,9 +8,9 @@ upstream: "https://github.com/EKA2L1/EKA2L1"
profiled_date: "2026-09-17" profiled_date: "2026-09-17"
source_commit: source_commit:
standalone: "1fcfadbff7f8b81d8c17bc82d04b0c94d16803bf" standalone: "1fcfadbff7f8b81d8c17bc82d04b0c94d16803bf"
libretro: "97ac3df6767f819c95405e4c86e7007d2e8ae61d" libretro: "1c49c1bcde3853273cd520a5360bff756f872e06"
upstream_commit: upstream_commit:
libretro: "47b4ad76a6dd7d3656f52be86dfd5b9ff9a50c70" libretro: "11f5b6cd141c0884a130ee45743f1f5e17bb1783"
core_version: "0.0.9" core_version: "0.0.9"
display_name: "Nokia - Symbian / N-Gage (EKA2L1)" display_name: "Nokia - Symbian / N-Gage (EKA2L1)"
cores: cores:
@@ -27,13 +27,13 @@ notes: |
N-Gage 2.0 libraries and the game libraries from S60v1 to Symbian Belle. One N-Gage 2.0 libraries and the game libraries from S60v1 to Symbian Belle. One
tree builds the Qt desktop frontend, the Android and iOS applications, and a tree builds the Qt desktop frontend, the Android and iOS applications, and a
libretro core; the core lives on the libretro branch of the WizzardSK fork, libretro core; the core lives on the libretro branch of the WizzardSK fork,
merged from upstream at 47b4ad76. The tip of that branch, 7a40bb0d, merges rebased on the upstream revision named in its upstream.version, 11f5b6cd.
upstream 0b7d9d67 and does not build the core: content.cpp still calls content.cpp passes isolate_drives false to install_firmware and
install_firmware and install_rom_with_optional_rpkg without the install_rom_with_optional_rpkg, so drives C, D and E are shared between
isolate_drives argument they take from that merge on. The libretro pin is the devices.
last revision of the branch that builds.
ref: README.md:20, README.md:29-32, src/emu/CMakeLists.txt:61-62, ref: README.md:20, README.md:29-32, src/emu/CMakeLists.txt:61-62,
CMakeLists.txt:18, .gitlab-ci.yml:14-22, src/emu/libretro/src/content.cpp:96-100, CMakeLists.txt:18, .gitlab-ci.yml:17-25, src/emu/libretro/CMakeLists.txt:40-46,
src/emu/libretro/src/content.cpp:178-184,
src/emu/system/src/installation/rpkg.cpp:208-210, src/emu/system/src/installation/rpkg.cpp:208-210,
src/emu/system/src/installation/firmware.cpp:290-293 src/emu/system/src/installation/firmware.cpp:290-293
@@ -54,12 +54,12 @@ notes: |
sw.txt or langsw.txt under z:\resource\versions, sw.txt or langsw.txt under sw.txt or langsw.txt under z:\resource\versions, sw.txt or langsw.txt under
z:\system\versions, or z:\system\install\sonyericssonp90xplatform.sis. That z:\system\versions, or z:\system\install\sonyericssonp90xplatform.sis. That
is the N-Gage case: its files are unpacked to drives/z/, the firmware code is is the N-Gage case: its files are unpacked to drives/z/, the firmware code is
read from them and the image is copied to SYM.ROM. The libretro pin tests read from them and the image is copied to SYM.ROM. Any other image needs
z:\system\versions\sw.txt alone. Any other image needs the RPKG made by the the RPKG made by the Dumber tool beside it, which supplies the ROFS. A .vpl
Dumber tool beside it, which supplies the ROFS. A .vpl variant packing list variant packing list names the .fpsx flash images of a factory firmware,
names the .fpsx flash images of a factory firmware, from which the core image, from which the core image, the ROFS and the drive contents are extracted.
the ROFS and the drive contents are extracted. The names of the dump files The names of the dump files are free; SYM.ROM is the name the installers
are free; SYM.ROM is the name the installers give the resident copy. give the resident copy.
ref: src/emu/system/src/installation/rpkg.cpp:44-68, ref: src/emu/system/src/installation/rpkg.cpp:44-68,
src/emu/system/src/software.cpp:396-408, src/emu/system/src/software.cpp:396-408,
src/emu/system/src/installation/rpkg.cpp:132-199, src/emu/system/src/installation/rpkg.cpp:132-199,
@@ -97,11 +97,11 @@ notes: |
<system>/eka2l1/libretro-content.txt against the UID it registered, or a <system>/eka2l1/libretro-content.txt against the UID it registered, or a
.eka2l1 text shortcut naming the UID of an application already installed. .eka2l1 text shortcut naming the UID of an application already installed.
supports_no_game is false. is_experimental is true in the .info. supports_no_game is false. is_experimental is true in the .info.
ref: src/emu/libretro/src/main.cpp:51-53, src/emu/libretro/src/state.cpp:12-17, ref: src/emu/libretro/src/main.cpp:55-57, src/emu/libretro/src/state.cpp:12-17,
src/emu/libretro/src/main.cpp:82-100, src/emu/libretro/src/content.cpp:44-111, src/emu/libretro/src/main.cpp:92-110, src/emu/libretro/src/content.cpp:129-195,
src/emu/libretro/src/main.cpp:161-169, src/emu/libretro/src/main.cpp:68-69, src/emu/libretro/src/main.cpp:173-181, src/emu/libretro/src/main.cpp:72-73,
src/emu/libretro/src/main.cpp:116-117, src/emu/libretro/src/content.cpp:119, src/emu/libretro/src/main.cpp:126-127, src/emu/libretro/src/content.cpp:203,
src/emu/libretro/src/content.cpp:244-249 src/emu/libretro/src/content.cpp:350-355
At this revision the libretro build stages none of the shipped resource At this revision the libretro build stages none of the shipped resource
folders and never calls set_runtime_resource_root, so the GL shaders, the folders and never calls set_runtime_resource_root, so the GL shaders, the
@@ -110,8 +110,8 @@ notes: |
and creates no audio or controller driver, so the patch libraries, the Lua and creates no audio or controller driver, so the patch libraries, the Lua
scripts, the MIDI banks and the controller database are not read there. scripts, the MIDI banks and the controller database are not read there.
Those entries carry mode standalone. Those entries carry mode standalone.
ref: src/emu/libretro/CMakeLists.txt:8-45, src/emu/libretro/src/thread.cpp:19-38, ref: src/emu/libretro/CMakeLists.txt:8-72, src/emu/libretro/src/thread.cpp:20-61,
src/emu/libretro/src/thread.cpp:41-60, src/emu/libretro/src/thread.cpp:83-89, src/emu/libretro/src/thread.cpp:87-93,
src/emu/common/src/path.cpp:427-450, src/emu/system/src/epoc.cpp:1389-1410 src/emu/common/src/path.cpp:427-450, src/emu/system/src/epoc.cpp:1389-1410
Shipped runtime files are read at their own name below the working Shipped runtime files are read at their own name below the working
@@ -156,7 +156,7 @@ notes: |
src/emu/config/src/panic_blacklist.cpp:27-35, src/emu/config/src/app_settings.cpp:82-114, src/emu/config/src/panic_blacklist.cpp:27-35, src/emu/config/src/app_settings.cpp:82-114,
src/emu/utils/src/panic.cpp:32-38, src/emu/scripting/src/manager.cpp:196-202, src/emu/utils/src/panic.cpp:32-38, src/emu/scripting/src/manager.cpp:196-202,
src/emu/scripting/src/manager.cpp:230-256, src/emu/services/src/fbs/fbs.cpp:443, src/emu/scripting/src/manager.cpp:230-256, src/emu/services/src/fbs/fbs.cpp:443,
src/emu/services/src/fbs/impls/font.cpp:1185-1208, .gitlab-ci.yml:19, src/emu/services/src/fbs/impls/font.cpp:1185-1208, .gitlab-ci.yml:22,
src/emu/qt/CMakeLists.txt:264-278, src/emu/qt/CMakeLists.txt:289-290, src/emu/qt/CMakeLists.txt:264-278, src/emu/qt/CMakeLists.txt:289-290,
src/emu/qt/CMakeLists.txt:376-377, cmake/Symbian.cmake:20-31 src/emu/qt/CMakeLists.txt:376-377, cmake/Symbian.cmake:20-31
@@ -182,7 +182,7 @@ files:
dump, an EKA2 image with its RPKG, or a VPL with its .fpsx images. dump, an EKA2 image with its RPKG, or a VPL with its .fpsx images.
source_ref: source_ref:
standalone: "src/emu/system/src/epoc.cpp:1486-1494, src/emu/system/src/epoc.cpp:824-848, src/emu/system/include/system/consts.h:23-25, src/emu/system/src/installation/rpkg.cpp:44-68, src/emu/system/src/installation/rpkg.cpp:196-199, src/emu/qt/src/device_install_dialog.cpp:162-172" standalone: "src/emu/system/src/epoc.cpp:1486-1494, src/emu/system/src/epoc.cpp:824-848, src/emu/system/include/system/consts.h:23-25, src/emu/system/src/installation/rpkg.cpp:44-68, src/emu/system/src/installation/rpkg.cpp:196-199, src/emu/qt/src/device_install_dialog.cpp:162-172"
libretro: "src/emu/libretro/src/main.cpp:82-100, src/emu/libretro/src/content.cpp:44-111, src/emu/libretro/src/main.cpp:161-169, src/emu/system/src/installation/rpkg.cpp:202-248" libretro: "src/emu/libretro/src/main.cpp:92-110, src/emu/libretro/src/content.cpp:129-195, src/emu/libretro/src/main.cpp:173-181, src/emu/system/src/installation/rpkg.cpp:208-255"
- name: audiooutputrouting.dll.map - name: audiooutputrouting.dll.map
standalone_path: patch/audiooutputrouting.dll.map standalone_path: patch/audiooutputrouting.dll.map
+2 -2
View File
@@ -21,7 +21,7 @@ systems:
- coleco-colecovision - coleco-colecovision
- sinclair-spectrum - sinclair-spectrum
- msx - msx
- nes-fds - nintendo-fds
- super-kaneko-nova - super-kaneko-nova
- deco-cassette - deco-cassette
- namco-na1 - namco-na1
@@ -1061,7 +1061,7 @@ files:
# ------------------------------------------------------- # -------------------------------------------------------
- name: "fdsbios.nes" - name: "fdsbios.nes"
archive: fdsbios.zip archive: fdsbios.zip
system: nes-fds system: nintendo-fds
required: true required: true
size: 8192 # 0x2000 size: 8192 # 0x2000
crc32: "5e607dcf" crc32: "5e607dcf"
+3 -3
View File
@@ -16,7 +16,7 @@ notes: |
contributed by Joe Zbiciak (jzIntv), mask ROM embedded in source contributed by Joe Zbiciak (jzIntv), mask ROM embedded in source
(src/ivoice.c:337-510, 4097-byte static array). No external ivoice ROM. (src/ivoice.c:337-510, 4097-byte static array). No external ivoice ROM.
BIOS loading in retro_init() (src/libretro.c:1184-1192). Paths joined BIOS loading in retro_init() (src/libretro.c:1175-1183). Paths joined
directly to system directory, no subdirectory. directly to system directory, no subdirectory.
loadExec() reads exec.bin as big-endian 16-bit words into 0x1000-0x1FFF. loadExec() reads exec.bin as big-endian 16-bit words into 0x1000-0x1FFF.
@@ -35,11 +35,11 @@ files:
required: true required: true
size: 8192 size: 8192
note: "Executive ROM. Big-endian 16-bit words loaded at 0x1000-0x1FFF." note: "Executive ROM. Big-endian 16-bit words loaded at 0x1000-0x1FFF."
source_ref: "src/intv.c:55-142, src/libretro.c:1187" source_ref: "src/intv.c:51-96, src/libretro.c:1178"
- name: "grom.bin" - name: "grom.bin"
system: intellivision system: intellivision
required: true required: true
size: 2048 size: 2048
note: "Graphics ROM. Raw bytes loaded at 0x3000-0x37FF." note: "Graphics ROM. Raw bytes loaded at 0x3000-0x37FF."
source_ref: "src/intv.c:144-190, src/libretro.c:1191" source_ref: "src/intv.c:98-144, src/libretro.c:1182"
+4 -4
View File
@@ -34,17 +34,17 @@ files:
- name: "bios.sms" - name: "bios.sms"
system: sega-mastersystem system: sega-mastersystem
required: false required: false
source_ref: "platforms/libretro/libretro.cpp:777, platforms/libretro/libretro.cpp:780 (load_bootroms)" source_ref: "platforms/libretro/libretro.cpp:736, platforms/libretro/libretro.cpp:731-743 (load_bootroms)"
- name: "bios.gg" - name: "bios.gg"
system: sega-gamegear system: sega-gamegear
required: false required: false
source_ref: "platforms/libretro/libretro.cpp:1067, platforms/libretro/libretro.cpp:780 (load_bootroms)" source_ref: "platforms/libretro/libretro.cpp:737, platforms/libretro/libretro.cpp:731-743 (load_bootroms)"
platform_details: platform_details:
mastersystem: mastersystem:
bios_filename: "bios.sms" bios_filename: "bios.sms"
source_ref: "src/Memory.cpp:227-232" source_ref: "src/Memory.cpp:267-272"
note: | note: |
Boot ROM loaded via LoadBootromSMS() -> LoadBootroom(path, false). Boot ROM loaded via LoadBootromSMS() -> LoadBootroom(path, false).
Raw binary read, stored in m_pBootromSMS. Bank count computed as Raw binary read, stored in m_pBootromSMS. Bank count computed as
@@ -52,7 +52,7 @@ platform_details:
gamegear: gamegear:
bios_filename: "bios.gg" bios_filename: "bios.gg"
source_ref: "src/Memory.cpp:234-239" source_ref: "src/Memory.cpp:274-279"
note: | note: |
Boot ROM loaded via LoadBootromGG() -> LoadBootroom(path, true). Boot ROM loaded via LoadBootromGG() -> LoadBootroom(path, true).
Raw binary read, stored in m_pBootromGG. Same bank computation. Raw binary read, stored in m_pBootromGG. Same bank computation.
+2 -2
View File
@@ -187,7 +187,7 @@ files:
- name: "<CaCertificateId>.der" - name: "<CaCertificateId>.der"
required: false required: false
path: system/ssl/ path: "system/ssl/<CaCertificateId>.der"
unsourceable: "replacement certificate the user picks for one certificate authority, with no canonical artefact behind the name" unsourceable: "replacement certificate the user picks for one certificate authority, with no canonical artefact behind the name"
description: "Override for one built-in root certificate" description: "Override for one built-in root certificate"
note: >- note: >-
@@ -199,7 +199,7 @@ files:
- name: "<driver>.zip" - name: "<driver>.zip"
required: false required: false
path: drivers/ path: "drivers/<driver>.zip"
unsourceable: "third-party Vulkan driver built for one GPU family, not an emulator artefact" unsourceable: "third-party Vulkan driver built for one GPU family, not an emulator artefact"
description: "Replacement Vulkan driver for the Android build" description: "Replacement Vulkan driver for the Android build"
note: >- note: >-
+5 -5
View File
@@ -30,7 +30,7 @@ notes: |
files: files:
- name: "<bios>.bin" - name: "<bios>.bin"
path: "pcsx2/bios/" path: "pcsx2/bios/<bios>.bin"
required: true required: true
description: "PS2 BIOS binary" description: "PS2 BIOS binary"
min_size: 4194304 min_size: 4194304
@@ -43,7 +43,7 @@ files:
pcsx2_bios. Falls back to first valid BIOS found if configured BIOS is missing. pcsx2_bios. Falls back to first valid BIOS found if configured BIOS is missing.
- name: "<bios>.rom1" - name: "<bios>.rom1"
path: "pcsx2/bios/" path: "pcsx2/bios/<bios>.rom1"
required: false required: false
max_size: 4194304 max_size: 4194304
description: "DVD player ROM" description: "DVD player ROM"
@@ -53,7 +53,7 @@ files:
(extension replaced). Silently skipped if not found. (extension replaced). Silently skipped if not found.
- name: "<bios>.rom2" - name: "<bios>.rom2"
path: "pcsx2/bios/" path: "pcsx2/bios/<bios>.rom2"
required: false required: false
max_size: 524288 max_size: 524288
description: "Chinese ROM extension" description: "Chinese ROM extension"
@@ -63,7 +63,7 @@ files:
Only present on Chinese region consoles. Only present on Chinese region consoles.
- name: "<bios>.nvm" - name: "<bios>.nvm"
path: "pcsx2/bios/" path: "pcsx2/bios/<bios>.nvm"
required: false required: false
hle_fallback: true hle_fallback: true
size: 1024 size: 1024
@@ -75,7 +75,7 @@ files:
Auto-created with region-appropriate defaults if missing or invalid. Auto-created with region-appropriate defaults if missing or invalid.
- name: "<bios>.mec" - name: "<bios>.mec"
path: "pcsx2/bios/" path: "pcsx2/bios/<bios>.mec"
required: false required: false
hle_fallback: true hle_fallback: true
size: 4 size: 4
+1 -1
View File
@@ -17,7 +17,7 @@ notes: |
standalone upstream. standalone upstream.
retro_load_game() calls lutro_load(info->path) to load the game retro_load_game() calls lutro_load(info->path) to load the game
directory or archive (libretro.c:267). The core exposes directory or archive (libretro.c:241). The core exposes
love.filesystem.getAppdataDirectory to games as the system_dir love.filesystem.getAppdataDirectory to games as the system_dir
(filesystem.c:296) but does not load any files from it itself. (filesystem.c:296) but does not load any files from it itself.
No BIOS, firmware, or system files required. No BIOS, firmware, or system files required.
+39 -43
View File
@@ -6,7 +6,7 @@ upstream: "https://github.com/mamedev/mame"
logo: "https://raw.githubusercontent.com/mamedev/mame/master/docs/source/images/MAMElogo.svg" logo: "https://raw.githubusercontent.com/mamedev/mame/master/docs/source/images/MAMElogo.svg"
profiled_date: "2026-03-29" profiled_date: "2026-03-29"
source_commit: "9069f39340f2d2b1795df8e71bb1d3d0fbc76598" source_commit: "9069f39340f2d2b1795df8e71bb1d3d0fbc76598"
upstream_commit: "728d957663041fbc943806bf151584acd03e2160" upstream_commit: "f34f02505e32c1993c6a782b6814232cbfc74e36"
core_version: "0.289" core_version: "0.289"
display_name: "Arcade (MAME)" display_name: "Arcade (MAME)"
@@ -269,7 +269,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: igs-pgm system: igs-pgm
source_ref: "src/mame/igs/pgm.cpp:5548" source_ref: "src/mame/igs/pgm.cpp:5545"
contents: contents:
- name: "pgm_p02s.u20" - name: "pgm_p02s.u20"
size: 131072 size: 131072
@@ -287,17 +287,13 @@ files:
size: 2097152 size: 2097152
crc32: "45ae7159" crc32: "45ae7159"
sha1: "d3ed3ff3464557fd0df6b069b2e431528b0ebfa8" sha1: "d3ed3ff3464557fd0df6b069b2e431528b0ebfa8"
- name: "pal.u7"
size: 279
crc32: "bfa23d78"
sha1: "32078fec6f3747a081b8dc735452dc1dae4b3d51"
# Sega NAOMI / NAOMI 2 / GD-ROM / Atomiswave # Sega NAOMI / NAOMI 2 / GD-ROM / Atomiswave
- name: naomi.zip - name: naomi.zip
required: true required: true
category: bios_zip category: bios_zip
system: sega-naomi system: sega-naomi
source_ref: "src/mame/sega/naomi.cpp:10918" source_ref: "src/mame/sega/naomi.cpp:10917"
contents: contents:
- name: "epr-21576h.ic27" - name: "epr-21576h.ic27"
size: 2097152 size: 2097152
@@ -434,7 +430,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: sega-naomi2 system: sega-naomi2
source_ref: "src/mame/sega/naomi.cpp:10923" source_ref: "src/mame/sega/naomi.cpp:10922"
contents: contents:
- name: "epr-23605c.ic27" - name: "epr-23605c.ic27"
size: 2097152 size: 2097152
@@ -505,7 +501,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: sega-naomigd system: sega-naomigd
source_ref: "src/mame/sega/naomi.cpp:10924" source_ref: "src/mame/sega/naomi.cpp:10923"
contents: contents:
- name: "epr-21576e.ic27" - name: "epr-21576e.ic27"
size: 2097152 size: 2097152
@@ -556,7 +552,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: sammy-atomiswave system: sammy-atomiswave
source_ref: "src/mame/sega/dc_atomiswave.cpp:1519" source_ref: "src/mame/sega/dc_atomiswave.cpp:1480"
contents: contents:
- name: "bios0.ic23" - name: "bios0.ic23"
size: 131072 size: 131072
@@ -574,7 +570,7 @@ files:
- name: hod2bios.zip - name: hod2bios.zip
required: false required: false
category: bios_zip category: bios_zip
source_ref: "src/mame/sega/naomi.cpp:10919" source_ref: "src/mame/sega/naomi.cpp:10918"
note: "game-specific BIOS for House of the Dead 2" note: "game-specific BIOS for House of the Dead 2"
contents: contents:
- name: "epr-21331.ic27" - name: "epr-21331.ic27"
@@ -609,7 +605,7 @@ files:
- name: f355dlx.zip - name: f355dlx.zip
required: false required: false
category: bios_zip category: bios_zip
source_ref: "src/mame/sega/naomi.cpp:10920" source_ref: "src/mame/sega/naomi.cpp:10919"
contents: contents:
- name: "epr-21864.ic27" - name: "epr-21864.ic27"
size: 2097152 size: 2097152
@@ -643,7 +639,7 @@ files:
- name: f355bios.zip - name: f355bios.zip
required: false required: false
category: bios_zip category: bios_zip
source_ref: "src/mame/sega/naomi.cpp:10921" source_ref: "src/mame/sega/naomi.cpp:10920"
contents: contents:
- name: "epr-22851.ic27" - name: "epr-22851.ic27"
size: 2097152 size: 2097152
@@ -669,7 +665,7 @@ files:
- name: airlbios.zip - name: airlbios.zip
required: false required: false
category: bios_zip category: bios_zip
source_ref: "src/mame/sega/naomi.cpp:10922" source_ref: "src/mame/sega/naomi.cpp:10921"
contents: contents:
- name: "epr-21802.ic27" - name: "epr-21802.ic27"
size: 2097152 size: 2097152
@@ -693,7 +689,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: sega-stv system: sega-stv
source_ref: "src/mame/sega/stv.cpp:4352" source_ref: "src/mame/sega/stv.cpp:3948"
contents: contents:
- name: "epr-23603.ic8" - name: "epr-23603.ic8"
size: 524288 size: 524288
@@ -760,7 +756,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: sega-lindbergh system: sega-lindbergh
source_ref: "src/mame/sega/lindbergh.cpp:640" source_ref: "src/mame/sega/lindbergh.cpp:639"
contents: contents:
- name: "6.0.0010a.bin" - name: "6.0.0010a.bin"
description: "6.0.0010 alternate version" description: "6.0.0010 alternate version"
@@ -876,7 +872,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: sega-chihiro system: sega-chihiro
source_ref: "src/mame/sega/chihiro.cpp:2611" source_ref: "src/mame/sega/chihiro.cpp:2614"
contents: contents:
- name: "chihiro_xbox_bios.bin" - name: "chihiro_xbox_bios.bin"
size: 524288 size: 524288
@@ -1230,7 +1226,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: nintendo-playch10 system: nintendo-playch10
source_ref: "src/mame/nintendo/playch10.cpp:2981" source_ref: "src/mame/nintendo/playch10.cpp:2980"
contents: contents:
- name: "pch1-c__8t_e-2.8t" - name: "pch1-c__8t_e-2.8t"
size: 16384 size: 16384
@@ -1363,7 +1359,7 @@ files:
- name: aleck64.zip - name: aleck64.zip
required: true required: true
category: bios_zip category: bios_zip
source_ref: "src/mame/nintendo/aleck64.cpp:1418" source_ref: "src/mame/nintendo/aleck64.cpp:1417"
contents: contents:
- name: "pifdata.bin" - name: "pifdata.bin"
size: 2048 size: 2048
@@ -1383,7 +1379,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: philips-cdi system: philips-cdi
source_ref: "src/mame/philips/cdi.cpp:1059" source_ref: "src/mame/philips/cdi.cpp:922"
contents: contents:
- name: "cdi200.rom" - name: "cdi200.rom"
description: "Magnavox CD-i 200" description: "Magnavox CD-i 200"
@@ -1418,7 +1414,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: namco-system246 system: namco-system246
source_ref: "src/mame/namco/namcops2.cpp:1725" source_ref: "src/mame/namco/namcops2.cpp:1716"
contents: contents:
- name: "r27v1602f.7d" - name: "r27v1602f.7d"
size: 2097152 size: 2097152
@@ -1429,7 +1425,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: namco-system256 system: namco-system256
source_ref: "src/mame/namco/namcops2.cpp:1770" source_ref: "src/mame/namco/namcops2.cpp:1761"
contents: contents:
- name: "r27v1602f.8g" - name: "r27v1602f.8g"
size: 2097152 size: 2097152
@@ -1440,7 +1436,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: namco-system573 system: namco-system573
source_ref: "src/mame/konami/ksys573.cpp:6429" source_ref: "src/mame/konami/ksys573.cpp:6415"
contents: contents:
- name: "700a01.22g" - name: "700a01.22g"
description: "Standard" description: "Standard"
@@ -1477,7 +1473,7 @@ files:
- name: konamigx.zip - name: konamigx.zip
required: true required: true
category: bios_zip category: bios_zip
source_ref: "src/mame/konami/konamigx.cpp:4202" source_ref: "src/mame/konami/konamigx.cpp:4191"
contents: contents:
- name: "300a01.34k" - name: "300a01.34k"
size: 131072 size: 131072
@@ -1488,7 +1484,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: konami-gv system: konami-gv
source_ref: "src/mame/konami/konamigv.cpp:1469" source_ref: "src/mame/konami/konamigv.cpp:1447"
contents: contents:
- name: "999a01.7e" - name: "999a01.7e"
size: 524288 size: 524288
@@ -1498,7 +1494,7 @@ files:
- name: kviper.zip - name: kviper.zip
required: true required: true
category: bios_zip category: bios_zip
source_ref: "src/mame/konami/viper.cpp:3225" source_ref: "src/mame/konami/viper.cpp:3213"
contents: contents:
- name: "941b01.u25" - name: "941b01.u25"
size: 262144 size: 262144
@@ -1517,7 +1513,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: konami-twinkle system: konami-twinkle
source_ref: "src/mame/konami/twinkle.cpp:1665" source_ref: "src/mame/konami/twinkle.cpp:1643"
contents: contents:
- name: "863a03.7b" - name: "863a03.7b"
size: 524288 size: 524288
@@ -1585,7 +1581,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: capcom-zn1 system: capcom-zn1
source_ref: "src/mame/sony/zn.cpp:5984" source_ref: "src/mame/sony/zn.cpp:5969"
contents: contents:
- name: "m534002c-13.ic353" - name: "m534002c-13.ic353"
description: "japan" description: "japan"
@@ -1606,7 +1602,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: capcom-zn2 system: capcom-zn2
source_ref: "src/mame/sony/zn.cpp:6070" source_ref: "src/mame/sony/zn.cpp:6055"
contents: contents:
- name: "m534002c-59.ic353" - name: "m534002c-59.ic353"
size: 524288 size: 524288
@@ -1620,7 +1616,7 @@ files:
- name: coh1000t.zip - name: coh1000t.zip
required: true required: true
category: bios_zip category: bios_zip
source_ref: "src/mame/sony/zn.cpp:6004" source_ref: "src/mame/sony/zn.cpp:5989"
contents: contents:
- name: "m534002c-14.ic353" - name: "m534002c-14.ic353"
size: 524288 size: 524288
@@ -1634,7 +1630,7 @@ files:
- name: coh1000a.zip - name: coh1000a.zip
required: true required: true
category: bios_zip category: bios_zip
source_ref: "src/mame/sony/zn.cpp:6030" source_ref: "src/mame/sony/zn.cpp:6015"
contents: contents:
- name: "m534002c-15.ic353" - name: "m534002c-15.ic353"
size: 524288 size: 524288
@@ -1648,7 +1644,7 @@ files:
- name: coh1000w.zip - name: coh1000w.zip
required: true required: true
category: bios_zip category: bios_zip
source_ref: "src/mame/sony/zn.cpp:6039" source_ref: "src/mame/sony/zn.cpp:6024"
contents: contents:
- name: "msm27c402zb.ic353" - name: "msm27c402zb.ic353"
size: 524288 size: 524288
@@ -1662,7 +1658,7 @@ files:
- name: coh1001l.zip - name: coh1001l.zip
required: true required: true
category: bios_zip category: bios_zip
source_ref: "src/mame/sony/zn.cpp:6043" source_ref: "src/mame/sony/zn.cpp:6028"
contents: contents:
- name: "m534002c-17.ic353" - name: "m534002c-17.ic353"
size: 524288 size: 524288
@@ -1676,7 +1672,7 @@ files:
- name: coh1002v.zip - name: coh1002v.zip
required: true required: true
category: bios_zip category: bios_zip
source_ref: "src/mame/sony/zn.cpp:6047" source_ref: "src/mame/sony/zn.cpp:6032"
contents: contents:
- name: "coh-1002v.ic353" - name: "coh-1002v.ic353"
size: 524288 size: 524288
@@ -1690,7 +1686,7 @@ files:
- name: coh1002e.zip - name: coh1002e.zip
required: true required: true
category: bios_zip category: bios_zip
source_ref: "src/mame/sony/zn.cpp:6053" source_ref: "src/mame/sony/zn.cpp:6038"
contents: contents:
- name: "m27c402cz-54.ic353" - name: "m27c402cz-54.ic353"
size: 524288 size: 524288
@@ -1704,7 +1700,7 @@ files:
- name: coh1002m.zip - name: coh1002m.zip
required: true required: true
category: bios_zip category: bios_zip
source_ref: "src/mame/sony/zn.cpp:6104" source_ref: "src/mame/sony/zn.cpp:6089"
contents: contents:
- name: "m534002c-61.ic353" - name: "m534002c-61.ic353"
size: 524288 size: 524288
@@ -1783,7 +1779,7 @@ files:
- name: taitotz.zip - name: taitotz.zip
required: true required: true
category: bios_zip category: bios_zip
source_ref: "src/mame/taito/taitotz.cpp:3018" source_ref: "src/mame/taito/taitotz.cpp:3016"
contents: contents:
- name: "e68-05-1.ic6" - name: "e68-05-1.ic6"
size: 524288 size: 524288
@@ -1884,7 +1880,7 @@ files:
- name: alg3do.zip - name: alg3do.zip
required: true required: true
category: bios_zip category: bios_zip
source_ref: "src/mame/misc/3do.cpp:855" source_ref: "src/mame/misc/3do.cpp:573"
contents: contents:
- name: "saot_rom2.bin" - name: "saot_rom2.bin"
size: 524288 size: 524288
@@ -2111,7 +2107,7 @@ files:
- name: maxaflex.zip - name: maxaflex.zip
required: true required: true
category: bios_zip category: bios_zip
source_ref: "src/mame/atari/maxaflex.cpp:448" source_ref: "src/mame/atari/maxaflex.cpp:447"
contents: contents:
- name: "atarixl.rom" - name: "atarixl.rom"
size: 16384 size: 16384
@@ -2130,7 +2126,7 @@ files:
- name: crysbios.zip - name: crysbios.zip
required: true required: true
category: bios_zip category: bios_zip
source_ref: "src/mame/misc/crystal.cpp:871" source_ref: "src/mame/misc/crystal.cpp:845"
contents: contents:
- name: "mx27l1000.u14" - name: "mx27l1000.u14"
description: "AMG0110B PCB" description: "AMG0110B PCB"
@@ -2153,7 +2149,7 @@ files:
- name: aristmk5.zip - name: aristmk5.zip
required: true required: true
category: bios_zip category: bios_zip
source_ref: "src/mame/acorn/aristmk5.cpp:9301" source_ref: "src/mame/acorn/aristmk5.cpp:9298"
contents: contents:
- name: "setchip v4.04.09.u7" - name: "setchip v4.04.09.u7"
size: 524288 size: 524288
@@ -2846,7 +2842,7 @@ files:
- name: cedmag.zip - name: cedmag.zip
required: true required: true
category: bios_zip category: bios_zip
source_ref: "src/mame/efo/cedar_magnet.cpp:1316" source_ref: "src/mame/efo/cedar_magnet.cpp:1166"
contents: contents:
- name: "magnet-master-vid-e03.bin" - name: "magnet-master-vid-e03.bin"
size: 8192 size: 8192
@@ -3186,7 +3182,7 @@ files:
- name: v4bios.zip - name: v4bios.zip
required: true required: true
category: bios_zip category: bios_zip
source_ref: "src/mame/barcrest/mpu4vid.cpp:8699" source_ref: "src/mame/barcrest/mpu4vid.cpp:8698"
contents: contents:
- name: "vid.p1" - name: "vid.p1"
size: 65536 size: 65536
+31 -35
View File
@@ -5,7 +5,7 @@ source: "https://github.com/libretro/mame"
upstream: "https://github.com/mamedev/mame" upstream: "https://github.com/mamedev/mame"
profiled_date: "2026-03-24" profiled_date: "2026-03-24"
source_commit: "9069f39340f2d2b1795df8e71bb1d3d0fbc76598" source_commit: "9069f39340f2d2b1795df8e71bb1d3d0fbc76598"
upstream_commit: "728d957663041fbc943806bf151584acd03e2160" upstream_commit: "f34f02505e32c1993c6a782b6814232cbfc74e36"
core_version: "0.289" core_version: "0.289"
display_name: "Arcade (MAME/Arcade)" display_name: "Arcade (MAME/Arcade)"
@@ -219,7 +219,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: igs-pgm system: igs-pgm
source_ref: "src/mame/igs/pgm.cpp:5548" source_ref: "src/mame/igs/pgm.cpp:5545"
contents: contents:
- name: "pgm_p02s.u20" - name: "pgm_p02s.u20"
size: 131072 size: 131072
@@ -237,17 +237,13 @@ files:
size: 2097152 size: 2097152
crc32: "45ae7159" crc32: "45ae7159"
sha1: "d3ed3ff3464557fd0df6b069b2e431528b0ebfa8" sha1: "d3ed3ff3464557fd0df6b069b2e431528b0ebfa8"
- name: "pal.u7"
size: 279
crc32: "bfa23d78"
sha1: "32078fec6f3747a081b8dc735452dc1dae4b3d51"
# Sega NAOMI / NAOMI 2 / GD-ROM / Atomiswave # Sega NAOMI / NAOMI 2 / GD-ROM / Atomiswave
- name: naomi.zip - name: naomi.zip
required: true required: true
category: bios_zip category: bios_zip
system: sega-naomi system: sega-naomi
source_ref: "src/mame/sega/naomi.cpp:10918" source_ref: "src/mame/sega/naomi.cpp:10917"
contents: contents:
- name: "epr-21576h.ic27" - name: "epr-21576h.ic27"
size: 2097152 size: 2097152
@@ -384,7 +380,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: sega-naomi2 system: sega-naomi2
source_ref: "src/mame/sega/naomi.cpp:10923" source_ref: "src/mame/sega/naomi.cpp:10922"
contents: contents:
- name: "epr-23605c.ic27" - name: "epr-23605c.ic27"
size: 2097152 size: 2097152
@@ -455,7 +451,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: sega-naomigd system: sega-naomigd
source_ref: "src/mame/sega/naomi.cpp:10924" source_ref: "src/mame/sega/naomi.cpp:10923"
contents: contents:
- name: "epr-21576e.ic27" - name: "epr-21576e.ic27"
size: 2097152 size: 2097152
@@ -506,7 +502,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: sammy-atomiswave system: sammy-atomiswave
source_ref: "src/mame/sega/dc_atomiswave.cpp:1519" source_ref: "src/mame/sega/dc_atomiswave.cpp:1480"
contents: contents:
- name: "bios0.ic23" - name: "bios0.ic23"
size: 131072 size: 131072
@@ -525,7 +521,7 @@ files:
required: false required: false
category: bios_zip category: bios_zip
system: sega-naomi system: sega-naomi
source_ref: "src/mame/sega/naomi.cpp:10919" source_ref: "src/mame/sega/naomi.cpp:10918"
note: "game-specific BIOS for House of the Dead 2" note: "game-specific BIOS for House of the Dead 2"
contents: contents:
- name: "epr-21331.ic27" - name: "epr-21331.ic27"
@@ -561,7 +557,7 @@ files:
required: false required: false
category: bios_zip category: bios_zip
system: sega-naomi system: sega-naomi
source_ref: "src/mame/sega/naomi.cpp:10920" source_ref: "src/mame/sega/naomi.cpp:10919"
contents: contents:
- name: "epr-21864.ic27" - name: "epr-21864.ic27"
size: 2097152 size: 2097152
@@ -596,7 +592,7 @@ files:
required: false required: false
category: bios_zip category: bios_zip
system: sega-naomi system: sega-naomi
source_ref: "src/mame/sega/naomi.cpp:10921" source_ref: "src/mame/sega/naomi.cpp:10920"
contents: contents:
- name: "epr-22851.ic27" - name: "epr-22851.ic27"
size: 2097152 size: 2097152
@@ -623,7 +619,7 @@ files:
required: false required: false
category: bios_zip category: bios_zip
system: sega-naomi system: sega-naomi
source_ref: "src/mame/sega/naomi.cpp:10922" source_ref: "src/mame/sega/naomi.cpp:10921"
contents: contents:
- name: "epr-21802.ic27" - name: "epr-21802.ic27"
size: 2097152 size: 2097152
@@ -647,7 +643,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: sega-stv system: sega-stv
source_ref: "src/mame/sega/stv.cpp:4352" source_ref: "src/mame/sega/stv.cpp:3948"
contents: contents:
- name: "epr-23603.ic8" - name: "epr-23603.ic8"
size: 524288 size: 524288
@@ -1004,7 +1000,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: nintendo-playch10 system: nintendo-playch10
source_ref: "src/mame/nintendo/playch10.cpp:2981" source_ref: "src/mame/nintendo/playch10.cpp:2980"
contents: contents:
- name: "pch1-c__8t_e-2.8t" - name: "pch1-c__8t_e-2.8t"
size: 16384 size: 16384
@@ -1117,7 +1113,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: nintendo-aleck64 system: nintendo-aleck64
source_ref: "src/mame/nintendo/aleck64.cpp:1418" source_ref: "src/mame/nintendo/aleck64.cpp:1417"
contents: contents:
- name: "pifdata.bin" - name: "pifdata.bin"
size: 2048 size: 2048
@@ -1137,7 +1133,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: philips-cdi system: philips-cdi
source_ref: "src/mame/philips/cdi.cpp:1059" source_ref: "src/mame/philips/cdi.cpp:922"
contents: contents:
- name: "cdi200.rom" - name: "cdi200.rom"
description: "Magnavox CD-i 200" description: "Magnavox CD-i 200"
@@ -1172,7 +1168,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: namco-system573 system: namco-system573
source_ref: "src/mame/konami/ksys573.cpp:6429" source_ref: "src/mame/konami/ksys573.cpp:6415"
contents: contents:
- name: "700a01.22g" - name: "700a01.22g"
description: "Standard" description: "Standard"
@@ -1210,7 +1206,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: konami-gx system: konami-gx
source_ref: "src/mame/konami/konamigx.cpp:4202" source_ref: "src/mame/konami/konamigx.cpp:4191"
contents: contents:
- name: "300a01.34k" - name: "300a01.34k"
size: 131072 size: 131072
@@ -1221,7 +1217,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: konami-gv system: konami-gv
source_ref: "src/mame/konami/konamigv.cpp:1469" source_ref: "src/mame/konami/konamigv.cpp:1447"
contents: contents:
- name: "999a01.7e" - name: "999a01.7e"
size: 524288 size: 524288
@@ -1232,7 +1228,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: konami-viper system: konami-viper
source_ref: "src/mame/konami/viper.cpp:3225" source_ref: "src/mame/konami/viper.cpp:3213"
contents: contents:
- name: "941b01.u25" - name: "941b01.u25"
size: 262144 size: 262144
@@ -1251,7 +1247,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: konami-twinkle system: konami-twinkle
source_ref: "src/mame/konami/twinkle.cpp:1665" source_ref: "src/mame/konami/twinkle.cpp:1643"
contents: contents:
- name: "863a03.7b" - name: "863a03.7b"
size: 524288 size: 524288
@@ -1290,7 +1286,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: capcom-zn1 system: capcom-zn1
source_ref: "src/mame/sony/zn.cpp:5984" source_ref: "src/mame/sony/zn.cpp:5969"
contents: contents:
- name: "m534002c-13.ic353" - name: "m534002c-13.ic353"
description: "japan" description: "japan"
@@ -1311,7 +1307,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: capcom-zn2 system: capcom-zn2
source_ref: "src/mame/sony/zn.cpp:6070" source_ref: "src/mame/sony/zn.cpp:6055"
contents: contents:
- name: "m534002c-59.ic353" - name: "m534002c-59.ic353"
size: 524288 size: 524288
@@ -1326,7 +1322,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: taito-fx1 system: taito-fx1
source_ref: "src/mame/sony/zn.cpp:6004" source_ref: "src/mame/sony/zn.cpp:5989"
contents: contents:
- name: "m534002c-14.ic353" - name: "m534002c-14.ic353"
size: 524288 size: 524288
@@ -1341,7 +1337,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: acclaim-zn1 system: acclaim-zn1
source_ref: "src/mame/sony/zn.cpp:6030" source_ref: "src/mame/sony/zn.cpp:6015"
contents: contents:
- name: "m534002c-15.ic353" - name: "m534002c-15.ic353"
size: 524288 size: 524288
@@ -1356,7 +1352,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: atari-zn1 system: atari-zn1
source_ref: "src/mame/sony/zn.cpp:6039" source_ref: "src/mame/sony/zn.cpp:6024"
contents: contents:
- name: "msm27c402zb.ic353" - name: "msm27c402zb.ic353"
size: 524288 size: 524288
@@ -1371,7 +1367,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: atlus-zn1 system: atlus-zn1
source_ref: "src/mame/sony/zn.cpp:6043" source_ref: "src/mame/sony/zn.cpp:6028"
contents: contents:
- name: "m534002c-17.ic353" - name: "m534002c-17.ic353"
size: 524288 size: 524288
@@ -1386,7 +1382,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: videosystem-zn1 system: videosystem-zn1
source_ref: "src/mame/sony/zn.cpp:6047" source_ref: "src/mame/sony/zn.cpp:6032"
contents: contents:
- name: "coh-1002v.ic353" - name: "coh-1002v.ic353"
size: 524288 size: 524288
@@ -1401,7 +1397,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: eighting-zn1 system: eighting-zn1
source_ref: "src/mame/sony/zn.cpp:6053" source_ref: "src/mame/sony/zn.cpp:6038"
contents: contents:
- name: "m27c402cz-54.ic353" - name: "m27c402cz-54.ic353"
size: 524288 size: 524288
@@ -1416,7 +1412,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: tecmo-tps system: tecmo-tps
source_ref: "src/mame/sony/zn.cpp:6104" source_ref: "src/mame/sony/zn.cpp:6089"
contents: contents:
- name: "m534002c-61.ic353" - name: "m534002c-61.ic353"
size: 524288 size: 524288
@@ -1767,7 +1763,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: exidy-maxaflex system: exidy-maxaflex
source_ref: "src/mame/atari/maxaflex.cpp:448" source_ref: "src/mame/atari/maxaflex.cpp:447"
contents: contents:
- name: "atarixl.rom" - name: "atarixl.rom"
size: 16384 size: 16384
@@ -1787,7 +1783,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: brezzasoft-crystal system: brezzasoft-crystal
source_ref: "src/mame/misc/crystal.cpp:871" source_ref: "src/mame/misc/crystal.cpp:845"
contents: contents:
- name: "mx27l1000.u14" - name: "mx27l1000.u14"
description: "AMG0110B PCB" description: "AMG0110B PCB"
@@ -1812,7 +1808,7 @@ files:
required: true required: true
category: bios_zip category: bios_zip
system: efo-cedar-magnet system: efo-cedar-magnet
source_ref: "src/mame/efo/cedar_magnet.cpp:1316" source_ref: "src/mame/efo/cedar_magnet.cpp:1166"
contents: contents:
- name: "magnet-master-vid-e03.bin" - name: "magnet-master-vid-e03.bin"
size: 8192 size: 8192
+1 -1
View File
@@ -215,4 +215,4 @@ files:
note: > note: >
Keyboard remapping configuration (text file). User-created file Keyboard remapping configuration (text file). User-created file
for custom keyboard layout. The core uses built-in defaults if absent. for custom keyboard layout. The core uses built-in defaults if absent.
source_ref: "keystat.c:43,113-148" source_ref: "keystat.c:46,116-151"
+6 -6
View File
@@ -20,7 +20,7 @@ notes: |
Doukutsu.exe there (nxengine/libretro/libretro.cpp:289-294). When launched with a .exe Doukutsu.exe there (nxengine/libretro/libretro.cpp:289-294). When launched with a .exe
content file, it uses the parent directory of that file (nxengine/libretro/libretro.cpp:272). content file, it uses the parent directory of that file (nxengine/libretro/libretro.cpp:272).
At startup (main.cpp:75-90), the core opens Doukutsu.exe and extracts: At startup (nxengine/main.cpp:75-90), the core opens Doukutsu.exe and extracts:
ORG music (extractorg.c), PXT sound effects (extractpxt.c), stage tile ORG music (extractorg.c), PXT sound effects (extractpxt.c), stage tile
attributes (extractstages.c), 18 credit BMPs + pixel.bmp (endpic/), attributes (extractstages.c), 18 credit BMPs + pixel.bmp (endpic/),
and the wavetable (nxengine/extract-auto/cachefiles.c:464-485). All kept in memory. and the wavetable (nxengine/extract-auto/cachefiles.c:464-485). All kept in memory.
@@ -28,7 +28,7 @@ notes: |
The data/ directory (399 files from the freeware release) is loaded into The data/ directory (399 files from the freeware release) is loaded into
an in-memory cache at init (nxengine/extract-auto/cachefiles.c:38-439, 497-540). All subsequent an in-memory cache at init (nxengine/extract-auto/cachefiles.c:38-439, 497-540). All subsequent
file access goes through this cache. The core verifies data/ by checking file access goes through this cache. The core verifies data/ by checking
for data/npc.tbl (main.cpp:47-58). for data/npc.tbl (nxengine/main.cpp:47-58).
The font is compiled-in (bitmap_font.h). The upstream standalone version The font is compiled-in (bitmap_font.h). The upstream standalone version
uses font.ttf via SDL_ttf. uses font.ttf via SDL_ttf.
@@ -45,7 +45,7 @@ files:
size: 1478656 size: 1478656
md5: "38695d3d69d7a0ada8178072dad4c58b" md5: "38695d3d69d7a0ada8178072dad4c58b"
sha1: "bb2d0441e073da9c584f23c2ad8c7ab8aac293bf" sha1: "bb2d0441e073da9c584f23c2ad8c7ab8aac293bf"
source_ref: "main.cpp:77-78 (opened for extraction), nxengine/libretro/libretro.cpp:290 (existence check)" source_ref: "nxengine/main.cpp:77-78 (opened for extraction), nxengine/libretro/libretro.cpp:278 (existence check)"
note: "Placed in system/nxengine/. The core extracts ORG music, PXT sounds, stage tile attributes, BMP graphics, and wavetable from this binary at each launch." note: "Placed in system/nxengine/. The core extracts ORG music, PXT sounds, stage tile attributes, BMP graphics, and wavetable from this binary at each launch."
- name: "data/" - name: "data/"
@@ -64,7 +64,7 @@ files:
system: cave-story system: cave-story
description: "NPC attribute table (entity behavior flags, HP, damage, display rect offsets)" description: "NPC attribute table (entity behavior flags, HP, damage, display rect offsets)"
required: true required: true
source_ref: "main.cpp:50 (existence check for data/ directory), ai/ai.cpp:56-59 (loaded for NPC properties)" source_ref: "nxengine/main.cpp:47-53 (existence check for data/ directory), nxengine/ai/ai.cpp:56-59 (loaded for NPC properties)"
note: "Located in system/nxengine/data/. Used to validate data directory presence and to load NPC behavior attributes." note: "Located in system/nxengine/data/. Used to validate data directory presence and to load NPC behavior attributes."
- name: "data/sprites.sif" - name: "data/sprites.sif"
@@ -84,5 +84,5 @@ files:
description: "Tile attribute lookup table (maps tile codes to collision/behavior attributes)" description: "Tile attribute lookup table (maps tile codes to collision/behavior attributes)"
required: false required: false
hle_fallback: true hle_fallback: true
source_ref: "map.cpp:290-303 (loaded at init, hardcoded default if missing)" source_ref: "nxengine/map.cpp:290-303 (loaded at init, hardcoded default if missing)"
note: "Not part of the freeware distribution. Generated by the standalone NXEngine extraction tool. The libretro core has hardcoded defaults in map.cpp:30." note: "Not part of the freeware distribution. Generated by the standalone NXEngine extraction tool. The libretro core has hardcoded defaults in nxengine/map.cpp:30."
+1 -1
View File
@@ -16,7 +16,7 @@ files:
required: false required: false
description: "64DD IPL ROM" description: "64DD IPL ROM"
note: "Only loaded when a 64DD disk image (.ndd) is present via subsystem API. Core fails to load disk games without it." note: "Only loaded when a 64DD disk image (.ndd) is present via subsystem API. Core fails to load disk games without it."
source_ref: "libretro/libretro.c:1021" source_ref: "libretro/libretro.c:848"
notes: notes:
hle_available: true hle_available: true
+5 -5
View File
@@ -83,7 +83,7 @@ notes: |
files: files:
- name: "<bios>.bin" - name: "<bios>.bin"
path: "pcsx2/bios/" path: "pcsx2/bios/<bios>.bin"
description: "PS2 BIOS image" description: "PS2 BIOS image"
min_size: 4194304 min_size: 4194304
max_size: 8388608 max_size: 8388608
@@ -96,7 +96,7 @@ files:
auto selection accept any size that passes the romdir check. auto selection accept any size that passes the romdir check.
- name: "<bios>.rom1" - name: "<bios>.rom1"
path: "pcsx2/bios/" path: "pcsx2/bios/<bios>.rom1"
description: "DVD player ROM" description: "DVD player ROM"
max_size: 4194304 max_size: 4194304
required: false required: false
@@ -106,7 +106,7 @@ files:
skipped with a console line when absent. skipped with a console line when absent.
- name: "<bios>.rom2" - name: "<bios>.rom2"
path: "pcsx2/bios/" path: "pcsx2/bios/<bios>.rom2"
description: "Chinese ROM extension" description: "Chinese ROM extension"
max_size: 4194304 max_size: 4194304
required: false required: false
@@ -115,7 +115,7 @@ files:
Same lookup as rom1. Present on Chinese region consoles only. Same lookup as rom1. Present on Chinese region consoles only.
- name: "<bios>.nvm" - name: "<bios>.nvm"
path: "pcsx2/bios/" path: "pcsx2/bios/<bios>.nvm"
description: "Console NVRAM (region parameters, language, iLink ID, OSD settings)" description: "Console NVRAM (region parameters, language, iLink ID, OSD settings)"
size: 1024 size: 1024
required: false required: false
@@ -127,7 +127,7 @@ files:
on save. on save.
- name: "<bios>.mec" - name: "<bios>.mec"
path: "pcsx2/bios/" path: "pcsx2/bios/<bios>.mec"
description: "Mechacon version, one u32" description: "Mechacon version, one u32"
size: 4 size: 4
required: false required: false
+5 -5
View File
@@ -12,7 +12,7 @@ systems:
- sega-megadrive - sega-megadrive
- sega-genesis - sega-genesis
- sega-megacd - sega-megacd
- sega-segacd - sega-megacd
- sega-32x - sega-32x
- sega-mastersystem - sega-mastersystem
- sega-gamegear - sega-gamegear
@@ -63,7 +63,7 @@ files:
# Mega CD / Sega CD - US region # Mega CD / Sega CD - US region
# ------------------------------------------------------- # -------------------------------------------------------
- name: "us_scd2_9306.bin" - name: "us_scd2_9306.bin"
system: sega-segacd system: sega-megacd
variant_group: "mega-cd-bios" variant_group: "mega-cd-bios"
region: [north-america] region: [north-america]
priority: 1 priority: 1
@@ -73,7 +73,7 @@ files:
source_ref: "platform/libretro/libretro.c:1273" source_ref: "platform/libretro/libretro.c:1273"
- name: "SegaCDBIOS9303.bin" - name: "SegaCDBIOS9303.bin"
system: sega-segacd system: sega-megacd
variant_group: "mega-cd-bios" variant_group: "mega-cd-bios"
region: [north-america] region: [north-america]
priority: 2 priority: 2
@@ -83,7 +83,7 @@ files:
source_ref: "platform/libretro/libretro.c:1273" source_ref: "platform/libretro/libretro.c:1273"
- name: "us_scd1_9210.bin" - name: "us_scd1_9210.bin"
system: sega-segacd system: sega-megacd
variant_group: "mega-cd-bios" variant_group: "mega-cd-bios"
region: [north-america] region: [north-america]
priority: 3 priority: 3
@@ -93,7 +93,7 @@ files:
source_ref: "platform/libretro/libretro.c:1273" source_ref: "platform/libretro/libretro.c:1273"
- name: "bios_CD_U.bin" - name: "bios_CD_U.bin"
system: sega-segacd system: sega-megacd
variant_group: "mega-cd-bios" variant_group: "mega-cd-bios"
region: [north-america] region: [north-america]
priority: 4 priority: 4
+1 -1
View File
@@ -51,5 +51,5 @@ files:
md5: "72ae1b47820fcc93cc0df9c428d0face" md5: "72ae1b47820fcc93cc0df9c428d0face"
sha1: "5f4aed208301449c2e9514edfd325fe9dead76fa" sha1: "5f4aed208301449c2e9514edfd325fe9dead76fa"
crc32: "a5751b99" crc32: "a5751b99"
source_ref: "src/d_main.c:1438-1476 (PACKAGE .wad load), libretro/libretro.c:4254-4330 (I_FindFile search), src/config.h:39 (PACKAGE=prboom)" source_ref: "src/d_main.c:1449-1465 (PACKAGE .wad load), libretro/libretro.c:4171-4252 (I_FindFile search), src/config.h:39 (PACKAGE=prboom)"
note: "Searched in system/prboom/ then system/. Falls back to internal defaults if missing, but menus and HUD may render incorrectly without it." note: "Searched in system/prboom/ then system/. Falls back to internal defaults if missing, but menus and HUD may render incorrectly without it."
+5 -5
View File
@@ -56,7 +56,7 @@ notes: |
capsimg.so/capsimg.dll is an optional shared library for IPF floppy image capsimg.so/capsimg.dll is an optional shared library for IPF floppy image
support (SPS/CAPS format). Not a BIOS file, loaded dynamically at runtime. support (SPS/CAPS format). Not a BIOS file, loaded dynamically at runtime.
Source: libretro/libretro-core.h:140-245, libretro/libretro-core.c:5612-5752, Source: libretro/libretro-core.h:140-245, libretro/libretro-core.c:5609-5777,
libretro/libretro-core.c:6044-6093 libretro/libretro-core.c:6044-6093
files: files:
@@ -243,20 +243,20 @@ files:
system: commodore-amiga system: commodore-amiga
required: false required: false
note: "Amiga Forever ROM decryption key. Copied to saves/WHDLoad/Devs/Kickstarts/ for WHDLoad." note: "Amiga Forever ROM decryption key. Copied to saves/WHDLoad/Devs/Kickstarts/ for WHDLoad."
source_ref: "libretro/libretro-core.c:5969-5980" source_ref: "libretro/libretro-core.c:5963-5978"
- name: "WHDLoad.key" - name: "WHDLoad.key"
system: commodore-amiga system: commodore-amiga
required: false required: false
unsourceable: "per-user signed registration key, never distributed generically, WHDLoad free since v18.2" unsourceable: "per-user signed registration key, never distributed generically, WHDLoad free since v18.2"
note: "WHDLoad license key. Copied to saves/WHDLoad/L/ for registered WHDLoad use." note: "WHDLoad license key. Copied to saves/WHDLoad/L/ for registered WHDLoad use."
source_ref: "libretro/libretro-core.c:5985-5998" source_ref: "libretro/libretro-core.c:5979-5994"
- name: "WHDLoad.prefs" - name: "WHDLoad.prefs"
system: commodore-amiga system: commodore-amiga
required: false required: false
note: "WHDLoad preferences. Copied to saves/WHDLoad/S/ when present." note: "WHDLoad preferences. Copied to saves/WHDLoad/S/ when present."
source_ref: "libretro/libretro-core.c:6000-6023" source_ref: "libretro/libretro-core.c:5995-6018"
model_kickstart_map: model_kickstart_map:
A500: {kickstart: "kick34005.A500", ext: null} A500: {kickstart: "kick34005.A500", ext: null}
@@ -290,4 +290,4 @@ platform_details:
amiga_forever: "amiga-os-{version}.rom" amiga_forever: "amiga-os-{version}.rom"
tosec: "Kickstart v{version} rev {rev} ({year})(Commodore)({model}).rom" tosec: "Kickstart v{version} rev {rev} ({year})(Commodore)({model}).rom"
scan_prefixes: ["kick", "amiga-os", "KS ROM"] scan_prefixes: ["kick", "amiga-os", "KS ROM"]
source_ref: "libretro/libretro-core.c:2651-2678" source_ref: "libretro/libretro-core.c:2650-2677"
+6 -6
View File
@@ -60,8 +60,8 @@ notes: |
capsimg.so/capsimg.dll is an optional shared library for IPF floppy image capsimg.so/capsimg.dll is an optional shared library for IPF floppy image
support (SPS/CAPS format). Not a BIOS file, loaded dynamically at runtime. support (SPS/CAPS format). Not a BIOS file, loaded dynamically at runtime.
Source: libretro/libretro-core.h:140-245, libretro/libretro-core.c:5384-5550, Source: libretro/libretro-core.h:140-245, libretro/libretro-core.c:5533-5701,
libretro/libretro-core.c:5636-5795 libretro/libretro-core.c:5788-5879
files: files:
# ------------------------------------------------------- # -------------------------------------------------------
@@ -247,20 +247,20 @@ files:
system: commodore-amiga system: commodore-amiga
required: false required: false
note: "Amiga Forever ROM decryption key. Copied to saves/WHDLoad/Devs/Kickstarts/ for WHDLoad." note: "Amiga Forever ROM decryption key. Copied to saves/WHDLoad/Devs/Kickstarts/ for WHDLoad."
source_ref: "libretro/libretro-core.c:5735-5746" source_ref: "libretro/libretro-core.c:5887-5902"
- name: "WHDLoad.key" - name: "WHDLoad.key"
system: commodore-amiga system: commodore-amiga
required: false required: false
unsourceable: "per-user signed registration key, never distributed generically, WHDLoad free since v18.2" unsourceable: "per-user signed registration key, never distributed generically, WHDLoad free since v18.2"
note: "WHDLoad license key. Copied to saves/WHDLoad/L/ for registered WHDLoad use." note: "WHDLoad license key. Copied to saves/WHDLoad/L/ for registered WHDLoad use."
source_ref: "libretro/libretro-core.c:5751-5764" source_ref: "libretro/libretro-core.c:5903-5918"
- name: "WHDLoad.prefs" - name: "WHDLoad.prefs"
system: commodore-amiga system: commodore-amiga
required: false required: false
note: "WHDLoad preferences. Copied to saves/WHDLoad/S/ when present." note: "WHDLoad preferences. Copied to saves/WHDLoad/S/ when present."
source_ref: "libretro/libretro-core.c:5768-5791" source_ref: "libretro/libretro-core.c:5919-5942"
model_kickstart_map: model_kickstart_map:
A500: {kickstart: "kick34005.A500", ext: null} A500: {kickstart: "kick34005.A500", ext: null}
@@ -294,4 +294,4 @@ platform_details:
amiga_forever: "amiga-os-{version}.rom" amiga_forever: "amiga-os-{version}.rom"
tosec: "Kickstart v{version} rev {rev} ({year})(Commodore)({model}).rom" tosec: "Kickstart v{version} rev {rev} ({year})(Commodore)({model}).rom"
scan_prefixes: ["kick", "amiga-os", "KS ROM"] scan_prefixes: ["kick", "amiga-os", "KS ROM"]
source_ref: "libretro/libretro-core.c:2577-2616" source_ref: "libretro/libretro-core.c:2629-2656"
+3 -3
View File
@@ -20,19 +20,19 @@ mode: standalone
files: files:
- name: "prod.keys" - name: "prod.keys"
required: true required: true
path: "switch/" path: "switch/prod.keys"
note: "Switch production keys for NCA decryption" note: "Switch production keys for NCA decryption"
source_ref: "src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs:239" source_ref: "src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs:239"
- name: "title.keys" - name: "title.keys"
required: false required: false
path: "switch/" path: "switch/title.keys"
note: "Per-title decryption keys, needed for specific game content" note: "Per-title decryption keys, needed for specific game content"
source_ref: "src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs:240" source_ref: "src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs:240"
- name: "console.keys" - name: "console.keys"
required: false required: false
path: "switch/" path: "switch/console.keys"
note: "Console-specific keys for encrypted content" note: "Console-specific keys for encrypted content"
source_ref: "src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs:241" source_ref: "src/Ryujinx.HLE/FileSystem/VirtualFileSystem.cs:241"
+26 -26
View File
@@ -48,20 +48,20 @@ files:
- name: "libSceLibcInternal.sprx" - name: "libSceLibcInternal.sprx"
required: false required: false
hle_fallback: true hle_fallback: true
path: "shadps4/sys_modules/" path: "shadps4/sys_modules/libSceLibcInternal.sprx"
description: "PS4 C standard library" description: "PS4 C standard library"
source_ref: "src/core/linker.cpp:80-91" source_ref: "src/core/linker.cpp:80-91"
- name: "libSceNgs2.sprx" - name: "libSceNgs2.sprx"
required: false required: false
hle_fallback: true hle_fallback: true
path: "shadps4/sys_modules/" path: "shadps4/sys_modules/libSceNgs2.sprx"
description: "PS4 next-generation audio system" description: "PS4 next-generation audio system"
source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:217" source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:217"
- name: "libSceUlt.sprx" - name: "libSceUlt.sprx"
required: false required: false
path: "shadps4/sys_modules/" path: "shadps4/sys_modules/libSceUlt.sprx"
description: "PS4 user-level threading library" description: "PS4 user-level threading library"
note: "No HLE implementation; the module is stubbed when absent." note: "No HLE implementation; the module is stubbed when absent."
source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:218" source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:218"
@@ -69,13 +69,13 @@ files:
- name: "libSceRtc.sprx" - name: "libSceRtc.sprx"
required: false required: false
hle_fallback: true hle_fallback: true
path: "shadps4/sys_modules/" path: "shadps4/sys_modules/libSceRtc.sprx"
description: "PS4 real-time clock library" description: "PS4 real-time clock library"
source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:221" source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:221"
- name: "libSceJpegDec.sprx" - name: "libSceJpegDec.sprx"
required: false required: false
path: "shadps4/sys_modules/" path: "shadps4/sys_modules/libSceJpegDec.sprx"
description: "PS4 JPEG decoder" description: "PS4 JPEG decoder"
note: "No HLE implementation; the module is stubbed when absent." note: "No HLE implementation; the module is stubbed when absent."
source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:222" source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:222"
@@ -83,83 +83,83 @@ files:
- name: "libSceJpegEnc.sprx" - name: "libSceJpegEnc.sprx"
required: false required: false
hle_fallback: true hle_fallback: true
path: "shadps4/sys_modules/" path: "shadps4/sys_modules/libSceJpegEnc.sprx"
description: "PS4 JPEG encoder" description: "PS4 JPEG encoder"
source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:223" source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:223"
- name: "libScePngEnc.sprx" - name: "libScePngEnc.sprx"
required: false required: false
hle_fallback: true hle_fallback: true
path: "shadps4/sys_modules/" path: "shadps4/sys_modules/libScePngEnc.sprx"
description: "PS4 PNG encoder" description: "PS4 PNG encoder"
source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:225" source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:225"
- name: "libSceJson.sprx" - name: "libSceJson.sprx"
required: false required: false
path: "shadps4/sys_modules/" path: "shadps4/sys_modules/libSceJson.sprx"
description: "PS4 JSON parser" description: "PS4 JSON parser"
note: "No HLE implementation; the module is stubbed when absent." note: "No HLE implementation; the module is stubbed when absent."
source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:226" source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:226"
- name: "libSceJson2.sprx" - name: "libSceJson2.sprx"
required: false required: false
path: "shadps4/sys_modules/" path: "shadps4/sys_modules/libSceJson2.sprx"
description: "PS4 JSON v2 parser" description: "PS4 JSON v2 parser"
note: "No HLE implementation; the module is stubbed when absent." note: "No HLE implementation; the module is stubbed when absent."
source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:227" source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:227"
- name: "libSceCesCs.sprx" - name: "libSceCesCs.sprx"
required: false required: false
path: "shadps4/sys_modules/" path: "shadps4/sys_modules/libSceCesCs.sprx"
description: "PS4 character encoding conversion" description: "PS4 character encoding conversion"
note: "No HLE implementation; the module is stubbed when absent." note: "No HLE implementation; the module is stubbed when absent."
source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:228" source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:228"
- name: "libSceAt9Enc.sprx" - name: "libSceAt9Enc.sprx"
required: false required: false
path: "shadps4/sys_modules/" path: "shadps4/sys_modules/libSceAt9Enc.sprx"
description: "PS4 ATRAC9 encoder" description: "PS4 ATRAC9 encoder"
note: "No HLE implementation; the module is stubbed when absent." note: "No HLE implementation; the module is stubbed when absent."
source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:229" source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:229"
- name: "libSceAudiodec.sprx" - name: "libSceAudiodec.sprx"
required: false required: false
path: "shadps4/sys_modules/" path: "shadps4/sys_modules/libSceAudiodec.sprx"
description: "PS4 audio decoder" description: "PS4 audio decoder"
note: "No HLE implementation; the module is stubbed when absent." note: "No HLE implementation; the module is stubbed when absent."
source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:230" source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:230"
- name: "libSceAudiodecCpu.sprx" - name: "libSceAudiodecCpu.sprx"
required: false required: false
path: "shadps4/sys_modules/" path: "shadps4/sys_modules/libSceAudiodecCpu.sprx"
description: "PS4 CPU audio decoder" description: "PS4 CPU audio decoder"
note: "No HLE implementation; the module is stubbed when absent." note: "No HLE implementation; the module is stubbed when absent."
source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:231" source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:231"
- name: "libSceAudiodecCpuDdp.sprx" - name: "libSceAudiodecCpuDdp.sprx"
required: false required: false
path: "shadps4/sys_modules/" path: "shadps4/sys_modules/libSceAudiodecCpuDdp.sprx"
description: "PS4 CPU Dolby Digital Plus decoder" description: "PS4 CPU Dolby Digital Plus decoder"
note: "No HLE implementation; the module is stubbed when absent." note: "No HLE implementation; the module is stubbed when absent."
source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:232" source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:232"
- name: "libSceAudiodecCpuM4aac.sprx" - name: "libSceAudiodecCpuM4aac.sprx"
required: false required: false
path: "shadps4/sys_modules/" path: "shadps4/sys_modules/libSceAudiodecCpuM4aac.sprx"
description: "PS4 CPU AAC decoder" description: "PS4 CPU AAC decoder"
note: "No HLE implementation; the module is stubbed when absent." note: "No HLE implementation; the module is stubbed when absent."
source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:233" source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:233"
- name: "libSceAudiodecCpuDtsHdLbr.sprx" - name: "libSceAudiodecCpuDtsHdLbr.sprx"
required: false required: false
path: "shadps4/sys_modules/" path: "shadps4/sys_modules/libSceAudiodecCpuDtsHdLbr.sprx"
description: "PS4 CPU DTS-HD LBR decoder" description: "PS4 CPU DTS-HD LBR decoder"
note: "No HLE implementation; the module is stubbed when absent." note: "No HLE implementation; the module is stubbed when absent."
source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:234" source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:234"
- name: "libSceAudiodecCpuHevag.sprx" - name: "libSceAudiodecCpuHevag.sprx"
required: false required: false
path: "shadps4/sys_modules/" path: "shadps4/sys_modules/libSceAudiodecCpuHevag.sprx"
description: "PS4 CPU HEVAG decoder" description: "PS4 CPU HEVAG decoder"
note: "No HLE implementation; the module is stubbed when absent." note: "No HLE implementation; the module is stubbed when absent."
source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:235" source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:235"
@@ -167,34 +167,34 @@ files:
- name: "libSceFont.sprx" - name: "libSceFont.sprx"
required: false required: false
hle_fallback: true hle_fallback: true
path: "shadps4/sys_modules/" path: "shadps4/sys_modules/libSceFont.sprx"
description: "PS4 font rendering library" description: "PS4 font rendering library"
source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:236" source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:236"
- name: "libSceFontFt.sprx" - name: "libSceFontFt.sprx"
required: false required: false
hle_fallback: true hle_fallback: true
path: "shadps4/sys_modules/" path: "shadps4/sys_modules/libSceFontFt.sprx"
description: "PS4 FreeType font library" description: "PS4 FreeType font library"
source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:237" source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:237"
- name: "libSceFreeTypeOt.sprx" - name: "libSceFreeTypeOt.sprx"
required: false required: false
path: "shadps4/sys_modules/" path: "shadps4/sys_modules/libSceFreeTypeOt.sprx"
description: "PS4 FreeType OpenType library" description: "PS4 FreeType OpenType library"
note: "No HLE implementation; the module is stubbed when absent." note: "No HLE implementation; the module is stubbed when absent."
source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:238" source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:238"
- name: "libSceFreeTypeOl.sprx" - name: "libSceFreeTypeOl.sprx"
required: false required: false
path: "shadps4/sys_modules/" path: "shadps4/sys_modules/libSceFreeTypeOl.sprx"
description: "PS4 FreeType outline library" description: "PS4 FreeType outline library"
note: "No HLE implementation; the module is stubbed when absent." note: "No HLE implementation; the module is stubbed when absent."
source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:239" source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:239"
- name: "libSceFreeTypeOptOl.sprx" - name: "libSceFreeTypeOptOl.sprx"
required: false required: false
path: "shadps4/sys_modules/" path: "shadps4/sys_modules/libSceFreeTypeOptOl.sprx"
description: "PS4 FreeType optimised outline library" description: "PS4 FreeType optimised outline library"
note: "No HLE implementation; the module is stubbed when absent." note: "No HLE implementation; the module is stubbed when absent."
source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:240" source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:240"
@@ -202,13 +202,13 @@ files:
- name: "libSceRudp.sprx" - name: "libSceRudp.sprx"
required: false required: false
hle_fallback: true hle_fallback: true
path: "shadps4/sys_modules/" path: "shadps4/sys_modules/libSceRudp.sprx"
description: "PS4 reliable UDP library" description: "PS4 reliable UDP library"
source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:243" source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:243"
- name: "libSceWkFontConfig.sprx" - name: "libSceWkFontConfig.sprx"
required: false required: false
path: "shadps4/sys_modules/" path: "shadps4/sys_modules/libSceWkFontConfig.sprx"
description: "PS4 WebKit font configuration" description: "PS4 WebKit font configuration"
note: "No HLE implementation; the module is stubbed when absent." note: "No HLE implementation; the module is stubbed when absent."
source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:244" source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:244"
@@ -216,13 +216,13 @@ files:
- name: "libSceSystemGesture.sprx" - name: "libSceSystemGesture.sprx"
required: false required: false
hle_fallback: true hle_fallback: true
path: "shadps4/sys_modules/" path: "shadps4/sys_modules/libSceSystemGesture.sprx"
description: "PS4 system gesture recognition" description: "PS4 system gesture recognition"
source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:249" source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:249"
- name: "libSceXml.sprx" - name: "libSceXml.sprx"
required: false required: false
path: "shadps4/sys_modules/" path: "shadps4/sys_modules/libSceXml.sprx"
description: "PS4 XML parser" description: "PS4 XML parser"
note: "No HLE implementation; the module is stubbed when absent." note: "No HLE implementation; the module is stubbed when absent."
source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:250" source_ref: "src/core/libraries/sysmodule/sysmodule_internal.cpp:250"
+4 -4
View File
@@ -25,25 +25,25 @@ mode: standalone
files: files:
- name: "prod.keys" - name: "prod.keys"
required: true required: true
path: "switch/" path: "switch/prod.keys"
note: "Production keys for NCA decryption (master, key area, header, titlekek)" note: "Production keys for NCA decryption (master, key area, header, titlekek)"
source_ref: "src/core/crypto/key_manager.cpp:654" source_ref: "src/core/crypto/key_manager.cpp:654"
- name: "title.keys" - name: "title.keys"
required: false required: false
path: "switch/" path: "switch/title.keys"
note: "Per-title encryption keys (rights_id to titlekey mappings)" note: "Per-title encryption keys (rights_id to titlekey mappings)"
source_ref: "src/core/crypto/key_manager.cpp:657" source_ref: "src/core/crypto/key_manager.cpp:657"
- name: "console.keys" - name: "console.keys"
required: false required: false
path: "switch/" path: "switch/console.keys"
note: "Console-specific keys (BIS, SD seed)" note: "Console-specific keys (BIS, SD seed)"
source_ref: "src/core/crypto/key_manager.cpp:658" source_ref: "src/core/crypto/key_manager.cpp:658"
- name: "key_retail.bin" - name: "key_retail.bin"
required: false required: false
path: "switch/" path: "switch/key_retail.bin"
size: 160 size: 160
note: "Amiibo decryption keys (two InternalKey structs, 0x50 bytes each)" note: "Amiibo decryption keys (two InternalKey structs, 0x50 bytes each)"
source_ref: "src/core/hle/service/nfc/common/amiibo_crypto.cpp:273-294" source_ref: "src/core/hle/service/nfc/common/amiibo_crypto.cpp:273-294"
+5 -5
View File
@@ -74,7 +74,7 @@ notes: |
Source: retrodep/embedded/*.c (embedded ROM tables), Source: retrodep/embedded/*.c (embedded ROM tables),
vice/src/*/rom.h (ROM name defines), vice/src/drive/driverom.h (drive ROMs), vice/src/*/rom.h (ROM name defines), vice/src/drive/driverom.h (drive ROMs),
retrodep/ui.c:274-315 (JiffyDOS loading), retrodep/ui.c:258-271 (SCPU kernal), retrodep/ui.c:274-315 (JiffyDOS loading), retrodep/ui.c:258-271 (SCPU kernal),
libretro/libretro-core.c:2807-2872 (core options for JiffyDOS/SCPU) libretro/libretro-core.c:2806-2871 (core options for JiffyDOS/SCPU)
files: files:
# ------------------------------------------------------- # -------------------------------------------------------
@@ -1082,7 +1082,7 @@ files:
hle_fallback: true hle_fallback: true
embedded: false embedded: false
note: "SuperCPU DOS v1.40 external kernal. Optional, selected via core option. Goes in system/vice/SCPU64/." note: "SuperCPU DOS v1.40 external kernal. Optional, selected via core option. Goes in system/vice/SCPU64/."
source_ref: "retrodep/ui.c:266, libretro/libretro-core.c:2832" source_ref: "retrodep/ui.c:266, libretro/libretro-core.c:2830"
- name: "vice/SCPU64/scpu-dos-2.04.bin" - name: "vice/SCPU64/scpu-dos-2.04.bin"
system: commodore-c64 system: commodore-c64
@@ -1090,7 +1090,7 @@ files:
hle_fallback: true hle_fallback: true
embedded: false embedded: false
note: "SuperCPU DOS v2.04 external kernal. Optional, selected via core option. Goes in system/vice/SCPU64/." note: "SuperCPU DOS v2.04 external kernal. Optional, selected via core option. Goes in system/vice/SCPU64/."
source_ref: "retrodep/ui.c:263, libretro/libretro-core.c:2834" source_ref: "retrodep/ui.c:263, libretro/libretro-core.c:2831"
# ------------------------------------------------------- # -------------------------------------------------------
# Drive ROMs (shared across all cores with True Drive Emulation) # Drive ROMs (shared across all cores with True Drive Emulation)
@@ -1274,7 +1274,7 @@ files:
hle_fallback: true hle_fallback: true
embedded: false embedded: false
note: "JiffyDOS C64 kernal replacement. Optional, proprietary. Requires True Drive Emulation + 1541/1571/1581 drive." note: "JiffyDOS C64 kernal replacement. Optional, proprietary. Requires True Drive Emulation + 1541/1571/1581 drive."
source_ref: "retrodep/ui.c:302, libretro/libretro-core.c:2862" source_ref: "retrodep/ui.c:302, libretro/libretro-core.c:2859"
- name: "vice/JiffyDOS_SX-64.bin" - name: "vice/JiffyDOS_SX-64.bin"
system: commodore-c64 system: commodore-c64
@@ -1290,7 +1290,7 @@ files:
hle_fallback: true hle_fallback: true
embedded: false embedded: false
note: "JiffyDOS C128 kernal replacement. Optional, proprietary. C128 core only." note: "JiffyDOS C128 kernal replacement. Optional, proprietary. C128 core only."
source_ref: "retrodep/ui.c:309, libretro/libretro-core.c:2864" source_ref: "retrodep/ui.c:309, libretro/libretro-core.c:2861"
- name: "vice/JiffyDOS_1541-II.bin" - name: "vice/JiffyDOS_1541-II.bin"
system: commodore-c64 system: commodore-c64
+4 -3
View File
@@ -112,10 +112,11 @@ files:
required: false required: false
size: 131072 size: 131072
validation: [size] validation: [size]
aliases: sha1: "af643900fea84234e08b616a6d27f03f0a61db96"
- boot.rom md5: "6e844759720226e58d55ecaf33608a13"
crc32: "ae25bdf5"
description: "Model-M cart boot ROM, read when the BIOS type is Model M" description: "Model-M cart boot ROM, read when the BIOS type is Model M"
source_ref: "libretro.c:5358-5380" source_ref: "libretro.c:5358-5380, src/bios/jagbios_m.c:9"
note: >- note: >-
Read from the system directory only, as jagboot_m.rom, and only when Read from the system directory only, as jagboot_m.rom, and only when
biosType is BT_M_SERIES. A short read falls back to the embedded biosType is BT_M_SERIES. A short read falls back to the embedded
+4 -4
View File
@@ -26,25 +26,25 @@ mode: standalone
files: files:
- name: "prod.keys" - name: "prod.keys"
required: true required: true
path: "switch/" path: "switch/prod.keys"
note: "Production keys for NCA decryption (master, key area, header, titlekek)" note: "Production keys for NCA decryption (master, key area, header, titlekek)"
source_ref: "src/core/crypto/key_manager.cpp:655-656" source_ref: "src/core/crypto/key_manager.cpp:655-656"
- name: "title.keys" - name: "title.keys"
required: false required: false
path: "switch/" path: "switch/title.keys"
note: "Per-title encryption keys (rights_id to titlekey mappings)" note: "Per-title encryption keys (rights_id to titlekey mappings)"
source_ref: "src/core/crypto/key_manager.cpp:659-660" source_ref: "src/core/crypto/key_manager.cpp:659-660"
- name: "console.keys" - name: "console.keys"
required: false required: false
path: "switch/" path: "switch/console.keys"
note: "Console-specific keys (BIS, SD seed)" note: "Console-specific keys (BIS, SD seed)"
source_ref: "src/core/crypto/key_manager.cpp:661-662" source_ref: "src/core/crypto/key_manager.cpp:661-662"
- name: "key_retail.bin" - name: "key_retail.bin"
required: false required: false
path: "switch/" path: "switch/key_retail.bin"
size: 160 size: 160
note: "Amiibo decryption keys (two InternalKey structs, 0x50 bytes each)" note: "Amiibo decryption keys (two InternalKey structs, 0x50 bytes each)"
source_ref: "src/core/hle/service/nfc/common/amiibo_crypto.cpp:273-294" source_ref: "src/core/hle/service/nfc/common/amiibo_crypto.cpp:273-294"
+109
View File
@@ -90,6 +90,7 @@ files:
# ============================================================ # ============================================================
- name: "48.rom" - name: "48.rom"
sha1: "5ea7c2b824672e914525d1d5c419d71b84a426a2"
size: 16384 size: 16384
validation: [size] validation: [size]
required: true required: true
@@ -100,6 +101,7 @@ files:
note: "48K BASIC. Also loaded four times over as the TBBlue fast-boot image (src/cpu.c:3653-3662)." note: "48K BASIC. Also loaded four times over as the TBBlue fast-boot image (src/cpu.c:3653-3662)."
- name: "48es.rom" - name: "48es.rom"
sha1: "9e535e2e24231ccb65e33d107f6d0ceb23e99477"
size: 16384 size: 16384
validation: [size] validation: [size]
required: true required: true
@@ -109,6 +111,7 @@ files:
note: "Spanish 48K+ ROM, selected for MACHINE_ID_SPECTRUM_48_PLUS_SPA." note: "Spanish 48K+ ROM, selected for MACHINE_ID_SPECTRUM_48_PLUS_SPA."
- name: "inves.rom" - name: "inves.rom"
sha1: "d020440638aff4d39467128413ef795677be9c23"
size: 16384 size: 16384
validation: [size] validation: [size]
required: true required: true
@@ -118,6 +121,7 @@ files:
note: "Investronica Inves Spectrum+. Loaded at memoria_spectrum[65536] (src/cpu.c:3551-3557)." note: "Investronica Inves Spectrum+. Loaded at memoria_spectrum[65536] (src/cpu.c:3551-3557)."
- name: "128.rom" - name: "128.rom"
sha1: "16375d42ea109b47edded7a16028de7fdb3013a1"
size: 32768 size: 32768
validation: [size] validation: [size]
required: true required: true
@@ -126,6 +130,7 @@ files:
source_ref: "src/cpu.c:3312-3314, src/utils.c:4100" source_ref: "src/cpu.c:3312-3314, src/utils.c:4100"
- name: "128s.rom" - name: "128s.rom"
sha1: "57c0b6681224378ce1d279fde13d644478423783"
size: 32768 size: 32768
validation: [size] validation: [size]
required: true required: true
@@ -134,6 +139,7 @@ files:
source_ref: "src/cpu.c:3316-3318, src/utils.c:4100" source_ref: "src/cpu.c:3316-3318, src/utils.c:4100"
- name: "p2.rom" - name: "p2.rom"
sha1: "8cafb292af58617907b9e6b9093d3588a75849b8"
size: 32768 size: 32768
validation: [size] validation: [size]
required: true required: true
@@ -143,6 +149,7 @@ files:
note: "ZX Spectrum +2." note: "ZX Spectrum +2."
- name: "p2f.rom" - name: "p2f.rom"
sha1: "8a5be00921afd4e2c74059c217b31b9fb51c3d41"
size: 32768 size: 32768
validation: [size] validation: [size]
required: true required: true
@@ -151,6 +158,7 @@ files:
source_ref: "src/cpu.c:3327-3329, src/utils.c:4100" source_ref: "src/cpu.c:3327-3329, src/utils.c:4100"
- name: "p2s.rom" - name: "p2s.rom"
sha1: "baca5d798b3e786078feded19486d9fc29dbd4b7"
size: 32768 size: 32768
validation: [size] validation: [size]
required: true required: true
@@ -159,6 +167,7 @@ files:
source_ref: "src/cpu.c:3331-3333, src/utils.c:4100" source_ref: "src/cpu.c:3331-3333, src/utils.c:4100"
- name: "p2a40.rom" - name: "p2a40.rom"
sha1: "929bf1a5e5687ebd8d7245f9b513a596c0ec21a4"
size: 65536 size: 65536
validation: [size] validation: [size]
required: true required: true
@@ -168,6 +177,7 @@ files:
note: "+2A/+3 ROM v4.0." note: "+2A/+3 ROM v4.0."
- name: "p2a41.rom" - name: "p2a41.rom"
sha1: "500c0945760abeefcbd08bc22c0d07b14b336cf0"
size: 65536 size: 65536
validation: [size] validation: [size]
required: true required: true
@@ -177,6 +187,7 @@ files:
note: "+2A/+3 ROM v4.1." note: "+2A/+3 ROM v4.1."
- name: "p2as.rom" - name: "p2as.rom"
sha1: "a4b311135ccbe010318dcb872dae2c8f078c7447"
size: 65536 size: 65536
validation: [size] validation: [size]
required: true required: true
@@ -185,6 +196,7 @@ files:
source_ref: "src/cpu.c:3347-3350, src/utils.c:4102" source_ref: "src/cpu.c:3347-3350, src/utils.c:4102"
- name: "pentagon.rom" - name: "pentagon.rom"
sha1: "a584272f21dc82c14b7d4f1ed440e23a976e71f0"
size: 32768 size: 32768
validation: [size] validation: [size]
required: true required: true
@@ -193,6 +205,7 @@ files:
source_ref: "src/cpu.c:3381-3383, src/utils.c:4100 (MACHINE_IS_SPECTRUM_128_P2 covers Pentagon, src/cpu.h:545)" source_ref: "src/cpu.c:3381-3383, src/utils.c:4100 (MACHINE_IS_SPECTRUM_128_P2 covers Pentagon, src/cpu.h:545)"
- name: "se.rom" - name: "se.rom"
sha1: "14321f2092b8af21b52b35c1518d9a753d6618d4"
size: 32768 size: 32768
validation: [size] validation: [size]
required: true required: true
@@ -202,6 +215,7 @@ files:
note: "Chloe 140SE/280SE, SE Basic IV." note: "Chloe 140SE/280SE, SE Basic IV."
- name: "chrome.rom" - name: "chrome.rom"
sha1: "fb305656769765224c58ca02445d4bf041891d18"
size: 65536 size: 65536
validation: [size] validation: [size]
required: true required: true
@@ -210,6 +224,7 @@ files:
source_ref: "src/cpu.c:3406-3408, src/utils.c:4112" source_ref: "src/cpu.c:3406-3408, src/utils.c:4112"
- name: "prism.rom" - name: "prism.rom"
sha1: "6e03d87f868c40d5662ef6906931c77ba1b8b2c2"
size: 327680 size: 327680
validation: [size] validation: [size]
required: true required: true
@@ -219,6 +234,7 @@ files:
note: "20 pages of 16K. Page map documented at src/cpu.c:3622-3638." note: "20 pages of 16K. Page map documented at src/cpu.c:3622-3638."
- name: "prism_failsafe.rom" - name: "prism_failsafe.rom"
sha1: "d020440638aff4d39467128413ef795677be9c23"
size: 16384 size: 16384
validation: [size] validation: [size]
required: true required: true
@@ -228,6 +244,7 @@ files:
note: "Loaded straight after prism.rom by prism_load_failsafe_rom(). The only ROM whose absence is fatal: the loader calls cpu_panic both when the file is missing and when the read is not 16384 bytes." note: "Loaded straight after prism.rom by prism_load_failsafe_rom(). The only ROM whose absence is fatal: the loader calls cpu_panic both when the file is missing and when the read is not 16384 bytes."
- name: "zxuno_bootloader.rom" - name: "zxuno_bootloader.rom"
sha1: "0afcb798128e6587e7ea0787714568f7b68be012"
size: 8126 size: 8126
required: true required: true
bundled: true bundled: true
@@ -236,6 +253,7 @@ files:
size_note: "Read caps at 8192 bytes and only errors when fewer than 1 byte is read, so no exact size check. get_rom_size returns 214 for ZX-Uno (src/utils.c:4104) but that value is unused on this path." size_note: "Read caps at 8192 bytes and only errors when fewer than 1 byte is read, so no exact size check. get_rom_size returns 214 for ZX-Uno (src/utils.c:4104) but that value is unused on this path."
- name: "zxevo_tsconf.rom" - name: "zxevo_tsconf.rom"
sha1: "2551026b7d63a2627f79701e26688a7d715dc96a"
size: 524288 size: 524288
validation: [size] validation: [size]
required: true required: true
@@ -244,6 +262,7 @@ files:
source_ref: "src/cpu.c:3410-3412, src/utils.c:4114" source_ref: "src/cpu.c:3410-3412, src/utils.c:4114"
- name: "zxevo_baseconf.rom" - name: "zxevo_baseconf.rom"
sha1: "1570048704f11dc239060ee4f63190d199c94987"
size: 524288 size: 524288
validation: [size] validation: [size]
required: true required: true
@@ -257,6 +276,7 @@ files:
# ============================================================ # ============================================================
- name: "tbblue_loader.rom" - name: "tbblue_loader.rom"
sha1: "8b2e927d0523bc27bffcc34c39527ab0d2362e0c"
size: 8192 size: 8192
validation: [size] validation: [size]
required: true required: true
@@ -266,6 +286,7 @@ files:
note: "FPGA loader, mirrored to 16K at src/cpu.c:3665. Bypassed when --tbblue-fast-boot-mode selects 48.rom instead." note: "FPGA loader, mirrored to 16K at src/cpu.c:3665. Bypassed when --tbblue-fast-boot-mode selects 48.rom instead."
- name: "tbblue.mmc" - name: "tbblue.mmc"
sha1: "e838e1d63dc9c1e518a79195c9512042dffb765f"
size: 67108864 size: 67108864
required: false required: false
bundled: true bundled: true
@@ -279,6 +300,7 @@ files:
# ============================================================ # ============================================================
- name: "tk90x.rom" - name: "tk90x.rom"
sha1: "9a943a008be13194fb006bddffa7d22d2277813f"
size: 16384 size: 16384
validation: [size] validation: [size]
required: true required: true
@@ -287,6 +309,7 @@ files:
source_ref: "src/cpu.c:3294-3296, src/utils.c:4098" source_ref: "src/cpu.c:3294-3296, src/utils.c:4098"
- name: "tk90xs.rom" - name: "tk90xs.rom"
sha1: "a9421b9e3a17689b7392da1f72dc71dd46e09ab1"
size: 16384 size: 16384
validation: [size] validation: [size]
required: true required: true
@@ -295,6 +318,7 @@ files:
source_ref: "src/cpu.c:3298-3300, src/utils.c:4098" source_ref: "src/cpu.c:3298-3300, src/utils.c:4098"
- name: "tk95.rom" - name: "tk95.rom"
sha1: "94edc401d43b0e9a9cdc1d35de4b6462dc414ab3"
size: 16384 size: 16384
validation: [size] validation: [size]
required: true required: true
@@ -303,6 +327,7 @@ files:
source_ref: "src/cpu.c:3302-3304, src/utils.c:4098" source_ref: "src/cpu.c:3302-3304, src/utils.c:4098"
- name: "tk95es.rom" - name: "tk95es.rom"
sha1: "54f3445374d1758a3133232672e8783cb4fc7a32"
size: 16384 size: 16384
validation: [size] validation: [size]
required: true required: true
@@ -315,6 +340,7 @@ files:
# ============================================================ # ============================================================
- name: "tc2048.rom" - name: "tc2048.rom"
sha1: "febb2d495b6eda7cdcb4074935d6e9d9f328972d"
size: 16384 size: 16384
validation: [size] validation: [size]
required: true required: true
@@ -323,6 +349,7 @@ files:
source_ref: "src/cpu.c:3418-3420, src/utils.c:4098 (TC2048 falls under MACHINE_IS_SPECTRUM_16_48, src/cpu.h:543)" source_ref: "src/cpu.c:3418-3420, src/utils.c:4098 (TC2048 falls under MACHINE_IS_SPECTRUM_16_48, src/cpu.h:543)"
- name: "ts2068.rom" - name: "ts2068.rom"
sha1: "34cd8113bf75b65a4da6eea355f9d46ad635f7e4"
size: 24576 size: 24576
validation: [size] validation: [size]
required: true required: true
@@ -332,6 +359,7 @@ files:
note: "Read as two blocks: 16384 into the main ROM table, then 8192 into the EX ROM table. Serves TS2068 and TC2068." note: "Read as two blocks: 16384 into the main ROM table, then 8192 into the EX ROM table. Serves TS2068 and TC2068."
- name: "ts1500.rom" - name: "ts1500.rom"
sha1: "3eb437359221b4406d236085ec66fa02278e7495"
size: 8192 size: 8192
validation: [size] validation: [size]
required: true required: true
@@ -344,6 +372,7 @@ files:
# ============================================================ # ============================================================
- name: "zx80.rom" - name: "zx80.rom"
sha1: "b6769a3197c77009e0933e038c15b43cf4c98c7a"
size: [4096, 8192] size: [4096, 8192]
validation: [size] validation: [size]
required: true required: true
@@ -353,6 +382,7 @@ files:
note: "Serves ZX80, TK80 and TK82. An 8192-byte file is accepted as-is so the ZX81 ROM can run on a ZX80 (src/cpu.c:3758)." note: "Serves ZX80, TK80 and TK82. An 8192-byte file is accepted as-is so the ZX81 ROM can run on a ZX80 (src/cpu.c:3758)."
- name: "zx81.rom" - name: "zx81.rom"
sha1: "7b143ee964e9ada89d1f9e88f0bd48d919184cfc"
size: 8192 size: 8192
validation: [size] validation: [size]
required: true required: true
@@ -362,6 +392,7 @@ files:
note: "Serves ZX81, Timex TS1000, TK82C, TK83 and the Czerweny CZ 1000/1500 models." note: "Serves ZX81, Timex TS1000, TK82C, TK83 and the Czerweny CZ 1000/1500 models."
- name: "tk85.rom" - name: "tk85.rom"
sha1: "7b961a1733fc047eb682150a32e17bca10a018d2"
size: 10240 size: 10240
validation: [size] validation: [size]
required: true required: true
@@ -371,6 +402,7 @@ files:
note: "The TK85 test precedes the generic ZX81 test in get_rom_size, so 10240 wins over 8192." note: "The TK85 test precedes the generic ZX81 test in get_rom_size, so 10240 wins over 8192."
- name: "zxpand_zx81.rom" - name: "zxpand_zx81.rom"
sha1: "3bdbf55720cf9623431a4d4017fa8f165aac4a66"
size: 8192 size: 8192
validation: [size] validation: [size]
required: false required: false
@@ -380,6 +412,7 @@ files:
note: "ZXpand interface firmware. A short read disables ZXpand emulation." note: "ZXpand interface firmware. A short read disables ZXpand emulation."
- name: "zxpand_zx80.rom" - name: "zxpand_zx80.rom"
sha1: "8968261beab240e8a06e8cfe9e070987d860064b"
size: 8192 size: 8192
validation: [size] validation: [size]
required: false required: false
@@ -388,6 +421,7 @@ files:
source_ref: "src/storage/zxpand.c:72, src/storage/zxpand.c:240, src/storage/zxpand.c:246-254" source_ref: "src/storage/zxpand.c:72, src/storage/zxpand.c:240, src/storage/zxpand.c:246-254"
- name: "ace.rom" - name: "ace.rom"
sha1: "597ba8a15a292688333c84dc9fd35172abe5e7e6"
size: 8192 size: 8192
validation: [size] validation: [size]
required: true required: true
@@ -400,6 +434,7 @@ files:
# ============================================================ # ============================================================
- name: "ql_js.rom" - name: "ql_js.rom"
sha1: "6e1e3cae275d0cd58977258c0b13480c8e910138"
size: 49152 size: 49152
min_size: 16384 min_size: 16384
validation: [size] validation: [size]
@@ -410,6 +445,7 @@ files:
size_note: "Reads up to 131072 bytes and errors only below 16384, so the check is a minimum rather than an exact size." size_note: "Reads up to 131072 bytes and errors only below 16384, so the check is a minimum rather than an exact size."
- name: "ql_jm.rom" - name: "ql_jm.rom"
sha1: "6a8468dcdb27dc1b194febba8c957c4ef4313538"
size: 49152 size: 49152
required: false required: false
bundled: true bundled: true
@@ -418,6 +454,7 @@ files:
note: "JM release. Reachable through --romfile; the switch selects ql_js.rom." note: "JM release. Reachable through --romfile; the switch selects ql_js.rom."
- name: "tkii220.rom" - name: "tkii220.rom"
sha1: "f364472a81352479290e388e9a5afb57a0de8249"
size: 16384 size: 16384
required: false required: false
bundled: true bundled: true
@@ -426,6 +463,7 @@ files:
note: "QJUMP Toolkit II 2.20, the ROM ql.c names as the example for --ql-rom-c000-file. ql_load_extra_roms() runs after the system ROM and reads 16K into memoria_ql at 0xC000 from the path the option gives; the read count is not checked." note: "QJUMP Toolkit II 2.20, the ROM ql.c names as the example for --ql-rom-c000-file. ql_load_extra_roms() runs after the system ROM and reads 16K into memoria_ql at 0xC000 from the path the option gives; the read count is not checked."
- name: "qsound_V1.94.rom" - name: "qsound_V1.94.rom"
sha1: "622a81dce98524148f067780c17eddbaf46e27f6"
aliases: ["Qsound_V1.94.rom"] aliases: ["Qsound_V1.94.rom"]
size: 8192 size: 8192
required: false required: false
@@ -435,6 +473,7 @@ files:
note: "QL Qsound firmware, read into memoria_ql at 0xC0000 once --ql-qsound-enabled is set; the read count is not checked and --ql-qsound-rom points the loader at another file. QSOUND_ROM_FILE is spelled qsound_V1.94.rom while the shipped file is Qsound_V1.94.rom, so the default only resolves on a case-insensitive filesystem." note: "QL Qsound firmware, read into memoria_ql at 0xC0000 once --ql-qsound-enabled is set; the read count is not checked and --ql-qsound-rom points the loader at another file. QSOUND_ROM_FILE is spelled qsound_V1.94.rom while the shipped file is Qsound_V1.94.rom, so the default only resolves on a case-insensitive filesystem."
- name: "Z88OZ47.rom" - name: "Z88OZ47.rom"
sha1: "35e528e81d8f4e5860adf4160a3da0926525c8fa"
size: 524288 size: 524288
required: true required: true
bundled: true bundled: true
@@ -443,6 +482,7 @@ files:
size_note: "Reads up to 512K and errors only when nothing is read; z88_internal_rom_size is derived from the byte count, so any size is accepted." size_note: "Reads up to 512K and errors only when nothing is read; z88_internal_rom_size is derived from the byte count, so any size is accepted."
- name: "mk14.rom" - name: "mk14.rom"
sha1: "802dc637ce5391a2a6627f76f919b12a869b56ef"
size: 512 size: 512
validation: [size] validation: [size]
required: true required: true
@@ -455,6 +495,7 @@ files:
# ============================================================ # ============================================================
- name: "cpc464.rom" - name: "cpc464.rom"
sha1: "56d39c463da60968d93e58b4ba0e675829412a20"
size: 32768 size: 32768
validation: [size] validation: [size]
required: true required: true
@@ -464,6 +505,7 @@ files:
note: "Serves CPC 464 and CPC 4128." note: "Serves CPC 464 and CPC 4128."
- name: "cpc664.rom" - name: "cpc664.rom"
sha1: "4a38f7f5bf7fd0a95cf882433c69e67251a46868"
size: 49152 size: 49152
validation: [size] validation: [size]
required: true required: true
@@ -472,6 +514,7 @@ files:
source_ref: "src/cpu.c:3476-3478, src/utils.c:4142" source_ref: "src/cpu.c:3476-3478, src/utils.c:4142"
- name: "cpc6128.rom" - name: "cpc6128.rom"
sha1: "29dd0487dc5a96cd00966d074de8700ddd4ee18b"
size: 49152 size: 49152
validation: [size] validation: [size]
required: true required: true
@@ -480,6 +523,7 @@ files:
source_ref: "src/cpu.c:3480-3482, src/utils.c:4142" source_ref: "src/cpu.c:3480-3482, src/utils.c:4142"
- name: "pcw_boot.rom" - name: "pcw_boot.rom"
sha1: "245a173cfc7976a4cae8d255f3d1422aeaf5f09a"
size: 275 size: 275
validation: [size] validation: [size]
required: true required: true
@@ -489,6 +533,7 @@ files:
note: "Boot block the PCW pulls into RAM rather than a mapped ROM (src/cpu.c:3849). Serves PCW 8256 and 8512." note: "Boot block the PCW pulls into RAM rather than a mapped ROM (src/cpu.c:3849). Serves PCW 8256 and 8512."
- name: "pcw_8x_boot1.dsk" - name: "pcw_8x_boot1.dsk"
sha1: "3be724b3bfa01d03931463b8501ffb5c3f3621b7"
size: 194864 size: 194864
required: false required: false
bundled: true bundled: true
@@ -497,6 +542,7 @@ files:
note: "LocoScript boot disc, inserted by pcw_boot_locoscript()." note: "LocoScript boot disc, inserted by pcw_boot_locoscript()."
- name: "pcw_8x_boot2.dsk" - name: "pcw_8x_boot2.dsk"
sha1: "4c13bba076897076322b375e34f87306640cf0d6"
size: 194864 size: 194864
required: false required: false
bundled: true bundled: true
@@ -509,6 +555,7 @@ files:
# ============================================================ # ============================================================
- name: "samcoupe.rom" - name: "samcoupe.rom"
sha1: "c86601633fb61a8c517f7657aad9af4e6870f2ee"
size: 32768 size: 32768
validation: [size] validation: [size]
required: true required: true
@@ -517,6 +564,7 @@ files:
source_ref: "src/cpu.c:3484-3487, src/utils.c:4144" source_ref: "src/cpu.c:3484-3487, src/utils.c:4144"
- name: "atomlite.rom" - name: "atomlite.rom"
sha1: "b2303b7200f5a17b54b841af9f0604db7bbdd1ab"
size: 32768 size: 32768
validation: [size] validation: [size]
required: false required: false
@@ -526,6 +574,7 @@ files:
note: "Replaces samcoupe.rom when Atom Lite emulation is on." note: "Replaces samcoupe.rom when Atom Lite emulation is on."
- name: "samram.rom" - name: "samram.rom"
sha1: "acac00b8b342d19ce9bb35a8744a9c0060d5eedd"
size: 32768 size: 32768
validation: [size] validation: [size]
required: false required: false
@@ -535,6 +584,7 @@ files:
note: "SamRam interface firmware, opened from a path the user selects." note: "SamRam interface firmware, opened from a path the user selects."
- name: "samram_eng.rom" - name: "samram_eng.rom"
sha1: "5344ee16523862fa66e9397d1f8cfa2b2cbaf341"
size: 32768 size: 32768
validation: [size] validation: [size]
required: false required: false
@@ -548,6 +598,7 @@ files:
# ============================================================ # ============================================================
- name: "coleco.rom" - name: "coleco.rom"
sha1: "99ba9be24ada3e86e5c17aeecb7a2d68c5edfe59"
size: 8192 size: 8192
validation: [size] validation: [size]
required: true required: true
@@ -556,6 +607,7 @@ files:
source_ref: "src/cpu.c:3385-3387, src/utils.c:4120" source_ref: "src/cpu.c:3385-3387, src/utils.c:4120"
- name: "sg1000.rom" - name: "sg1000.rom"
sha1: "fdea3168fda81e5f25f7dee3a7231192a492836e"
size: 155 size: 155
required: true required: true
bundled: true bundled: true
@@ -564,6 +616,7 @@ files:
size_note: "Nothing is read from this file. The SG-1000 branch is empty and the source comment states the file only has to exist for the open to succeed." size_note: "Nothing is read from this file. The SG-1000 branch is empty and the source comment states the file only has to exist for the open to succeed."
- name: "sms.rom" - name: "sms.rom"
sha1: "c315672807d8ddb8d91443729405c766dd95cae7"
size: 8192 size: 8192
validation: [size] validation: [size]
required: true required: true
@@ -572,6 +625,7 @@ files:
source_ref: "src/cpu.c:3393-3395, src/utils.c:4124" source_ref: "src/cpu.c:3393-3395, src/utils.c:4124"
- name: "msx.rom" - name: "msx.rom"
sha1: "e998f0c441f4f1800ef44e42cd1659150206cf79"
size: 32768 size: 32768
validation: [size] validation: [size]
required: true required: true
@@ -580,6 +634,7 @@ files:
source_ref: "src/cpu.c:3397-3399, src/utils.c:4126" source_ref: "src/cpu.c:3397-3399, src/utils.c:4126"
- name: "svi.rom" - name: "svi.rom"
sha1: "7d8daf52f78371ca2c9443e04827c8e1f98c8f2c"
size: 32768 size: 32768
validation: [size] validation: [size]
required: true required: true
@@ -593,6 +648,7 @@ files:
# ============================================================ # ============================================================
- name: "esxmmc085.rom" - name: "esxmmc085.rom"
sha1: "aa4c31dc9b08d80d0107b580d5a9216d23214776"
size: 8192 size: 8192
validation: [size] validation: [size]
required: false required: false
@@ -602,6 +658,7 @@ files:
note: "divMMC esxdos firmware, the default when --divmmc-rom is unset. Needed by --enable-esxdos-handler, which the ES-DE zxnext command line sets." note: "divMMC esxdos firmware, the default when --divmmc-rom is unset. Needed by --enable-esxdos-handler, which the ES-DE zxnext command line sets."
- name: "esxide085.rom" - name: "esxide085.rom"
sha1: "178bae9e9209c43282e12c09884562e4ddf07e68"
size: 8192 size: 8192
validation: [size] validation: [size]
required: false required: false
@@ -611,6 +668,7 @@ files:
note: "divIDE esxdos firmware, the default when --divide-rom is unset." note: "divIDE esxdos firmware, the default when --divide-rom is unset."
- name: "unodos3.rom" - name: "unodos3.rom"
sha1: "cdb90bd05b073ec7a5f875444e29ab0f6bcee9ac"
size: 8192 size: 8192
validation: [size] validation: [size]
required: false required: false
@@ -620,6 +678,7 @@ files:
note: "divMMC firmware substituted for esxmmc085.rom on Chloe machines." note: "divMMC firmware substituted for esxmmc085.rom on Chloe machines."
- name: "trdos.rom" - name: "trdos.rom"
sha1: "0a74bd34538a03d0e1d214b425d95c14ad10c8c4"
size: 16384 size: 16384
validation: [size] validation: [size]
required: false required: false
@@ -629,6 +688,7 @@ files:
note: "Beta Disk / TR-DOS firmware." note: "Beta Disk / TR-DOS firmware."
- name: "if1-v1.rom" - name: "if1-v1.rom"
sha1: "4ffd9ed9c00cdc6f92ce69fdd8b618ef1203f48e"
size: [8192, 16384] size: [8192, 16384]
validation: [size] validation: [size]
required: false required: false
@@ -638,6 +698,7 @@ files:
note: "Interface 1 firmware version 1, selected when if1_rom_version is 1. The load accepts 8192 or 16384 bytes and disables Interface 1 emulation otherwise." note: "Interface 1 firmware version 1, selected when if1_rom_version is 1. The load accepts 8192 or 16384 bytes and disables Interface 1 emulation otherwise."
- name: "if1-v2.rom" - name: "if1-v2.rom"
sha1: "5cfb6bca4177c45fefd571734576b55e3a127c08"
size: [8192, 16384] size: [8192, 16384]
validation: [size] validation: [size]
required: false required: false
@@ -647,6 +708,7 @@ files:
note: "Interface 1 firmware version 2, the default branch of if1_return_rom_name()." note: "Interface 1 firmware version 2, the default branch of if1_return_rom_name()."
- name: "hilow_datadrive.rom" - name: "hilow_datadrive.rom"
sha1: "fd3f165860c2564847805cfa7a7f769a627a687c"
size: 8192 size: 8192
validation: [size] validation: [size]
required: false required: false
@@ -656,6 +718,7 @@ files:
note: "HiLow Data Drive firmware." note: "HiLow Data Drive firmware."
- name: "zxmmcplus.flash" - name: "zxmmcplus.flash"
sha1: "9f0697c261c37e92bf2e488e3db223443c341b9d"
size: 1048576 size: 1048576
validation: [size] validation: [size]
required: false required: false
@@ -666,6 +729,7 @@ files:
size_note: "The image holds ROM and RAM together, so the check is ZXMMCPLUS_FLASHROM_SIZE + ZXMMCPLUS_RAM_SIZE, not the 512K ROM constant alone." size_note: "The image holds ROM and RAM together, so the check is ZXMMCPLUS_FLASHROM_SIZE + ZXMMCPLUS_RAM_SIZE, not the 512K ROM constant alone."
- name: "zxuno.flash" - name: "zxuno.flash"
sha1: "bb6740f15b5ed8d1d60f7f61f0737f769215ca57"
size: 4194304 size: 4194304
validation: [size] validation: [size]
required: false required: false
@@ -679,6 +743,7 @@ files:
# ============================================================ # ============================================================
- name: "mf1.rom" - name: "mf1.rom"
sha1: "ba28754a3cc31a4ca579829ed4310c313409cf5d"
size: 8192 size: 8192
validation: [size] validation: [size]
required: false required: false
@@ -688,6 +753,7 @@ files:
note: "Multiface One. The file selector rejects anything that is not exactly 8 KB." note: "Multiface One. The file selector rejects anything that is not exactly 8 KB."
- name: "mf128.rom" - name: "mf128.rom"
sha1: "f03f4ecbcf4a654f4775d16bda0d4cc47f884379"
size: 8192 size: 8192
validation: [size] validation: [size]
required: false required: false
@@ -697,6 +763,7 @@ files:
note: "Multiface 128." note: "Multiface 128."
- name: "mf3.rom" - name: "mf3.rom"
sha1: "5d74d2e2e5a537639da92ff120f8a6d86f474495"
size: 8192 size: 8192
validation: [size] validation: [size]
required: false required: false
@@ -706,6 +773,7 @@ files:
note: "Multiface 3." note: "Multiface 3."
- name: "specmate.rom" - name: "specmate.rom"
sha1: "c8f128610eeb5142d4d80f28e5dd07b7a5ab6b84"
size: 8192 size: 8192
validation: [size] validation: [size]
required: false required: false
@@ -714,6 +782,7 @@ files:
source_ref: "src/copy_interfaces/specmate.h:27, src/copy_interfaces/specmate.h:29, src/copy_interfaces/specmate.c:189-205" source_ref: "src/copy_interfaces/specmate.h:27, src/copy_interfaces/specmate.h:29, src/copy_interfaces/specmate.c:189-205"
- name: "phoenix3.rom" - name: "phoenix3.rom"
sha1: "d4f01500e0383a1cf34683fb30d96230c14c7da3"
size: 16384 size: 16384
validation: [size] validation: [size]
required: false required: false
@@ -722,6 +791,7 @@ files:
source_ref: "src/copy_interfaces/phoenix.h:27, src/copy_interfaces/phoenix.h:29, src/copy_interfaces/phoenix.c:142" source_ref: "src/copy_interfaces/phoenix.h:27, src/copy_interfaces/phoenix.h:29, src/copy_interfaces/phoenix.c:142"
- name: "defcon.rom" - name: "defcon.rom"
sha1: "4c7d702e295a3383326606b805eab74affc501ba"
size: 16384 size: 16384
validation: [size] validation: [size]
required: false required: false
@@ -730,6 +800,7 @@ files:
source_ref: "src/copy_interfaces/defcon.h:27, src/copy_interfaces/defcon.h:29, src/copy_interfaces/defcon.c:139" source_ref: "src/copy_interfaces/defcon.h:27, src/copy_interfaces/defcon.h:29, src/copy_interfaces/defcon.c:139"
- name: "dinamid3.rom" - name: "dinamid3.rom"
sha1: "9ee4ca7abee12ba6fb34aede2185f6aa3cb2800b"
size: 2048 size: 2048
validation: [size] validation: [size]
required: false required: false
@@ -738,6 +809,7 @@ files:
source_ref: "src/copy_interfaces/dinamid3.h:27, src/copy_interfaces/dinamid3.h:29, src/copy_interfaces/dinamid3.c:142" source_ref: "src/copy_interfaces/dinamid3.h:27, src/copy_interfaces/dinamid3.h:29, src/copy_interfaces/dinamid3.c:142"
- name: "interface007.rom" - name: "interface007.rom"
sha1: "81af60ecd412854069227487417eb80b6c89934d"
size: 2048 size: 2048
validation: [size] validation: [size]
required: false required: false
@@ -746,6 +818,7 @@ files:
source_ref: "src/copy_interfaces/interface007.h:27, src/copy_interfaces/interface007.h:29, src/copy_interfaces/interface007.c:143" source_ref: "src/copy_interfaces/interface007.h:27, src/copy_interfaces/interface007.h:29, src/copy_interfaces/interface007.c:143"
- name: "ramjet2.rom" - name: "ramjet2.rom"
sha1: "a3852ee24ab57e0e1d800c9d99cd244f7363449b"
size: 16384 size: 16384
validation: [size] validation: [size]
required: false required: false
@@ -754,6 +827,7 @@ files:
source_ref: "src/copy_interfaces/ramjet.h:27, src/copy_interfaces/ramjet.h:28-30, src/copy_interfaces/ramjet.c:165" source_ref: "src/copy_interfaces/ramjet.h:27, src/copy_interfaces/ramjet.h:28-30, src/copy_interfaces/ramjet.c:165"
- name: "ramjet3.rom" - name: "ramjet3.rom"
sha1: "362589adb01ebbf006a162e76062921c03ceb26e"
size: 16384 size: 16384
validation: [size] validation: [size]
required: false required: false
@@ -762,6 +836,7 @@ files:
source_ref: "src/copy_interfaces/ramjet.h:28, src/copy_interfaces/ramjet.c:165" source_ref: "src/copy_interfaces/ramjet.h:28, src/copy_interfaces/ramjet.c:165"
- name: "transtape2.rom" - name: "transtape2.rom"
sha1: "45d3e32d58b1c405b75ee72d80153dd0305bbd9d"
size: 16384 size: 16384
validation: [size] validation: [size]
required: false required: false
@@ -771,6 +846,7 @@ files:
note: "Selected when transtape_version is 2." note: "Selected when transtape_version is 2."
- name: "transtape3-vC.rom" - name: "transtape3-vC.rom"
sha1: "b7ec72fa0a55243a53c55d649e0555acea25d0c1"
size: 16384 size: 16384
validation: [size] validation: [size]
required: false required: false
@@ -780,6 +856,7 @@ files:
note: "Default for Transtape version 3." note: "Default for Transtape version 3."
- name: "transtape3-vA.rom" - name: "transtape3-vA.rom"
sha1: "c627527551e441a000184565a72df8ff37fce891"
size: 16384 size: 16384
validation: [size] validation: [size]
required: false required: false
@@ -789,6 +866,7 @@ files:
note: "Revision A, reached through the user-set transtape_rom_filename and loaded by the same size-checked path as the defaults." note: "Revision A, reached through the user-set transtape_rom_filename and loaded by the same size-checked path as the defaults."
- name: "transtape3-vB.rom" - name: "transtape3-vB.rom"
sha1: "82bf848bac698d1ffd9ca809c4293f004152645a"
size: 16384 size: 16384
validation: [size] validation: [size]
required: false required: false
@@ -798,6 +876,7 @@ files:
note: "Revision B, reached through the user-set transtape_rom_filename." note: "Revision B, reached through the user-set transtape_rom_filename."
- name: "hilow_barbanegra.rom" - name: "hilow_barbanegra.rom"
sha1: "3b3862943758ad35f340537b742ca25a8286267a"
size: 8192 size: 8192
validation: [size] validation: [size]
required: false required: false
@@ -806,6 +885,7 @@ files:
source_ref: "src/copy_interfaces/hilow_barbanegra.h:27, src/copy_interfaces/hilow_barbanegra.h:30, src/copy_interfaces/hilow_barbanegra.c:282" source_ref: "src/copy_interfaces/hilow_barbanegra.h:27, src/copy_interfaces/hilow_barbanegra.h:30, src/copy_interfaces/hilow_barbanegra.c:282"
- name: "mhpokeador.rom" - name: "mhpokeador.rom"
sha1: "557f91755ede646a89e1146904a6d713f38d6b05"
size: 1024 size: 1024
validation: [size] validation: [size]
required: false required: false
@@ -814,6 +894,7 @@ files:
source_ref: "src/copy_interfaces/mhpokeador.h:27, src/copy_interfaces/mhpokeador.h:39, src/copy_interfaces/mhpokeador.c:264" source_ref: "src/copy_interfaces/mhpokeador.h:27, src/copy_interfaces/mhpokeador.h:39, src/copy_interfaces/mhpokeador.c:264"
- name: "mhpokeador-transfer.rom" - name: "mhpokeador-transfer.rom"
sha1: "4099c5d8f65a25bc0053e5730b7c40a29bcfb614"
size: 1024 size: 1024
validation: [size] validation: [size]
required: false required: false
@@ -822,6 +903,7 @@ files:
source_ref: "src/copy_interfaces/mhpokeador.h:28, src/copy_interfaces/mhpokeador.c:264" source_ref: "src/copy_interfaces/mhpokeador.h:28, src/copy_interfaces/mhpokeador.c:264"
- name: "mhpokeador-salvapan.rom" - name: "mhpokeador-salvapan.rom"
sha1: "4e438541ec2da0c3b9cf2cbf0cc3f5bc6cfb295e"
size: 1024 size: 1024
validation: [size] validation: [size]
required: false required: false
@@ -830,6 +912,7 @@ files:
source_ref: "src/copy_interfaces/mhpokeador.h:29, src/copy_interfaces/mhpokeador.c:264" source_ref: "src/copy_interfaces/mhpokeador.h:29, src/copy_interfaces/mhpokeador.c:264"
- name: "mantransfev3.bin" - name: "mantransfev3.bin"
sha1: "e134a38468ed84d53df164d97fa24990aa557da3"
size: 200 size: 200
required: false required: false
bundled: true bundled: true
@@ -842,6 +925,7 @@ files:
# ============================================================ # ============================================================
- name: "gs105a.rom" - name: "gs105a.rom"
sha1: "1ba78923dc7d803e7995c165992e14e4608c2153"
size: 32768 size: 32768
validation: [size] validation: [size]
required: false required: false
@@ -851,6 +935,7 @@ files:
note: "General Sound firmware v1.05a. GS_ROM_SIZE is in KB and the read is GS_ROM_SIZE*1024." note: "General Sound firmware v1.05a. GS_ROM_SIZE is in KB and the read is GS_ROM_SIZE*1024."
- name: "gs104.rom" - name: "gs104.rom"
sha1: "c865121306eb3a7d811d82fbcc653b4dc1d6fa3d"
size: 32768 size: 32768
required: false required: false
bundled: true bundled: true
@@ -864,6 +949,7 @@ files:
# ============================================================ # ============================================================
- name: "80-lec.rom" - name: "80-lec.rom"
sha1: "bb7a77d66e95d2e28ebb610e543c065e0d428619"
size: 16384 size: 16384
required: false required: false
bundled: true bundled: true
@@ -871,6 +957,7 @@ files:
source_ref: "src/start.c:4876-4880, src/cpu.c:3520-3523" source_ref: "src/start.c:4876-4880, src/cpu.c:3520-3523"
- name: "tk90x_v1br.rom" - name: "tk90x_v1br.rom"
sha1: "eab2eab803d8ec717b55a915de3bcb931b410a2f"
size: 16384 size: 16384
required: false required: false
bundled: true bundled: true
@@ -878,6 +965,7 @@ files:
source_ref: "src/ALTERNATEROMS:97, src/start.c:4876-4880, src/cpu.c:3520-3523" source_ref: "src/ALTERNATEROMS:97, src/start.c:4876-4880, src/cpu.c:3520-3523"
- name: "tk90x_v2es.rom" - name: "tk90x_v2es.rom"
sha1: "a9af9c4d38882e2bd85086fc0c2d8d9afe90afad"
size: 16384 size: 16384
required: false required: false
bundled: true bundled: true
@@ -886,6 +974,7 @@ files:
note: "Corrected TK90X ROM." note: "Corrected TK90X ROM."
- name: "tk95es_modif.rom" - name: "tk95es_modif.rom"
sha1: "1dd39ee98e236389467e91a86cf5d8ed52583ba5"
size: 16384 size: 16384
required: false required: false
bundled: true bundled: true
@@ -894,6 +983,7 @@ files:
note: "TK95 Spanish ROM with message and symbol fixes by Rodolfo Edison Guerra." note: "TK95 Spanish ROM with message and symbol fixes by Rodolfo Edison Guerra."
- name: "Z88OZ431.rom" - name: "Z88OZ431.rom"
sha1: "82e12674ba91eb85d4904c3948e4167aea6fd1b7"
size: 524288 size: 524288
required: false required: false
bundled: true bundled: true
@@ -901,6 +991,7 @@ files:
source_ref: "src/ALTERNATEROMS:83-92, src/start.c:4876-4880" source_ref: "src/ALTERNATEROMS:83-92, src/start.c:4876-4880"
- name: "Z88OZ441.rom" - name: "Z88OZ441.rom"
sha1: "778d188c201bd9eb6bf795e0cc8344a87600cd45"
size: 524288 size: 524288
required: false required: false
bundled: true bundled: true
@@ -908,6 +999,7 @@ files:
source_ref: "src/ALTERNATEROMS:83-92, src/start.c:4876-4880" source_ref: "src/ALTERNATEROMS:83-92, src/start.c:4876-4880"
- name: "Z88OZ45.rom" - name: "Z88OZ45.rom"
sha1: "aecf46de3563c6c6443e9780d633d7b7507e79ef"
size: 524288 size: 524288
required: false required: false
bundled: true bundled: true
@@ -915,6 +1007,7 @@ files:
source_ref: "src/ALTERNATEROMS:83-92, src/start.c:4876-4880" source_ref: "src/ALTERNATEROMS:83-92, src/start.c:4876-4880"
- name: "Z88OZ46.rom" - name: "Z88OZ46.rom"
sha1: "9e0e29baa3720abcbb8006cda8f19ea365faf20e"
size: 524288 size: 524288
required: false required: false
bundled: true bundled: true
@@ -922,6 +1015,7 @@ files:
source_ref: "src/ALTERNATEROMS:83-92, src/start.c:4876-4880" source_ref: "src/ALTERNATEROMS:83-92, src/start.c:4876-4880"
- name: "Z88UK220.rom" - name: "Z88UK220.rom"
sha1: "5d89e8d98d2cc0acb8cd42dbfca601b7bd09c51e"
size: 131072 size: 131072
required: false required: false
bundled: true bundled: true
@@ -929,6 +1023,7 @@ files:
source_ref: "src/ALTERNATEROMS:83-92, src/start.c:4876-4880" source_ref: "src/ALTERNATEROMS:83-92, src/start.c:4876-4880"
- name: "Z88UK300.rom" - name: "Z88UK300.rom"
sha1: "ceb688025b79454cf229cae4dbd0449df2747f79"
size: 131072 size: 131072
required: false required: false
bundled: true bundled: true
@@ -936,6 +1031,7 @@ files:
source_ref: "src/ALTERNATEROMS:83-92, src/start.c:4876-4880" source_ref: "src/ALTERNATEROMS:83-92, src/start.c:4876-4880"
- name: "Z88UK400.rom" - name: "Z88UK400.rom"
sha1: "23c63ceced72d0a9031cba08d2ebc72ca336921d"
size: 131072 size: 131072
required: false required: false
bundled: true bundled: true
@@ -944,6 +1040,7 @@ files:
- name: "aszmice07.rom" - name: "aszmice07.rom"
path: "alternate_roms/aszmice07.rom" path: "alternate_roms/aszmice07.rom"
sha1: "7a37a248572689c8ac226c082b7026972d399b1c"
size: 8192 size: 8192
required: false required: false
bundled: true bundled: true
@@ -953,6 +1050,7 @@ files:
- name: "sp-rom.rom" - name: "sp-rom.rom"
path: "alternate_roms/sp-rom.rom" path: "alternate_roms/sp-rom.rom"
sha1: "ef4b575b5b5cc18a3e6c71c241c65aa18957e2d5"
size: 4096 size: 4096
required: false required: false
bundled: true bundled: true
@@ -962,6 +1060,7 @@ files:
- name: "ace.rom" - name: "ace.rom"
path: "alternate_roms/ace.rom" path: "alternate_roms/ace.rom"
sha1: "6e5c9416a1cc89fe4012627f496f71ff2bb07b81"
size: 8192 size: 8192
required: false required: false
bundled: true bundled: true
@@ -972,6 +1071,7 @@ files:
- name: "opense.rom" - name: "opense.rom"
path: "alternate_roms/opense.rom" path: "alternate_roms/opense.rom"
sha1: "24a339c5c163a687a8fed50f93714c6450e2562d"
size: 16384 size: 16384
required: false required: false
bundled: true bundled: true
@@ -981,6 +1081,7 @@ files:
- name: "sebasic4_rom1.rom" - name: "sebasic4_rom1.rom"
path: "alternate_roms/sebasic4_rom1.rom" path: "alternate_roms/sebasic4_rom1.rom"
sha1: "d7092d6d55ee8280057399362b5798d1f65d8eeb"
size: 16384 size: 16384
required: false required: false
bundled: true bundled: true
@@ -990,6 +1091,7 @@ files:
- name: "derby_p_32K.rom" - name: "derby_p_32K.rom"
path: "alternate_roms/derby_p_32K.rom" path: "alternate_roms/derby_p_32K.rom"
sha1: "f9c337782f9a4781548528841babd5bd13f2b745"
size: 32768 size: 32768
required: false required: false
bundled: true bundled: true
@@ -999,6 +1101,7 @@ files:
- name: "derby_pp_64K.rom" - name: "derby_pp_64K.rom"
path: "alternate_roms/derby_pp_64K.rom" path: "alternate_roms/derby_pp_64K.rom"
sha1: "7eaaf1ff19f74d5449033bacce9834d7b13022a9"
size: 65536 size: 65536
required: false required: false
bundled: true bundled: true
@@ -1008,6 +1111,7 @@ files:
- name: "plus2c.rom" - name: "plus2c.rom"
path: "alternate_roms/plus2c.rom" path: "alternate_roms/plus2c.rom"
sha1: "98effb0e2dc4ecf6e9f70be9828a688341396c0c"
size: 32768 size: 32768
required: false required: false
bundled: true bundled: true
@@ -1017,6 +1121,7 @@ files:
- name: "plus2b.rom" - name: "plus2b.rom"
path: "alternate_roms/plus2b.rom" path: "alternate_roms/plus2b.rom"
sha1: "420d8f1cd7bd75a79803eaa49e191d90cfd24f38"
size: 65536 size: 65536
required: false required: false
bundled: true bundled: true
@@ -1026,6 +1131,7 @@ files:
- name: "plus3e_mmcen3eE.rom" - name: "plus3e_mmcen3eE.rom"
path: "alternate_roms/plus3e_mmcen3eE.rom" path: "alternate_roms/plus3e_mmcen3eE.rom"
sha1: "a579f9c3571669294dad37b04dcb889a9c147d94"
size: 65536 size: 65536
required: false required: false
bundled: true bundled: true
@@ -1035,6 +1141,7 @@ files:
- name: "plus3e_mmces3eE.rom" - name: "plus3e_mmces3eE.rom"
path: "alternate_roms/plus3e_mmces3eE.rom" path: "alternate_roms/plus3e_mmces3eE.rom"
sha1: "44e98a3dfc966f7c8725c252487a44a2c0b450d6"
size: 65536 size: 65536
required: false required: false
bundled: true bundled: true
@@ -1044,6 +1151,7 @@ files:
- name: "plus3e_sm8en3eE.rom" - name: "plus3e_sm8en3eE.rom"
path: "alternate_roms/plus3e_sm8en3eE.rom" path: "alternate_roms/plus3e_sm8en3eE.rom"
sha1: "89b6227798a88a6ad0e3b9e58db979694912ee04"
size: 65536 size: 65536
required: false required: false
bundled: true bundled: true
@@ -1053,6 +1161,7 @@ files:
- name: "plus3e_sm8es3eE.rom" - name: "plus3e_sm8es3eE.rom"
path: "alternate_roms/plus3e_sm8es3eE.rom" path: "alternate_roms/plus3e_sm8es3eE.rom"
sha1: "a5b62b647ccaec4af9989b8fd7857d5f6426ae5d"
size: 65536 size: 65536
required: false required: false
bundled: true bundled: true
+1 -1
View File
@@ -8,7 +8,7 @@ param(
$ErrorActionPreference = "Stop" $ErrorActionPreference = "Stop"
$defaultInstallUrl = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.py" $defaultInstallUrl = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
$defaultInstallSha256 = "df0adf1fd5088ef9509429fb1d5720e5ca5e7bf46aff9ad0021851dce7349107" $defaultInstallSha256 = "d5f352fbe34cfd8ae40c0980c3986158ec1d9dd1babfe2bbc0918ffc0fa0bdf0"
$maximumInstallerBytes = 2MB $maximumInstallerBytes = 2MB
$installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null } $installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null }
$temporary = $null $temporary = $null
+529 -8
View File
@@ -10,6 +10,7 @@ Usage:
python install.py --platform retroarch --dest ~/custom/bios python install.py --platform retroarch --dest ~/custom/bios
python install.py --check python install.py --check
python install.py --list-platforms python install.py --list-platforms
python install.py --platform retroarch --system sony-playstation --region us
""" """
from __future__ import annotations from __future__ import annotations
@@ -81,9 +82,59 @@ MAX_TARGETS_BYTES = 4 * 1024 * 1024
MAX_MANIFEST_FILES = 100_000 MAX_MANIFEST_FILES = 100_000
MAX_DOWNLOAD_SIZE = 1024 * 1024 * 1024 MAX_DOWNLOAD_SIZE = 1024 * 1024 * 1024
MAX_TOTAL_DOWNLOAD_SIZE = 64 * 1024 * 1024 * 1024 MAX_TOTAL_DOWNLOAD_SIZE = 64 * 1024 * 1024 * 1024
# Why a manifest leaves a file out, as the installer words it. The manifest
# schema and the generator are held to this list by the tests.
OMISSION_REASONS = {
"hash_mismatch": "hash mismatch",
"not_found": "not found",
"external": "external",
"user_provided": "user provided",
"no_platform_slug": "no platform folder",
}
_SHA1_RE = re.compile(r"^[0-9a-fA-F]{40}$") _SHA1_RE = re.compile(r"^[0-9a-fA-F]{40}$")
_SHA256_RE = re.compile(r"^[0-9a-fA-F]{64}$") _SHA256_RE = re.compile(r"^[0-9a-fA-F]{64}$")
# The region vocabulary of scripts/region.py, which this file cannot import.
# A test holds the two copies equal.
WORLD_REGION = "world"
REGION_TREE: dict[str, frozenset] = {
"north-america": frozenset({"canada"}),
"latin-america": frozenset({"brazil", "mexico", "argentina"}),
"europe": frozenset({
"uk", "france", "germany", "italy", "spain", "netherlands", "portugal",
"greece", "poland", "russia", "sweden", "norway", "denmark", "finland",
}),
"asia": frozenset({
"japan", "south-korea", "china", "taiwan", "hong-kong", "singapore",
"india", "asia-ntsc", "asia-pal",
}),
"oceania": frozenset({"australia", "new-zealand"}),
}
REGION_ALIASES: dict[str, str] = {
"jp": "japan",
"ntsc-j": "japan",
"us": "north-america",
"usa": "north-america",
"na": "north-america",
"ntsc-u": "north-america",
"eu": "europe",
"pal": "europe",
"kr": "south-korea",
"korea": "south-korea",
"auto": WORLD_REGION,
"gb": "uk",
}
_REGION_PARENT = {
member: parent for parent, members in REGION_TREE.items() for member in members
}
REGIONS = frozenset({WORLD_REGION} | set(REGION_TREE) | set(_REGION_PARENT))
MAX_SELECTION_TAGS = 1000
SELECTION_FIELDS = ("systems", "regions", "region_groups", "region_system_groups")
# The files cores load for systems the platform does not list (game engines,
# computers): no system of the platform owns them, and choosing systems
# leaves them out unless this name is chosen too.
OTHER_SYSTEMS = "other"
# Platforms with a manifest in install/. Manifest URLs are case sensitive, # Platforms with a manifest in install/. Manifest URLs are case sensitive,
# so user input is normalized against this list before any fetch. # so user input is normalized against this list before any fetch.
AVAILABLE_PLATFORMS = ( AVAILABLE_PLATFORMS = (
@@ -804,6 +855,19 @@ def _destination_path(root: Path, value: object) -> Path:
return root.resolve() / Path(*relative.parts) return root.resolve() / Path(*relative.parts)
def _check_tags(entry: dict, field: str, where: str) -> None:
"""A selection field is absent or a short list of short strings."""
value = entry.get(field)
if value is None:
return
if (
not isinstance(value, list)
or len(value) > MAX_SELECTION_TAGS
or not all(isinstance(tag, str) and 0 < len(tag) <= 256 for tag in value)
):
raise ValueError(f"invalid {field} for {where}")
def _validate_manifest(data: object, plat: str) -> dict: def _validate_manifest(data: object, plat: str) -> dict:
"""Validate the untrusted install-manifest boundary using stdlib only.""" """Validate the untrusted install-manifest boundary using stdlib only."""
if not isinstance(data, dict): if not isinstance(data, dict):
@@ -862,6 +926,8 @@ def _validate_manifest(data: object, plat: str) -> dict:
not isinstance(cores, list) or not all(isinstance(core, str) for core in cores) not isinstance(cores, list) or not all(isinstance(core, str) for core in cores)
): ):
raise ValueError(f"invalid cores list for {dest}") raise ValueError(f"invalid cores list for {dest}")
for field in SELECTION_FIELDS:
_check_tags(entry, field, dest)
declared_total_files = data.get("total_files") declared_total_files = data.get("total_files")
if declared_total_files is not None and declared_total_files != len(files): if declared_total_files is not None and declared_total_files != len(files):
@@ -874,9 +940,6 @@ def _validate_manifest(data: object, plat: str) -> dict:
if not isinstance(omitted, list) or len(omitted) > MAX_MANIFEST_FILES: if not isinstance(omitted, list) or len(omitted) > MAX_MANIFEST_FILES:
raise ValueError("invalid omitted_files list") raise ValueError("invalid omitted_files list")
seen_omitted: set[str] = set() seen_omitted: set[str] = set()
allowed_omission_reasons = {
"hash_mismatch", "not_found", "external", "user_provided"
}
for index, entry in enumerate(omitted): for index, entry in enumerate(omitted):
if not isinstance(entry, dict): if not isinstance(entry, dict):
raise ValueError(f"omitted_files[{index}] must be an object") raise ValueError(f"omitted_files[{index}] must be an object")
@@ -894,7 +957,7 @@ def _validate_manifest(data: object, plat: str) -> dict:
raise ValueError(f"invalid omitted system for {dest}") raise ValueError(f"invalid omitted system for {dest}")
if not isinstance(entry.get("required"), bool): if not isinstance(entry.get("required"), bool):
raise ValueError(f"invalid omitted required flag for {dest}") raise ValueError(f"invalid omitted required flag for {dest}")
if entry.get("reason") not in allowed_omission_reasons: if entry.get("reason") not in OMISSION_REASONS:
raise ValueError(f"invalid omission reason for {dest}") raise ValueError(f"invalid omission reason for {dest}")
cores = entry.get("cores") cores = entry.get("cores")
if cores is not None and ( if cores is not None and (
@@ -902,6 +965,8 @@ def _validate_manifest(data: object, plat: str) -> dict:
or not all(isinstance(core, str) for core in cores) or not all(isinstance(core, str) for core in cores)
): ):
raise ValueError(f"invalid omitted cores list for {dest}") raise ValueError(f"invalid omitted cores list for {dest}")
for field in SELECTION_FIELDS:
_check_tags(entry, field, dest)
declared_total_omitted = data.get("total_omitted") declared_total_omitted = data.get("total_omitted")
if ( if (
declared_total_omitted is not None declared_total_omitted is not None
@@ -915,6 +980,24 @@ def _validate_manifest(data: object, plat: str) -> dict:
for index, entry in enumerate(copies): for index, entry in enumerate(copies):
if not isinstance(entry, dict): if not isinstance(entry, dict):
raise ValueError(f"standalone_copies[{index}] must be an object") raise ValueError(f"standalone_copies[{index}] must be an object")
# The shape do_standalone_copies reads, checked here: a note with a
# list for detect, a copy naming neither file nor pattern, or a list
# for emulator passed and crashed the copy step after the download.
where = f"standalone_copies[{index}]"
if "note" in entry:
detect = entry.get("detect", {})
if not isinstance(entry["note"], str) or not isinstance(detect, dict):
raise ValueError(f"invalid note entry at {where}")
for values in detect.values():
if not isinstance(values, list) or not all(
isinstance(value, str) for value in values
):
raise ValueError(f"invalid note entry at {where}")
continue
if "file" not in entry and "pattern" not in entry:
raise ValueError(f"{where} names neither a file nor a pattern")
if not isinstance(entry.get("emulator", ""), str):
raise ValueError(f"invalid emulator at {where}")
if "file" in entry: if "file" in entry:
_safe_relative_path( _safe_relative_path(
entry["file"], f"standalone_copies[{index}].file" entry["file"], f"standalone_copies[{index}].file"
@@ -1018,6 +1101,228 @@ def _filter_by_target(
return result return result
def canonical_region(raw: str) -> str:
"""One region name or alias, spelled the way manifests spell it."""
key = raw.strip().lower()
key = REGION_ALIASES.get(key, key)
if key not in REGIONS:
raise ValueError(f"unknown region: {raw.strip()}")
return key
def _region_comparable(a: str, b: str) -> bool:
return a == b or _REGION_PARENT.get(a) == b or _REGION_PARENT.get(b) == a
def _region_rank(file_regions: set[str], requested: list[str]) -> int:
for index, wanted in enumerate(requested):
if any(_region_comparable(wanted, region) for region in file_regions):
return index
return len(requested)
def region_drops(
entries: list[dict], requested: list[str], systems: list[str] | None = None
) -> set[str]:
"""Destinations a region priority list withdraws.
The pack builder's rule (region.resolve_region_drops), replayed over the
groups the manifest records. Per group, the best-ranked regional file
stays; a world file beats unmatched regional ones the code does not search
before it (`priority`, lower first); when nothing matches and no world
file exists, every regional file stays, so a group is never emptied. A file with no region never competes and always stays.
A core extra competes whatever systems are chosen; a platform's own
declaration competes only when its system is kept, as in the pack.
"""
if not requested:
return set()
kept_systems = set(systems) if systems else None
groups: dict[str, list[tuple[set, str, object]]] = {}
for entry in entries:
regions = entry.get("regions")
if not regions:
continue
joined = list(entry.get("region_groups") or [])
joined += [
group for group in entry.get("region_system_groups") or []
if kept_systems is None or group in kept_systems
]
priority = entry.get("priority")
if not isinstance(priority, int) or isinstance(priority, bool):
priority = None
for group in joined:
groups.setdefault(group, []).append((set(regions), entry["dest"], priority))
keep: set[str] = set()
drop: set[str] = set()
for members in groups.values():
world = {dest for regions, dest, _p in members if WORLD_REGION in regions}
ranks = {dest: priority for _regions, dest, priority in members}
regional = [
(_region_rank(regions, requested), dest)
for regions, dest, _p in members
if WORLD_REGION not in regions
]
keep |= world
if not regional:
continue
matched = [(rank, dest) for rank, dest in regional if rank < len(requested)]
if matched:
best = min(rank for rank, _dest in matched)
keep |= {dest for rank, dest in matched if rank == best}
drop |= {dest for _rank, dest in regional}
elif world:
world_ranks = [ranks[dest] for dest in world]
best_world = min(world_ranks) if None not in world_ranks else None
for _rank, dest in regional:
ours = ranks[dest]
if best_world is not None and ours is not None and ours < best_world:
keep.add(dest)
else:
drop.add(dest)
else:
keep |= {dest for _rank, dest in regional}
return drop - keep
def entry_systems(entry: dict) -> list[str]:
"""The platform systems a file or an omission belongs to.
An omission's `system` is spelled the way its profile spells it; only
`systems` names the platform's own, the ones --system accepts.
"""
return list(entry.get("systems") or [])
def narrow(
files: list[dict],
omitted: list[dict],
systems: list[str],
cores: list[str],
regions: list[str],
) -> tuple[list[dict], list[dict]]:
"""Keep what the chosen systems, cores and regions need.
A core choice keeps the platform's own list and narrows only the files
cores load, the rule --target already follows. Regions are then decided
the way the pack builder decides them under --system: over the chosen
systems' declarations and every core extra. The system filter comes last.
"""
if cores:
files = _filter_by_target(files, cores)
omitted = _filter_by_target(omitted, cores)
if regions:
dropped = region_drops(files + omitted, regions, systems)
files = [f for f in files if f["dest"] not in dropped]
omitted = [o for o in omitted if o["dest"] not in dropped]
if systems:
wanted = set(systems)
files = [f for f in files if _chosen(f, wanted)]
omitted = [o for o in omitted if _chosen(o, wanted)]
return files, omitted
def _chosen(entry: dict, wanted: set[str]) -> bool:
systems = entry_systems(entry)
if not systems:
return OTHER_SYSTEMS in wanted
return bool(wanted & set(systems))
def records_selection(files: list[dict]) -> bool:
"""Whether a manifest carries what narrowing reads; older ones do not."""
return any("systems" in entry for entry in files)
def available_choices(files: list[dict], omitted: list[dict]) -> dict[str, list[str]]:
"""The systems, cores and regions a manifest lets one narrow to."""
systems = {s for e in files + omitted for s in entry_systems(e)}
ordered = sorted(systems)
if any(not entry_systems(f) for f in files):
ordered.append(OTHER_SYSTEMS)
cores = {c for f in files for c in (f.get("cores") or [])}
regions = {
r for e in files + omitted for r in (e.get("regions") or [])
if r != WORLD_REGION
}
return {
"systems": ordered,
"cores": sorted(cores),
"regions": sorted(regions),
}
def resolve_choices(values: list[str], known: list[str], label: str) -> list[str]:
"""Names given on the command line, matched without regard to case.
Each value is one name or a comma-separated list. A whole value that is
itself a name wins over splitting it: Recalbox names one system
`msx1,msx2,msxturbor`. An unknown name is refused, never ignored, since
carrying on would install something other than what was asked.
"""
by_lower = {name.lower(): name for name in known}
chosen: list[str] = []
unknown: list[str] = []
for value in values:
value = value.strip()
tokens = [value] if value.lower() in by_lower else value.split(",")
for token in (t.strip() for t in tokens):
if not token:
continue
name = by_lower.get(token.lower())
if name is None:
unknown.append(token)
elif name not in chosen:
chosen.append(name)
if unknown:
available = ", ".join(known) if known else "none"
raise ValueError(
f"unknown {label}: {', '.join(unknown)} (available: {available})"
)
if values and not chosen:
raise ValueError(f"--{label} needs at least one name")
return chosen
def resolve_regions(values: list[str]) -> list[str]:
"""A region priority list, best first, from names or aliases."""
chosen: list[str] = []
for value in values:
for token in value.split(","):
if token.strip():
region = canonical_region(token)
if region not in chosen:
chosen.append(region)
if values and not chosen:
raise ValueError("--region needs at least one region")
return chosen
def parse_selection(text: str, count: int, ordered: bool = False) -> list[int]:
"""Numbers typed at a prompt: `3`, `1,4`, `2-5`, or `all`, 1-based.
Returned sorted, or in the typed order when the order is a priority.
"""
if text.strip().lower() in ("all", "*"):
return list(range(1, count + 1))
chosen: list[int] = []
for token in (t.strip() for t in text.split(",")):
if not token:
continue
low, sep, high = token.partition("-")
try:
start = int(low)
end = int(high) if sep else start
except ValueError:
raise ValueError(f"not a number or range: {token}") from None
if start > end or start < 1 or end > count:
raise ValueError(f"out of range: {token} (1-{count})")
for number in range(start, end + 1):
if number not in chosen:
chosen.append(number)
return chosen if ordered else sorted(chosen)
def _digest_file(path: Path, algorithms: tuple[str, ...]) -> dict[str, str]: def _digest_file(path: Path, algorithms: tuple[str, ...]) -> dict[str, str]:
"""Compute several digests of a file in a single read. """Compute several digests of a file in a single read.
@@ -1492,6 +1797,155 @@ def _prompt_platform_choice(
return platforms return platforms
def _ask(prompt: str) -> str:
try:
return input(prompt).strip()
except (EOFError, KeyboardInterrupt):
print()
sys.exit(0)
def _sized(files: list[dict]) -> str:
return f"{_plural(len(files), 'file')}, {format_size(sum(f.get('size', 0) for f in files))}"
def _pick(title: str, rows: list[tuple[str, list[dict]]], ordered: bool = False) -> list[str]:
"""Numbered list of names; an empty answer keeps every one."""
if not rows:
return []
print(f"\n{title}")
width = len(str(len(rows)))
name_width = min(max(len(name) for name, _files in rows), 32)
for index, (name, files) in enumerate(rows, 1):
print(f" {index:>{width}}) {name:<{name_width}} {_sized(files)}")
hint = "order of preference, e.g. 2,1" if ordered else "e.g. 1,3-5"
while True:
text = _ask(f"Numbers ({hint}), Enter for all, q to quit: ")
if not text:
return []
if text.lower() == "q":
sys.exit(0)
try:
chosen = parse_selection(text, len(rows), ordered=ordered)
except ValueError as exc:
print(f" {exc}")
continue
if chosen:
return [rows[number - 1][0] for number in chosen]
def _prompt_custom_selection(
plat: str, files: list[dict], omitted: list[dict]
) -> tuple[list[str], list[str], list[str]] | None:
"""Offer to narrow the install by system, core and region.
Enter installs everything, the default most runs want. Returns None
then, or the three choices; each empty list keeps everything on its axis.
"""
print(f"\n{plat.capitalize()}: {_sized(files)}.")
print(" Enter) install everything")
print(" c) choose systems, cores and regions")
print(" q) quit")
while True:
answer = _ask("> ").lower()
if answer == "":
return None
if answer == "q":
sys.exit(0)
if answer == "c":
break
choices = available_choices(files, omitted)
if OTHER_SYSTEMS in choices["systems"]:
print(f"\n'{OTHER_SYSTEMS}' holds what cores load for systems this platform")
print("does not list, such as game engines and extra computers.")
systems = _pick("Systems:", [
(name, [f for f in files if _chosen(f, {name})])
for name in choices["systems"]
])
kept, kept_omitted = narrow(files, omitted, systems, [], [])
cores: list[str] = []
core_names = available_choices(kept, kept_omitted)["cores"]
if core_names:
print("\nThe platform's own files for these systems are always kept;")
print("the extra files each core loads can be narrowed.")
cores = _pick("Cores:", [
(name, [f for f in kept if name in (f.get("cores") or [])])
for name in core_names
])
kept, kept_omitted = narrow(kept, kept_omitted, [], cores, [])
regions: list[str] = []
region_names = available_choices(kept, kept_omitted)["regions"]
if region_names:
print("\nSome systems have one BIOS per region. Keeping only the")
print("preferred region removes the others where a match exists.")
regions = _pick("Regions:", [
(name, [f for f in kept if name in (f.get("regions") or [])])
for name in region_names
], ordered=True)
return systems, cores, regions
def _same_choice_hint(plat: str, systems: list[str], cores: list[str], regions: list[str]) -> str:
"""The options that repeat a choice made at the prompts."""
def quoted(value: str) -> str:
return f'"{value}"' if any(c in value for c in " ;&|") else value
parts = [f"--platform {plat}"]
parts += [f"--system {quoted(name)}" for name in systems]
parts += [f"--core {quoted(name)}" for name in cores]
if regions:
parts.append(f"--region {','.join(regions)}")
return " ".join(parts)
def _print_choices(
plat: str, files: list[dict], omitted: list[dict], systems: bool
) -> None:
"""What --system or --core accepts for a platform, with what each holds."""
choices = available_choices(files, omitted)
names = choices["systems"] if systems else choices["cores"]
if not names:
print(f" {plat} records no {'systems' if systems else 'cores'}")
return
width = min(max(len(name) for name in names), 40)
for name in names:
held = (
[f for f in files if _chosen(f, {name})]
if systems
else [f for f in files if name in (f.get("cores") or [])]
)
print(f" {name:<{width}} {_sized(held)}")
def _requested_narrowing(
args: argparse.Namespace, plat: str, files: list[dict], omitted: list[dict]
) -> tuple[list[str], list[str], list[str]] | None:
"""The systems, cores and regions named on the command line, checked."""
if not (args.system or args.core or args.region):
return None
if not records_selection(files):
# Without the fields, a region filter would match nothing and install
# everything: refused rather than ignored.
print(
f"Error: the {plat} file list records no systems or regions; "
"narrowing needs a newer one",
file=sys.stderr,
)
sys.exit(1)
choices = available_choices(files, omitted)
try:
systems = resolve_choices(args.system or [], choices["systems"], "system")
cores = resolve_choices(args.core or [], choices["cores"], "core")
regions = resolve_regions(args.region or [])
except ValueError as exc:
print(f"Error: {plat}: {exc}", file=sys.stderr)
sys.exit(1)
return systems, cores, regions
def main() -> None: def main() -> None:
"""Entry point.""" """Entry point."""
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
@@ -1541,10 +1995,56 @@ def main() -> None:
action="store_true", action="store_true",
help="opt in to copies into detected standalone-emulator directories", help="opt in to copies into detected standalone-emulator directories",
) )
parser.add_argument(
"--system",
action="append",
metavar="NAME",
help="install only these systems (repeat or comma-separate; see --list-systems)",
)
parser.add_argument(
"--core",
action="append",
metavar="NAME",
help="keep only the extra files these cores load (see --list-cores)",
)
parser.add_argument(
"--region",
action="append",
metavar="LIST",
help="preferred regions, best first, e.g. us,eu,jp: keep one BIOS per "
"region where a system has several",
)
parser.add_argument(
"--list-systems",
action="store_true",
help="list a platform's systems with their file counts and exit",
)
parser.add_argument(
"--list-cores",
action="store_true",
help="list the cores whose extra files a platform installs and exit",
)
parser.add_argument(
"--no-input",
action="store_true",
help="never ask a question; fail where an answer is needed",
)
args = parser.parse_args() args = parser.parse_args()
if not 1 <= args.jobs <= 32: if not 1 <= args.jobs <= 32:
parser.error("--jobs must be between 1 and 32") parser.error("--jobs must be between 1 and 32")
if args.region:
try:
resolve_regions(args.region)
except ValueError as exc:
parser.error(f"{exc} (known: {', '.join(sorted(REGIONS))})")
for option, values in (("--system", args.system), ("--core", args.core)):
if values is not None and not any(
token.strip() for value in values for token in value.split(",")
):
parser.error(f"{option} needs at least one name")
interactive = sys.stdin.isatty() and not args.no_input
listing = args.list_targets or args.list_systems or args.list_cores
print("RetroBIOS\n") print("RetroBIOS\n")
os_type = detect_os() os_type = detect_os()
@@ -1592,7 +2092,7 @@ def main() -> None:
platforms = detect_platforms(os_type) platforms = detect_platforms(os_type)
if not platforms: if not platforms:
print(" No supported platform detected.") print(" No supported platform detected.")
if not sys.stdin.isatty() or args.list_targets: if not interactive or listing:
for line in _manual_usage_hint(os_type): for line in _manual_usage_hint(os_type):
print(line) print(line)
sys.exit(1) sys.exit(1)
@@ -1600,7 +2100,7 @@ def main() -> None:
for name, path in platforms: for name, path in platforms:
print(f" Found {name.capitalize()} at {path}") print(f" Found {name.capitalize()} at {path}")
if len(platforms) > 1 and not args.list_targets and sys.stdin.isatty(): if len(platforms) > 1 and not listing and interactive:
platforms = _prompt_platform_choice(platforms) platforms = _prompt_platform_choice(platforms)
total_downloaded = 0 total_downloaded = 0
@@ -1666,6 +2166,27 @@ def main() -> None:
omitted_files = _filter_by_target(omitted_files, target_cores) omitted_files = _filter_by_target(omitted_files, target_cores)
print(f" Filtered {before} -> {len(files)} files for target {args.target}") print(f" Filtered {before} -> {len(files)} files for target {args.target}")
selection = _requested_narrowing(args, plat_name, files, omitted_files)
if (
selection is None
and interactive
and not args.check
and not listing
and records_selection(files)
):
selection = _prompt_custom_selection(plat_name, files, omitted_files)
if selection is not None:
print("\n Same choice without questions: "
+ _same_choice_hint(plat_name, *selection))
if selection is not None:
before = len(files)
files, omitted_files = narrow(files, omitted_files, *selection)
print(f" Narrowed {before} -> {len(files)} files")
if args.list_systems or args.list_cores:
_print_choices(plat_name, files, omitted_files, args.list_systems)
continue
total_size = sum(f.get("size", 0) for f in files) total_size = sum(f.get("size", 0) for f in files)
print(f" {len(files)} files ({format_size(total_size)})") print(f" {len(files)} files ({format_size(total_size)})")
if omitted_files: if omitted_files:
@@ -1677,7 +2198,7 @@ def main() -> None:
reason = entry.get("reason", "unknown") reason = entry.get("reason", "unknown")
reasons[reason] = reasons.get(reason, 0) + 1 reasons[reason] = reasons.get(reason, 0) + 1
reason_summary = ", ".join( reason_summary = ", ".join(
f"{reason.replace('_', ' ')}: {count}" f"{OMISSION_REASONS.get(reason, reason)}: {count}"
for reason, count in sorted(reasons.items()) for reason, count in sorted(reasons.items())
) )
print( print(
@@ -1758,7 +2279,7 @@ def main() -> None:
"(use --standalone-copies to opt in)." "(use --standalone-copies to opt in)."
) )
if not args.check and not args.list_targets: if not args.check and not listing:
_report_outcome( _report_outcome(
installed_paths, installed_paths,
total_downloaded, total_downloaded,
+1 -1
View File
@@ -18,7 +18,7 @@ esac
TEMP_INSTALLER="" TEMP_INSTALLER=""
TEMP_DIRECTORY="" TEMP_DIRECTORY=""
DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/install.py" DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
DEFAULT_INSTALL_SHA256="df0adf1fd5088ef9509429fb1d5720e5ca5e7bf46aff9ad0021851dce7349107" DEFAULT_INSTALL_SHA256="d5f352fbe34cfd8ae40c0980c3986158ec1d9dd1babfe2bbc0918ffc0fa0bdf0"
MAX_INSTALLER_BYTES=2097152 MAX_INSTALLER_BYTES=2097152
cleanup() { cleanup() {
+8284 -448
View File
File diff suppressed because it is too large. Load diff
+3127 -127
View File
File diff suppressed because it is too large. Load diff
+1999 -123
View File
File diff suppressed because it is too large. Load diff
+6209 -650
View File
File diff suppressed because it is too large. Load diff
+325 -82
View File
@@ -5,7 +5,7 @@
"platform": "misterfpga", "platform": "misterfpga",
"display_name": "MiSTer FPGA", "display_name": "MiSTer FPGA",
"version": "1.0", "version": "1.0",
"generated": "2026-10-04T20:41:16Z", "generated": "2026-10-10T05:24:26Z",
"base_destination": "games", "base_destination": "games",
"detect": [ "detect": [
{ {
@@ -29,7 +29,10 @@
"sha256": "41b5b8e7c44e55eaf5cfd4f2a58df42f4a1bbc776723368fb25bf118dd631b1b", "sha256": "41b5b8e7c44e55eaf5cfd4f2a58df42f4a1bbc776723368fb25bf118dd631b1b",
"size": 1048576, "size": 1048576,
"repo_path": "bios/3DO Company/3DO/panafz1.bin", "repo_path": "bios/3DO Company/3DO/panafz1.bin",
"cores": null "cores": null,
"systems": [
"3do"
]
}, },
{ {
"dest": "3DO/kanji.rom", "dest": "3DO/kanji.rom",
@@ -37,7 +40,10 @@
"sha256": "8974193e76cf255ebc40fe20b95b405db2cc1ea07910584380e50c6d9ca181aa", "sha256": "8974193e76cf255ebc40fe20b95b405db2cc1ea07910584380e50c6d9ca181aa",
"size": 1048576, "size": 1048576,
"repo_path": "bios/3DO Company/3DO/panafz1j-kanji.bin", "repo_path": "bios/3DO Company/3DO/panafz1j-kanji.bin",
"cores": null "cores": null,
"systems": [
"3do"
]
}, },
{ {
"dest": "Apple-IIgs/boot.rom", "dest": "Apple-IIgs/boot.rom",
@@ -45,7 +51,10 @@
"sha256": "01398a51b3aa16c40bb3d5df43494316cc12eebb87871712cecc396e1beb2df8", "sha256": "01398a51b3aa16c40bb3d5df43494316cc12eebb87871712cecc396e1beb2df8",
"size": 262144, "size": 262144,
"repo_path": "bios/Apple/Apple II/apple2gs.rom", "repo_path": "bios/Apple/Apple II/apple2gs.rom",
"cores": null "cores": null,
"systems": [
"apple-iigs"
]
}, },
{ {
"dest": "Apple-IIgs/boot1.rom", "dest": "Apple-IIgs/boot1.rom",
@@ -53,7 +62,10 @@
"sha256": "34cd454c6201bfd26839d6ace2ff6b1231e09d6279e49b4890311756dc3825fb", "sha256": "34cd454c6201bfd26839d6ace2ff6b1231e09d6279e49b4890311756dc3825fb",
"size": 131072, "size": 131072,
"repo_path": "bios/Apple/Apple IIGS/ROM", "repo_path": "bios/Apple/Apple IIGS/ROM",
"cores": null "cores": null,
"systems": [
"apple-iigs"
]
}, },
{ {
"dest": "Jaguar/boot.rom", "dest": "Jaguar/boot.rom",
@@ -61,7 +73,10 @@
"sha256": "f0bd29599b3dfca7f5620c011256c1dc45c9d82c428a318f965c2ba3ded2e9f9", "sha256": "f0bd29599b3dfca7f5620c011256c1dc45c9d82c428a318f965c2ba3ded2e9f9",
"size": 131072, "size": 131072,
"repo_path": "bios/Atari/Jaguar/boot.rom", "repo_path": "bios/Atari/Jaguar/boot.rom",
"cores": null "cores": null,
"systems": [
"atari-jaguar"
]
}, },
{ {
"dest": "Jaguar/boot1.rom", "dest": "Jaguar/boot1.rom",
@@ -69,7 +84,10 @@
"sha256": "7ebadb56f8082c86d6f74e2c38e02b218db420d3049f8f9c988f6490dc7f9fc0", "sha256": "7ebadb56f8082c86d6f74e2c38e02b218db420d3049f8f9c988f6490dc7f9fc0",
"size": 262144, "size": 262144,
"repo_path": "bios/Atari/Jaguar/boot1.rom", "repo_path": "bios/Atari/Jaguar/boot1.rom",
"cores": null "cores": null,
"systems": [
"atari-jaguar"
]
}, },
{ {
"dest": "Jaguar/boot2.rom", "dest": "Jaguar/boot2.rom",
@@ -77,7 +95,10 @@
"sha256": "bf02591cfe4ab4f082ea23c447da5c53afbfddace6d700b785ba04b7ac5c64e0", "sha256": "bf02591cfe4ab4f082ea23c447da5c53afbfddace6d700b785ba04b7ac5c64e0",
"size": 131072, "size": 131072,
"repo_path": "bios/Atari/Jaguar/boot2.rom", "repo_path": "bios/Atari/Jaguar/boot2.rom",
"cores": null "cores": null,
"systems": [
"atari-jaguar"
]
}, },
{ {
"dest": "AtariLynx/boot.rom", "dest": "AtariLynx/boot.rom",
@@ -85,7 +106,10 @@
"sha256": "c26a36c1990bcf841155e5a6fea4d2ee1a4d53b3cc772e70f257a962ad43b383", "sha256": "c26a36c1990bcf841155e5a6fea4d2ee1a4d53b3cc772e70f257a962ad43b383",
"size": 512, "size": 512,
"repo_path": "bios/Atari/Lynx/Atari_LYNX_boot.img", "repo_path": "bios/Atari/Lynx/Atari_LYNX_boot.img",
"cores": null "cores": null,
"systems": [
"atari-lynx"
]
}, },
{ {
"dest": "Astrocade/boot.rom", "dest": "Astrocade/boot.rom",
@@ -93,7 +117,10 @@
"sha256": "f01220d94b82145a6d18f114ff59be9c178f69de24f8ad0f4d19eee8b86b17c7", "sha256": "f01220d94b82145a6d18f114ff59be9c178f69de24f8ad0f4d19eee8b86b17c7",
"size": 8192, "size": 8192,
"repo_path": "bios/Bally/Astrocade/astro.bin", "repo_path": "bios/Bally/Astrocade/astro.bin",
"cores": null "cores": null,
"systems": [
"bally-astrocade"
]
}, },
{ {
"dest": "Casio_PV-2000/boot0.rom", "dest": "Casio_PV-2000/boot0.rom",
@@ -101,7 +128,10 @@
"sha256": "2587d206eb63917848ea30e14232448ed4f873c6796a22e2bf6543b01f46308e", "sha256": "2587d206eb63917848ea30e14232448ed4f873c6796a22e2bf6543b01f46308e",
"size": 16384, "size": 16384,
"repo_path": "bios/Casio/PV-2000/hn613128pc64.bin", "repo_path": "bios/Casio/PV-2000/hn613128pc64.bin",
"cores": null "cores": null,
"systems": [
"casio-pv2000"
]
}, },
{ {
"dest": "CreatiVision/boot.rom", "dest": "CreatiVision/boot.rom",
@@ -109,7 +139,10 @@
"sha256": "48a7fda99c1b9c0d90b70de0b4a9431a5a781f94d1874ee6d91830a638c9e5c3", "sha256": "48a7fda99c1b9c0d90b70de0b4a9431a5a781f94d1874ee6d91830a638c9e5c3",
"size": 16384, "size": 16384,
"repo_path": "bios/VTech/CreatiVision/laser2001.rom", "repo_path": "bios/VTech/CreatiVision/laser2001.rom",
"cores": null "cores": null,
"systems": [
"creativision"
]
}, },
{ {
"dest": "SCV/boot.rom", "dest": "SCV/boot.rom",
@@ -117,7 +150,10 @@
"sha256": "25c7ff2d735d1ae895af9262f31e083c5c1bd99a8f31b5b378dfbc0f166ec97f", "sha256": "25c7ff2d735d1ae895af9262f31e083c5c1bd99a8f31b5b378dfbc0f166ec97f",
"size": 6144, "size": 6144,
"repo_path": "bios/Epoch/Super Cassette Vision/boot.rom", "repo_path": "bios/Epoch/Super Cassette Vision/boot.rom",
"cores": null "cores": null,
"systems": [
"epoch-scv"
]
}, },
{ {
"dest": "Gamate/boot.rom", "dest": "Gamate/boot.rom",
@@ -125,7 +161,10 @@
"sha256": "26f206fa4eedbe040f619e3af78efa1d38588535b1d746f6a462da639ced4b1e", "sha256": "26f206fa4eedbe040f619e3af78efa1d38588535b1d746f6a462da639ced4b1e",
"size": 4096, "size": 4096,
"repo_path": "bios/Bit Corporation/Gamate/gamate_bios_umc.bin", "repo_path": "bios/Bit Corporation/Gamate/gamate_bios_umc.bin",
"cores": null "cores": null,
"systems": [
"gamate"
]
}, },
{ {
"dest": "GameKing/boot.rom", "dest": "GameKing/boot.rom",
@@ -133,7 +172,10 @@
"sha256": "7843fd082889cac046d446160f38755f7c8c4a8e4f9dd9d5104b185f0bc1dec4", "sha256": "7843fd082889cac046d446160f38755f7c8c4a8e4f9dd9d5104b185f0bc1dec4",
"size": 524288, "size": 524288,
"repo_path": "bios/TimeTop/GameKing/boot.rom", "repo_path": "bios/TimeTop/GameKing/boot.rom",
"cores": null "cores": null,
"systems": [
"gameking"
]
}, },
{ {
"dest": "Interact/boot.rom", "dest": "Interact/boot.rom",
@@ -141,7 +183,10 @@
"sha256": "e0888ba82382aabb7557aa4a3445e613bd7f33bae1af5ece211e2cb6bc9ffa82", "sha256": "e0888ba82382aabb7557aa4a3445e613bd7f33bae1af5ece211e2cb6bc9ffa82",
"size": 2048, "size": 2048,
"repo_path": "bios/Interact/Home Computer/interact.rom", "repo_path": "bios/Interact/Home Computer/interact.rom",
"cores": null "cores": null,
"systems": [
"interact"
]
}, },
{ {
"dest": "Loopy/boot0.rom", "dest": "Loopy/boot0.rom",
@@ -149,7 +194,10 @@
"sha256": "82a8d6add06e324e36c71b0a7a92d607af3db5e65574a6892794d70df668e9d5", "sha256": "82a8d6add06e324e36c71b0a7a92d607af3db5e65574a6892794d70df668e9d5",
"size": 32768, "size": 32768,
"repo_path": "bios/Casio/Loopy/boot0.rom", "repo_path": "bios/Casio/Loopy/boot0.rom",
"cores": null "cores": null,
"systems": [
"loopy"
]
}, },
{ {
"dest": "Loopy/boot1.rom", "dest": "Loopy/boot1.rom",
@@ -157,7 +205,10 @@
"sha256": "7540ff64268e500a34fb83c8642510d341440ac6258f6c36851e182870e40f75", "sha256": "7540ff64268e500a34fb83c8642510d341440ac6258f6c36851e182870e40f75",
"size": 524288, "size": 524288,
"repo_path": "bios/Casio/Loopy/boot1.rom", "repo_path": "bios/Casio/Loopy/boot1.rom",
"cores": null "cores": null,
"systems": [
"loopy"
]
}, },
{ {
"dest": "Marty/mrom.m36", "dest": "Marty/mrom.m36",
@@ -165,7 +216,10 @@
"sha256": "2cd7466183ba965259df35ff078b7b057a99086feea0050ddf87aebc6932f429", "sha256": "2cd7466183ba965259df35ff078b7b057a99086feea0050ddf87aebc6932f429",
"size": 2097152, "size": 2097152,
"repo_path": "bios/Fujitsu/FM Towns/mrom.m36", "repo_path": "bios/Fujitsu/FM Towns/mrom.m36",
"cores": null "cores": null,
"systems": [
"marty"
]
}, },
{ {
"dest": "Marty/mrom.m37", "dest": "Marty/mrom.m37",
@@ -173,7 +227,10 @@
"sha256": "10fc6d984d0a440c3410d805f4bea247fb5dd5a509befff481164923f883797d", "sha256": "10fc6d984d0a440c3410d805f4bea247fb5dd5a509befff481164923f883797d",
"size": 2097152, "size": 2097152,
"repo_path": "bios/Fujitsu/FM Towns/mrom.m37", "repo_path": "bios/Fujitsu/FM Towns/mrom.m37",
"cores": null "cores": null,
"systems": [
"marty"
]
}, },
{ {
"dest": "Intellivision/boot0.rom", "dest": "Intellivision/boot0.rom",
@@ -181,7 +238,10 @@
"sha256": "1aeb614856beba95463166daf09304b414d5617d3f37d221724b3337fc4b2722", "sha256": "1aeb614856beba95463166daf09304b414d5617d3f37d221724b3337fc4b2722",
"size": 8192, "size": 8192,
"repo_path": "bios/Mattel/Intellivision/INTV_EROM.bin", "repo_path": "bios/Mattel/Intellivision/INTV_EROM.bin",
"cores": null "cores": null,
"systems": [
"mattel-intellivision"
]
}, },
{ {
"dest": "Intellivision/boot1.rom", "dest": "Intellivision/boot1.rom",
@@ -189,7 +249,10 @@
"sha256": "a80b6841182547d08635ad30a6af71441c4c9eed9391b3dd22feb30d8e50cc85", "sha256": "a80b6841182547d08635ad30a6af71441c4c9eed9391b3dd22feb30d8e50cc85",
"size": 2048, "size": 2048,
"repo_path": "bios/Mattel/Intellivision/INTV_GROM.bin", "repo_path": "bios/Mattel/Intellivision/INTV_GROM.bin",
"cores": null "cores": null,
"systems": [
"mattel-intellivision"
]
}, },
{ {
"dest": "Intellivision/boot2.rom", "dest": "Intellivision/boot2.rom",
@@ -197,7 +260,10 @@
"sha256": "dabade432af1523f43029057071ca8c1ed3604498c784603e8733023382ad7a5", "sha256": "dabade432af1523f43029057071ca8c1ed3604498c784603e8733023382ad7a5",
"size": 2048, "size": 2048,
"repo_path": "bios/Mattel/Intellivision/IVOICE.BIN", "repo_path": "bios/Mattel/Intellivision/IVOICE.BIN",
"cores": null "cores": null,
"systems": [
"mattel-intellivision"
]
}, },
{ {
"dest": "Intellivision/boot3.rom", "dest": "Intellivision/boot3.rom",
@@ -205,7 +271,10 @@
"sha256": "2fcea60039c4a8836b9bb71e391d70a145c724b4dae516be733fe842ba6687c7", "sha256": "2fcea60039c4a8836b9bb71e391d70a145c724b4dae516be733fe842ba6687c7",
"size": 24576, "size": 24576,
"repo_path": "bios/Mattel/Intellivision/ECS.bin", "repo_path": "bios/Mattel/Intellivision/ECS.bin",
"cores": null "cores": null,
"systems": [
"mattel-intellivision"
]
}, },
{ {
"dest": "MSX1/boot.rom", "dest": "MSX1/boot.rom",
@@ -213,7 +282,10 @@
"sha256": "04fd77f02f652770cbb3cc6b4a594cc736d56486a106c1068965f1fe090746a5", "sha256": "04fd77f02f652770cbb3cc6b4a594cc736d56486a106c1068965f1fe090746a5",
"size": 32768, "size": 32768,
"repo_path": "bios/Microsoft/MSX/share/systemroms/vg8020_basic-bios1.rom", "repo_path": "bios/Microsoft/MSX/share/systemroms/vg8020_basic-bios1.rom",
"cores": null "cores": null,
"systems": [
"microsoft-msx"
]
}, },
{ {
"dest": "PC8801/boot.rom", "dest": "PC8801/boot.rom",
@@ -221,7 +293,10 @@
"sha256": "0dc3fcf58c90c1f119a710babd54b57babf81ba8573c070b33f0cc21948b3086", "sha256": "0dc3fcf58c90c1f119a710babd54b57babf81ba8573c070b33f0cc21948b3086",
"size": 393216, "size": 393216,
"repo_path": "bios/NEC/PC-88/PC8801 MKII SR BIOS.rom", "repo_path": "bios/NEC/PC-88/PC8801 MKII SR BIOS.rom",
"cores": null "cores": null,
"systems": [
"nec-pc-88"
]
}, },
{ {
"dest": "TGFX16-CD/cd_bios.rom", "dest": "TGFX16-CD/cd_bios.rom",
@@ -229,7 +304,10 @@
"sha256": "b057e09822c3767120ba56f2ce02f089b5824d4b489ea3cd7ad9405b3a339a52", "sha256": "b057e09822c3767120ba56f2ce02f089b5824d4b489ea3cd7ad9405b3a339a52",
"size": 262144, "size": 262144,
"repo_path": "bios/NEC/PC Engine CD/Super CD 3.1.pce", "repo_path": "bios/NEC/PC Engine CD/Super CD 3.1.pce",
"cores": null "cores": null,
"systems": [
"nec-pc-engine-cd"
]
}, },
{ {
"dest": "NGPC/boot0.rom", "dest": "NGPC/boot0.rom",
@@ -237,7 +315,10 @@
"sha256": "8fb845a2f71514cec20728e2f0fecfade69444f8d50898b92c2259f1ba63e10d", "sha256": "8fb845a2f71514cec20728e2f0fecfade69444f8d50898b92c2259f1ba63e10d",
"size": 65536, "size": 65536,
"repo_path": "bios/SNK/Neo Geo Pocket/SNK Neo-Geo Pocket Color BIOS (1999)(SNK)(en-ja).bin", "repo_path": "bios/SNK/Neo Geo Pocket/SNK Neo-Geo Pocket Color BIOS (1999)(SNK)(en-ja).bin",
"cores": null "cores": null,
"systems": [
"ngpc"
]
}, },
{ {
"dest": "NGPC/boot1.rom", "dest": "NGPC/boot1.rom",
@@ -245,7 +326,10 @@
"sha256": "0293555b21c4fac516d25199df7809b26beeae150e1d4504a050db32264a6ad7", "sha256": "0293555b21c4fac516d25199df7809b26beeae150e1d4504a050db32264a6ad7",
"size": 65536, "size": 65536,
"repo_path": "bios/SNK/Neo Geo Pocket/SNK Neo-Geo Pocket BIOS (1998)(SNK)(en-ja).bin", "repo_path": "bios/SNK/Neo Geo Pocket/SNK Neo-Geo Pocket BIOS (1998)(SNK)(en-ja).bin",
"cores": null "cores": null,
"systems": [
"ngpc"
]
}, },
{ {
"dest": "N64/boot.rom", "dest": "N64/boot.rom",
@@ -253,7 +337,10 @@
"sha256": "fa7b09795ef1e54461e59f6f2d902368133e3f1cd980e34383e6a780d74beffd", "sha256": "fa7b09795ef1e54461e59f6f2d902368133e3f1cd980e34383e6a780d74beffd",
"size": 1984, "size": 1984,
"repo_path": "bios/Nintendo/Nintendo 64/boot.rom", "repo_path": "bios/Nintendo/Nintendo 64/boot.rom",
"cores": null "cores": null,
"systems": [
"nintendo-64"
]
}, },
{ {
"dest": "N64/boot1.rom", "dest": "N64/boot1.rom",
@@ -261,7 +348,10 @@
"sha256": "79c81fffae934b6df251da6c3a394f52c6a0eaa07115a3b9677743e3c1335f4a", "sha256": "79c81fffae934b6df251da6c3a394f52c6a0eaa07115a3b9677743e3c1335f4a",
"size": 1984, "size": 1984,
"repo_path": "bios/Nintendo/Nintendo 64/boot1.rom", "repo_path": "bios/Nintendo/Nintendo 64/boot1.rom",
"cores": null "cores": null,
"systems": [
"nintendo-64"
]
}, },
{ {
"dest": "N64/boot3.rom", "dest": "N64/boot3.rom",
@@ -269,7 +359,10 @@
"sha256": "806400ec0df94b0755de6c5b8249d6b6a9866124c5ddbdac198bde22499bfb8b", "sha256": "806400ec0df94b0755de6c5b8249d6b6a9866124c5ddbdac198bde22499bfb8b",
"size": 4194304, "size": 4194304,
"repo_path": "bios/Nintendo/64DD/64DD_IPL_v12_JPN.bin", "repo_path": "bios/Nintendo/64DD/64DD_IPL_v12_JPN.bin",
"cores": null "cores": null,
"systems": [
"nintendo-64"
]
}, },
{ {
"dest": "N64/boot4.rom", "dest": "N64/boot4.rom",
@@ -277,7 +370,10 @@
"sha256": "9c2962a8b994a29e4cd04b3a6e4ed730a751414655ab6a9799ebf5fc08b79d44", "sha256": "9c2962a8b994a29e4cd04b3a6e4ed730a751414655ab6a9799ebf5fc08b79d44",
"size": 4194304, "size": 4194304,
"repo_path": "bios/Nintendo/64DD/64DD_IPL_DEV.bin", "repo_path": "bios/Nintendo/64DD/64DD_IPL_DEV.bin",
"cores": null "cores": null,
"systems": [
"nintendo-64"
]
}, },
{ {
"dest": "N64/boot5.rom", "dest": "N64/boot5.rom",
@@ -285,7 +381,10 @@
"sha256": "e9fec87a45fba02399e88064b9e2f8cf0f2106e351c58279a87f05da5bc984ad", "sha256": "e9fec87a45fba02399e88064b9e2f8cf0f2106e351c58279a87f05da5bc984ad",
"size": 4194304, "size": 4194304,
"repo_path": "bios/Nintendo/64DD/64DD_IPL_USA.bin", "repo_path": "bios/Nintendo/64DD/64DD_IPL_USA.bin",
"cores": null "cores": null,
"systems": [
"nintendo-64"
]
}, },
{ {
"dest": "GAMEBOY/boot1.rom", "dest": "GAMEBOY/boot1.rom",
@@ -293,7 +392,10 @@
"sha256": "b4f2e416a35eef52cba161b159c7c8523a92594facb924b3ede0d722867c50c7", "sha256": "b4f2e416a35eef52cba161b159c7c8523a92594facb924b3ede0d722867c50c7",
"size": 2304, "size": 2304,
"repo_path": "bios/Nintendo/Game Boy Color/GBC_cgb.bin", "repo_path": "bios/Nintendo/Game Boy Color/GBC_cgb.bin",
"cores": null "cores": null,
"systems": [
"nintendo-gb"
]
}, },
{ {
"dest": "GBA/boot.rom", "dest": "GBA/boot.rom",
@@ -301,7 +403,10 @@
"sha256": "fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570", "sha256": "fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570",
"size": 16384, "size": 16384,
"repo_path": "bios/Nintendo/Game Boy Advance/GBA_bios.rom", "repo_path": "bios/Nintendo/Game Boy Advance/GBA_bios.rom",
"cores": null "cores": null,
"systems": [
"nintendo-gba"
]
}, },
{ {
"dest": "NES/boot0.rom", "dest": "NES/boot0.rom",
@@ -309,7 +414,10 @@
"sha256": "99c18490ed9002d9c6d999b9d8d15be5c051bdfa7cc7e73318053c9a994b0178", "sha256": "99c18490ed9002d9c6d999b9d8d15be5c051bdfa7cc7e73318053c9a994b0178",
"size": 8192, "size": 8192,
"repo_path": "bios/Arcade/FBNeo/fdsbios.nes", "repo_path": "bios/Arcade/FBNeo/fdsbios.nes",
"cores": null "cores": null,
"systems": [
"nintendo-nes"
]
}, },
{ {
"dest": "PokemonMini/boot.rom", "dest": "PokemonMini/boot.rom",
@@ -317,7 +425,10 @@
"sha256": "45a1c7f28b9ad585e67f047abe9c1c956724bfcab8c9011002af4274e7c50e8f", "sha256": "45a1c7f28b9ad585e67f047abe9c1c956724bfcab8c9011002af4274e7c50e8f",
"size": 4096, "size": 4096,
"repo_path": "bios/Nintendo/Pokemon Mini/bios.min", "repo_path": "bios/Nintendo/Pokemon Mini/bios.min",
"cores": null "cores": null,
"systems": [
"nintendo-pokemon-mini"
]
}, },
{ {
"dest": "SGB/Super Game Boy 2.sfc", "dest": "SGB/Super Game Boy 2.sfc",
@@ -325,7 +436,10 @@
"sha256": "c172498a23d1176672931bab33b629c7d28f914a43dca9e540b8af1b37ccf2c6", "sha256": "c172498a23d1176672931bab33b629c7d28f914a43dca9e540b8af1b37ccf2c6",
"size": 524288, "size": 524288,
"repo_path": "bios/Nintendo/Super Game Boy/SGB2.sfc/program.rom", "repo_path": "bios/Nintendo/Super Game Boy/SGB2.sfc/program.rom",
"cores": null "cores": null,
"systems": [
"nintendo-sgb"
]
}, },
{ {
"dest": "SGB/Super Game Boy.sfc", "dest": "SGB/Super Game Boy.sfc",
@@ -333,7 +447,10 @@
"sha256": "a75160f7b89b1f0e20fd2f6441bb86285c7378db5035ef6885485eaff6059376", "sha256": "a75160f7b89b1f0e20fd2f6441bb86285c7378db5035ef6885485eaff6059376",
"size": 262144, "size": 262144,
"repo_path": "bios/Nintendo/Super Game Boy/SGB1.sfc/program.rom", "repo_path": "bios/Nintendo/Super Game Boy/SGB1.sfc/program.rom",
"cores": null "cores": null,
"systems": [
"nintendo-sgb"
]
}, },
{ {
"dest": "SNES/bsx_bios.rom", "dest": "SNES/bsx_bios.rom",
@@ -341,7 +458,10 @@
"sha256": "a49827b45ff9ac9cf5b4658190e1428e59251bc82d8a63d8e9e0f71e439f008f", "sha256": "a49827b45ff9ac9cf5b4658190e1428e59251bc82d8a63d8e9e0f71e439f008f",
"size": 1048576, "size": 1048576,
"repo_path": "bios/Nintendo/SNES/BS-X.bin", "repo_path": "bios/Nintendo/SNES/BS-X.bin",
"cores": null "cores": null,
"systems": [
"nintendo-snes"
]
}, },
{ {
"dest": "CD-i/boot0.rom", "dest": "CD-i/boot0.rom",
@@ -349,7 +469,10 @@
"sha256": "fd123e66beadaf844cb220a44166ea33f9fd0d64bafb9e6399febff445429db2", "sha256": "fd123e66beadaf844cb220a44166ea33f9fd0d64bafb9e6399febff445429db2",
"size": 524288, "size": 524288,
"repo_path": "bios/Philips/CD-i/cdi220b.rom", "repo_path": "bios/Philips/CD-i/cdi220b.rom",
"cores": null "cores": null,
"systems": [
"philips-cdi"
]
}, },
{ {
"dest": "CD-i/boot1.rom", "dest": "CD-i/boot1.rom",
@@ -357,7 +480,10 @@
"sha256": "15794460f2ad630f6dd483a02ef958818c7a5c65f8afe76364bc9ffb7ccad3cc", "sha256": "15794460f2ad630f6dd483a02ef958818c7a5c65f8afe76364bc9ffb7ccad3cc",
"size": 8192, "size": 8192,
"repo_path": "bios/Philips/CD-i/zx405042p__cdi_slave_2.0__b43t__zzmk9213.mc68hc705c8a_withtestrom.7206", "repo_path": "bios/Philips/CD-i/zx405042p__cdi_slave_2.0__b43t__zzmk9213.mc68hc705c8a_withtestrom.7206",
"cores": null "cores": null,
"systems": [
"philips-cdi"
]
}, },
{ {
"dest": "CD-i/boot2.rom", "dest": "CD-i/boot2.rom",
@@ -365,7 +491,10 @@
"sha256": "e8a8cabb23650b53c2cb0763c3a1409f6c0d580a52fbe8ef834d44d4021db165", "sha256": "e8a8cabb23650b53c2cb0763c3a1409f6c0d580a52fbe8ef834d44d4021db165",
"size": 131072, "size": 131072,
"repo_path": "bios/Philips/CD-i/vmpega_split.rom", "repo_path": "bios/Philips/CD-i/vmpega_split.rom",
"cores": null "cores": null,
"systems": [
"philips-cdi"
]
}, },
{ {
"dest": "PocketChallengeV2/boot.rom", "dest": "PocketChallengeV2/boot.rom",
@@ -373,7 +502,10 @@
"sha256": "bf4480dbea1c47c8b54ce7be9382bc1006148f431fbe4277e136351fa74f635e", "sha256": "bf4480dbea1c47c8b54ce7be9382bc1006148f431fbe4277e136351fa74f635e",
"size": 4096, "size": 4096,
"repo_path": "bios/Bandai/WonderSwan/bootrom.ws", "repo_path": "bios/Bandai/WonderSwan/bootrom.ws",
"cores": null "cores": null,
"systems": [
"pocket-challenge-v2"
]
}, },
{ {
"dest": "PocketChallengeV2/boot1.rom", "dest": "PocketChallengeV2/boot1.rom",
@@ -381,7 +513,10 @@
"sha256": "f5a5c044d84ce1681f94e9ef74287cb989784497be5bd5108df17908dfa55db2", "sha256": "f5a5c044d84ce1681f94e9ef74287cb989784497be5bd5108df17908dfa55db2",
"size": 8192, "size": 8192,
"repo_path": "bios/Bandai/WonderSwan/bootrom.wsc", "repo_path": "bios/Bandai/WonderSwan/bootrom.wsc",
"cores": null "cores": null,
"systems": [
"pocket-challenge-v2"
]
}, },
{ {
"dest": "MegaCD/Europe/cd_bios.rom", "dest": "MegaCD/Europe/cd_bios.rom",
@@ -389,7 +524,10 @@
"sha256": "fe608a2a07676a23ab5fd5eee2f53c9e2526d69a28aa16ccd85c0ec42e6933cb", "sha256": "fe608a2a07676a23ab5fd5eee2f53c9e2526d69a28aa16ccd85c0ec42e6933cb",
"size": 131072, "size": 131072,
"repo_path": "bios/Sega/Mega CD/eu_mcd2_9303.bin", "repo_path": "bios/Sega/Mega CD/eu_mcd2_9303.bin",
"cores": null "cores": null,
"systems": [
"sega-mega-cd"
]
}, },
{ {
"dest": "MegaCD/Japan/cd_bios.rom", "dest": "MegaCD/Japan/cd_bios.rom",
@@ -397,7 +535,10 @@
"sha256": "7133fc2dd2fe5b7d0acd53a5f10f3d00b5d31270239ad20d74ef32393e24af88", "sha256": "7133fc2dd2fe5b7d0acd53a5f10f3d00b5d31270239ad20d74ef32393e24af88",
"size": 131072, "size": 131072,
"repo_path": "bios/Sega/Mega CD/jp_mcd2_921222.bin", "repo_path": "bios/Sega/Mega CD/jp_mcd2_921222.bin",
"cores": null "cores": null,
"systems": [
"sega-mega-cd"
]
}, },
{ {
"dest": "MegaCD/USA/cd_bios.rom", "dest": "MegaCD/USA/cd_bios.rom",
@@ -405,7 +546,10 @@
"sha256": "fb477cdbf94c84424c2feca4fe40656d85393fe7b7b401911b45ad2eb991258c", "sha256": "fb477cdbf94c84424c2feca4fe40656d85393fe7b7b401911b45ad2eb991258c",
"size": 131072, "size": 131072,
"repo_path": "bios/Sega/Mega CD/SegaCDBIOS9303.bin", "repo_path": "bios/Sega/Mega CD/SegaCDBIOS9303.bin",
"cores": null "cores": null,
"systems": [
"sega-mega-cd"
]
}, },
{ {
"dest": "MegaCD/boot.rom", "dest": "MegaCD/boot.rom",
@@ -413,7 +557,10 @@
"sha256": "59403e7f8be1d4e98d673cca17cfec072770a56415165ad7ac88dcd3f7273317", "sha256": "59403e7f8be1d4e98d673cca17cfec072770a56415165ad7ac88dcd3f7273317",
"size": 131072, "size": 131072,
"repo_path": "bios/Sega/Mega CD/US Sega CD 2 (Region Free) 930601 l_oliveira.bin", "repo_path": "bios/Sega/Mega CD/US Sega CD 2 (Region Free) 930601 l_oliveira.bin",
"cores": null "cores": null,
"systems": [
"sega-mega-cd"
]
}, },
{ {
"dest": "Saturn/boot.rom", "dest": "Saturn/boot.rom",
@@ -421,7 +568,10 @@
"sha256": "37561c4444bec52ae1aae31de5579f1856d5b23002ee083ade42f9ed13c8616f", "sha256": "37561c4444bec52ae1aae31de5579f1856d5b23002ee083ade42f9ed13c8616f",
"size": 524288, "size": 524288,
"repo_path": "bios/Sega/Saturn/.variants/hisaturn_v103.bin", "repo_path": "bios/Sega/Saturn/.variants/hisaturn_v103.bin",
"cores": null "cores": null,
"systems": [
"sega-saturn"
]
}, },
{ {
"dest": "NEOGEO/000-lo.lo", "dest": "NEOGEO/000-lo.lo",
@@ -429,7 +579,10 @@
"sha256": "2a76f023dc578804c6bcd58ed514ffc04fcb433b3bd75d4452575ffda57a0707", "sha256": "2a76f023dc578804c6bcd58ed514ffc04fcb433b3bd75d4452575ffda57a0707",
"size": 131072, "size": 131072,
"repo_path": "bios/SNK/Neo Geo CD/000-lo.lo", "repo_path": "bios/SNK/Neo Geo CD/000-lo.lo",
"cores": null "cores": null,
"systems": [
"snk-neogeo"
]
}, },
{ {
"dest": "NEOGEO/neo-epo.sp1", "dest": "NEOGEO/neo-epo.sp1",
@@ -437,7 +590,10 @@
"sha256": "70f7906d68acbc3630b6e7792ebb485288b05ca72c6d4c243e07e230aae8d1e4", "sha256": "70f7906d68acbc3630b6e7792ebb485288b05ca72c6d4c243e07e230aae8d1e4",
"size": 131072, "size": 131072,
"repo_path": "bios/SNK/Neo Geo/neo-epo.bin", "repo_path": "bios/SNK/Neo Geo/neo-epo.bin",
"cores": null "cores": null,
"systems": [
"snk-neogeo"
]
}, },
{ {
"dest": "NEOGEO/sfix.sfix", "dest": "NEOGEO/sfix.sfix",
@@ -445,7 +601,10 @@
"sha256": "fcf51e688c006408be92c5adaf37d4302d05e7e681193dd0623072e53915c90e", "sha256": "fcf51e688c006408be92c5adaf37d4302d05e7e681193dd0623072e53915c90e",
"size": 131072, "size": 131072,
"repo_path": "bios/SNK/Neo Geo/sfix.sfix", "repo_path": "bios/SNK/Neo Geo/sfix.sfix",
"cores": null "cores": null,
"systems": [
"snk-neogeo"
]
}, },
{ {
"dest": "NEOGEO/sp-s2.sp1", "dest": "NEOGEO/sp-s2.sp1",
@@ -453,7 +612,10 @@
"sha256": "ae1c5cc9a0acdead8a3d5a16ee84f575582f2209677464b05a2b8b0451f658a1", "sha256": "ae1c5cc9a0acdead8a3d5a16ee84f575582f2209677464b05a2b8b0451f658a1",
"size": 131072, "size": 131072,
"repo_path": "bios/SNK/Neo Geo/sp-s2.sp1", "repo_path": "bios/SNK/Neo Geo/sp-s2.sp1",
"cores": null "cores": null,
"systems": [
"snk-neogeo"
]
}, },
{ {
"dest": "NEOGEO/uni-bios.rom", "dest": "NEOGEO/uni-bios.rom",
@@ -461,7 +623,10 @@
"sha256": "e016ce75097df0b5f5910e8eb4914439f5c77511de65df5a1e089eef147b256b", "sha256": "e016ce75097df0b5f5910e8eb4914439f5c77511de65df5a1e089eef147b256b",
"size": 131072, "size": 131072,
"repo_path": "bios/SNK/Neo Geo/uni-bios_4_0.rom", "repo_path": "bios/SNK/Neo Geo/uni-bios_4_0.rom",
"cores": null "cores": null,
"systems": [
"snk-neogeo"
]
}, },
{ {
"dest": "NeoGeo-CD/neocd.bin", "dest": "NeoGeo-CD/neocd.bin",
@@ -469,7 +634,10 @@
"sha256": "2e93af5848080ea04d17a7841b742f009330d30e4ff40c3410547581d921c892", "sha256": "2e93af5848080ea04d17a7841b742f009330d30e4ff40c3410547581d921c892",
"size": 524288, "size": 524288,
"repo_path": "bios/SNK/Neo Geo CD/neocd.bin", "repo_path": "bios/SNK/Neo Geo CD/neocd.bin",
"cores": null "cores": null,
"systems": [
"snk-neogeo-cd"
]
}, },
{ {
"dest": "NeoGeo-CD/top-sp1.bin", "dest": "NeoGeo-CD/top-sp1.bin",
@@ -477,7 +645,10 @@
"sha256": "c11c33589b2057008a7e4c7c700fcd989a8c0f95f869e7e7539fdd00414d99a7", "sha256": "c11c33589b2057008a7e4c7c700fcd989a8c0f95f869e7e7539fdd00414d99a7",
"size": 524288, "size": 524288,
"repo_path": "bios/SNK/Neo Geo CD/top-sp1.bin", "repo_path": "bios/SNK/Neo Geo CD/top-sp1.bin",
"cores": null "cores": null,
"systems": [
"snk-neogeo-cd"
]
}, },
{ {
"dest": "NeoGeo-CD/uni-bioscd.rom", "dest": "NeoGeo-CD/uni-bioscd.rom",
@@ -485,7 +656,10 @@
"sha256": "1c3ec20824a58e5f5cbf47ccc2c91a10f34d21cfa2791b3413cea89b0c920db8", "sha256": "1c3ec20824a58e5f5cbf47ccc2c91a10f34d21cfa2791b3413cea89b0c920db8",
"size": 524288, "size": 524288,
"repo_path": "bios/SNK/Neo Geo/uni-bioscd33.rom", "repo_path": "bios/SNK/Neo Geo/uni-bioscd33.rom",
"cores": null "cores": null,
"systems": [
"snk-neogeo-cd"
]
}, },
{ {
"dest": "PSX/boot.rom", "dest": "PSX/boot.rom",
@@ -493,7 +667,10 @@
"sha256": "39dcc1a0717036c9b6ac52fefd1ee7a57d3808e8cfbc755879fa685a0a738278", "sha256": "39dcc1a0717036c9b6ac52fefd1ee7a57d3808e8cfbc755879fa685a0a738278",
"size": 524288, "size": 524288,
"repo_path": "bios/Sony/PlayStation/scph7001.bin", "repo_path": "bios/Sony/PlayStation/scph7001.bin",
"cores": null "cores": null,
"systems": [
"sony-playstation"
]
}, },
{ {
"dest": "PSX/boot1.rom", "dest": "PSX/boot1.rom",
@@ -501,7 +678,10 @@
"sha256": "e900504d1755f021f861b82c8258c5e6658c7b592f800cccd91f5d32ea380d28", "sha256": "e900504d1755f021f861b82c8258c5e6658c7b592f800cccd91f5d32ea380d28",
"size": 524288, "size": 524288,
"repo_path": "bios/Sony/PlayStation/scph7000.bin", "repo_path": "bios/Sony/PlayStation/scph7000.bin",
"cores": null "cores": null,
"systems": [
"sony-playstation"
]
}, },
{ {
"dest": "PSX/boot2.rom", "dest": "PSX/boot2.rom",
@@ -509,7 +689,10 @@
"sha256": "5e84a94818cf5282f4217591fefd88be36b9b174b3cc7cb0bcd75199beb450f1", "sha256": "5e84a94818cf5282f4217591fefd88be36b9b174b3cc7cb0bcd75199beb450f1",
"size": 524288, "size": 524288,
"repo_path": "bios/Sony/PlayStation/scph7002.bin", "repo_path": "bios/Sony/PlayStation/scph7002.bin",
"cores": null "cores": null,
"systems": [
"sony-playstation"
]
}, },
{ {
"dest": "PSX/sbi.zip", "dest": "PSX/sbi.zip",
@@ -517,7 +700,10 @@
"sha256": "32db7bb64131623fa912ef30a85f7d70cee2abbca56c21d360d2025e12b8aca9", "sha256": "32db7bb64131623fa912ef30a85f7d70cee2abbca56c21d360d2025e12b8aca9",
"size": 218953, "size": 218953,
"repo_path": "bios/Sony/PlayStation/sbi.zip", "repo_path": "bios/Sony/PlayStation/sbi.zip",
"cores": null "cores": null,
"systems": [
"sony-playstation"
]
}, },
{ {
"dest": "Studio-II/boot0.rom", "dest": "Studio-II/boot0.rom",
@@ -525,7 +711,10 @@
"sha256": "1e7ae3cd972eb39681e6c6df5f1ac6f7d86c9e59788e40d6b4c948375e9ca5f7", "sha256": "1e7ae3cd972eb39681e6c6df5f1ac6f7d86c9e59788e40d6b4c948375e9ca5f7",
"size": 2048, "size": 2048,
"repo_path": "bios/RCA/Studio II/boot0.rom", "repo_path": "bios/RCA/Studio II/boot0.rom",
"cores": null "cores": null,
"systems": [
"studio-ii"
]
}, },
{ {
"dest": "Studio-II/boot1.rom", "dest": "Studio-II/boot1.rom",
@@ -533,7 +722,10 @@
"sha256": "e90cb0c5be993689a2d4fa03e664f5943967160153520d435391aaefff06da35", "sha256": "e90cb0c5be993689a2d4fa03e664f5943967160153520d435391aaefff06da35",
"size": 4096, "size": 4096,
"repo_path": "bios/RCA/Studio II/boot1.rom", "repo_path": "bios/RCA/Studio II/boot1.rom",
"cores": null "cores": null,
"systems": [
"studio-ii"
]
}, },
{ {
"dest": "Studio-II/boot2.rom", "dest": "Studio-II/boot2.rom",
@@ -541,7 +733,10 @@
"sha256": "a0d6c1852fb407f86d25c802441ff15ec7fd041683c6a9ae0d23db0ca0df8c0b", "sha256": "a0d6c1852fb407f86d25c802441ff15ec7fd041683c6a9ae0d23db0ca0df8c0b",
"size": 4096, "size": 4096,
"repo_path": "bios/RCA/Studio II/boot2.rom", "repo_path": "bios/RCA/Studio II/boot2.rom",
"cores": null "cores": null,
"systems": [
"studio-ii"
]
}, },
{ {
"dest": "Studio-II/boot3.rom", "dest": "Studio-II/boot3.rom",
@@ -549,7 +744,10 @@
"sha256": "7a95530b9be7fe487e027a5ea7ffe4a7561762cc3fc8cdc535d6f36e5f23c8a0", "sha256": "7a95530b9be7fe487e027a5ea7ffe4a7561762cc3fc8cdc535d6f36e5f23c8a0",
"size": 2048, "size": 2048,
"repo_path": "bios/RCA/Studio II/boot3.rom", "repo_path": "bios/RCA/Studio II/boot3.rom",
"cores": null "cores": null,
"systems": [
"studio-ii"
]
}, },
{ {
"dest": "Studio-II/chip8.bin", "dest": "Studio-II/chip8.bin",
@@ -557,7 +755,10 @@
"sha256": "8764ba7e02fe00489997b45ac1f15f188f726cbcd5311e252225e62eb58d6d04", "sha256": "8764ba7e02fe00489997b45ac1f15f188f726cbcd5311e252225e62eb58d6d04",
"size": 768, "size": 768,
"repo_path": "bios/RCA/Studio II/boot4.rom", "repo_path": "bios/RCA/Studio II/boot4.rom",
"cores": null "cores": null,
"systems": [
"studio-ii"
]
}, },
{ {
"dest": "Studio-II/openstudio2.bin", "dest": "Studio-II/openstudio2.bin",
@@ -565,7 +766,10 @@
"sha256": "36ddd9ffa17a194bd8103efb807e104a529134685502839a4f14ea4d5de648ad", "sha256": "36ddd9ffa17a194bd8103efb807e104a529134685502839a4f14ea4d5de648ad",
"size": 2048, "size": 2048,
"repo_path": "bios/RCA/Studio II/openstudio2.bin", "repo_path": "bios/RCA/Studio II/openstudio2.bin",
"cores": null "cores": null,
"systems": [
"studio-ii"
]
}, },
{ {
"dest": "SuperAcan/boot0.rom", "dest": "SuperAcan/boot0.rom",
@@ -573,7 +777,10 @@
"sha256": "2e4d88bec69b5e7e4803368c233ce0d20f6dd107c5af0cfcc0089d310c695d7c", "sha256": "2e4d88bec69b5e7e4803368c233ce0d20f6dd107c5af0cfcc0089d310c695d7c",
"size": 4096, "size": 4096,
"repo_path": "bios/Funtech/Super A'Can/boot0.rom", "repo_path": "bios/Funtech/Super A'Can/boot0.rom",
"cores": null "cores": null,
"systems": [
"superacan"
]
}, },
{ {
"dest": "SuperAcan/boot1.rom", "dest": "SuperAcan/boot1.rom",
@@ -581,7 +788,10 @@
"sha256": "219f51bcb8544fe733bf784e087544f97aa5457945260c7fa07a8639f30f3a68", "sha256": "219f51bcb8544fe733bf784e087544f97aa5457945260c7fa07a8639f30f3a68",
"size": 8192, "size": 8192,
"repo_path": "bios/Funtech/Super A'Can/boot1.rom", "repo_path": "bios/Funtech/Super A'Can/boot1.rom",
"cores": null "cores": null,
"systems": [
"superacan"
]
}, },
{ {
"dest": "SuperAcan/boot2.rom", "dest": "SuperAcan/boot2.rom",
@@ -589,7 +799,10 @@
"sha256": "9889590805a97b7bb439d853d9ae4d6b31067bacb8225ab0538f3491dedab4b8", "sha256": "9889590805a97b7bb439d853d9ae4d6b31067bacb8225ab0538f3491dedab4b8",
"size": 8192, "size": 8192,
"repo_path": "bios/Funtech/Super A'Can/boot2.rom", "repo_path": "bios/Funtech/Super A'Can/boot2.rom",
"cores": null "cores": null,
"systems": [
"superacan"
]
}, },
{ {
"dest": "COCO3/boot0.rom", "dest": "COCO3/boot0.rom",
@@ -597,7 +810,10 @@
"sha256": "ad358430568250eb8330a37f85361d9ce67e5ae6d5a28c17b31802cd3953a5d2", "sha256": "ad358430568250eb8330a37f85361d9ce67e5ae6d5a28c17b31802cd3953a5d2",
"size": 32768, "size": 32768,
"repo_path": "bios/Tandy/CoCo/coco3.rom", "repo_path": "bios/Tandy/CoCo/coco3.rom",
"cores": null "cores": null,
"systems": [
"tandy-coco"
]
}, },
{ {
"dest": "COCO3/boot1.rom", "dest": "COCO3/boot1.rom",
@@ -605,7 +821,10 @@
"sha256": "1c40c5a74e72edaeecc2511fee1aab347eae2c3d5fc6778cb622e3df14d63157", "sha256": "1c40c5a74e72edaeecc2511fee1aab347eae2c3d5fc6778cb622e3df14d63157",
"size": 8192, "size": 8192,
"repo_path": "bios/Tandy/CoCo/disk11.rom", "repo_path": "bios/Tandy/CoCo/disk11.rom",
"cores": null "cores": null,
"systems": [
"tandy-coco"
]
}, },
{ {
"dest": "COCO3/boot2.rom", "dest": "COCO3/boot2.rom",
@@ -613,7 +832,10 @@
"sha256": "1a5a3e95522296f66a192e9a7ca0725ecc88862f9bb31f50ef37ae789404692f", "sha256": "1a5a3e95522296f66a192e9a7ca0725ecc88862f9bb31f50ef37ae789404692f",
"size": 8192, "size": 8192,
"repo_path": "bios/Tandy/CoCo/orch90.rom", "repo_path": "bios/Tandy/CoCo/orch90.rom",
"cores": null "cores": null,
"systems": [
"tandy-coco"
]
}, },
{ {
"dest": "TI-99_4A/boot0.rom", "dest": "TI-99_4A/boot0.rom",
@@ -621,7 +843,10 @@
"sha256": "53ad354861a62b9144e02e878845c7e1a5dd75342b715047db64b780954f7c88", "sha256": "53ad354861a62b9144e02e878845c7e1a5dd75342b715047db64b780954f7c88",
"size": 196608, "size": 196608,
"repo_path": "bios/Texas Instruments/TI-99/boot0.rom", "repo_path": "bios/Texas Instruments/TI-99/boot0.rom",
"cores": null "cores": null,
"systems": [
"ti99"
]
}, },
{ {
"dest": "GameCom/boot.rom", "dest": "GameCom/boot.rom",
@@ -629,7 +854,10 @@
"sha256": "64cbaa1a5e8bae8fae90a7affd6743df5d23edfc4e1e23002ca77c8b7ec1bb64", "sha256": "64cbaa1a5e8bae8fae90a7affd6743df5d23edfc4e1e23002ca77c8b7ec1bb64",
"size": 262144, "size": 262144,
"repo_path": "bios/Tiger/Game.com/GameCom External BIOS (Kitrinx v0.03).bin", "repo_path": "bios/Tiger/Game.com/GameCom External BIOS (Kitrinx v0.03).bin",
"cores": null "cores": null,
"systems": [
"tiger-game-com"
]
}, },
{ {
"dest": "VideoBrain/boot.rom", "dest": "VideoBrain/boot.rom",
@@ -637,7 +865,10 @@
"sha256": "545b466e7cc0c2cce7e9479857dbedee1476fc56245f0de45f816c1b99b75562", "sha256": "545b466e7cc0c2cce7e9479857dbedee1476fc56245f0de45f816c1b99b75562",
"size": 4096, "size": 4096,
"repo_path": "bios/VideoBrain/Family Computer/boot.rom", "repo_path": "bios/VideoBrain/Family Computer/boot.rom",
"cores": null "cores": null,
"systems": [
"videobrain"
]
}, },
{ {
"dest": "WonderSwan/boot.rom", "dest": "WonderSwan/boot.rom",
@@ -645,7 +876,10 @@
"sha256": "bf4480dbea1c47c8b54ce7be9382bc1006148f431fbe4277e136351fa74f635e", "sha256": "bf4480dbea1c47c8b54ce7be9382bc1006148f431fbe4277e136351fa74f635e",
"size": 4096, "size": 4096,
"repo_path": "bios/Bandai/WonderSwan/bootrom.ws", "repo_path": "bios/Bandai/WonderSwan/bootrom.ws",
"cores": null "cores": null,
"systems": [
"wonderswan"
]
}, },
{ {
"dest": "WonderSwan/boot1.rom", "dest": "WonderSwan/boot1.rom",
@@ -653,7 +887,10 @@
"sha256": "f5a5c044d84ce1681f94e9ef74287cb989784497be5bd5108df17908dfa55db2", "sha256": "f5a5c044d84ce1681f94e9ef74287cb989784497be5bd5108df17908dfa55db2",
"size": 8192, "size": 8192,
"repo_path": "bios/Bandai/WonderSwan/bootrom.wsc", "repo_path": "bios/Bandai/WonderSwan/bootrom.wsc",
"cores": null "cores": null,
"systems": [
"wonderswan"
]
}, },
{ {
"dest": "WonderSwanColor/boot.rom", "dest": "WonderSwanColor/boot.rom",
@@ -661,7 +898,10 @@
"sha256": "bf4480dbea1c47c8b54ce7be9382bc1006148f431fbe4277e136351fa74f635e", "sha256": "bf4480dbea1c47c8b54ce7be9382bc1006148f431fbe4277e136351fa74f635e",
"size": 4096, "size": 4096,
"repo_path": "bios/Bandai/WonderSwan/bootrom.ws", "repo_path": "bios/Bandai/WonderSwan/bootrom.ws",
"cores": null "cores": null,
"systems": [
"wonderswan-color"
]
}, },
{ {
"dest": "WonderSwanColor/boot1.rom", "dest": "WonderSwanColor/boot1.rom",
@@ -669,7 +909,10 @@
"sha256": "f5a5c044d84ce1681f94e9ef74287cb989784497be5bd5108df17908dfa55db2", "sha256": "f5a5c044d84ce1681f94e9ef74287cb989784497be5bd5108df17908dfa55db2",
"size": 8192, "size": 8192,
"repo_path": "bios/Bandai/WonderSwan/bootrom.wsc", "repo_path": "bios/Bandai/WonderSwan/bootrom.wsc",
"cores": null "cores": null,
"systems": [
"wonderswan-color"
]
} }
] ]
} }
+5271 -446
View File
File diff suppressed because it is too large. Load diff
+6209 -650
View File
File diff suppressed because it is too large. Load diff
+7210 -367
View File
File diff suppressed because it is too large. Load diff
+14395 -2188
View File
File diff suppressed because it is too large. Load diff
+7164 -665
View File
File diff suppressed because it is too large. Load diff
+2004 -147
View File
File diff suppressed because it is too large. Load diff
+4943 -488
View File
File diff suppressed because it is too large. Load diff
+14
View File
@@ -384,6 +384,7 @@ systems:
- name: qsound.zip - name: qsound.zip
destination: qsound.zip destination: qsound.zip
required: true required: true
curated: true
native_id: Arcade native_id: Arcade
core: fbneo core: fbneo
manufacturer: Various manufacturer: Various
@@ -1907,24 +1908,31 @@ systems:
- name: gc-ntsc-12.bin - name: gc-ntsc-12.bin
destination: dolphin-emu/Sys/GC/USA/IPL.bin destination: dolphin-emu/Sys/GC/USA/IPL.bin
required: false required: false
curated: true
- name: gc-pal-12.bin - name: gc-pal-12.bin
destination: dolphin-emu/Sys/GC/EUR/IPL.bin destination: dolphin-emu/Sys/GC/EUR/IPL.bin
required: false required: false
curated: true
- name: gc-ntsc-12.bin - name: gc-ntsc-12.bin
destination: dolphin-emu/Sys/GC/JAP/IPL.bin destination: dolphin-emu/Sys/GC/JAP/IPL.bin
required: false required: false
curated: true
- name: dsp_coef.bin - name: dsp_coef.bin
destination: dolphin-emu/Sys/GC/dsp_coef.bin destination: dolphin-emu/Sys/GC/dsp_coef.bin
required: true required: true
curated: true
- name: dsp_rom.bin - name: dsp_rom.bin
destination: dolphin-emu/Sys/GC/dsp_rom.bin destination: dolphin-emu/Sys/GC/dsp_rom.bin
required: true required: true
curated: true
- name: font_western.bin - name: font_western.bin
destination: dolphin-emu/Sys/GC/font_western.bin destination: dolphin-emu/Sys/GC/font_western.bin
required: false required: false
curated: true
- 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
curated: true
native_id: Nintendo - GameCube native_id: Nintendo - GameCube
core: dolphin core: dolphin
manufacturer: Nintendo manufacturer: Nintendo
@@ -1997,15 +2005,19 @@ systems:
- name: dsi_bios7.bin - name: dsi_bios7.bin
destination: dsi_bios7.bin destination: dsi_bios7.bin
required: true required: true
curated: true
- name: dsi_bios9.bin - name: dsi_bios9.bin
destination: dsi_bios9.bin destination: dsi_bios9.bin
required: true required: true
curated: true
- name: dsi_firmware.bin - name: dsi_firmware.bin
destination: dsi_firmware.bin destination: dsi_firmware.bin
required: true required: true
curated: true
- name: dsi_nand.bin - name: dsi_nand.bin
destination: dsi_nand.bin destination: dsi_nand.bin
required: true required: true
curated: true
native_id: Nintendo - Nintendo DS native_id: Nintendo - Nintendo DS
core: desmume core: desmume
manufacturer: Nintendo manufacturer: Nintendo
@@ -2554,6 +2566,7 @@ systems:
- name: segasp.zip - name: segasp.zip
destination: dc/segasp.zip destination: dc/segasp.zip
required: true required: true
curated: true
native_id: Sega - Dreamcast-based Arcade native_id: Sega - Dreamcast-based Arcade
sega-game-gear: sega-game-gear:
files: files:
@@ -4731,6 +4744,7 @@ systems:
- name: data.zip - name: data.zip
destination: xrick/data.zip destination: xrick/data.zip
required: true required: true
curated: true
core: xrick core: xrick
manufacturer: Other manufacturer: Other
docs: "https://docs.libretro.com/library/xrick/" docs: "https://docs.libretro.com/library/xrick/"
+7
View File
@@ -787,6 +787,7 @@ systems:
crc32: 51a12d90 crc32: 51a12d90
size: 32768 size: 32768
native_system: turbografx-cd native_system: turbografx-cd
mirror_of: tg16
- name: gexpress.pce - name: gexpress.pce
destination: turbografx-cd/gexpress.pce destination: turbografx-cd/gexpress.pce
required: true required: true
@@ -795,6 +796,7 @@ systems:
crc32: 51a12d90 crc32: 51a12d90
size: 32768 size: 32768
native_system: turbografx-cd native_system: turbografx-cd
mirror_of: tg16
- name: syscard1.pce - name: syscard1.pce
destination: turbografx-cd/syscard1.pce destination: turbografx-cd/syscard1.pce
required: true required: true
@@ -803,6 +805,7 @@ systems:
crc32: 3f9f95a4 crc32: 3f9f95a4
size: 262144 size: 262144
native_system: turbografx-cd native_system: turbografx-cd
mirror_of: tg16
- name: syscard2.pce - name: syscard2.pce
destination: turbografx-cd/syscard2.pce destination: turbografx-cd/syscard2.pce
required: true required: true
@@ -811,6 +814,7 @@ systems:
crc32: 283b74e0 crc32: 283b74e0
size: 262144 size: 262144
native_system: turbografx-cd native_system: turbografx-cd
mirror_of: tg16
- name: syscard2u.pce - name: syscard2u.pce
destination: turbografx-cd/syscard2u.pce destination: turbografx-cd/syscard2u.pce
required: true required: true
@@ -819,6 +823,7 @@ systems:
crc32: ff2a5ec3 crc32: ff2a5ec3
size: 262144 size: 262144
native_system: turbografx-cd native_system: turbografx-cd
mirror_of: tg16
- name: syscard3.pce - name: syscard3.pce
destination: turbografx-cd/syscard3.pce destination: turbografx-cd/syscard3.pce
required: true required: true
@@ -827,6 +832,7 @@ systems:
crc32: 6d9a73ef crc32: 6d9a73ef
size: 262144 size: 262144
native_system: turbografx-cd native_system: turbografx-cd
mirror_of: tg16
- name: syscard3u.pce - name: syscard3u.pce
destination: turbografx-cd/syscard3u.pce destination: turbografx-cd/syscard3u.pce
required: true required: true
@@ -835,6 +841,7 @@ systems:
crc32: 2b5b75fe crc32: 2b5b75fe
size: 262144 size: 262144
native_system: turbografx-cd native_system: turbografx-cd
mirror_of: tg16
nec-pc-98: nec-pc-98:
files: files:
- name: 2608_bd.wav - name: 2608_bd.wav
+19
View File
@@ -644,6 +644,25 @@
] ]
} }
}, },
{
"if": {
"anyOf": [
{
"required": ["path"],
"properties": {"path": {"type": "string", "pattern": "/$"}}
},
{
"required": ["standalone_path"],
"properties": {"standalone_path": {"type": "string", "pattern": "/$"}}
}
]
},
"then": {
"required": [
"type"
]
}
},
{ {
"if": { "if": {
"required": [ "required": [
+174
View File
@@ -146,6 +146,93 @@
"uniqueItems": true "uniqueItems": true
} }
] ]
},
"regions": {
"description": "The regions the emulator code selects the file for. Absent when it never competes.",
"type": "array",
"items": {
"type": "string",
"enum": [
"argentina",
"asia",
"asia-ntsc",
"asia-pal",
"australia",
"brazil",
"canada",
"china",
"denmark",
"europe",
"finland",
"france",
"germany",
"greece",
"hong-kong",
"india",
"italy",
"japan",
"latin-america",
"mexico",
"netherlands",
"new-zealand",
"north-america",
"norway",
"oceania",
"poland",
"portugal",
"russia",
"singapore",
"south-korea",
"spain",
"sweden",
"taiwan",
"uk",
"world"
]
},
"minItems": 1,
"maxItems": 1000,
"uniqueItems": true
},
"region_groups": {
"description": "Groups the file competes in as a core extra, whatever systems are chosen.",
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 256
},
"minItems": 1,
"maxItems": 1000,
"uniqueItems": true
},
"region_system_groups": {
"description": "Groups the file competes in as the platform's own declaration: present only when that system is kept.",
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 256
},
"minItems": 1,
"maxItems": 1000,
"uniqueItems": true
},
"systems": {
"description": "Every platform system that declares the file, for the installer's --system.",
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 256
},
"minItems": 1,
"maxItems": 1000,
"uniqueItems": true
},
"priority": {
"description": "The rank the code searches the file at, lower first: a world file withdraws an unmatched regional one only when it ranks no worse.",
"type": "integer"
} }
} }
} }
@@ -203,6 +290,93 @@
"release_asset": { "release_asset": {
"type": "string", "type": "string",
"pattern": "^[^/\\\\]+$" "pattern": "^[^/\\\\]+$"
},
"systems": {
"description": "Every platform system that declares the file, for the installer's --system.",
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 256
},
"minItems": 1,
"maxItems": 1000,
"uniqueItems": true
},
"regions": {
"description": "The regions the emulator code selects the file for. Absent when it never competes.",
"type": "array",
"items": {
"type": "string",
"enum": [
"argentina",
"asia",
"asia-ntsc",
"asia-pal",
"australia",
"brazil",
"canada",
"china",
"denmark",
"europe",
"finland",
"france",
"germany",
"greece",
"hong-kong",
"india",
"italy",
"japan",
"latin-america",
"mexico",
"netherlands",
"new-zealand",
"north-america",
"norway",
"oceania",
"poland",
"portugal",
"russia",
"singapore",
"south-korea",
"spain",
"sweden",
"taiwan",
"uk",
"world"
]
},
"minItems": 1,
"maxItems": 1000,
"uniqueItems": true
},
"region_groups": {
"description": "Groups the file competes in as a core extra, whatever systems are chosen.",
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 256
},
"minItems": 1,
"maxItems": 1000,
"uniqueItems": true
},
"region_system_groups": {
"description": "Groups the file competes in as the platform's own declaration: present only when that system is kept.",
"type": "array",
"items": {
"type": "string",
"minLength": 1,
"maxLength": 256
},
"minItems": 1,
"maxItems": 1000,
"uniqueItems": true
},
"priority": {
"description": "The rank the code searches the file at, lower first: a world file withdraws an unmatched regional one only when it ranks no worse.",
"type": "integer"
} }
}, },
"oneOf": [ "oneOf": [
+8
View File
@@ -280,6 +280,14 @@
"type": "string", "type": "string",
"description": "System id the platform itself uses for this file" "description": "System id the platform itself uses for this file"
}, },
"mirror_of": {
"type": "string",
"description": "Native system whose entry this one repeats; the platform's own file does not carry the copy"
},
"curated": {
"type": "boolean",
"description": "Added by our scraper from the cores' source, not declared in the platform's own file"
},
"native_name": { "native_name": {
"type": "string", "type": "string",
"description": "Display name the platform gives its own system" "description": "Display name the platform gives its own system"
+39 -1
View File
@@ -60,7 +60,7 @@ def write_text_atomic(path: str, content: str) -> None:
dir=directory, prefix=f".{os.path.basename(path)}.", suffix=".tmp" dir=directory, prefix=f".{os.path.basename(path)}.", suffix=".tmp"
) )
try: try:
with os.fdopen(handle, "w") as f: with os.fdopen(handle, "w", encoding="utf-8") as f:
f.write(content) f.write(content)
os.replace(scratch, path) os.replace(scratch, path)
except BaseException: except BaseException:
@@ -69,6 +69,44 @@ def write_text_atomic(path: str, content: str) -> None:
raise raise
def write_bytes_atomic(path: str, content: bytes) -> None:
"""Write a whole binary file or nothing, as write_text_atomic does."""
directory = os.path.dirname(os.path.abspath(path))
handle, scratch = tempfile.mkstemp(
dir=directory, prefix=f".{os.path.basename(path)}.", suffix=".tmp"
)
try:
with os.fdopen(handle, "wb") as f:
f.write(content)
os.replace(scratch, path)
except BaseException:
with contextlib.suppress(OSError):
os.unlink(scratch)
raise
@contextlib.contextmanager
def file_lock(lock_path: str | os.PathLike, shared: bool = False):
"""Hold a lock on lock_path, waiting for it if taken.
Several sessions refresh the same caches: without it, two swaps of one
tree interleave, or two read-modify-writes of one index lose an entry.
On platforms without flock the lock is a no-op.
"""
try:
import fcntl
except ImportError:
yield
return
os.makedirs(os.path.dirname(os.path.abspath(lock_path)), exist_ok=True)
with open(lock_path, "a") as handle:
fcntl.flock(handle, fcntl.LOCK_SH if shared else fcntl.LOCK_EX)
try:
yield
finally:
fcntl.flock(handle, fcntl.LOCK_UN)
def copy_file_atomic(source: str, path: str) -> None: def copy_file_atomic(source: str, path: str) -> None:
"""Copy a file into place whole or not at all, metadata included.""" """Copy a file into place whole or not at all, metadata included."""
import shutil import shutil
+1 -1
View File
@@ -1057,7 +1057,7 @@ def main() -> int:
parser.add_argument("--profiles", action="store_true", help="also run profile_sync (slow)") parser.add_argument("--profiles", action="store_true", help="also run profile_sync (slow)")
parser.add_argument("--offline", action="store_true", help="no network, local ages only") parser.add_argument("--offline", action="store_true", help="no network, local ages only")
parser.add_argument("--jobs", type=int, default=4, help="parallel scrapers") parser.add_argument("--jobs", type=int, default=4, help="parallel scrapers")
parser.add_argument("--cache-dir", default=".cache/upstream") parser.add_argument("--cache-dir", default=upstream.CACHE_DIR)
parser.add_argument("--native-cache-dir", default=export_native.DEFAULT_CACHE) parser.add_argument("--native-cache-dir", default=export_native.DEFAULT_CACHE)
parser.add_argument("--truth-dir", default="dist/truth") parser.add_argument("--truth-dir", default="dist/truth")
parser.add_argument("--platforms-dir", default="platforms") parser.add_argument("--platforms-dir", default="platforms")
+167 -45
View File
@@ -6,6 +6,8 @@ and file resolution - eliminates DRY violations across scripts.
from __future__ import annotations from __future__ import annotations
from collections import Counter
import hashlib import hashlib
import functools import functools
import json import json
@@ -157,7 +159,11 @@ def load_platform_config(platform_name: str, platforms_dir: str = "platforms") -
key = (gf.get("name"), gf.get("destination", gf.get("name"))) key = (gf.get("name"), gf.get("destination", gf.get("name")))
dest_lower = gf.get("destination", gf.get("name", "")).lower() dest_lower = gf.get("destination", gf.get("name", "")).lower()
if key not in existing and dest_lower not in existing_lower: if key not in existing and dest_lower not in existing_lower:
system.setdefault("files", []).append(gf) # Marked as ours: the platform's own file does not
# carry it, and an export must not count it kept.
system.setdefault("files", []).append(
{**gf, "shared_group": group_name}
)
existing.add(key) existing.add(key)
# Merge metadata from _registry.yml. The registry is our curated source; # Merge metadata from _registry.yml. The registry is our curated source;
@@ -239,6 +245,50 @@ def list_registered_platforms(
return platforms return platforms
def _target_overrides(platform_name: str, targets_dir: str) -> dict:
overrides_file = os.path.join(targets_dir, "_overrides.yml")
if not os.path.exists(overrides_file):
return {}
with open(overrides_file) as f:
all_overrides = yaml_load(f) or {}
return all_overrides.get(platform_name, {}).get("targets", {})
def _target_alias_index(targets: dict, overrides: dict) -> dict[str, str]:
"""Every name a target answers to, mapped to the name its file uses."""
alias_index: dict[str, str] = {}
for tname in targets:
alias_index[tname] = tname
for alias in overrides.get(tname, {}).get("aliases", []):
alias_index[alias] = tname
return alias_index
def canonical_target_name(
platforms: list[str], target: str, platforms_dir: str = "platforms"
) -> str:
"""The name a target is filed under, whichever alias was typed.
`switch`, `nx` and `nintendo-switch` named three packs of one target, and
--verify-packs looked for a name no build had written. The typed name is
kept when no platform knows it or when platforms file it differently.
"""
targets_dir = os.path.join(platforms_dir, "targets")
found: set[str] = set()
for platform_name in platforms:
target_file = os.path.join(targets_dir, f"{platform_name}.yml")
if not os.path.exists(target_file):
continue
with open(target_file) as f:
targets = (yaml_load(f) or {}).get("targets", {})
canonical = _target_alias_index(
targets, _target_overrides(platform_name, targets_dir)
).get(target)
if canonical:
found.add(canonical)
return found.pop() if len(found) == 1 else target
def load_target_config( def load_target_config(
platform_name: str, platform_name: str,
target: str, target: str,
@@ -260,19 +310,8 @@ def load_target_config(
data = yaml_load(f) or {} data = yaml_load(f) or {}
targets = data.get("targets", {}) targets = data.get("targets", {})
overrides = _target_overrides(platform_name, targets_dir)
overrides_file = os.path.join(targets_dir, "_overrides.yml") alias_index = _target_alias_index(targets, overrides)
overrides = {}
if os.path.exists(overrides_file):
with open(overrides_file) as f:
all_overrides = yaml_load(f) or {}
overrides = all_overrides.get(platform_name, {}).get("targets", {})
alias_index: dict[str, str] = {}
for tname in targets:
alias_index[tname] = tname
for alias in overrides.get(tname, {}).get("aliases", []):
alias_index[alias] = tname
canonical = alias_index.get(target) canonical = alias_index.get(target)
if canonical is None: if canonical is None:
@@ -324,21 +363,20 @@ def list_available_targets(
with open(target_file) as f: with open(target_file) as f:
data = yaml_load(f) or {} data = yaml_load(f) or {}
overrides_file = os.path.join(targets_dir, "_overrides.yml") # The count --target filters on: overrides applied, as load_target_config
overrides = {} # applies them. The scraped list counted Batocera's machine variants and
if os.path.exists(overrides_file): # launchers, 198 cores shown for 187 used.
with open(overrides_file) as f: overrides = _target_overrides(platform_name, targets_dir)
all_overrides = yaml_load(f) or {}
overrides = all_overrides.get(platform_name, {}).get("targets", {})
result = [] result = []
for tname, tdata in sorted(data.get("targets", {}).items()): for tname, tdata in sorted(data.get("targets", {}).items()):
aliases = overrides.get(tname, {}).get("aliases", []) aliases = (overrides.get(tname) or {}).get("aliases", [])
cores = {str(c) for c in tdata.get("cores", [])}
result.append( result.append(
{ {
"name": tname, "name": tname,
"architecture": tdata.get("architecture", ""), "architecture": tdata.get("architecture", ""),
"core_count": len(tdata.get("cores", [])), "core_count": len(apply_target_overrides(cores, overrides, tname)),
"aliases": aliases, "aliases": aliases,
} }
) )
@@ -1555,6 +1593,11 @@ SYSTEM_ALIASES: dict[str, str] = {
"sega32x": "sega-32x", "sega32x": "sega-32x",
"segastv": "sega-stv", "segastv": "sega-stv",
"ti994a": "ti99", "ti994a": "ti99",
# Other spellings of one machine: RomM files the Mega CD as sega-mega-cd
# and the disk system as nintendo-fds, which normalize to the ids the
# profiles use only through these.
"sega-segacd": "sega-megacd",
"nes-fds": "nintendo-fds",
} }
@@ -1618,13 +1661,47 @@ def filter_systems_by_target(
norm_key = _norm_sid(sid) norm_key = _norm_sid(sid)
norm_plat_system_cores.setdefault(norm_key, set()).add(name) norm_plat_system_cores.setdefault(norm_key, set()).add(name)
# A platform can file a system under an id no profile uses: Batocera's
# enterprise-64-128 is ep128emu's enterprise-64 and enterprise-128, and
# only CLK, off most targets, carries the joined id. The destinations a
# system declares name the cores that read its files. A bare name proves
# nothing (bios.bin belongs to many systems), so only a path counts.
dest_cores: dict[str, set[str]] = {}
for name, p in profiles.items():
if p.get("type") == "alias":
continue
for f in p.get("files", []):
for key in ("path", "standalone_path"):
dest = f.get(key)
if not isinstance(dest, str):
continue
if dest.endswith("/"):
dest += f.get("name", "")
dest = sanitize_pack_path(dest).lower()
if "/" in dest:
dest_cores.setdefault(dest, set()).add(name)
filtered = {} filtered = {}
for sys_id, sys_data in systems.items(): for sys_id, sys_data in systems.items():
norm_key = _norm_sid(sys_id) norm_key = _norm_sid(sys_id)
file_cores = {
core
for fe in sys_data.get("files", [])
for core in dest_cores.get(
sanitize_pack_path(fe.get("destination") or fe.get("name", "")).lower(),
(),
)
}
all_cores = norm_system_cores.get(norm_key, set()) all_cores = norm_system_cores.get(norm_key, set())
plat_cores_here = norm_plat_system_cores.get(norm_key, set()) plat_cores_here = norm_plat_system_cores.get(norm_key, set())
if not all_cores and not plat_cores_here: if file_cores & expanded_target:
# An on-target core reads the system's files. The evidence only
# keeps: a path shared with an off-target core says nothing of the
# other files, and Batocera's bbc runs on MAME, which no profile
# ties to bbcb.zip, while CLK reads two of its ROMs.
filtered[sys_id] = sys_data
elif not all_cores and not plat_cores_here:
# No profile maps to this system -keep it # No profile maps to this system -keep it
filtered[sys_id] = sys_data filtered[sys_id] = sys_data
elif all_cores & expanded_target: elif all_cores & expanded_target:
@@ -1637,40 +1714,85 @@ def filter_systems_by_target(
return filtered return filtered
def expand_platform_declared_names(config: dict, db: dict) -> set[str]: def platform_declarations(
"""Build set of file names declared by a platform config. config: dict, db: dict, *, enrich: bool = True
) -> dict[str, list[tuple[int | None, str]]]:
"""Every file name a platform declares, with each declaration's size and slot.
Enriches the set with canonical names and aliases from the database A name alone does not say which file a declaration is: RetroArch declares
by resolving each platform file's MD5 through by_md5. This handles galaksija/ROM1.BIN, a 4 KiB ROM, and DOSBox reads a 32 KiB SC-55 ROM1.BIN.
cases where a platform declares a file under a different name than The size, from the platform entry or else from the file its hash names,
the emulator profile (e.g. Batocera ROM1 vs gsplus ROM). tells a declaration of the same file from a same-named one; None stands
for a size nobody knows. The slot is the declared destination, sanitized
and case-folded, relative to the directory core files go to: RetroDECK
writes bios/ in front of every destination, the others put it in
base_destination.
With ``enrich``, a declaration also answers for the canonical name and
the aliases the database knows its MD5 under. This handles a platform
declaring a file under a different name than the emulator profile
(e.g. Batocera ROM1 vs gsplus ROM).
""" """
declared: set[str] = set() prefix = declared_root(config).lower()
declared: dict[str, list[tuple[int | None, str]]] = {}
by_md5 = db.get("indexes", {}).get("by_md5", {}) by_md5 = db.get("indexes", {}).get("by_md5", {})
files_db = db.get("files", {}) files_db = db.get("files", {})
for system in config.get("systems", {}).values(): for system in config.get("systems", {}).values():
for fe in system.get("files", []): for fe in system.get("files", []):
name = fe.get("name", "") name = fe.get("name", "")
slot = sanitize_pack_path(fe.get("destination") or name).lower()
if prefix and slot.startswith(prefix + "/"):
slot = slot[len(prefix) + 1:]
# A zipped_file entry pins a member of the archive it names: its
# md5 and its size, when stated, are not the archive's.
by_hash: dict = {}
size = None
if not fe.get("zipped_file"):
md5 = (fe.get("md5") or "").lower()
if md5 and "," not in md5:
by_hash = files_db.get(by_md5.get(md5, ""), {})
size = fe.get("size")
if size is None:
size = files_db.get(
(fe.get("sha1") or "").lower(), by_hash
).get("size")
if name: if name:
declared.add(name) declared.setdefault(name, []).append((size, slot))
md5 = fe.get("md5", "") if not enrich:
if not md5:
continue continue
# Skip multi-hash and zipped_file entries (inner ROM MD5, not file MD5) for other in (by_hash.get("name", ""), *by_hash.get("aliases", [])):
if "," in md5 or fe.get("zipped_file"): if other:
continue declared.setdefault(other, []).append((by_hash.get("size"), slot))
sha1 = by_md5.get(md5.lower())
if not sha1:
continue
entry = files_db.get(sha1, {})
db_name = entry.get("name", "")
if db_name:
declared.add(db_name)
for alias in entry.get("aliases", []):
declared.add(alias)
return declared return declared
def declared_root(config: dict) -> str:
"""The BIOS directory a platform writes into every destination, if any.
Only a platform whose base_destination is explicitly empty does that
(RetroDECK writes bios/ in front of each file), and core files go under
the same directory. A config that does not state a base says nothing,
and its common folder is a system's, not the BIOS root.
"""
if config.get("base_destination", None) != "":
return ""
roots = [
d.split("/", 1)[0]
for system in config.get("systems", {}).values()
for f in system.get("files", [])
if "/" in (d := f.get("destination", "") or "")
]
if not roots:
return ""
root, count = Counter(roots).most_common(1)[0]
return root if count / len(roots) > 0.9 else ""
def expand_platform_declared_names(config: dict, db: dict) -> set[str]:
"""The names a platform declares, enriched as `platform_declarations`."""
return set(platform_declarations(config, db))
import re import re
+15 -5
View File
@@ -242,10 +242,17 @@ class FileConsumers:
self.platforms: dict[str, set[str]] = {} self.platforms: dict[str, set[str]] = {}
self.emulators: dict[str, set[str]] = {} self.emulators: dict[str, set[str]] = {}
def sha1_of(self, entry: dict, dest: str = "") -> str | None: def sha1_of(self, entry: dict, dest: str = "", owner: str = "") -> str | None:
"""The collected file an entry resolves to, or None.""" """The collected file an entry resolves to, or None.
A profile entry names its owner, as the builder does: without it the
name step chose another tree's copy, and the page credited the file
the pack does not ship.
"""
if not entry.get("name"): if not entry.get("name"):
return None return None
if owner:
entry = {**entry, "source_profile": owner}
path, status = resolve_local_file( path, status = resolve_local_file(
entry, self._db, self._zip_contents, dest_hint=dest entry, self._db, self._zip_contents, dest_hint=dest
) )
@@ -266,7 +273,7 @@ class FileConsumers:
if profile.get("type") == "alias": if profile.get("type") == "alias":
continue continue
for entry in profile.get("files", []): for entry in profile.get("files", []):
sha1 = self.sha1_of(entry, entry.get("path") or "") sha1 = self.sha1_of(entry, entry.get("path") or "", owner=name)
if sha1: if sha1:
self.emulators.setdefault(sha1, set()).add(name) self.emulators.setdefault(sha1, set()).add(name)
@@ -395,8 +402,11 @@ def _cross_reference_profile(
archive = f.get("archive") archive = f.get("archive")
# Check platform declaration (by name or archive) # Check platform declaration: by name, by an alias the core answers
in_platform = fname in platform_names # to (RetroArch lists amiberry's cd32.rom as kick40060.CD32), or by
# archive.
answers = {fname, *f.get("aliases", [])}
in_platform = bool(answers & platform_names)
if not in_platform and archive: if not in_platform and archive:
in_platform = archive in platform_names in_platform = archive in platform_names
+43 -8
View File
@@ -24,6 +24,7 @@ from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent)) sys.path.insert(0, str(Path(__file__).resolve().parent))
from artifacts import file_lock, write_bytes_atomic, write_text_atomic
from common import list_registered_platforms, load_platform_config, yaml_load from common import list_registered_platforms, load_platform_config, yaml_load
from exporter import discover_exporters from exporter import discover_exporters
from exporter.baseline import build_native_model from exporter.baseline import build_native_model
@@ -80,9 +81,12 @@ def fetch(
it is what a rescrape calls, so the original and its transcription it is what a rescrape calls, so the original and its transcription
describe the same moment. describe the same moment.
""" """
recorded = _load_sources(index)
key = source_key(destination, index) key = source_key(destination, index)
if not refresh and destination.exists() and recorded.get(key, url) == url: if (
not refresh
and destination.exists()
and _load_sources(index).get(key, url) == url
):
return destination.read_bytes() return destination.read_bytes()
request = urllib.request.Request(url, headers={"User-Agent": _USER_AGENT}) request = urllib.request.Request(url, headers={"User-Agent": _USER_AGENT})
with urllib.request.urlopen(request, timeout=60) as response: with urllib.request.urlopen(request, timeout=60) as response:
@@ -90,13 +94,17 @@ def fetch(
if len(payload) > _MAX_BYTES: if len(payload) > _MAX_BYTES:
raise ValueError(f"{url}: response larger than {_MAX_BYTES} bytes") raise ValueError(f"{url}: response larger than {_MAX_BYTES} bytes")
destination.parent.mkdir(parents=True, exist_ok=True) destination.parent.mkdir(parents=True, exist_ok=True)
destination.write_bytes(payload) write_bytes_atomic(str(destination), payload)
if index is not None: if index is not None:
recorded[key] = url # Platforms refresh in parallel: the index is read again under the
# lock, or the last writer erased what the others had recorded.
index.parent.mkdir(parents=True, exist_ok=True) index.parent.mkdir(parents=True, exist_ok=True)
index.write_text( with file_lock(index.with_name(f".{index.name}.lock")):
json.dumps(recorded, indent=2, sort_keys=True) + "\n", encoding="utf-8" recorded = _load_sources(index)
) recorded[key] = url
write_text_atomic(
str(index), json.dumps(recorded, indent=2, sort_keys=True) + "\n"
)
return payload return payload
@@ -197,7 +205,34 @@ def load_inputs(
scraped = load_platform_config(platform, platforms_dir) scraped = load_platform_config(platform, platforms_dir)
except (FileNotFoundError, OSError): except (FileNotFoundError, OSError):
scraped = None scraped = None
return truth, scraped return truth, _native_layer(scraped)
def _native_layer(config: dict | None) -> dict | None:
"""The platform config without what we add to it.
A shared group's files join the config for the pack, but the platform's
file does not carry them: counted kept, they were written into
libretro's System.dat (N88EXT0-3.ROM) as if libretro declared them. What
a core needs from a group reaches the export through the truth, as an
addition the format's gates apply to. A scraper's mirror of an entry
under another system (RomM's tg16 firmware under turbografx-cd), and a
file our scraper adds from the cores' source (Dolphin's DSP ROMs in
RetroArch's config), are not in the platform's file either.
"""
if config is None:
return None
systems = {
sys_id: {
**system,
"files": [
f for f in system.get("files", [])
if not ({"shared_group", "mirror_of", "curated"} & f.keys())
],
}
for sys_id, system in config.get("systems", {}).items()
}
return {**config, "systems": systems}
def refresh_cache( def refresh_cache(
+6 -1
View File
@@ -325,8 +325,13 @@ def build_native_model(
def by_destination(candidate: NativeFile, t_dest: str = t_dest) -> bool: def by_destination(candidate: NativeFile, t_dest: str = t_dest) -> bool:
# Dolphin writes dolphin-emu/Sys/GC/USA/IPL.bin for the truth's # Dolphin writes dolphin-emu/Sys/GC/USA/IPL.bin for the truth's
# GC/USA/IPL.bin: a destination ending in the path is the file. # GC/USA/IPL.bin: a destination ending in the path is the file.
# A bare name is no path: dc/boot.bin ends in /boot.bin, and
# fbneo's 480-byte boot.bin is not that 2 MB Dreamcast BIOS.
# Names go through by_name and its size guard.
theirs = _match_key(candidate.platform or {})[0] theirs = _match_key(candidate.platform or {})[0]
return bool(t_dest) and (theirs == t_dest or theirs.endswith("/" + t_dest)) if not t_dest:
return False
return theirs == t_dest or ("/" in t_dest and theirs.endswith("/" + t_dest))
def by_name( def by_name(
candidate: NativeFile, candidate: NativeFile,
+8 -6
View File
@@ -52,12 +52,15 @@ class Exporter(BaseExporter):
def native_sources() -> dict[str, str]: def native_sources() -> dict[str, str]:
return {"es_bios.xml": SOURCE_URL, "es_bios.xsd": SCHEMA_URL} return {"es_bios.xml": SOURCE_URL, "es_bios.xsd": SCHEMA_URL}
def _path(self, fe: NativeFile, native_id: str) -> str: def _path(self, fe: NativeFile) -> str:
"""The path Recalbox reads, pipe-joined when it accepts several. """The path Recalbox reads, pipe-joined when it accepts several.
A path Recalbox already states is reproduced exactly: several of its A path Recalbox already states is reproduced exactly: several of its
entries sit at the BIOS root with no directory at all, and prefixing entries sit at the BIOS root with no directory at all, and prefixing
them with the system would point the frontend somewhere else. them with the system would point the frontend somewhere else. An
addition goes where the core reads it, relative to the BIOS directory
Recalbox gives its cores: DuckStation reads scph5000.bin at the root,
not under psx/.
""" """
if fe.platform is not None: if fe.platform is not None:
path = str(fe.platform.get("destination") or fe.name) path = str(fe.platform.get("destination") or fe.name)
@@ -65,11 +68,10 @@ class Exporter(BaseExporter):
if alternatives: if alternatives:
return "|".join([path, *[str(a) for a in alternatives]]) return "|".join([path, *[str(a) for a in alternatives]])
return path return path
dest = fe.destination or fe.name return fe.destination or fe.name
return dest if "/" in dest else f"{native_id}/{dest}"
def _bios_element(self, fe: NativeFile, native_id: str) -> str: def _bios_element(self, fe: NativeFile, native_id: str) -> str:
attrs = [f"path={quoteattr(self._path(fe, native_id))}"] attrs = [f"path={quoteattr(self._path(fe))}"]
attrs.append(f'md5={quoteattr(",".join(fe.hashes("md5")))}') attrs.append(f'md5={quoteattr(",".join(fe.hashes("md5")))}')
attrs.append(f'core={quoteattr(",".join(fe.cores()))}') attrs.append(f'core={quoteattr(",".join(fe.cores()))}')
@@ -162,6 +164,6 @@ class Exporter(BaseExporter):
for fe in system.files: for fe in system.files:
if not self.writable(fe): if not self.writable(fe):
continue continue
if self._path(fe, system.native_id).casefold() not in exported: if self._path(fe).casefold() not in exported:
issues.append(f"absent: {system.native_id}/{fe.name}") issues.append(f"absent: {system.native_id}/{fe.name}")
return issues return issues
+39 -4
View File
@@ -37,6 +37,14 @@ def _md5_set(value: object) -> set[str]:
return {m.strip().lower() for m in str(value or "").split(",") if m.strip()} return {m.strip().lower() for m in str(value or "").split(",") if m.strip()}
def _paths_key(entry: dict) -> tuple[str, ...]:
"""Where an entry says the file goes: no paths is the BIOS root."""
paths = entry.get("paths")
if isinstance(paths, list):
return tuple(sorted(str(p) for p in paths))
return (str(paths),) if paths else ()
def _matching_keys( def _matching_keys(
entry: dict, by_key: dict[tuple[str, str], OrderedDict] entry: dict, by_key: dict[tuple[str, str], OrderedDict]
) -> list[tuple[str, str]]: ) -> list[tuple[str, str]]:
@@ -177,7 +185,12 @@ class Exporter(BaseExporter):
return None return None
@classmethod @classmethod
def _merge(cls, existing: object, ours: list[OrderedDict]) -> list[OrderedDict]: def _merge(
cls,
existing: object,
ours: list[OrderedDict],
additions: list[OrderedDict] | None = None,
) -> list[OrderedDict]:
"""Correct the component's own list; never replace it. """Correct the component's own list; never replace it.
Assigning our entries wholesale dropped every file RetroDECK declares Assigning our entries wholesale dropped every file RetroDECK declares
@@ -234,6 +247,21 @@ class Exporter(BaseExporter):
merged.extend( merged.extend(
entry for key, entry in by_key.items() if key not in corrected entry for key, entry in by_key.items() if key not in corrected
) )
# A file the platform does not declare is appended unless the list
# already holds that name for that system at that path. The key
# above is name and system only, and folded melonDS's SkyEmu/
# firmware.bin into the root firmware.bin the platform declares,
# while the report counted it added.
held = {
(str(e.get("filename", "")), system, _paths_key(e))
for e in merged
for system in (_systems_of(e) or [""])
}
for entry in additions or []:
key = (str(entry.get("filename", "")), str(entry.get("system", "")), _paths_key(entry))
if key[0] and key not in held:
held.add(key)
merged.append(entry)
return merged return merged
def render( def render(
@@ -257,16 +285,17 @@ class Exporter(BaseExporter):
except json.JSONDecodeError: except json.JSONDecodeError:
continue continue
entries = [self._entry(fe) for fe in files] entries = [self._entry(fe) for fe in files if fe.platform is not None]
additions = [self._entry(fe) for fe in files if fe.platform is None]
for component_value in manifest.values(): for component_value in manifest.values():
if not isinstance(component_value, dict): if not isinstance(component_value, dict):
continue continue
holder = self._bios_holder(component_value) holder = self._bios_holder(component_value)
if holder is None: if holder is None:
component_value["bios"] = self._merge(None, entries) component_value["bios"] = self._merge(None, entries, additions)
else: else:
container, key = holder container, key = holder
container[key] = self._merge(container.get(key), entries) container[key] = self._merge(container.get(key), entries, additions)
break break
produced[path] = json.dumps(manifest, indent=2, ensure_ascii=False) + "\n" produced[path] = json.dumps(manifest, indent=2, ensure_ascii=False) + "\n"
@@ -293,6 +322,7 @@ class Exporter(BaseExporter):
continue continue
declared: set[str] = set() declared: set[str] = set()
placed: set[tuple[str, tuple[str, ...]]] = set()
for component_value in manifest.values(): for component_value in manifest.values():
if not isinstance(component_value, dict): if not isinstance(component_value, dict):
continue continue
@@ -302,6 +332,7 @@ class Exporter(BaseExporter):
container, key = holder container, key = holder
for entry in container[key]: for entry in container[key]:
declared.add(entry.get("filename", "")) declared.add(entry.get("filename", ""))
placed.add((entry.get("filename", ""), _paths_key(entry)))
if not component_value.get("name") and not component_value.get( if not component_value.get("name") and not component_value.get(
"system" "system"
): ):
@@ -310,4 +341,8 @@ class Exporter(BaseExporter):
for fe in files: for fe in files:
if fe.name not in declared: if fe.name not in declared:
issues.append(f"absent from {path}: {fe.name}") issues.append(f"absent from {path}: {fe.name}")
elif fe.platform is None and (
fe.name, _paths_key(self._entry(fe))
) not in placed:
issues.append(f"absent from {path}: {fe.destination or fe.name}")
return issues return issues
+31 -12
View File
@@ -48,6 +48,7 @@ _FILENAME = re.compile(r"[A-Za-z0-9][\w.+-]*\.[A-Za-z0-9]{1,5}\b")
# than by a sentence template, and the names in it are the list to extend. # than by a sentence template, and the names in it are the list to extend.
_BIOS_CLAUSE = re.compile(r"BIOS\b.*?(?=\\n|$)", re.DOTALL) _BIOS_CLAUSE = re.compile(r"BIOS\b.*?(?=\\n|$)", re.DOTALL)
_BIOS_NOUN = re.compile(r"BIOS (files?)\b") _BIOS_NOUN = re.compile(r"BIOS (files?)\b")
_SEPARATOR = re.compile(r"\s*(?:,\s*(?:and\s+)?|and\s+)")
class Exporter(BaseExporter): class Exporter(BaseExporter):
@@ -141,12 +142,13 @@ class Exporter(BaseExporter):
return {match.group(0).lower() for match in cls._names_in(plain)} return {match.group(0).lower() for match in cls._names_in(plain)}
@classmethod @classmethod
def _insertion_point(cls, help_text: str) -> int | None: def _enumeration(cls, help_text: str) -> tuple[int, int, list[str]] | None:
"""Where a name joins the list, or None when there is no list. """The list of names to extend: its span and its names, or None.
A list of alternatives ("a or b", "a/b") is not an enumeration: an A list of alternatives ("a or b", "a/b") is not an enumeration: an
appended ", c" reads as one more required file and the plural turns appended ", c" reads as one more required file and the plural turns
"one of these" into "all of these". Such a list is left alone. "one of these" into "all of these". Such a list is left alone, and so
is one with words between its names, which a rewrite would lose.
""" """
clause = _BIOS_CLAUSE.search(help_text) clause = _BIOS_CLAUSE.search(help_text)
if clause is None: if clause is None:
@@ -158,7 +160,14 @@ class Exporter(BaseExporter):
between = text[names[0].start():names[-1].end()] between = text[names[0].start():names[-1].end()]
if re.search(r"\bor\b|/", between): if re.search(r"\bor\b|/", between):
return None return None
return clause.start() + names[-1].end() gaps = [text[a.end():b.start()] for a, b in zip(names, names[1:])]
if not all(_SEPARATOR.fullmatch(gap) for gap in gaps):
return None
return (
clause.start() + names[0].start(),
clause.start() + names[-1].end(),
[match.group(0) for match in names],
)
@staticmethod @staticmethod
def _in_search_order(candidates: list[NativeFile]) -> list[str]: def _in_search_order(candidates: list[NativeFile]) -> list[str]:
@@ -195,6 +204,21 @@ class Exporter(BaseExporter):
return names[0] return names[0]
return ", ".join(names[:-1]) + " and " + names[-1] return ", ".join(names[:-1]) + " and " + names[-1]
@classmethod
def _extended(
cls,
help_text: str,
enumeration: tuple[int, int, list[str]],
missing: list[str],
) -> str:
"""The help with the names added to its list.
The whole list is written again so the "and" moves before the last
name: appending ", c and d" after "a and b" read "a and b, c and d".
"""
start, end, written = enumeration
return help_text[:start] + cls._join([*written, *missing]) + help_text[end:]
def modules(self, originals: dict[str, str]) -> dict[str, tuple[str, str]]: def modules(self, originals: dict[str, str]) -> dict[str, tuple[str, str]]:
"""Module id and help string of every script that mentions BIOS.""" """Module id and help string of every script that mentions BIOS."""
found: dict[str, tuple[str, str]] = {} found: dict[str, tuple[str, str]] = {}
@@ -270,8 +294,8 @@ class Exporter(BaseExporter):
if not missing: if not missing:
continue continue
insert_at = self._insertion_point(help_text) enumeration = self._enumeration(help_text)
if insert_at is None: if enumeration is None:
# No enumeration to extend, and a sentence we would have to # No enumeration to extend, and a sentence we would have to
# write ourselves is a documentation change, not a correction. # write ourselves is a documentation change, not a correction.
skip("names no file to extend", module_id) skip("names no file to extend", module_id)
@@ -281,12 +305,7 @@ class Exporter(BaseExporter):
skip("more names than the help enumerates", module_id) skip("more names than the help enumerates", module_id)
continue continue
new_help = ( new_help = self._extended(help_text, enumeration, missing)
help_text[:insert_at]
+ ", "
+ self._join(missing)
+ help_text[insert_at:]
)
if len(listed) + len(missing) > 1: if len(listed) + len(missing) > 1:
new_help = _BIOS_NOUN.sub("BIOS files", new_help, count=1) new_help = _BIOS_NOUN.sub("BIOS files", new_help, count=1)
produced[relative] = originals[relative].replace( produced[relative] = originals[relative].replace(
+25 -4
View File
@@ -29,8 +29,12 @@ SOURCE_URL = (
class Exporter(BaseExporter): class Exporter(BaseExporter):
"""Write RomM's known_bios_files.json, corrected.""" """Write RomM's known_bios_files.json, corrected."""
# (system, filename) keys the platform itself declares, set by render. def __init__(self) -> None:
_platform_keys: frozenset[tuple[str, str]] = frozenset() super().__init__()
# (system, filename) keys the platform itself declares, and the
# addition that claims each other key first: both set by render.
self._platform_keys: set[tuple[str, str]] = set()
self._first_addition: dict[tuple[str, str], int] = {}
@staticmethod @staticmethod
def platform_name() -> str: def platform_name() -> str:
@@ -75,9 +79,15 @@ class Exporter(BaseExporter):
""" """
if fe.platform is not None: if fe.platform is not None:
return True return True
if (fe.native_system, fe.name) in self._platform_keys: key = (fe.native_system, fe.name)
if key in self._platform_keys:
return False return False
return self._verifiable(fe) and self._known_platform(fe.native_system) if not (self._verifiable(fe) and self._known_platform(fe.native_system)):
return False
# Two additions under one key: the second would overwrite the first
# in the dict and both were counted landed. The first one in render
# order is written, the other is not.
return self._first_addition.get(key, id(fe)) == id(fe)
def render( def render(
self, self,
@@ -93,6 +103,17 @@ class Exporter(BaseExporter):
for fe in system.files for fe in system.files
if fe.platform is not None if fe.platform is not None
} }
self._first_addition = {}
for system in sorted(systems.values(), key=lambda s: s.native_id):
for fe in sorted(system.files, key=lambda f: f.name):
key = (fe.native_system, fe.name)
if (
fe.platform is None
and key not in self._platform_keys
and self._verifiable(fe)
and self._known_platform(fe.native_system)
):
self._first_addition.setdefault(key, id(fe))
for system in sorted(systems.values(), key=lambda s: s.native_id): for system in sorted(systems.values(), key=lambda s: s.native_id):
for fe in sorted(system.files, key=lambda f: f.name): for fe in sorted(system.files, key=lambda f: f.name):
+226 -112
View File
@@ -45,6 +45,7 @@ from common import (
group_identical_platforms, group_identical_platforms,
list_emulator_profiles, list_emulator_profiles,
list_platform_system_ids, list_platform_system_ids,
canonical_target_name,
list_registered_platforms, list_registered_platforms,
list_system_ids, list_system_ids,
load_data_dir_registry, load_data_dir_registry,
@@ -64,6 +65,7 @@ from common import (
import packresolve import packresolve
from refresh_data_dirs import cache_lock from refresh_data_dirs import cache_lock
import region as region_mod import region as region_mod
from manifestselect import SelectionIndex
import slot as slot_mod import slot as slot_mod
import slots import slots
import split_pack import split_pack
@@ -440,7 +442,6 @@ def _preferred_entries(
base_dest: str, base_dest: str,
required_only: bool, required_only: bool,
zip_contents, zip_contents,
data_registry,
offline, offline,
) -> dict[str, int]: ) -> dict[str, int]:
"""Which declaration wins when several claim the same destination. """Which declaration wins when several claim the same destination.
@@ -449,6 +450,9 @@ def _preferred_entries(
in one and hash-constrained in another. First-come dedup would let in one and hash-constrained in another. First-come dedup would let
the bare entry pack whatever answers to the name, so a constrained the bare entry pack whatever answers to the name, so a constrained
sibling that resolves by content claims the destination instead. sibling that resolves by content claims the destination instead.
The choice is about the baseline and reads no data directory, for the
pack, the manifest and verify alike: verify alone passed the registry
and could crown a declaration only a data/ cache satisfies.
""" """
preferred_entries: dict[str, int] = {} preferred_entries: dict[str, int] = {}
dest_entries: dict[str, list[dict]] = {} dest_entries: dict[str, list[dict]] = {}
@@ -479,7 +483,6 @@ def _preferred_entries(
db, db,
bios_dir, bios_dir,
zip_contents, zip_contents,
data_dir_registry=data_registry,
offline=offline, offline=offline,
)) ))
for fe in constrained for fe in constrained
@@ -732,7 +735,7 @@ def generate_pack(
# atari-5200). No data directories: the choice is about the baseline. # atari-5200). No data directories: the choice is about the baseline.
preferred_entries = _preferred_entries( preferred_entries = _preferred_entries(
pack_systems, db, bios_dir, base_dest, required_only, pack_systems, db, bios_dir, base_dest, required_only,
zip_contents, None, offline, zip_contents, offline,
) )
# Region selection is decided once, over both the platform baseline and the # Region selection is decided once, over both the platform baseline and the
@@ -779,9 +782,24 @@ def generate_pack(
if _has_path_conflict(full_dest, seen_destinations, seen_parents): if _has_path_conflict(full_dest, seen_destinations, seen_parents):
# Content ships under the conflicting shape (file vs dir, # Content ships under the conflicting shape (file vs dir,
# e.g. SGB1.sfc): count it so pack totals match verify.py, # e.g. SGB1.sfc). verify.py resolves each declaration on
# which resolves each declaration independently # its own, so this one is counted by what it resolves to:
file_status.setdefault(dedup_key, "ok") # an unresolved one counted OK had the pack claim a file
# verify reports missing.
_conflict_path, conflict_status = resolve_file(
file_entry, db, bios_dir, zip_contents,
data_dir_registry=data_registry, offline=offline,
)
if conflict_status == "not_found":
file_status[dedup_key] = "missing"
file_reasons[dedup_key] = "not found"
elif conflict_status == "hash_mismatch" and (
hash_mismatch_excludes_file(verification_mode)
):
file_status[dedup_key] = "excluded"
file_reasons[dedup_key] = "hash mismatch"
else:
file_status.setdefault(dedup_key, "ok")
continue continue
storage = file_entry.get("storage", "embedded") storage = file_entry.get("storage", "embedded")
@@ -1069,17 +1087,21 @@ def generate_pack(
else: else:
_add_pack_member(zf, local_path, flat_dest) _add_pack_member(zf, local_path, flat_dest)
if file_status.get(full_dest) in ("missing", "excluded"): if file_status.get(full_dest) in ("missing", "excluded"):
previous = file_status[full_dest] source_name = fe.get("source_profile") or fe.get(
file_status[full_dest] = "ok" "source_emulator", "core profile"
if previous == "excluded": )
source_name = fe.get("source_profile") or fe.get( if reads_file_contents(verification_mode):
"source_emulator", "core profile" # The frontend hashes what sits at the path, and the core's
) # copy is not the hash it declares: the file ships for the
# core, the platform's own check still fails, as verify says.
file_reasons[full_dest] = ( file_reasons[full_dest] = (
"platform-declared hash unavailable; " "platform-declared hash unavailable; "
f"packed the validated {source_name} requirement" f"packed the validated {source_name} requirement, "
"which the platform check does not accept"
) )
else: else:
# An existence frontend is satisfied by any file at the path.
file_status[full_dest] = "ok"
file_reasons.pop(full_dest, None) file_reasons.pop(full_dest, None)
seen_destinations.add(full_dest) seen_destinations.add(full_dest)
_register_path(full_dest, seen_destinations, seen_parents) _register_path(full_dest, seen_destinations, seen_parents)
@@ -1329,6 +1351,8 @@ def generate_emulator_pack(
total_files = 0 total_files = 0
missing_files = [] missing_files = []
rejected_files: list[str] = []
discrepancies: list[str] = []
seen_destinations: set[str] = set() seen_destinations: set[str] = set()
seen_lower: set[str] = set() seen_lower: set[str] = set()
seen_parents: set[str] = ( seen_parents: set[str] = (
@@ -1495,15 +1519,27 @@ def generate_emulator_pack(
# The file verify --emulator credits is the one shipped: a # The file verify --emulator credits is the one shipped: a
# dump the core's own check rejects gives way to a held one # dump the core's own check rejects gives way to a held one
# it accepts (azahar's otp.bin, dolphin's dsp_rom.bin). # it accepts (azahar's otp.bin, dolphin's dsp_rom.bin). With
if check_file_validation( # none, the core would refuse what the pack ships: it is named
# and left out, as verify reports it.
refused = check_file_validation(
local_path, fe["name"], validation_index, bios_dir local_path, fe["name"], validation_index, bios_dir
): )
if refused:
better = find_validated_variant( better = find_validated_variant(
fe, db, local_path, validation_index, bios_dir fe, db, local_path, validation_index, bios_dir
) )
if better: if not better:
local_path = better reason, _owners = refused
rejected_files.append(f"{fe['name']} -{reason}")
continue
local_path = better
elif status == "hash_mismatch":
# Not a check the core runs, so the file still serves;
# it is not the dump the profile documents, and says so.
discrepancies.append(
f"{fe['name']} -not the dump the {emu_name} profile declares"
)
# SHA1 dedup: skip if same physical file AND same destination # SHA1 dedup: skip if same physical file AND same destination
# (but allow same file to be packed under different destinations, # (but allow same file to be packed under different destinations,
@@ -1525,7 +1561,7 @@ def generate_emulator_pack(
total_files += 1 total_files += 1
# Remove empty ZIP (no files packed and no missing = nothing to ship) # Remove empty ZIP (no files packed and no missing = nothing to ship)
if total_files == 0 and not missing_files: if total_files == 0 and not missing_files and not rejected_files:
os.unlink(zip_path) os.unlink(zip_path)
# Report # Report
@@ -1535,15 +1571,21 @@ def generate_emulator_pack(
parts = [f"{ok_count} files packed"] parts = [f"{ok_count} files packed"]
if missing_count: if missing_count:
parts.append(f"{missing_count} missing") parts.append(f"{missing_count} missing")
if rejected_files:
parts.append(f"{len(rejected_files)} refused by the core")
print(f" {zip_path}: {', '.join(parts)}") print(f" {zip_path}: {', '.join(parts)}")
for name in missing_files: for name in missing_files:
print(f" MISSING: {name}") print(f" MISSING: {name}")
for line in rejected_files:
print(f" REFUSED: {line}")
for line in discrepancies:
print(f" DISCREPANCY: {line}")
for ref in sorted(set(data_dir_notices)): for ref in sorted(set(data_dir_notices)):
print( print(
f" Note: data directory '{ref}' required but not included (use refresh_data_dirs.py)" f" Note: data directory '{ref}' required but not included (use refresh_data_dirs.py)"
) )
return zip_path if total_files > 0 or missing_files else None return zip_path if total_files > 0 or missing_files or rejected_files else None
def generate_system_pack( def generate_system_pack(
@@ -1614,7 +1656,12 @@ def generate_system_pack(
"full", regions, None, False, required_only, standalone=standalone "full", regions, None, False, required_only, standalone=standalone
) )
) )
new_path = os.path.join(output_dir, f"{sys_display}{tags}_BIOS_Pack.zip") # The selection mode is part of the name: `--system sega-triforce`
# and `--emulator triforce` both wrote Triforce_BIOS_Pack.zip, and
# the second run replaced the first without a word.
new_path = os.path.join(
output_dir, f"{sys_display}_System{tags}_BIOS_Pack.zip"
)
os.replace(result, new_path) os.replace(result, new_path)
return new_path return new_path
finally: finally:
@@ -1786,6 +1833,33 @@ def generate_split_packs(
return results return results
def _entry_for_file(candidates: list[dict], held: dict) -> dict:
"""The declaration a held file answers when several share its name.
BBKEmu declares 8.BIN once per model; the last one won, and the A4980
dump went to the A4988 folder. The declared hash decides, then the
destination the collection's own path ends with, then the last one.
"""
def declared(fe: dict, key: str) -> set[str]:
value = fe.get(key)
values = value if isinstance(value, list) else str(value or "").split(",")
return {str(v).strip().lower() for v in values if str(v).strip()}
for fe in candidates:
if any(
str(held.get(key, "")).lower() in declared(fe, key)
for key in ("sha1", "md5", "sha256", "crc32")
if held.get(key)
):
return fe
held_paths = [str(held.get("path", ""))] + [str(p) for p in held.get("paths") or []]
for fe in candidates:
tail = str(fe.get("path") or fe.get("destination") or "").lower()
if tail and any(path.lower().endswith("/" + tail) for path in held_paths):
return fe
return candidates[-1]
def generate_md5_pack( def generate_md5_pack(
hashes: list[tuple[str, str]], hashes: list[tuple[str, str]],
db: dict, db: dict,
@@ -1805,7 +1879,7 @@ def generate_md5_pack(
if zip_contents is None: if zip_contents is None:
zip_contents = {} zip_contents = {}
plat_file_index: dict[str, dict] = {} plat_file_index: dict[str, list[dict]] = {}
base_dest = "" base_dest = ""
plat_display = "Custom" plat_display = "Custom"
if platform_name and platforms_dir: if platform_name and platforms_dir:
@@ -1814,29 +1888,28 @@ def generate_md5_pack(
plat_display = config.get("platform", platform_name) plat_display = config.get("platform", platform_name)
for _sys_id, system in config.get("systems", {}).items(): for _sys_id, system in config.get("systems", {}).items():
for fe in system.get("files", []): for fe in system.get("files", []):
plat_file_index[fe.get("name", "").lower()] = fe plat_file_index.setdefault(fe.get("name", "").lower(), []).append(fe)
emu_pack_structure = None emu_pack_structure = None
emu_display = "" emu_display = ""
if emulator_name and emulators_dir: if emulator_name and emulators_dir:
profiles = load_emulator_profiles(emulators_dir, skip_aliases=False) profiles = load_emulator_profiles(emulators_dir, skip_aliases=False)
if emulator_name in profiles: # The same gate as --emulator, unknown names included: a typo or a
# The same gate as --emulator: a core with no standalone build # buildbot name gave a generic pack in the layout of no emulator. A
# has no standalone layout, and a pack named for one would be # core with no standalone build has no standalone layout either.
# the libretro pack under a name that promises otherwise. try:
try: (_name, profile), = select_emulator_profiles(
(_name, profile), = select_emulator_profiles( [emulator_name], profiles, standalone
[emulator_name], profiles, standalone )
) except ProfileSelectionError as exc:
except ProfileSelectionError as exc: print(f"Error: {exc}", file=sys.stderr)
print(f"Error: {exc}", file=sys.stderr) return None
return None emu_display = profile.get("emulator", emulator_name)
emu_display = profile.get("emulator", emulator_name) emu_pack_structure = profile.get("pack_structure")
emu_pack_structure = profile.get("pack_structure") for fe in profile.get("files", []):
for fe in profile.get("files", []): plat_file_index.setdefault(fe.get("name", "").lower(), []).append(fe)
plat_file_index[fe.get("name", "").lower()] = fe for alias in fe.get("aliases", []):
for alias in fe.get("aliases", []): plat_file_index.setdefault(alias.lower(), []).append(fe)
plat_file_index[alias.lower()] = fe
context_name = plat_display if platform_name else (emu_display or "Custom") context_name = plat_display if platform_name else (emu_display or "Custom")
# --standalone changes the destination layout, so a run with it must not # --standalone changes the destination layout, so a run with it must not
@@ -1881,11 +1954,16 @@ def generate_md5_pack(
matched_fe = None matched_fe = None
for lookup_name in [name] + aliases: for lookup_name in [name] + aliases:
if lookup_name.lower() in plat_file_index: if lookup_name.lower() in plat_file_index:
matched_fe = plat_file_index[lookup_name.lower()] matched_fe = _entry_for_file(
plat_file_index[lookup_name.lower()], entry
)
break break
if matched_fe: if matched_fe:
if emulator_name and emu_pack_structure is not None: if emulator_name:
# Laid out as the emulator pack lays it out: a profile
# without pack_structure still names its path and its
# standalone_path, and --standalone has to reach them.
dest = _resolve_destination( dest = _resolve_destination(
matched_fe, emu_pack_structure, standalone matched_fe, emu_pack_structure, standalone
) )
@@ -2690,6 +2768,16 @@ def main():
packresolve.set_offline(bool(args.offline)) packresolve.set_offline(bool(args.offline))
_refuse_unapplied_flags(args, parser) _refuse_unapplied_flags(args, parser)
if args.target:
# Named once, by the name the target is filed under: every artifact
# name below derives from it.
args.target = canonical_target_name(
[args.platform]
if args.platform
else list_registered_platforms(args.platforms_dir, include_archived=True),
args.target,
args.platforms_dir,
)
# Quick-exit modes: --verify-packs alone = verify existing packs only # Quick-exit modes: --verify-packs alone = verify existing packs only
# Combined with --all-variants, generation runs first then verify # Combined with --all-variants, generation runs first then verify
@@ -2946,6 +3034,52 @@ def _get_repo_path(sha1: str, db: dict) -> str:
return entry.get("path", "") return entry.get("path", "")
def _manifest_entry(
local_path: str | None,
dest: str,
cores: list[str] | None,
db: dict,
repo_root: str,
selection_fields: dict,
) -> tuple[dict | None, int]:
"""The download record of a resolved file, and the size it occupies.
The installer fetches by hash, so the entry records the copy this
repository holds: an upstream hash carried by no local file resolves to
no URL. An entry needs somewhere to be fetched from, and a file the
database does not index (a data directory, a copy added since the last
scan, a hash computed wrong) has neither a repo path nor a release
asset. None then, so the caller records an omission instead of a dead
entry that makes the installer refuse the whole manifest.
`selection_fields` carries what the installer narrows on.
"""
sha1 = ""
sha256 = ""
file_size = 0
if local_path and os.path.exists(local_path):
file_size = os.path.getsize(local_path)
hashes = compute_hashes(local_path)
sha1 = hashes["sha1"]
sha256 = hashes["sha256"]
repo_path = _get_repo_path(sha1, db) if sha1 else ""
is_release_asset = _is_release_asset(local_path or "", repo_root)
if not repo_path and not is_release_asset:
return None, file_size
entry: dict = {
"dest": dest,
"sha1": sha1,
"sha256": sha256,
"size": file_size,
"repo_path": repo_path,
"cores": cores,
**selection_fields,
}
if is_release_asset:
entry["storage"] = "release"
entry["release_asset"] = _release_asset_name(local_path, repo_root)
return entry, file_size
def _manifest_core_entries( def _manifest_core_entries(
core_files: list, core_files: list,
config: dict, config: dict,
@@ -2963,6 +3097,8 @@ def _manifest_core_entries(
manifest_files: list, manifest_files: list,
omitted_by_destination: dict, omitted_by_destination: dict,
record_omission, record_omission,
pack_only_sizes: list[int],
selection: SelectionIndex,
required_only: bool = False, required_only: bool = False,
) -> int: ) -> int:
"""Add the files a platform's cores need but its list does not name. """Add the files a platform's cores need but its list does not name.
@@ -3018,16 +3154,6 @@ def _manifest_core_entries(
) )
continue continue
sha1 = ""
sha256 = ""
file_size = 0
if local_path and os.path.exists(local_path):
file_size = os.path.getsize(local_path)
hashes = compute_hashes(local_path)
sha1 = hashes["sha1"]
sha256 = hashes["sha256"]
repo_path = _get_repo_path(sha1, db) if sha1 else ""
source_emu = fe.get("source_profile") or fe.get("source_emulator", "") source_emu = fe.get("source_profile") or fe.get("source_emulator", "")
# Manifest dests are relative to base_destination; keep the inferred # Manifest dests are relative to base_destination; keep the inferred
@@ -3036,18 +3162,20 @@ def _manifest_core_entries(
if base_dest and manifest_dest.startswith(f"{base_dest}/"): if base_dest and manifest_dest.startswith(f"{base_dest}/"):
manifest_dest = manifest_dest[len(base_dest) + 1:] manifest_dest = manifest_dest[len(base_dest) + 1:]
entry = { entry, file_size = _manifest_entry(
"dest": manifest_dest, local_path, manifest_dest, [source_emu] if source_emu else [],
"sha1": sha1, db, repo_root,
"sha256": sha256, selection.fields(dest, fe.get("name", "")),
"size": file_size, )
"repo_path": repo_path, if entry is None:
"cores": [source_emu] if source_emu else [], systems = _extra_system_ids(fe)
} record_omission(
full_dest, fe, systems[0] if systems else "", "not_found",
if _is_release_asset(local_path or "", repo_root): [source_emu] if source_emu else [],
entry["storage"] = "release" )
entry["release_asset"] = _release_asset_name(local_path, repo_root) if file_size:
pack_only_sizes.append(file_size)
continue
manifest_files.append(entry) manifest_files.append(entry)
omitted_by_destination.pop(full_dest, None) omitted_by_destination.pop(full_dest, None)
@@ -3060,7 +3188,7 @@ def _manifest_core_entries(
return total_size return total_size
def _manifest_region_drops( def _manifest_selection(
config: dict, config: dict,
pack_systems: dict, pack_systems: dict,
emulators_dir: str, emulators_dir: str,
@@ -3071,10 +3199,13 @@ def _manifest_region_drops(
source: str, source: str,
required_only: bool, required_only: bool,
regions: list[str] | None, regions: list[str] | None,
) -> set[str]: ) -> tuple[set[str], SelectionIndex]:
"""Destinations a region list removes, grouped as the pack groups them.""" """Destinations a region list removes, and the groups the installer replays.
if not regions:
return set() Grouped as the pack groups them, so a region the installer applies to
the full manifest withdraws what `--region` withdraws from the pack.
"""
extras: list[dict] = []
region_groups, _extra_dests = platform_region_groups( region_groups, _extra_dests = platform_region_groups(
config, config,
pack_systems, pack_systems,
@@ -3086,9 +3217,16 @@ def _manifest_region_drops(
include_extras=(source != "platform"), include_extras=(source != "platform"),
include_all=(source == "truth"), include_all=(source == "truth"),
required_only=required_only, required_only=required_only,
extras_out=extras,
) )
return region_mod.resolve_region_drops( region_index = region_mod.build_region_index(emu_profiles)
region_groups, region_mod.build_region_index(emu_profiles), regions drops = (
region_mod.resolve_region_drops(region_groups, region_index, regions)
if regions
else set()
)
return drops, SelectionIndex.build(
region_groups, region_index, pack_systems, extras, required_only
) )
@@ -3199,9 +3337,17 @@ def generate_manifest(
"required": bool(file_entry.get("required", True)), "required": bool(file_entry.get("required", True)),
"reason": reason, "reason": reason,
"cores": cores, "cores": cores,
# The builder counts an unavailable regional file in its groups:
# the installer must too, or it keeps a fallback the pack drops.
**selection.fields(
sanitize_pack_path(
file_entry.get("destination", file_entry.get("name", ""))
),
str(file_entry.get("name") or ""),
),
} }
region_drops = _manifest_region_drops( region_drops, selection = _manifest_selection(
config, pack_systems, emulators_dir, db, base_dest, emu_profiles, config, pack_systems, emulators_dir, db, base_dest, emu_profiles,
target_cores, source, required_only, regions, target_cores, source, required_only, regions,
) )
@@ -3217,7 +3363,7 @@ def generate_manifest(
# does; a manifest carries no data directories. # does; a manifest carries no data directories.
preferred_entries = _preferred_entries( preferred_entries = _preferred_entries(
pack_systems, db, bios_dir, base_dest, required_only, pack_systems, db, bios_dir, base_dest, required_only,
zip_contents, None, offline, zip_contents, offline,
) )
for sys_id, system in sorted(pack_systems.items()): for sys_id, system in sorted(pack_systems.items()):
for file_entry in system.get("files", []): for file_entry in system.get("files", []):
@@ -3289,29 +3435,11 @@ def generate_manifest(
digest_algorithm(verification_mode), dest, manifest_owners, digest_algorithm(verification_mode), dest, manifest_owners,
) )
# Get SHA1 and size. The installer fetches by hash, so record entry, file_size = _manifest_entry(
# the copy this repo holds: an upstream hash carried by no local_path, dest, None, db, repo_root,
# local file resolves to no download URL at all. selection.fields(dest, file_entry["name"]),
sha1 = "" )
sha256 = "" if entry is None:
file_size = 0
if local_path and os.path.exists(local_path):
file_size = os.path.getsize(local_path)
hashes = compute_hashes(local_path)
sha1 = hashes["sha1"]
sha256 = hashes["sha256"]
repo_path = _get_repo_path(sha1, db) if sha1 else ""
is_release_asset = _is_release_asset(local_path or "", repo_root)
# An entry needs somewhere to be fetched from. Resolution can
# land on a file the database does not index -- a data
# directory, or a copy added since the last scan -- and then
# neither a repo path nor a release asset exists, so the
# installer would list a file it can never download. Recording
# it as omitted keeps that visible instead of shipping a dead
# entry.
if not repo_path and not is_release_asset:
record_omission(full_dest, file_entry, sys_id, "not_found", None) record_omission(full_dest, file_entry, sys_id, "not_found", None)
if file_size and full_dest not in seen_destinations: if file_size and full_dest not in seen_destinations:
pack_only_sizes.append(file_size) pack_only_sizes.append(file_size)
@@ -3321,21 +3449,6 @@ def generate_manifest(
seen_lower.add(dedup_key.lower()) seen_lower.add(dedup_key.lower())
continue continue
entry: dict = {
"dest": dest,
"sha1": sha1,
"sha256": sha256,
"size": file_size,
"repo_path": repo_path,
"cores": None,
}
if is_release_asset:
entry["storage"] = "release"
entry["release_asset"] = _release_asset_name(
local_path, repo_root
)
manifest_files.append(entry) manifest_files.append(entry)
omitted_by_destination.pop(full_dest, None) omitted_by_destination.pop(full_dest, None)
total_size += file_size total_size += file_size
@@ -3365,7 +3478,8 @@ def generate_manifest(
core_files, config, db, bios_dir, base_dest, repo_root, core_files, config, db, bios_dir, base_dest, repo_root,
zip_contents, offline, region_drops, case_insensitive, zip_contents, offline, region_drops, case_insensitive,
seen_destinations, seen_lower, seen_parents, manifest_files, seen_destinations, seen_lower, seen_parents, manifest_files,
omitted_by_destination, record_omission, required_only, omitted_by_destination, record_omission, pack_only_sizes, selection,
required_only,
) )
# Phase 3: data directories. The installer does not fetch them, so they # Phase 3: data directories. The installer does not fetch them, so they
+1 -1
View File
@@ -372,7 +372,7 @@ def generate_readme(db: dict, platforms_dir: str) -> str:
"", "",
"## Download BIOS packs", "## Download BIOS packs",
"", "",
"One pack per platform, and it holds everything the platform runs: its own BIOS list plus every file its emulator cores load. Pick your platform, download the ZIP, extract to the BIOS path. The installer above does the same file by file, and `--target` narrows it to one machine; for a region or a bare minimum, build your own pack below.", "One pack per platform, and it holds everything the platform runs: its own BIOS list plus every file its emulator cores load. Pick your platform, download the ZIP, extract to the BIOS path. The installer above does the same file by file. At a terminal it asks whether to install everything or to choose systems, cores and regions, and `--system`, `--core`, `--region` and `--target` make the same choice without questions; for a bare minimum, build your own pack below.",
"Files is how many the pack holds once extracted, the figure a file" "Files is how many the pack holds once extracted, the figure a file"
" manager shows for the folder. It differs from one platform to the" " manager shows for the folder. It differs from one platform to the"
" next because a pack carries only what that platform's emulators" " next because a pack carries only what that platform's emulators"
+7 -5
View File
@@ -3405,11 +3405,13 @@ guarantee either: source profiles can document files nobody has dumped or
that only the user can provide, all of them visible in the that only the user can provide, all of them visible in the
[gap analysis](gaps.md). [gap analysis](gaps.md).
Want less than everything? The installer takes `--target switch` to install Want less than everything? At a terminal the installer asks whether to install
only what one machine's cores need. From a clone of the repository, everything or to choose systems, cores and regions, and `--system`, `--core`,
`python scripts/generate_pack.py --platform retroarch --region us` keeps one `--region` and `--target` make the same choice without questions (see the
BIOS per region and `--required-only` the bare minimum each core needs to [installer reference](wiki/installer.md#choosing-what-to-install)). From a
start; `--help` lists every way to build your own. clone of the repository, `python scripts/generate_pack.py --platform retroarch
--region us` builds the same narrowing as a pack and `--required-only` the bare
minimum each core needs to start; `--help` lists every way to build your own.
--- ---
+1 -1
View File
@@ -115,7 +115,7 @@ def main() -> None:
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
parser.add_argument("emulators", nargs="+") parser.add_argument("emulators", nargs="+")
parser.add_argument("--emulators-dir", default="emulators") parser.add_argument("--emulators-dir", default="emulators")
parser.add_argument("--cache-dir", default=".cache") parser.add_argument("--cache-dir", default=upstream.CACHE_DIR)
parser.add_argument("--offline", action="store_true") parser.add_argument("--offline", action="store_true")
parser.add_argument("--write", action="store_true") parser.add_argument("--write", action="store_true")
args = parser.parse_args() args = parser.parse_args()
+117
View File
@@ -0,0 +1,117 @@
"""What an install manifest records about each file so the installer can narrow it.
The installer runs without the profiles or the database, yet a narrowed
install must hold the same files as a pack built with the same narrowing.
Each entry therefore carries the platform systems it belongs to and, when
the code selects it by territory, its regions and the groups in which it
competes. The installer replays region.resolve_region_drops over them.
The pack's `--system X` keeps the platform's declarations under X and every
core extra owned by X, then groups regions over the declarations it kept and
over every core extra of the platform. A group membership is therefore either
unconditional (a core extra) or present only when the declaring system is
kept (a platform declaration), and the manifest records the two apart.
"""
from __future__ import annotations
from dataclasses import dataclass
import region as region_mod
from common import _norm_system_id, sanitize_pack_path
from packextras import _extra_system_ids, _kept, extra_region_groups
def _dest(entry: dict) -> str:
return sanitize_pack_path(entry.get("destination", entry.get("name", "")))
@dataclass(frozen=True)
class SelectionIndex:
"""System and region facts for one platform, as the pack builder reads them."""
region_index: dict[str, dict]
competing: frozenset[str]
systems_by_dest: dict[str, frozenset[str]]
declared_by: dict[str, frozenset[str]]
extra_groups: dict[str, frozenset[str]]
@classmethod
def build(
cls,
region_groups: dict[str, list[tuple[str, str]]],
region_index: dict[str, dict],
pack_systems: dict[str, dict],
extras: list[dict],
required_only: bool = False,
) -> SelectionIndex:
by_norm: dict[str, set[str]] = {}
for sys_id in pack_systems:
by_norm.setdefault(_norm_system_id(sys_id), set()).add(sys_id)
declared_by: dict[str, set[str]] = {}
for sys_id, system in pack_systems.items():
for file_entry in _kept(system.get("files", []), required_only):
dest = _dest(file_entry)
if dest:
declared_by.setdefault(dest, set()).add(sys_id)
owned_by: dict[str, set[str]] = {}
extra_groups: dict[str, set[str]] = {}
for extra in extras:
dest = _dest(extra)
if not dest:
continue
extra_groups.setdefault(dest, set()).update(extra_region_groups(extra))
# A profile system the platform does not have is no system the
# pack's --system accepts: it would offer what the pack refuses.
for sys_id in _extra_system_ids(extra):
owned_by.setdefault(dest, set()).update(
by_norm.get(_norm_system_id(sys_id), ())
)
systems_by_dest = {
dest: frozenset(declared_by.get(dest, set()) | owned_by.get(dest, set()))
for dest in set(declared_by) | set(owned_by)
}
return cls(
region_index,
frozenset(
dest for members in region_groups.values() for dest, _name in members
),
systems_by_dest,
{dest: frozenset(ids) for dest, ids in declared_by.items()},
{dest: frozenset(ids) for dest, ids in extra_groups.items()},
)
def fields(self, dest: str, name: str) -> dict:
"""What an entry carries for the installer's selection.
`systems` drives --system. A regional entry adds its regions,
`region_groups` it always competes in as a core extra, and
`region_system_groups` it competes in only when that platform system
is kept, and `priority` the rank the code searches it at. An entry that never competes carries no region field: an
untagged file always survives a region filter.
"""
out: dict = {}
systems = self.systems_by_dest.get(dest)
if systems:
out["systems"] = sorted(systems)
if dest not in self.competing:
return out
regions = region_mod.lookup_regions(self.region_index, dest, name)
if not regions:
return out
out["regions"] = sorted(regions)
always = self.extra_groups.get(dest)
if always:
out["region_groups"] = sorted(always)
declared = self.declared_by.get(dest)
if declared:
out["region_system_groups"] = sorted(declared)
# The code's search rank decides whether a world file may withdraw
# this one when no region matches.
priority = region_mod.lookup_priority(self.region_index, dest, name)
if priority is not None:
out["priority"] = priority
return out
+101 -58
View File
@@ -7,7 +7,7 @@ this module builds so the report and the pack withdraw the same files."""
from __future__ import annotations from __future__ import annotations
from common import PROFILE_IDENTITY_FIELDS from common import PROFILE_IDENTITY_FIELDS
from common import expand_platform_declared_names from common import declared_root, expand_platform_declared_names, platform_declarations
from common import load_emulator_profiles from common import load_emulator_profiles
from common import resolution_is_hash_exact from common import resolution_is_hash_exact
from common import resolve_local_file from common import resolve_local_file
@@ -33,29 +33,12 @@ def _emulator_systems_index(emu_profiles: dict | None) -> dict[str, list[str]]:
return index return index
def _detect_extras_prefix(config: dict, base_dest: str) -> str: def _detect_extras_prefix(config: dict, base_dest: str) -> str:
"""Detect the effective BIOS prefix for core extras. """The prefix core-extra destinations take (may be empty).
When base_destination is empty (RetroDECK), infer the prefix from When base_destination is empty (RetroDECK), it is the dominant root of
the dominant root of YAML-declared destinations. Returns the prefix the YAML-declared destinations.
to prepend to every core-extra destination (may be empty).
""" """
if base_dest: return base_dest or declared_root(config)
return base_dest
dests: list[str] = []
for sys_data in config.get("systems", {}).values():
for f in sys_data.get("files", []):
d = f.get("destination", "")
if d and "/" in d:
dests.append(d)
if not dests:
return ""
from collections import Counter
roots = Counter(d.split("/", 1)[0] for d in dests)
most_common, count = roots.most_common(1)[0]
if count / len(dests) > 0.9:
return most_common
return ""
def _detect_slug_structure(config: dict) -> tuple[bool, dict[str, str]]: def _detect_slug_structure(config: dict) -> tuple[bool, dict[str, str]]:
"""Detect whether a platform uses per-system slug destinations. """Detect whether a platform uses per-system slug destinations.
@@ -137,7 +120,19 @@ def _map_emulator_to_slug(
def _slug_for( def _slug_for(
u: dict, profiles: dict, platform_systems: set, norm_map: dict, sys_to_slug: dict u: dict, profiles: dict, platform_systems: set, norm_map: dict, sys_to_slug: dict
) -> str: ) -> str:
"""The platform slug a report entry's emulator files under, or empty.""" """The platform slug a report entry files under, or empty.
The entry's own system decides first: picodrive's Mega CD BIOS belongs
under segacd/, where RomM looks for a Mega CD game's firmware, not under
gamegear/, the first of the emulator's systems.
"""
own = u.get("system")
if own:
slug = _map_emulator_to_slug(
{"systems": [own]}, platform_systems, norm_map, sys_to_slug
)
if slug:
return slug
emu_name = u.get("profile") or u.get("emulator", "") emu_name = u.get("profile") or u.get("emulator", "")
profile = profiles.get(emu_name, {}) profile = profiles.get(emu_name, {})
if not profile: if not profile:
@@ -213,7 +208,9 @@ def _agnostic_scan_extras(
if not isinstance(candidate, dict): if not isinstance(candidate, dict):
continue continue
local, status = resolve_local_file( local, status = resolve_local_file(
candidate, db, dest_hint=candidate.get("path", "") {**candidate, "source_profile": emu_name},
db,
dest_hint=candidate.get("path", ""),
) )
if not local or "/" not in local: if not local or "/" not in local:
continue continue
@@ -439,6 +436,45 @@ def _archive_prefix_extras(
return extras return extras
class ExtraPlacement:
"""Where a pack puts a core's file, decided once for every reader.
The builder, and slots.py judging what a destination holds, both read
this: placed apart, slot arbitration compared RetroDECK's bios/ claims
with core claims at the root and never saw them meet.
"""
def __init__(self, config: dict, base_dest: str, profiles: dict) -> None:
from common import _norm_system_id
self.prefix = _detect_extras_prefix(config, base_dest)
self.slug_based, self.sys_to_slug = _detect_slug_structure(config)
self.platform_systems = set(config.get("systems", {}).keys())
self.norm_map = (
{_norm_system_id(sid): sid for sid in self.platform_systems}
if self.slug_based
else {}
)
self.profiles = profiles
def place(self, entry: dict, dest: str) -> tuple[str, str] | None:
"""(pack-relative destination, full destination), or None.
``entry`` names its profile and may name its own system. A
slug-based platform cannot place a file of a system it has no
folder for.
"""
if self.slug_based:
slug = _slug_for(
entry, self.profiles, self.platform_systems, self.norm_map,
self.sys_to_slug,
)
if not slug:
return None
dest = f"{slug}/{dest}"
return dest, (f"{self.prefix}/{dest}" if self.prefix else dest)
def _collect_emulator_extras( def _collect_emulator_extras(
config: dict, config: dict,
emulators_dir: str, emulators_dir: str,
@@ -472,7 +508,7 @@ def _collect_emulator_extras(
Works for ANY platform (RetroArch, Batocera, Recalbox, etc.) Works for ANY platform (RetroArch, Batocera, Recalbox, etc.)
""" """
from common import _norm_system_id, resolve_platform_cores from common import resolve_platform_cores
from verify import find_undeclared_files from verify import find_undeclared_files
profiles = ( profiles = (
@@ -482,22 +518,13 @@ def _collect_emulator_extras(
) )
# Detect destination conventions for core extras # Detect destination conventions for core extras
extras_prefix = _detect_extras_prefix(config, base_dest) placement = ExtraPlacement(config, base_dest, profiles)
is_slug_based, sys_to_slug = _detect_slug_structure(config) extras_prefix = placement.prefix
platform_systems = set(config.get("systems", {}).keys()) is_slug_based = placement.slug_based
norm_map: dict[str, str] = {}
if is_slug_based:
for sid in platform_systems:
norm_map[_norm_system_id(sid)] = sid
# Use strict YAML names (no DB alias enrichment) so that files known # Use strict YAML names (no DB alias enrichment) so that files known
# under an alias still get packed at the emulator's expected path. # under an alias still get packed at the emulator's expected path.
yaml_names: set[str] = set() yaml_names = platform_declarations(config, db, enrich=False)
for system in config.get("systems", {}).values():
for fe in system.get("files", []):
name = fe.get("name", "")
if name:
yaml_names.add(name)
undeclared = find_undeclared_files( undeclared = find_undeclared_files(
config, emulators_dir, db, emu_profiles, target_cores=target_cores, config, emulators_dir, db, emu_profiles, target_cores=target_cores,
@@ -516,15 +543,12 @@ def _collect_emulator_extras(
dest = f"{raw_dest}{u['name']}" if raw_dest.endswith("/") else raw_dest dest = f"{raw_dest}{u['name']}" if raw_dest.endswith("/") else raw_dest
# Slug-based platforms: prefix dest with system slug # Slug-based platforms: prefix dest with system slug
if is_slug_based: placed = placement.place(u, dest)
slug = _slug_for(u, profiles, platform_systems, norm_map, sys_to_slug) if placed is None:
if not slug: if unplaceable is not None:
if unplaceable is not None: unplaceable.append(u)
unplaceable.append(u) continue
continue dest, full_dest = placed
dest = f"{slug}/{dest}"
full_dest = f"{extras_prefix}/{dest}" if extras_prefix else dest
if claimants is not None: if claimants is not None:
claimants[(u.get("emulator", ""), u.get("name", ""), u.get("path") or "")] = ( claimants[(u.get("emulator", ""), u.get("name", ""), u.get("path") or "")] = (
sanitize_pack_path(dest) sanitize_pack_path(dest)
@@ -685,6 +709,7 @@ def platform_region_groups(
include_extras: bool = True, include_extras: bool = True,
include_all: bool = False, include_all: bool = False,
required_only: bool = False, required_only: bool = False,
extras_out: list[dict] | None = None,
) -> tuple[dict[str, list[tuple[str, str]]], dict[tuple[str, str, str], str]]: ) -> tuple[dict[str, list[tuple[str, str]]], dict[tuple[str, str, str], str]]:
"""Group a platform's pack candidates the way region filtering reads them. """Group a platform's pack candidates the way region filtering reads them.
@@ -692,7 +717,8 @@ def platform_region_groups(
grouped under, keyed by (emulator, name). verify.py needs that mapping to grouped under, keyed by (emulator, name). verify.py needs that mapping to
withdraw from its report exactly what the builder withdraws from the pack: withdraw from its report exactly what the builder withdraws from the pack:
grouping the declared files here and the core extras there would let the grouping the declared files here and the core extras there would let the
two answer differently on the same request. two answer differently on the same request. `extras_out` receives the
core extras that were grouped, for the install manifest.
""" """
groups: dict[str, list[tuple[str, str]]] = {} groups: dict[str, list[tuple[str, str]]] = {}
for sys_id, system in systems.items(): for sys_id, system in systems.items():
@@ -733,19 +759,36 @@ def platform_region_groups(
extra.get("source_path", ""), extra.get("source_path", ""),
) )
] = dest ] = dest
variant = extra.get("variant_group") for group_id in extra_region_groups(extra):
if variant:
# A variant group is one slot of one core, whatever system each
# member is filed under: PicoDrive files its US Mega CD BIOS under
# sega-segacd and the EU/JP ones under sega-megacd, and find_bios
# picks one of the twelve. Keyed as emulator mode keys it.
group_ids = [f"{extra.get('source_emulator', '')}:variant:{variant}"]
else:
group_ids = _extra_system_ids(extra) or ["_extras"]
for group_id in group_ids:
groups.setdefault(group_id, []).append((dest, name)) groups.setdefault(group_id, []).append((dest, name))
if extras_out is not None:
extras_out.append(extra)
return groups, extra_dests return groups, extra_dests
def extra_region_groups(extra: dict) -> list[str]:
"""The region groups a core extra competes in."""
emulator = extra.get("source_emulator", "")
variant = extra.get("variant_group")
if variant:
# A variant group is one slot of one core, whatever system each
# member is filed under: PicoDrive files its US Mega CD BIOS under
# sega-segacd and the EU/JP ones under sega-megacd, and find_bios
# picks one of the twelve. Keyed as emulator mode keys it.
return [f"{emulator}:variant:{variant}"]
explicit = extra.get("source_system")
if explicit:
return [str(explicit)]
systems = [str(s) for s in extra.get("source_systems", []) if s]
if len(systems) == 1:
return systems
# No system names the machine this file is chosen for. Filed under every
# system of a 30-system profile, CLK's American MSX ROM made the
# Enterprise group drop its German ROM. Alone, it drops nothing and is
# never dropped.
destination = extra.get("destination", extra.get("name", ""))
return [f"{emulator}:file:{destination}"]
def emulator_region_drops( def emulator_region_drops(
selected: list[tuple[str, dict]], selected: list[tuple[str, dict]],
standalone: bool, standalone: bool,
+15 -1
View File
@@ -19,6 +19,7 @@ from ziptools import check_inside_zip
from nativemode import digest_algorithm from nativemode import digest_algorithm
from validation import settle_mismatch from validation import settle_mismatch
from nativemode import hash_mismatch_excludes_file from nativemode import hash_mismatch_excludes_file
import functools
import hashlib import hashlib
from common import filter_systems_by_target from common import filter_systems_by_target
from common import load_emulator_profiles from common import load_emulator_profiles
@@ -35,6 +36,16 @@ import io
from collections.abc import Callable from collections.abc import Callable
@functools.lru_cache(maxsize=None)
def _file_sha1(path: str) -> str:
"""SHA-1 of a cached data-directory file, read once per run."""
digest = hashlib.sha1()
with open(path, "rb") as handle:
for chunk in iter(lambda: handle.read(1 << 20), b""):
digest.update(chunk)
return digest.hexdigest()
def _members_are_held(data: bytes, by_md5: dict, held_inside: dict) -> bool: def _members_are_held(data: bytes, by_md5: dict, held_inside: dict) -> bool:
"""Whether every member of an archive is a dump the collection holds.""" """Whether every member of an archive is a dump the collection holds."""
try: try:
@@ -215,7 +226,10 @@ def verify_pack(
for _dp in _cands: for _dp in _cands:
if not os.path.exists(_dp): if not os.path.exists(_dp):
continue continue
if os.path.getsize(_dp) == size: # The content, not the size: a member written corrupt
# keeps its length, and a same-sized homonym from another
# cache is another file.
if os.path.getsize(_dp) == size and _file_sha1(_dp) == sha1:
status = "verified_data" status = "verified_data"
file_name = _bn file_name = _bn
break break
+67 -10
View File
@@ -31,7 +31,7 @@ import upstream
from common import load_emulator_profiles from common import load_emulator_profiles
from upstream import CompareResult, find_renamed from upstream import CompareResult, find_renamed
DEFAULT_CACHE = ".cache/upstream" DEFAULT_CACHE = upstream.CACHE_DIR
ANON_QUOTA = 60 ANON_QUOTA = 60
TRIAGE_PATH_SAMPLE = 5 TRIAGE_PATH_SAMPLE = 5
@@ -1047,10 +1047,15 @@ class ProfileReport:
upstream_pin: str | None = None upstream_pin: str | None = None
upstream_head: str | None = None upstream_head: str | None = None
upstream_origin: str | None = None upstream_origin: str | None = None
# Repositories whose pin is no ancestor of the branch the profile
# tracks: history was rewritten, or the pin lies on another branch.
orphaned: list[str] = field(default_factory=list)
def needs_review(self) -> int: def needs_review(self) -> int:
counts = self.counts or {} counts = self.counts or {}
return sum(counts.get(status, 0) for status in REVIEW_STATUSES) return sum(counts.get(status, 0) for status in REVIEW_STATUSES) + len(
self.orphaned
)
def select_repo(profile: dict) -> upstream.Repo | None: def select_repo(profile: dict) -> upstream.Repo | None:
@@ -1422,6 +1427,16 @@ def build_report(
report.pinned_tag = frozen or detect_pinned_tag( report.pinned_tag = frozen or detect_pinned_tag(
profile, views, cache_dir, offline profile, views, cache_dir, offline
) )
if not report.pinned_tag:
# A tag or a held pin is off the tracked branch by design.
report.orphaned = [
f"{view.repo.slug}: pin {view.pin[:8]} is no ancestor of {view.head[:8]}"
for view in views
if view.pin != view.head
and upstream.compare(
view.repo, view.pin, view.head, cache_dir, offline
).diverged
]
# A profile carrying no source_ref still has a pin worth writing and a # A profile carrying no source_ref still has a pin worth writing and a
# version worth checking, so the revisions above are resolved first. # version worth checking, so the revisions above are resolved first.
@@ -1666,10 +1681,17 @@ def build_report(
token on the cited line, so no single attribution can be right for all token on the cited line, so no single attribution can be right for all
of them; taking the best outcome across repositories settles it. of them; taking the best outcome across repositories settles it.
""" """
owner, _actual = resolve_path(part.path, part.start, tuple(tokens))
carrying = [
view for view in views
if fetch_from(view.repo, view.pin, part.path) is not None
]
live = {
id(view) for view in carrying
if fetch_from(view.repo, view.head, part.path) is not None
}
best = None best = None
for view in views: for view in carrying:
if fetch_from(view.repo, view.pin, part.path) is None:
continue
result = anchor_part( result = anchor_part(
part, part,
lambda which, path, start=None, toks=(), _v=view: fetch( lambda which, path, start=None, toks=(), _v=view: fetch(
@@ -1681,8 +1703,19 @@ def build_report(
), ),
tokens, tokens,
) )
if (
live and id(view) not in live
and result.status in ("AMBIGUOUS", "GONE")
):
# dosbox-pure's upstream was emptied: its rename search
# offered the .info, .sln and .vcxproj as candidates and hid
# the real change in the port that still carries the file.
continue
rank = STATUS_ORDER.index(result.status) rank = STATUS_ORDER.index(result.status)
if best is None or rank < best[0]: # On a tie the repository the citation is attributed to speaks:
# mame's "written against HEAD, pin names an older revision" was
# replaced by whatever the first repository said.
if best is None or rank < best[0] or (rank == best[0] and view is owner):
best = (rank, result) best = (rank, result)
return best[1] if best else anchor_part( return best[1] if best else anchor_part(
part, fetch, rename_getter, describe, tokens part, fetch, rename_getter, describe, tokens
@@ -1796,6 +1829,8 @@ def format_report(report: ProfileReport, changed_only: bool = False) -> str:
lines.append(" pin is HEAD: checked for self-consistency") lines.append(" pin is HEAD: checked for self-consistency")
for url in report.unread or []: for url in report.unread or []:
lines.append(f" not read (unsupported host): {url}") lines.append(f" not read (unsupported host): {url}")
for line in report.orphaned:
lines.append(f" orphaned pin: {line}")
if report.skipped: if report.skipped:
lines.append(f" skipped: {report.skipped}") lines.append(f" skipped: {report.skipped}")
return "\n".join(lines) return "\n".join(lines)
@@ -2636,9 +2671,15 @@ def _resolve_at(
def _realign_part( def _realign_part(
part: RefPart, pairs, cache_dir: str, offline: bool part: RefPart, pairs, cache_dir: str, offline: bool, pinned=()
) -> tuple[str, object] | None: ) -> tuple[str, object] | None:
"""One range, anchored from its writing revision to the current pin.""" """One range, anchored from its writing revision to the current pin.
`pinned` lists every declared (repository, current pin). A path no moved
repository carries but an unmoved one does lives where nothing moved:
it needs no move, and calling it absent made two readings of the same
history disagree on every run (nethersx2's AetherSX2 citations).
"""
if is_external_citation(part.path): if is_external_citation(part.path):
return None return None
unclear = None unclear = None
@@ -2665,6 +2706,13 @@ def _realign_part(
return "skip", f"{part.path}:{part.start} {anchored.status.lower()}" return "skip", f"{part.path}:{part.start} {anchored.status.lower()}"
if unclear: if unclear:
return "skip", f"{part.path}: {unclear}" return "skip", f"{part.path}: {unclear}"
moved = {repo for repo, _written, _current in pairs}
if any(
upstream.fetch_file(repo, pin, part.path, cache_dir, offline) is not None
for repo, pin in pinned
if repo not in moved
):
return None
return "skip", f"{part.path} absent at the writing revision" return "skip", f"{part.path} absent at the writing revision"
@@ -2704,6 +2752,7 @@ def _realign_citation(
ambiguous: list[str], ambiguous: list[str],
cache_dir: str, cache_dir: str,
offline: bool, offline: bool,
pinned=(),
) -> tuple[dict[int, tuple[int, int, str | None]], list[str]]: ) -> tuple[dict[int, tuple[int, int, str | None]], list[str]]:
"""The moves one prose run needs, and what blocks them. """The moves one prose run needs, and what blocks them.
@@ -2719,7 +2768,7 @@ def _realign_citation(
outcomes = { outcomes = {
repr(result): result repr(result): result
for result in ( for result in (
_realign_part(part, pairs, cache_dir, offline) if pairs else None _realign_part(part, pairs, cache_dir, offline, pinned) if pairs else None
for pairs in readings for pairs in readings
) )
} }
@@ -2766,6 +2815,12 @@ def realign_prose(
repos = [(field, repo) for field, repo in repos if repo is not None] repos = [(field, repo) for field, repo in repos if repo is not None]
if not repos: if not repos:
return [] return []
pinned = [
(repo, document[f"{field}_commit"])
for field, repo in repos
if isinstance(document.get(f"{field}_commit"), str)
and document[f"{field}_commit"]
]
history = _git_history(path) history = _git_history(path)
if not history: if not history:
return [] return []
@@ -2801,7 +2856,9 @@ def realign_prose(
readings, ambiguous = _writing_pairs(document, repos, revisions, intro_sha) readings, ambiguous = _writing_pairs(document, repos, revisions, intro_sha)
if not any(readings): if not any(readings):
continue continue
moves, blocked = _realign_citation(citation, readings, ambiguous, cache_dir, offline) moves, blocked = _realign_citation(
citation, readings, ambiguous, cache_dir, offline, pinned
)
if blocked: if blocked:
# Half a run must not move: the untouched ranges would read as # Half a run must not move: the untouched ranges would read as
# already realigned when they were never even located. # already realigned when they were never even located.
+24 -27
View File
@@ -24,6 +24,7 @@ import urllib.error
import urllib.request import urllib.request
import zipfile import zipfile
from pathlib import Path from pathlib import Path
from artifacts import file_lock as _file_lock
from common import yaml_load from common import yaml_load
try: try:
@@ -51,28 +52,6 @@ def load_registry(registry_path: str = DEFAULT_REGISTRY) -> dict[str, dict]:
return data.get("data_directories", {}) return data.get("data_directories", {})
@contextlib.contextmanager
def _file_lock(lock_path: Path, shared: bool = False):
"""Hold a lock on lock_path, waiting for it if taken.
Several sessions refresh the same data directories: without it, two
swaps of one tree interleave and the second lands inside the first.
On platforms without flock the lock is a no-op.
"""
try:
import fcntl
except ImportError:
yield
return
lock_path.parent.mkdir(parents=True, exist_ok=True)
with open(lock_path, "a") as handle:
fcntl.flock(handle, fcntl.LOCK_SH if shared else fcntl.LOCK_EX)
try:
yield
finally:
fcntl.flock(handle, fcntl.LOCK_UN)
def cache_lock(cache_dir: str | Path, shared: bool = False): def cache_lock(cache_dir: str | Path, shared: bool = False):
"""The lock a refresh holds while it swaps cache_dir. """The lock a refresh holds while it swaps cache_dir.
@@ -210,6 +189,15 @@ def get_remote_sha(source_url: str, version: str) -> str | None:
return None return None
class NothingExtractedError(Exception):
"""An archive that yields no file for the cache.
Promoting it replaced the cache with an empty directory, recorded the new
version and reported success, so the next run read "up to date" and the
packs left without the directory.
"""
def _is_safe_tar_member(member: tarfile.TarInfo, dest: Path) -> bool: def _is_safe_tar_member(member: tarfile.TarInfo, dest: Path) -> bool:
"""Reject path traversal, absolute paths, and symlinks in tar members.""" """Reject path traversal, absolute paths, and symlinks in tar members."""
if member.issym() or member.islnk(): if member.issym() or member.islnk():
@@ -293,6 +281,8 @@ def _download_and_extract(
shutil.copyfileobj(src, dst) shutil.copyfileobj(src, dst)
file_count += 1 file_count += 1
if not file_count:
raise NothingExtractedError(f"no file under {source_path} in the archive")
_promote(extract_dir, cache_dir, Path(tmpdir)) _promote(extract_dir, cache_dir, Path(tmpdir))
return file_count return file_count
@@ -354,6 +344,8 @@ def _download_and_extract_zip(
shutil.copyfileobj(src, dst) shutil.copyfileobj(src, dst)
file_count += 1 file_count += 1
if not file_count:
raise NothingExtractedError("the archive holds no file to extract")
# The old tree is stepped aside rather than deleted: removing it # The old tree is stepped aside rather than deleted: removing it
# first and then failing to move the new one in left the cache with # first and then failing to move the new one in left the cache with
# nothing at all, and the next run reads that as "never fetched". # nothing at all, and the next run reads that as "never fetched".
@@ -434,6 +426,15 @@ def _refresh_entry(
) )
return True return True
if remote_tag is None:
# Read before the download. A release published in between then
# leaves an older tag on newer content, which the next run refreshes;
# read after, it left a newer tag on older content, trusted for good.
if source_type == "zip":
remote_tag = _get_remote_etag(source_url)
else:
remote_tag = get_remote_sha(entry["source_url"], version)
try: try:
if source_type == "zip": if source_type == "zip":
strip = entry.get("strip_components", 0) strip = entry.get("strip_components", 0)
@@ -450,15 +451,11 @@ def _refresh_entry(
OSError, OSError,
tarfile.TarError, tarfile.TarError,
zipfile.BadZipFile, zipfile.BadZipFile,
NothingExtractedError,
) as exc: ) as exc:
log.warning("[%s] download failed: %s", key, exc) log.warning("[%s] download failed: %s", key, exc)
return None return None
if remote_tag is None:
if source_type == "zip":
remote_tag = _get_remote_etag(source_url)
else:
remote_tag = get_remote_sha(entry["source_url"], version)
_record_version(key, {"sha": remote_tag or "", "version": version}, versions_path) _record_version(key, {"sha": remote_tag or "", "version": version}, versions_path)
log.info("[%s] refreshed: %d files extracted to %s", key, file_count, local_cache) log.info("[%s] refreshed: %d files extracted to %s", key, file_count, local_cache)
+51 -4
View File
@@ -150,12 +150,15 @@ def build_region_index(profiles: dict) -> dict[str, dict]:
# Path-keyed so same-named entries stay separate (Dolphin declares # Path-keyed so same-named entries stay separate (Dolphin declares
# three IPL.bin), name-keyed so a candidate identified by name alone # three IPL.bin), name-keyed so a candidate identified by name alone
# sees the union and is never dropped on ambiguity. # sees the union and is never dropped on ambiguity.
priority = f.get("priority")
for key in {path, name} - {""}: for key in {path, name} - {""}:
entry = index.setdefault( entry = index.setdefault(
key, key,
{"regions": set(), "has_untagged": False, "emulators": []}, {"regions": set(), "has_untagged": False, "emulators": [],
"priorities": set()},
) )
entry["regions"] |= regions entry["regions"] |= regions
entry["priorities"].add(priority if isinstance(priority, int) else None)
if not regions: if not regions:
entry["has_untagged"] = True entry["has_untagged"] = True
if emu_name not in entry["emulators"]: if emu_name not in entry["emulators"]:
@@ -185,6 +188,33 @@ def lookup_regions(index: dict[str, dict], destination: str, name: str) -> set[s
return set(entry["regions"]) return set(entry["regions"])
def _index_entry(index: dict[str, dict], destination: str, name: str) -> dict | None:
"""The index entry a candidate resolves to, by the lookup_regions order."""
if destination:
entry = index.get(destination)
if entry:
return entry
parts = destination.split("/")
for i in range(1, len(parts)):
entry = index.get("/".join(parts[i:]))
if entry:
return entry
return index.get(name)
def lookup_priority(index: dict[str, dict], destination: str, name: str) -> int | None:
"""The search rank the code gives a candidate, lower first.
None when no entry declares one, or when two declarations disagree: a
rank in doubt cannot decide what to drop.
"""
entry = _index_entry(index, destination, name)
priorities = (entry or {}).get("priorities", set())
if len(priorities) != 1:
return None
return next(iter(priorities))
def _competing_ranks( def _competing_ranks(
members: list[tuple[str, str]], members: list[tuple[str, str]],
index: dict[str, dict], index: dict[str, dict],
@@ -212,8 +242,8 @@ def resolve_region_drops(
"""Destinations to skip for a requested region priority list. """Destinations to skip for a requested region priority list.
Per group, an exact/parent regional match beats other regional candidates. Per group, an exact/parent regional match beats other regional candidates.
A world candidate beats unmatched regional fallbacks, while untagged files A world candidate beats unmatched regional fallbacks the code does not
always survive. If neither a requested nor a world candidate exists, all search before it, while untagged files always survive. If neither a requested nor a world candidate exists, all
regional candidates survive so filtering can never empty a group. regional candidates survive so filtering can never empty a group.
""" """
if not requested: if not requested:
@@ -243,7 +273,24 @@ def resolve_region_drops(
keep |= {destination for r, destination in matched if r == best} keep |= {destination for r, destination in matched if r == best}
drop |= {destination for _r, destination in regional if destination not in keep} drop |= {destination for _r, destination in regional if destination not in keep}
elif world: elif world:
drop |= {destination for _r, destination in regional} # A world file beats an unmatched regional one, unless the code
# provably looks for the regional one first: DuckStation ranks its
# PS1 images 5 to 50 and its world PS2 images 100, so a request
# for Brazil would otherwise leave it only the PS2 fallbacks.
world_ranks = [
lookup_priority(index, destination, name)
for destination, name in members
if destination in world
]
known = None not in world_ranks
best_world = min(world_ranks) if known and world_ranks else None
for _r, destination in regional:
name = next(n for d, n in members if d == destination)
ours = lookup_priority(index, destination, name)
if best_world is not None and ours is not None and ours < best_world:
keep.add(destination)
else:
drop.add(destination)
else: else:
# Preserve every unmatched candidate as a visible fallback. # Preserve every unmatched candidate as a visible fallback.
keep |= {destination for _r, destination in regional} keep |= {destination for _r, destination in regional}
+29 -4
View File
@@ -24,7 +24,7 @@ import zipfile
from pathlib import Path from pathlib import Path
import split_pack import split_pack
from common import write_text_atomic from common import ArtifactLockBusy, artifact_lock, write_text_atomic
RECORD = "release.json" RECORD = "release.json"
@@ -34,11 +34,32 @@ def _match_key(value: str) -> str:
return re.sub(r"[^a-z0-9]", "", value.lower()) return re.sub(r"[^a-z0-9]", "", value.lower())
class UnfinishedSplitError(Exception):
"""A pack lies beside its own parts: a split is running or was killed."""
def build_record(dist: Path, tag: str) -> dict: def build_record(dist: Path, tag: str) -> dict:
"""Read every pack of a directory, parts counted once across them.""" """Read every pack of a directory, parts counted once across them.
Read under the shared artifact lock, so a split that is reading its parts
back cannot be recorded half done. A pack still beside its parts after
that is a split that never finished: summed, it doubled the download size
and named an asset that is never published.
"""
with artifact_lock(str(dist), exclusive=False):
return _read_record(Path(dist), tag)
def _read_record(dist: Path, tag: str) -> dict:
assets: dict[str, list[Path]] = {} assets: dict[str, list[Path]] = {}
for path in sorted(Path(dist).glob("*_BIOS_Pack*.zip")): for path in sorted(dist.glob("*_BIOS_Pack*.zip")):
assets.setdefault(split_pack.pack_of(path.name), []).append(path) assets.setdefault(split_pack.pack_of(path.name), []).append(path)
for paths in assets.values():
whole = [p for p in paths if not split_pack.is_part(p.name)]
if whole and len(paths) > len(whole):
raise UnfinishedSplitError(
f"{whole[0].name} lies beside its parts: the split did not finish"
)
packs = {} packs = {}
for name, paths in sorted(assets.items()): for name, paths in sorted(assets.items()):
@@ -110,7 +131,11 @@ def main() -> int:
parser.add_argument("--output", default=RECORD, type=Path) parser.add_argument("--output", default=RECORD, type=Path)
args = parser.parse_args() args = parser.parse_args()
record = build_record(args.dist, args.tag) try:
record = build_record(args.dist, args.tag)
except (UnfinishedSplitError, ArtifactLockBusy) as exc:
print(f"Error: {exc}", file=sys.stderr)
return 1
if not record["packs"]: if not record["packs"]:
print(f"Error: no pack in {args.dist}", file=sys.stderr) print(f"Error: no pack in {args.dist}", file=sys.stderr)
return 1 return 1
+18 -8
View File
@@ -14,10 +14,11 @@ from pathlib import Path
from typing import Any from typing import Any
from common import yaml_load from common import write_text_atomic, yaml_load
_MAME_RELEASE_RE = re.compile(r"^0\.\d+") _MAME_RELEASE_RE = re.compile(r"^0\.\d+")
_MAMEDEV = "https://github.com/mamedev/mame"
def merge_mame_profile( def merge_mame_profile(
@@ -45,6 +46,11 @@ def merge_mame_profile(
# never built from. # never built from.
if _MAME_RELEASE_RE.match(str(profile.get("core_version") or "")): if _MAME_RELEASE_RE.match(str(profile.get("core_version") or "")):
profile["core_version"] = hashes.get("version", profile.get("core_version")) profile["core_version"] = hashes.get("version", profile.get("core_version"))
# The refs about to be written are line numbers of that release's
# mamedev/mame tree: the pin moves with them, or the profile cites
# lines its declared revision does not hold.
if profile.get("upstream") == _MAMEDEV and hashes.get("commit"):
profile["upstream_commit"] = hashes["commit"]
files = profile.get("files", []) files = profile.get("files", [])
bios_zip, non_bios = _split_files(files, lambda f: f.get("category") == "bios_zip") bios_zip, non_bios = _split_files(files, lambda f: f.get("category") == "bios_zip")
@@ -380,21 +386,25 @@ def _backup_and_write(path: str, data: dict) -> None:
original = p.read_text(encoding="utf-8") original = p.read_text(encoding="utf-8")
patched = _patch_core_version(original, data.get("core_version", "")) patched = _patch_core_version(original, data.get("core_version", ""))
patched = _patch_scalar(patched, "upstream_commit", data.get("upstream_commit", ""))
patched = _patch_bios_entries(patched, data.get("files", [])) patched = _patch_bios_entries(patched, data.get("files", []))
patched = _append_new_entries(patched, data.get("files", []), original) patched = _append_new_entries(patched, data.get("files", []), original)
p.write_text(patched, encoding="utf-8") write_text_atomic(str(p), patched)
def _patch_core_version(text: str, version: str) -> str: def _patch_core_version(text: str, version: str) -> str:
"""Replace core_version value in-place.""" """Replace core_version value in-place."""
if not version: return _patch_scalar(text, "core_version", version)
return text
import re
def _patch_scalar(text: str, key: str, value: str) -> str:
"""Replace a top-level scalar in-place, quoted; absent value, no change."""
if not value:
return text
return re.sub( return re.sub(
r"^(core_version:\s*).*$", rf"^({re.escape(key)}:\s*).*$",
rf'\g<1>"{version}"', rf'\g<1>"{value}"',
text, text,
count=1, count=1,
flags=re.MULTILINE, flags=re.MULTILINE,
@@ -596,4 +606,4 @@ def _backup_and_write_fbneo(path: str, data: dict, hashes: dict) -> None:
lines.append("") lines.append("")
patched = patched.rstrip("\n") + "\n\n" + "\n".join(lines) patched = patched.rstrip("\n") + "\n\n" + "\n".join(lines)
p.write_text(patched, encoding="utf-8") write_text_atomic(str(p), patched)
+12 -6
View File
@@ -12,7 +12,7 @@ import yaml
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from dataclasses import dataclass, field from dataclasses import dataclass, field
from pathlib import Path from pathlib import Path
from common import yaml_load from common import write_text_atomic, yaml_load
@dataclass @dataclass
@@ -35,6 +35,9 @@ class BiosRequirement:
# is not always derivable from ours: libretro writes iplromco.dat bare # is not always derivable from ours: libretro writes iplromco.dat bare
# but ep128emu/roms/cpc464.rom with its directory. # but ep128emu/roms/cpc464.rom with its directory.
native_path: str | None = None native_path: str | None = None
# The native system whose entry this one repeats under another system.
# The platform's own file does not carry the copy: an export skips it.
mirror_of: str | None = None
# Fields the platform declares that have no equivalent in our model. # Fields the platform declares that have no equivalent in our model.
# Kept verbatim so the native file can be written back unchanged. # Kept verbatim so the native file can be written back unchanged.
native: dict[str, object] = field(default_factory=dict) native: dict[str, object] = field(default_factory=dict)
@@ -95,6 +98,8 @@ def requirement_entry(req: BiosRequirement) -> dict:
entry["native_system"] = req.native_id entry["native_system"] = req.native_id
if req.native_path and req.native_path != req.name: if req.native_path and req.native_path != req.name:
entry["native_path"] = req.native_path entry["native_path"] = req.native_path
if req.mirror_of:
entry["mirror_of"] = req.mirror_of
for key in sorted(req.native): for key in sorted(req.native):
value = req.native[key] value = req.native[key]
if value not in (None, "", [], {}): if value not in (None, "", [], {}):
@@ -373,11 +378,12 @@ def scraper_cli(
for field in ("data_directories",): for field in ("data_directories",):
if field in old_sys and field not in sys_data: if field in old_sys and field not in sys_data:
sys_data[field] = old_sys[field] sys_data[field] = old_sys[field]
with open(output_path, "w") as f: # Written whole or not at all: a reader in the middle of a streamed
yaml.dump( # dump parsed a valid platform with a fraction of its systems.
config, f, Dumper=_PlatformDumper, write_text_atomic(output_path, yaml.dump(
default_flow_style=False, allow_unicode=True, sort_keys=False, config, Dumper=_PlatformDumper,
) default_flow_style=False, allow_unicode=True, sort_keys=False,
))
print(f"Written {len(reqs)} entries to {args.output}") print(f"Written {len(reqs)} entries to {args.output}")
return return
+3 -4
View File
@@ -20,7 +20,7 @@ from typing import Any
import yaml import yaml
from common import yaml_load from common import write_text_atomic, yaml_load
from scripts.scraper._hash_merge import compute_diff, merge_fbneo_profile from scripts.scraper._hash_merge import compute_diff, merge_fbneo_profile
from scripts.scraper.fbneo_parser import parse_fbneo_source_tree from scripts.scraper.fbneo_parser import parse_fbneo_source_tree
@@ -187,9 +187,8 @@ def fetch_and_cache(force: bool = False) -> dict[str, Any]:
} }
CACHE_PATH.parent.mkdir(parents=True, exist_ok=True) CACHE_PATH.parent.mkdir(parents=True, exist_ok=True)
CACHE_PATH.write_text( write_text_atomic(
json.dumps(cache, indent=2, ensure_ascii=False) + "\n", str(CACHE_PATH), json.dumps(cache, indent=2, ensure_ascii=False) + "\n"
encoding="utf-8",
) )
log.info("wrote %d BIOS sets to %s", len(bios_sets), CACHE_PATH) log.info("wrote %d BIOS sets to %s", len(bios_sets), CACHE_PATH)
+9 -3
View File
@@ -279,9 +279,14 @@ class Scraper(BaseScraper):
"docs": "https://docs.libretro.com/library/xrick/", "docs": "https://docs.libretro.com/library/xrick/",
}, },
} }
# Every entry below is ours, not System.dat's: marked curated so an
# export of System.dat does not take it for libretro's.
for sys_id, sys_data in EXTRA_SYSTEMS.items(): for sys_id, sys_data in EXTRA_SYSTEMS.items():
if sys_id not in systems: if sys_id not in systems:
systems[sys_id] = sys_data systems[sys_id] = {
**sys_data,
"files": [{**f, "curated": True} for f in sys_data["files"]],
}
# Arcade BIOS present in the repo but absent from System.dat. # Arcade BIOS present in the repo but absent from System.dat.
# FBNeo expects them in system/ or system/fbneo/. # FBNeo expects them in system/ or system/fbneo/.
@@ -310,7 +315,7 @@ class Scraper(BaseScraper):
existing = {f["name"] for f in systems["arcade"].get("files", [])} existing = {f["name"] for f in systems["arcade"].get("files", [])}
for ef in EXTRA_ARCADE_FILES: for ef in EXTRA_ARCADE_FILES:
if ef["name"] not in existing: if ef["name"] not in existing:
systems["arcade"]["files"].append(ef) systems["arcade"]["files"].append({**ef, "curated": True})
# segasp.zip for Sega System SP (Flycast) # segasp.zip for Sega System SP (Flycast)
if "sega-dreamcast-arcade" in systems: if "sega-dreamcast-arcade" in systems:
@@ -323,6 +328,7 @@ class Scraper(BaseScraper):
"name": "segasp.zip", "name": "segasp.zip",
"destination": "dc/segasp.zip", "destination": "dc/segasp.zip",
"required": True, "required": True,
"curated": True,
} }
) )
@@ -442,7 +448,7 @@ class Scraper(BaseScraper):
for ef in extra_files: for ef in extra_files:
key = (ef["name"], ef.get("destination", ef["name"])) key = (ef["name"], ef.get("destination", ef["name"]))
if key not in existing: if key not in existing:
systems[sys_id]["files"].append(ef) systems[sys_id]["files"].append({**ef, "curated": True})
existing.add(key) existing.add(key)
# ep128emu shared group for Enterprise # ep128emu shared group for Enterprise
+24 -9
View File
@@ -22,7 +22,7 @@ from typing import Any
import yaml import yaml
from common import yaml_load from common import write_text_atomic, yaml_load
from ._hash_merge import compute_diff, merge_mame_profile from ._hash_merge import compute_diff, merge_mame_profile
from .mame_parser import parse_mame_source_tree from .mame_parser import parse_mame_source_tree
@@ -66,8 +66,9 @@ def _is_stale(cache: dict[str, Any] | None) -> bool:
def _write_cache(data: dict[str, Any]) -> None: def _write_cache(data: dict[str, Any]) -> None:
_CACHE_PATH.parent.mkdir(parents=True, exist_ok=True) _CACHE_PATH.parent.mkdir(parents=True, exist_ok=True)
with open(_CACHE_PATH, "w", encoding="utf-8") as f: write_text_atomic(
json.dump(data, f, indent=2, ensure_ascii=False) str(_CACHE_PATH), json.dumps(data, indent=2, ensure_ascii=False)
)
log.info("cache written to %s", _CACHE_PATH) log.info("cache written to %s", _CACHE_PATH)
@@ -86,17 +87,25 @@ def _run_git(
) )
def _sparse_clone() -> None: def _sparse_clone(tag: str) -> None:
"""Clone the release the data will be labelled with, not master.
The version comes from the latest release; cloning master gave line
numbers and hashes of a later revision under that release's label, and
the profiles cited lines no declared revision holds.
"""
if _CLONE_DIR.exists(): if _CLONE_DIR.exists():
shutil.rmtree(_CLONE_DIR) shutil.rmtree(_CLONE_DIR)
_CLONE_DIR.parent.mkdir(parents=True, exist_ok=True) _CLONE_DIR.parent.mkdir(parents=True, exist_ok=True)
log.info("sparse cloning mamedev/mame into %s", _CLONE_DIR) log.info("sparse cloning mamedev/mame %s into %s", tag, _CLONE_DIR)
_run_git( _run_git(
[ [
"clone", "clone",
"--depth", "--depth",
"1", "1",
"--branch",
tag,
"--filter=blob:none", "--filter=blob:none",
"--sparse", "--sparse",
_REPO_URL, _REPO_URL,
@@ -110,7 +119,12 @@ def _sparse_clone() -> None:
def _get_version() -> str: def _get_version() -> str:
"""The latest MAME release, from the GitHub API. """The latest MAME release version, such as 0.289."""
return _parse_version_tag(_get_release_tag())
def _get_release_tag() -> str:
"""The latest MAME release tag, from the GitHub API.
version.cpp is generated at build time, not in the repo. A failed lookup version.cpp is generated at build time, not in the repo. A failed lookup
raises: "unknown" was cached for a day and written as core_version. raises: "unknown" was cached for a day and written as core_version.
@@ -131,7 +145,7 @@ def _get_version() -> str:
raise RuntimeError(f"cannot read the MAME release from {url}: {exc}") from exc raise RuntimeError(f"cannot read the MAME release from {url}: {exc}") from exc
if not tag: if not tag:
raise RuntimeError(f"no tag_name in {url}") raise RuntimeError(f"no tag_name in {url}")
return _parse_version_tag(tag) return tag
def _parse_version_tag(tag: str) -> str: def _parse_version_tag(tag: str) -> str:
@@ -251,9 +265,10 @@ def _fetch_hashes(force: bool) -> dict[str, Any]:
return cache # type: ignore[return-value] return cache # type: ignore[return-value]
try: try:
_sparse_clone() tag = _get_release_tag()
_sparse_clone(tag)
bios_sets = parse_mame_source_tree(str(_CLONE_DIR)) bios_sets = parse_mame_source_tree(str(_CLONE_DIR))
version = _get_version() version = _parse_version_tag(tag)
commit = _get_commit() commit = _get_commit()
data: dict[str, Any] = { data: dict[str, Any] = {
+16 -6
View File
@@ -248,7 +248,9 @@ class Scraper(BaseScraper):
raise ConnectionError(f"Failed to fetch component tree: {e}") from e raise ConnectionError(f"Failed to fetch component tree: {e}") from e
if tree.get("truncated"): if tree.get("truncated"):
print(" WARNING: GitHub tree response truncated", file=sys.stderr) # A partial tree lists some components: writing the YAML from it
# drops the others' systems without a word.
raise ConnectionError("GitHub tree response truncated")
component_dirs = [ component_dirs = [
item["path"] item["path"]
@@ -260,16 +262,24 @@ class Scraper(BaseScraper):
for comp in sorted(component_dirs): for comp in sorted(component_dirs):
url = f"{RAW_BASE}/{comp}/component_manifest.json" url = f"{RAW_BASE}/{comp}/component_manifest.json"
print(f" {comp} ...", file=sys.stderr, end="", flush=True) print(f" {comp} ...", file=sys.stderr, end="", flush=True)
# Only a 404 says the component has no manifest. A 429, a 503 or
# a dropped connection is no answer: skipping it wrote
# retrodeck.yml without that component's systems, exit code 0.
try: try:
req = urllib.request.Request(url, headers=headers) req = urllib.request.Request(url, headers=headers)
with urllib.request.urlopen(req, timeout=15) as resp: with urllib.request.urlopen(req, timeout=15) as resp:
data = json.loads(resp.read().decode()) data = json.loads(resp.read().decode())
manifests.append((comp, data)) except urllib.error.HTTPError as e:
print(" ok", file=sys.stderr) if e.code == 404:
except (urllib.error.HTTPError, urllib.error.URLError): print(" no manifest", file=sys.stderr)
print(" skip", file=sys.stderr) continue
raise ConnectionError(f"{url}: HTTP {e.code}") from e
except urllib.error.URLError as e:
raise ConnectionError(f"{url}: {e.reason}") from e
except json.JSONDecodeError as e: except json.JSONDecodeError as e:
print(f" parse error: {e}", file=sys.stderr) raise ValueError(f"{url}: not JSON: {e}") from e
manifests.append((comp, data))
print(" ok", file=sys.stderr)
return manifests return manifests
def _fetch_local_manifests(self) -> list[tuple[str, dict]]: def _fetch_local_manifests(self) -> list[tuple[str, dict]]:
+1
View File
@@ -175,6 +175,7 @@ class Scraper(BaseScraper):
destination=f"{slug}/{filename}", destination=f"{slug}/{filename}",
required=True, required=True,
native_id=slug, native_id=slug,
mirror_of=None if slug == igdb_slug else igdb_slug,
) )
) )
+5 -2
View File
@@ -28,8 +28,11 @@ class BaseTargetScraper(ABC):
import yaml import yaml
except ImportError: except ImportError:
raise ImportError("PyYAML required: pip install pyyaml") raise ImportError("PyYAML required: pip install pyyaml")
with open(output_path, "w") as f: from common import write_text_atomic
yaml.dump(data, f, default_flow_style=False, sort_keys=False)
write_text_atomic(
output_path, yaml.dump(data, default_flow_style=False, sort_keys=False)
)
_scrapers: dict[str, type] = {} _scrapers: dict[str, type] = {}
+23 -3
View File
@@ -136,9 +136,14 @@ def profile_claims(
standalone_cores: set[str] | None = None, standalone_cores: set[str] | None = None,
zip_contents: dict | None = None, zip_contents: dict | None = None,
data_dir_registry: dict | None = None, data_dir_registry: dict | None = None,
config: dict | None = None,
) -> list[Claim]: ) -> list[Claim]:
"""What each emulator profile says belongs at each destination it names. """What each emulator profile says belongs at each destination it names.
With the platform's config, a claim sits where the pack puts the core's
file (ExtraPlacement): under RetroDECK's bios/, under RomM's system
folder. A file the platform cannot place claims nothing.
A file can exist in one build of an emulator and not the other, and the A file can exist in one build of an emulator and not the other, and the
two builds read from different directories. The mode is decided per two builds read from different directories. The mode is decided per
emulator, not per pack: a platform runs some of its emulators as libretro emulator, not per pack: a platform runs some of its emulators as libretro
@@ -148,6 +153,11 @@ def profile_claims(
``standalone_path``. This is the gate verify already applies. ``standalone_path``. This is the gate verify already applies.
""" """
standalone_cores = standalone_cores or set() standalone_cores = standalone_cores or set()
placement = None
if config is not None:
from packextras import ExtraPlacement # noqa: PLC0415
placement = ExtraPlacement(config, base_dest, profiles)
claims: list[Claim] = [] claims: list[Claim] = []
for emu_name, profile in sorted(profiles.items()): for emu_name, profile in sorted(profiles.items()):
if profile.get("type") in ("launcher", "alias"): if profile.get("type") in ("launcher", "alias"):
@@ -174,9 +184,18 @@ def profile_claims(
) or entry.get("name") or "" ) or entry.get("name") or ""
if not dest: if not dest:
continue continue
full = f"{base_dest}/{dest}" if base_dest else dest if placement is None:
full = f"{base_dest}/{dest}" if base_dest else dest
else:
placed = placement.place(
{"profile": emu_name, "system": entry.get("system")}, dest
)
if placed is None:
continue
full = placed[1]
# The owner's own copy is the one the builder ships.
local, status = resolve_local_file( local, status = resolve_local_file(
entry, {**entry, "source_profile": emu_name},
db, db,
zip_contents, zip_contents,
dest_hint=dest, dest_hint=dest,
@@ -235,7 +254,8 @@ def find_conflicts(
# claim at all matches what ships is a disagreement. # claim at all matches what ships is a disagreement.
by_slot: dict[str, list[Claim]] = {} by_slot: dict[str, list[Claim]] = {}
for claim in profile_claims( for claim in profile_claims(
profiles, db, base_dest, standalone_cores, zip_contents, data_dir_registry profiles, db, base_dest, standalone_cores, zip_contents, data_dir_registry,
config=config,
): ):
key = _normalize(claim.destination) key = _normalize(claim.destination)
platform = by_dest.get(key) platform = by_dest.get(key)
+65 -6
View File
@@ -310,6 +310,43 @@ def _system_dir_files(profile: dict, standalone: bool) -> list[dict]:
) )
def _archives_not_members(files: list[dict]) -> list[dict]:
"""A profile's entries with the members of each archive folded into it.
fbneo declares bubsys.zip as the ROMs it holds, each carrying
`archive: bubsys.zip`. The platform reads the archive, never a member:
as loose files the members were added to RetroArch's System.dat, and
the 480-byte boot.bin was filed under the Dreamcast by its bare name.
The archive stands in their place with the members as its contents,
the shape a profile that declares the archive itself already has.
"""
out: list[dict] = []
archives: dict[tuple[str, str], dict] = {}
for fe in files:
archive = fe.get("archive")
if not archive:
out.append(fe)
continue
key = (archive, fe.get("system", ""))
entry = archives.get(key)
if entry is None:
entry = {"name": archive, "category": "bios_zip", "contents": []}
if fe.get("system"):
entry["system"] = fe["system"]
if fe.get("source_ref") is not None:
entry["source_ref"] = fe["source_ref"]
archives[key] = entry
out.append(entry)
if fe.get("required"):
entry["required"] = True
entry["contents"].append({
field: fe[field]
for field in ("name", "size", "crc32", "sha1", "md5")
if fe.get(field) is not None
})
return out
def generate_platform_truth( def generate_platform_truth(
platform_name: str, platform_name: str,
config: dict, config: dict,
@@ -376,6 +413,7 @@ def generate_platform_truth(
return norm_to_platform.get(normed, profile_sid) return norm_to_platform.get(normed, profile_sid)
systems: dict[str, dict] = {} systems: dict[str, dict] = {}
unattributed: dict[str, int] = {}
cores_profiled: set[str] = set() cores_profiled: set[str] = set()
cores_unprofiled: set[str] = set() cores_unprofiled: set[str] = set()
# Track which cores contribute to each system # Track which cores contribute to each system
@@ -388,16 +426,34 @@ def generate_platform_truth(
continue continue
cores_profiled.add(emu_name) cores_profiled.add(emu_name)
filtered = _system_dir_files( filtered = _archives_not_members(_system_dir_files(
profile, runs_standalone(emu_name, profile, standalone_set) profile, runs_standalone(emu_name, profile, standalone_set)
) ))
on_platform = {
_map_sys_id(sid) for sid in profile.get("systems", [])
} & platform_sys_ids
for fe in filtered: for fe in filtered:
profile_sid = fe.get("system", "") profile_sid = fe.get("system", "")
if not profile_sid: sys_ids = profile.get("systems", [])
sys_ids = profile.get("systems", []) if profile_sid:
profile_sid = sys_ids[0] if sys_ids else "unknown" sys_id = _map_sys_id(profile_sid, fe.get("name", ""))
sys_id = _map_sys_id(profile_sid, fe.get("name", "")) elif len(sys_ids) <= 1:
sys_id = _map_sys_id(sys_ids[0] if sys_ids else "unknown", fe.get("name", ""))
else:
# A multi-system profile's entry without a system: the
# platform's own declaration of the name decides, or the one
# profile system the platform has. Otherwise it is not filed:
# the first system was a guess, and CLK's Apple, Mac, ZX and
# Amiga ROMs went to RomM as Amstrad CPC firmware.
declared = file_to_plat_sys.get(fe.get("name", "").lower())
if declared:
sys_id = declared
elif len(on_platform) == 1:
sys_id = next(iter(on_platform))
else:
unattributed[emu_name] = unattributed.get(emu_name, 0) + 1
continue
system = systems.setdefault(sys_id, {}) system = systems.setdefault(sys_id, {})
_merge_file_into_system(system, fe, emu_name, db) _merge_file_into_system(system, fe, emu_name, db)
# Track core contribution per system # Track core contribution per system
@@ -469,6 +525,9 @@ def generate_platform_truth(
"cores_resolved": len(resolved), "cores_resolved": len(resolved),
"cores_profiled": len(cores_profiled), "cores_profiled": len(cores_profiled),
"cores_unprofiled": sorted(cores_unprofiled), "cores_unprofiled": sorted(cores_unprofiled),
# Entries of multi-system cores no system of this platform can
# be said to own: counted, never filed under a guess.
"unattributed": dict(sorted(unattributed.items())),
}, },
} }
+7 -1
View File
@@ -21,6 +21,8 @@ from dataclasses import dataclass
from pathlib import Path from pathlib import Path
USER_AGENT = "retrobios-profile-sync/1.0" USER_AGENT = "retrobios-profile-sync/1.0"
# Where fetched upstream files live, shared by every reader of this cache.
CACHE_DIR = ".cache/upstream"
ABSENT = "\0absent\0" ABSENT = "\0absent\0"
GITHUB_COMPARE_CAP = 300 GITHUB_COMPARE_CAP = 300
RETRIES = 3 RETRIES = 3
@@ -120,6 +122,10 @@ class FileChange:
class CompareResult: class CompareResult:
files: list[FileChange] files: list[FileChange]
truncated: bool truncated: bool
# The forge reports the two revisions on separate lines of history: the
# base is no ancestor of the head. A pin in that state survives only
# while the forge keeps the orphaned object (advancemame, 2026-10-05).
diverged: bool = False
def parse_repo(url: str) -> Repo | None: def parse_repo(url: str) -> Repo | None:
@@ -524,7 +530,7 @@ def compare(
else: else:
files = _changes_from_github(payload) files = _changes_from_github(payload)
truncated = bool(payload.get("truncated")) or len(files) >= GITHUB_COMPARE_CAP truncated = bool(payload.get("truncated")) or len(files) >= GITHUB_COMPARE_CAP
return CompareResult(files, truncated) return CompareResult(files, truncated, payload.get("status") == "diverged")
def find_renamed(result: CompareResult, path: str) -> str | None: def find_renamed(result: CompareResult, path: str) -> str | None:
+60 -18
View File
@@ -29,6 +29,7 @@ import json
import os import os
import sys import sys
import zipfile import zipfile
from collections.abc import Mapping
import slots import slots
@@ -43,7 +44,8 @@ from common import (
check_inside_zip, check_inside_zip,
compute_hashes, compute_hashes,
expand_directory_entries, expand_directory_entries,
expand_platform_declared_names, name_match_size_ok,
platform_declarations,
filter_systems_by_target, filter_systems_by_target,
group_identical_platforms, group_identical_platforms,
list_emulator_profiles, list_emulator_profiles,
@@ -287,6 +289,19 @@ def verify_entry_sha1(
# Severity mapping per platform # Severity mapping per platform
def _emulator_severity(status: str, required: bool, hle_fallback: bool) -> str:
"""Severity when the emulator's own code is the check.
A file present but refused by the core's validation, or a different dump
than the one the profile declares, is not a file the core runs with:
read in existence mode it counted OK, and `verify --emulator` announced
1/1 OK above the line that said the core would refuse it.
"""
if status == Status.UNTESTED:
return Severity.WARNING
return compute_severity(status, required, "existence", hle_fallback)
def compute_severity( def compute_severity(
status: str, status: str,
required: bool, required: bool,
@@ -383,7 +398,8 @@ def _candidate_verdict(
fname: str, fname: str,
is_standalone: bool, is_standalone: bool,
include_all: bool, include_all: bool,
declared_names: set, declared_names: Mapping[str, list[tuple[int | None, str]]] | set[str],
dest: str = "",
) -> str: ) -> str:
"""Whether a profile entry can be a gap, and whether it is settled. """Whether a profile entry can be a gap, and whether it is settled.
@@ -399,12 +415,39 @@ def _candidate_verdict(
if outside_gap_scope(file_entry, is_standalone): if outside_gap_scope(file_entry, is_standalone):
return "skip" return "skip"
if not include_all: if not include_all:
# A platform declaring any name the core answers to has met the # A platform declaring the name the core reads has met the
# requirement: quasi88 reads n88sub.rom or disk.rom, and System.dat # requirement where the declaration fills the entry's own slot,
# names disk.rom. Without the aliases the pack carried the ROM twice. # whose content the slot arbitration decides, or elsewhere at a size
answers = {fname, file_entry.get("archive") or "", *file_entry.get("aliases", [])} # the core accepts: the builder copies that file to the core's path.
if answers & declared_names: # Galaksija's 4 KiB ROM1.BIN does not stand for DOSBox's 32 KiB SC-55
# one. The builder copies nothing for an alias, so an alias answers
# only where the core reads it: quasi88 reads n88sub.rom or disk.rom
# in quasi88/, and System.dat names quasi88/disk.rom. RetroDECK's
# root MSX.ROM is not CLK's MSX/MSX.ROM, and RetroBat's 16 KiB MSX
# DISK.ROM at the root is not the 256-byte Disk II ROM gsplus also
# accepts as c600.rom. The archive answers by its name.
if file_entry.get("archive") in declared_names:
return "settled" return "settled"
slot = sanitize_pack_path(dest or fname).lower()
directory = slot if dest.endswith("/") else slot.rpartition("/")[0]
for name in (fname, *file_entry.get("aliases", [])):
if name not in declared_names:
continue
if not isinstance(declared_names, Mapping):
return "settled"
if name == fname:
if any(
where == slot or name_match_size_ok(file_entry, size)
for size, where in declared_names[name]
):
return "settled"
continue
read_at = f"{directory}/{name}".lower() if directory else name.lower()
if any(
where == read_at and name_match_size_ok(file_entry, size)
for size, where in declared_names[name]
):
return "settled"
return "keep" return "keep"
@@ -425,16 +468,16 @@ def find_undeclared_files(
target_cores: set[str] | None = None, target_cores: set[str] | None = None,
data_names: set[str] | None = None, data_names: set[str] | None = None,
include_all: bool = False, include_all: bool = False,
declared_names: set[str] | None = None, declared_names: Mapping[str, list[tuple[int | None, str]]] | None = None,
) -> list[dict]: ) -> list[dict]:
"""Find files needed by cores but not declared in platform config. """Find files needed by cores but not declared in platform config.
declared_names overrides the default enriched set from declared_names overrides the default enriched declarations from
expand_platform_declared_names. Pass a strict set (YAML names only) platform_declarations. Pass the strict ones (YAML names only) when
when building packs so alias-only names still get packed. building packs so alias-only names still get packed.
""" """
if declared_names is None: if declared_names is None:
declared_names = expand_platform_declared_names(config, db) declared_names = platform_declarations(config, db)
# Whether the builder drops a file whose local copy contradicts its # Whether the builder drops a file whose local copy contradicts its
# declared hash, which decides if such a copy counts as held here. # declared hash, which decides if such a copy counts as held here.
@@ -510,7 +553,7 @@ def find_undeclared_files(
if not fname or seen_key in seen_files: if not fname or seen_key in seen_files:
continue continue
verdict = _candidate_verdict( verdict = _candidate_verdict(
f, fname, is_standalone, include_all, declared_names f, fname, is_standalone, include_all, declared_names, dest
) )
if verdict == "settled": if verdict == "settled":
seen_files.add(seen_key) seen_files.add(seen_key)
@@ -733,14 +776,13 @@ def _mark_unplaced(config: dict, undeclared: list[dict], profiles: dict) -> None
def _twin_index( def _twin_index(
verify_systems: dict, db: dict, base_dest: str, zip_contents: dict, verify_systems: dict, db: dict, base_dest: str, zip_contents: dict,
data_dir_registry: dict | None,
) -> tuple[dict[str, int], dict[int, tuple[str, dict]]]: ) -> tuple[dict[str, int], dict[int, tuple[str, dict]]]:
"""Which declaration the pack ships at each destination, and every declaration by id.""" """Which declaration the pack ships at each destination, and every declaration by id."""
from generate_pack import _preferred_entries from generate_pack import _preferred_entries
preferred_entries = _preferred_entries( preferred_entries = _preferred_entries(
verify_systems, db, DEFAULT_BIOS_DIR, base_dest, False, verify_systems, db, DEFAULT_BIOS_DIR, base_dest, False,
zip_contents, data_dir_registry, True, zip_contents, True,
) )
winners = { winners = {
id(fe): (sid, fe) id(fe): (sid, fe)
@@ -925,7 +967,7 @@ def verify_platform(
# xroar component hashes bios/disk.rom and finds the PC-88 ROM. # xroar component hashes bios/disk.rom and finds the PC-88 ROM.
base_dest = config.get("base_destination", "") base_dest = config.get("base_destination", "")
preferred_entries, winners = _twin_index( preferred_entries, winners = _twin_index(
verify_systems, db, base_dest, zip_contents, data_dir_registry verify_systems, db, base_dest, zip_contents
) )
for sys_id, system in verify_systems.items(): for sys_id, system in verify_systems.items():
@@ -1499,7 +1541,7 @@ def verify_emulator(
prev, 0 prev, 0
): ):
file_status[dest] = cur file_status[dest] = cur
sev = compute_severity(cur, required, "existence", False) sev = _emulator_severity(cur, required, False)
prev_sev = file_severity.get(dest) prev_sev = file_severity.get(dest)
if prev_sev is None or _SEVERITY_ORDER.get( if prev_sev is None or _SEVERITY_ORDER.get(
sev, 0 sev, 0
@@ -1612,7 +1654,7 @@ def verify_emulator(
prev, 0 prev, 0
): ):
file_status[dest] = cur file_status[dest] = cur
sev = compute_severity(cur, required, "existence", hle) sev = _emulator_severity(cur, required, hle)
prev_sev = file_severity.get(dest) prev_sev = file_severity.get(dest)
if prev_sev is None or _SEVERITY_ORDER.get( if prev_sev is None or _SEVERITY_ORDER.get(
sev, 0 sev, 0
Loaded 100 of 133 files, more files were not shown because too many files have changed in this diff. Show more