mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-10 21:43:23 -05:00
Compare commits
216
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c4599cd69e | ||
|
|
913ba09600 | ||
|
|
38655d1d23 | ||
|
|
9af89cb120 | ||
|
|
fbc92c99cf | ||
|
|
49b3cffa58 | ||
|
|
af4f980bab | ||
|
|
3582101af4 | ||
|
|
a4f3f016f8 | ||
|
|
a252e2d158 | ||
|
|
067d962210 | ||
|
|
0d81f19831 | ||
|
|
52d128bcf5 | ||
|
|
0b46880604 | ||
|
|
0f293b6c14 | ||
|
|
9ca14bb77e | ||
|
|
443639c33d | ||
|
|
0ea8840f14 | ||
|
|
53c5504330 | ||
|
|
7164e8a125 | ||
|
|
cea6e516a4 | ||
|
|
8d2df0b651 | ||
|
|
b20c38d1c1 | ||
|
|
45ede873b3 | ||
|
|
14e4dd919c | ||
|
|
dc5cdb8d70 | ||
|
|
87ef905c44 | ||
|
|
e3173b1fc5 | ||
|
|
514becd784 | ||
|
|
0cc6ab5990 | ||
|
|
4609102772 | ||
|
|
92594b1751 | ||
|
|
2499a0eabc | ||
|
|
91e113a7d3 | ||
|
|
e723f346f9 | ||
|
|
139bb99088 | ||
|
|
e35aa9a323 | ||
|
|
be2c325b53 | ||
|
|
b39b187243 | ||
|
|
6380a92e6c | ||
|
|
22f018995e | ||
|
|
5d5170dc12 | ||
|
|
64ed737ec6 | ||
|
|
87addda6e1 | ||
|
|
b8a7aa74ff | ||
|
|
d4ae1a76e8 | ||
|
|
eb8c7e5267 | ||
|
|
6748982d94 | ||
|
|
e335511b72 | ||
|
|
3088db7088 | ||
|
|
204637cfeb | ||
|
|
519c02646a | ||
|
|
72e47becb4 | ||
|
|
4a1a9dd861 | ||
|
|
d610aad990 | ||
|
|
a0af1b141e | ||
|
|
4d5340c5f4 | ||
|
|
4923873a7f | ||
|
|
ced7588d17 | ||
|
|
44cf638e06 | ||
|
|
a9b33484ed | ||
|
|
39d02b3b18 | ||
|
|
e5420a9dba | ||
|
|
9604e933ed | ||
|
|
dcc12e62b7 | ||
|
|
25e8eeb37b | ||
|
|
5d5befc002 | ||
|
|
c4e16e583e | ||
|
|
b0f2c3bb88 | ||
|
|
d89b193780 | ||
|
|
afdd38826c | ||
|
|
7ddb849c89 | ||
|
|
8e3f3fe057 | ||
|
|
af030fdd6d | ||
|
|
f159d35f18 | ||
|
|
dce43a1281 | ||
|
|
d7e7ec13ae | ||
|
|
098b2f0b70 | ||
|
|
6c9a41327b | ||
|
|
fb3c577521 | ||
|
|
821cbf4717 | ||
|
|
9c84b7990a | ||
|
|
4f8fbdfabb | ||
|
|
eccd0b0a28 | ||
|
|
8fe961a1ea | ||
|
|
ce239d32c1 | ||
|
|
13995047fc | ||
|
|
31e86b6db5 | ||
|
|
da5e95cd0f | ||
|
|
3dce7b5f09 | ||
|
|
97ccf9027b | ||
|
|
1caf5c4b5e | ||
|
|
acfd80c928 | ||
|
|
4d8ea05494 | ||
|
|
3df423e84f | ||
|
|
8a9924f1d8 | ||
|
|
52fbea5faa | ||
|
|
421a7afb3f | ||
|
|
0b7acf1e7f | ||
|
|
c4dab556e7 | ||
|
|
0390f2d19b | ||
|
|
e6afd06191 | ||
|
|
03929ddaf0 | ||
|
|
2b71e47510 | ||
|
|
cd90f46937 | ||
|
|
d02672c17b | ||
|
|
c6ba139493 | ||
|
|
f0bab9766c | ||
|
|
c55e3af5ac | ||
|
|
1c34503f4e | ||
|
|
72834837a5 | ||
|
|
ae0bbb9d3c | ||
|
|
e4cc978614 | ||
|
|
f51c3a3fc7 | ||
|
|
c62a34b1b2 | ||
|
|
ed18e61580 | ||
|
|
2cdb98239e | ||
|
|
623b0a6d47 | ||
|
|
82583fe17a | ||
|
|
85501c41e8 | ||
|
|
7273f5c8b9 | ||
|
|
e723c8c1b9 | ||
|
|
4db5866bbe | ||
|
|
dc3a35bdc8 | ||
|
|
0c881820c7 | ||
|
|
d839ce0a1c | ||
|
|
1b885632a6 | ||
|
|
ad9f834f55 | ||
|
|
49e0f83ce9 | ||
|
|
2b6dbb5e92 | ||
|
|
3d31a887cc | ||
|
|
e3f773dd82 | ||
|
|
c1b3e1fc33 | ||
|
|
b3c7e76adf | ||
|
|
b2208d889e | ||
|
|
af357a2931 | ||
|
|
1b40211366 | ||
|
|
a484754c76 | ||
|
|
46268bea47 | ||
|
|
0fbe99a14a | ||
|
|
16f4c071fe | ||
|
|
a8415c33c3 | ||
|
|
7e054fbcaf | ||
|
|
5784d45531 | ||
|
|
c526180351 | ||
|
|
36f7ee0b2b | ||
|
|
cf7d9ecb89 | ||
|
|
ccc51b861b | ||
|
|
3ae9729143 | ||
|
|
26ce416163 | ||
|
|
bd3bcc36ea | ||
|
|
f631174c85 | ||
|
|
22648e8a5f | ||
|
|
0de03b5e4c | ||
|
|
3bd19b9e97 | ||
|
|
ccf1a1573a | ||
|
|
ca86236c12 | ||
|
|
994836938c | ||
|
|
71cbbd9b21 | ||
|
|
65c0f5ddc1 | ||
|
|
ef16ed2af2 | ||
|
|
da0c843bdd | ||
|
|
e32df743d5 | ||
|
|
72d3654245 | ||
|
|
bdcf134877 | ||
|
|
aec1755c61 | ||
|
|
53ee1bfd0e | ||
|
|
d2d88cf6b8 | ||
|
|
fbdee43e6d | ||
|
|
284f1aa6b7 | ||
|
|
10f3379751 | ||
|
|
76387d1962 | ||
|
|
9465a250cc | ||
|
|
3bc80b2e06 | ||
|
|
dcdfe5936e | ||
|
|
ed47f437fc | ||
|
|
15abbec663 | ||
|
|
077dcda775 | ||
|
|
27610c1d33 | ||
|
|
25034cd958 | ||
|
|
480c02882d | ||
|
|
056953d833 | ||
|
|
49d425aa8a | ||
|
|
58cb61a08f | ||
|
|
485e5bc2b3 | ||
|
|
c5165ffb96 | ||
|
|
183793481c | ||
|
|
a762152c12 | ||
|
|
b650f9eb8a | ||
|
|
4ca4592f65 | ||
|
|
0fb09c6c94 | ||
|
|
34e382eae8 | ||
|
|
9e327a05ed | ||
|
|
f010e737c5 | ||
|
|
edce60bae1 | ||
|
|
44663db144 | ||
|
|
757fad69bc | ||
|
|
ca6462c510 | ||
|
|
3f5d825ac6 | ||
|
|
151dd5ea04 | ||
|
|
eaf315ea8f | ||
|
|
366598e662 | ||
|
|
5f3e9fbacd | ||
|
|
045372992c | ||
|
|
38ffccc465 | ||
|
|
68bcef544d | ||
|
|
81995a4c18 | ||
|
|
95d14819ce | ||
|
|
dc31a90dca | ||
|
|
8e3a486107 | ||
|
|
a3d5e6c048 | ||
|
|
8e0c7bad38 | ||
|
|
67f1b29fec | ||
|
|
b9644f720c | ||
|
|
0a9d36cae3 | ||
|
|
5816562bf6 |
No files matched your search
@@ -72,9 +72,9 @@ Each file is checked the way your platform checks it. Most compare a checksum, t
|
||||
- **12 platforms** supported with platform-specific verification
|
||||
- **506 emulators** profiled from source (RetroArch cores + standalone)
|
||||
- **546 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
|
||||
- **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
|
||||
- **16687 MB** total collection size
|
||||
- **18,997 files in the collection**, each with its SHA1, MD5, SHA256, CRC32 and Adler-32 fingerprints: 5,261 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,261 system files** matched to dump-preservation catalogs (No-Intro, Redump, TOSEC); arcade sets and engine data fall outside what those catalogs index
|
||||
- **16677 MB** total collection size
|
||||
|
||||
## Supported systems
|
||||
|
||||
@@ -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 |
|
||||
|----------|-----------------:|-------------------------:|------------|
|
||||
| Batocera | 353/353 | 12,287/12,718 | MD5 hash |
|
||||
| BizHawk | 118/118 | 800/801 | SHA1 hash |
|
||||
| EmuDeck | 168/168 | 430/432 | MD5 hash |
|
||||
| Lakka | 526/526 | 5,256/5,286 | file presence |
|
||||
| Batocera | 353/353 | 12,306/12,737 | MD5 hash |
|
||||
| BizHawk | 118/118 | 806/807 | SHA1 hash |
|
||||
| EmuDeck | 168/168 | 431/433 | MD5 hash |
|
||||
| Lakka | 527/527 | 5,264/5,294 | file presence |
|
||||
| MiSTer FPGA | 81/81 | - | MD5 hash |
|
||||
| ROCKNIX | 38/38 | 5,695/5,725 | MD5 hash |
|
||||
| Recalbox | 351/351 | 2,721/2,729 | MD5 hash |
|
||||
| RetroArch | 526/526 | 5,256/5,286 | file presence |
|
||||
| RetroBat | 348/348 | 11,875/12,305 | MD5 hash |
|
||||
| RetroDECK | 2,008/2,008 | 5,387/5,437 | MD5 hash |
|
||||
| RetroPie * | 526/526 | 9,171/9,313 | file presence |
|
||||
| RomM | 381/381 | 746/752 | MD5 hash |
|
||||
| ROCKNIX | 38/38 | 5,698/5,728 | MD5 hash |
|
||||
| Recalbox | 351/351 | 2,730/2,738 | MD5 hash |
|
||||
| RetroArch | 527/527 | 5,264/5,294 | file presence |
|
||||
| RetroBat | 348/348 | 11,889/12,319 | MD5 hash |
|
||||
| RetroDECK | 2,008/2,008 | 5,407/5,457 | MD5 hash |
|
||||
| RetroPie * | 527/527 | 9,183/9,325 | file presence |
|
||||
| RomM | 381/381 | 751/757 | MD5 hash |
|
||||
|
||||
Each fraction is what the pack has over what is needed, counting required and optional files alike since both ship. The first column is the BIOS list the platform publishes. The second counts files its emulators load that this list never mentions, found by reading their source code, and it is routinely several times larger. A short fraction means files are still missing, and they are named in the [gap analysis](https://abdess.github.io/retrobios/gaps/).
|
||||
That second number is a floor, not a ceiling: an emulator that accepts any file handed to it names none in its code, so nothing there can be counted.
|
||||
@@ -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 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-09T21:03:52Z*
|
||||
*Auto-generated on 2026-10-06T11:05:39Z*
|
||||
File diff suppressed because it is too large.
Load diff
File renamed without changes.
File diff suppressed because it is too large.
Load diff
Binary file not shown.
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
+46
-166
@@ -1,9 +1,8 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"generated_at": "2026-10-09T20:41:14Z",
|
||||
"total_files": 19002,
|
||||
"total_size": 17497698358,
|
||||
"bios_dir": "bios",
|
||||
"generated_at": "2026-10-06T10:11:17Z",
|
||||
"total_files": 18997,
|
||||
"total_size": 17487414657,
|
||||
"files": {
|
||||
"738c7f5fd0c8b57ee2e87ae7a97bf8e21a821d07": {
|
||||
"path": "bios/3D Realms/Duke Nukem 3D/DUKE.RTS",
|
||||
@@ -131660,16 +131659,6 @@
|
||||
"crc32": "6b7152b1",
|
||||
"adler32": "d84f79e3"
|
||||
},
|
||||
"71fc274cbe27f382afb0324024ebdc6f59aa666d": {
|
||||
"path": "bios/Other/aethersx2/resources/GameIndex.yaml",
|
||||
"name": "GameIndex.yaml",
|
||||
"size": 984983,
|
||||
"sha1": "71fc274cbe27f382afb0324024ebdc6f59aa666d",
|
||||
"md5": "3758db793dae7bc206fbbd799c3cbb05",
|
||||
"sha256": "e7c70faf16ce180e6a17c040dc2bb326c6fcca63e7fb3f83f992d65fbe480443",
|
||||
"crc32": "5acc416a",
|
||||
"adler32": "986d242e"
|
||||
},
|
||||
"8acfbc4a51909de382e33a221a82f0b8efc04794": {
|
||||
"path": "bios/Other/altirra/.variants/810-archiver.rom.8080d51e",
|
||||
"name": "810-archiver.rom",
|
||||
@@ -132720,6 +132709,16 @@
|
||||
"crc32": "a5b357e6",
|
||||
"adler32": "44a50462"
|
||||
},
|
||||
"dceb4536fba7fb89452864fc512a4d071a19eae0": {
|
||||
"path": "bios/Other/armsx2/RedumpDatabase.yaml",
|
||||
"name": "RedumpDatabase.yaml",
|
||||
"size": 1906463,
|
||||
"sha1": "dceb4536fba7fb89452864fc512a4d071a19eae0",
|
||||
"md5": "dbf48fe7b36fab316a1682961178ffd7",
|
||||
"sha256": "755962450efd5effd2f3fe2f9a60c73d6cb3f9b4acea964abf5bb25a86ee25bc",
|
||||
"crc32": "0496a60f",
|
||||
"adler32": "c76311d2"
|
||||
},
|
||||
"00d62fbdc8d5dec4c659005e116d0ba2ee63b547": {
|
||||
"path": "bios/Other/armsx2/Roboto-Regular.ttf",
|
||||
"name": "Roboto-Regular.ttf",
|
||||
@@ -132960,16 +132959,6 @@
|
||||
"crc32": "6e8b2b47",
|
||||
"adler32": "f5bc538e"
|
||||
},
|
||||
"72bfdf91fa11a86e2810a7c167cdcf11e20418a8": {
|
||||
"path": "bios/Other/armsx2/pcsx2/resources/GameIndex.yaml",
|
||||
"name": "GameIndex.yaml",
|
||||
"size": 2690355,
|
||||
"sha1": "72bfdf91fa11a86e2810a7c167cdcf11e20418a8",
|
||||
"md5": "ec65dbfe7379cfac816b4d02259c0ca5",
|
||||
"sha256": "61c77b789ccde856096f024e4a3eef6891e15ca59c127e0dd13e762a7d338a7e",
|
||||
"crc32": "1a8e4cf7",
|
||||
"adler32": "479c1f04"
|
||||
},
|
||||
"456e74b0805d1931a7786d3d04673a3b02dd4dd3": {
|
||||
"path": "bios/Other/armsx2/pcsx2/resources/fonts/NotoSansArabic-Regular.ttf",
|
||||
"name": "NotoSansArabic-Regular.ttf",
|
||||
@@ -133040,16 +133029,6 @@
|
||||
"crc32": "05eb51ca",
|
||||
"adler32": "cd7061bf"
|
||||
},
|
||||
"f7d0f1606c87019f00109ebdf0f260299f0a72da": {
|
||||
"path": "bios/Other/armsx2/pcsx2/resources/patches.zip",
|
||||
"name": "patches.zip",
|
||||
"size": 2007224,
|
||||
"sha1": "f7d0f1606c87019f00109ebdf0f260299f0a72da",
|
||||
"md5": "809868f006eebec4e4615dd8bc397ef7",
|
||||
"sha256": "8d95f1fba3acc83195bbac2d3e042b5f34c3c399856d9865e681d173f4cee609",
|
||||
"crc32": "5120eae7",
|
||||
"adler32": "68c47b84"
|
||||
},
|
||||
"8618516b26a0bc319ed174b66fd2d990eb5b74a3": {
|
||||
"path": "bios/Other/armsx2/pcsx2/resources/shaders/common/ffx_a_fsr1.h",
|
||||
"name": "ffx_a_fsr1.h",
|
||||
@@ -135697,16 +135676,6 @@
|
||||
"crc32": "e8038ec9",
|
||||
"adler32": "933aaa5d"
|
||||
},
|
||||
"dceb4536fba7fb89452864fc512a4d071a19eae0": {
|
||||
"path": "bios/Other/emucorex/resources/RedumpDatabase.yaml",
|
||||
"name": "RedumpDatabase.yaml",
|
||||
"size": 1906463,
|
||||
"sha1": "dceb4536fba7fb89452864fc512a4d071a19eae0",
|
||||
"md5": "dbf48fe7b36fab316a1682961178ffd7",
|
||||
"sha256": "755962450efd5effd2f3fe2f9a60c73d6cb3f9b4acea964abf5bb25a86ee25bc",
|
||||
"crc32": "0496a60f",
|
||||
"adler32": "c76311d2"
|
||||
},
|
||||
"a3d939af27fe0929e61b8e20d49ef58316c0a66c": {
|
||||
"path": "bios/Other/emucorex/resources/patches.zip",
|
||||
"name": "patches.zip",
|
||||
@@ -192862,16 +192831,6 @@
|
||||
"crc32": "0b805686",
|
||||
"adler32": "5b7b9c1d"
|
||||
},
|
||||
"5e2d50a3376dc6a0750505bec68626d39a5bd4c1": {
|
||||
"path": "bios/Sony/PlayStation 2/resources/.variants/GameIndex.yaml.fc3c1420",
|
||||
"name": "GameIndex.yaml",
|
||||
"size": 2683897,
|
||||
"sha1": "5e2d50a3376dc6a0750505bec68626d39a5bd4c1",
|
||||
"md5": "fc3c14208a929c386445635b19baa3f2",
|
||||
"sha256": "6531239315d759d0588985b31576696d2672c1a0e192633ea2d4f3d011bfdb37",
|
||||
"crc32": "a7541e89",
|
||||
"adler32": "078dc6f1"
|
||||
},
|
||||
"40ecf6138c99a0aba775ef93240b295025a45500": {
|
||||
"path": "bios/Sony/PlayStation 2/resources/GameIndex.yaml",
|
||||
"name": "GameIndex.yaml",
|
||||
@@ -192882,16 +192841,6 @@
|
||||
"crc32": "a3e573a0",
|
||||
"adler32": "e808396a"
|
||||
},
|
||||
"bf2c73d668cd671df8adfe2ec3c7074e8ba9176a": {
|
||||
"path": "bios/Sony/PlayStation 2/resources/RedumpDatabase.yaml",
|
||||
"name": "RedumpDatabase.yaml",
|
||||
"size": 1917242,
|
||||
"sha1": "bf2c73d668cd671df8adfe2ec3c7074e8ba9176a",
|
||||
"md5": "0525ad1f1381152f20bfb05775cf39cd",
|
||||
"sha256": "302b57fdd1f9472ef590789e4b347381cc8442a04a765c7eae3d3f2e4b06394c",
|
||||
"crc32": "28709e73",
|
||||
"adler32": "ffcd6c8e"
|
||||
},
|
||||
"c54f04b63ac6f04e0a0944ef260b5497e4494dbd": {
|
||||
"path": "bios/Sony/PlayStation 2/resources/sounds/achievements/message.wav",
|
||||
"name": "message.wav",
|
||||
@@ -208117,7 +208066,6 @@
|
||||
"dfdd9924295f491d54f30569874287f8": "0c15d6753cf0ae2a1737b6e857e8e82ee135a697",
|
||||
"9a293eaaebca601f671070ad56cb81b6": "56306e7234bcffd14b4f977d42d237c4ad9c12d5",
|
||||
"b494140c6250b2faa8a801e0efef08d5": "042ce2001fb438a0a34e0c5d0001a9d95d5516df",
|
||||
"3758db793dae7bc206fbbd799c3cbb05": "71fc274cbe27f382afb0324024ebdc6f59aa666d",
|
||||
"8080d51e2d787aa3fa181e4ddbd455ed": "8acfbc4a51909de382e33a221a82f0b8efc04794",
|
||||
"5bac80439b16ee31feb5b45489a1a5da": "ad28302b3bdd8aef667969fceb073f0845942017",
|
||||
"888b281fa986de2eaccb0f9c8b591395": "7fad1628fd7da3b7a4bad6a7040f9fb4ed945d3a",
|
||||
@@ -208223,6 +208171,7 @@
|
||||
"dcde75e5d5c62a03f7bf35312f5116a9": "0f37f2795ca071eb712d5243247675f140897c50",
|
||||
"65e1739196c3163ec5e77f49eea118ae": "bd9262879f8a72ec610a9554b503d747194a0f49",
|
||||
"4981d3d22bda9c3d0791c13cb612e95d": "343f90558952049a0232faf6cf136fc2b8d4be84",
|
||||
"dbf48fe7b36fab316a1682961178ffd7": "dceb4536fba7fb89452864fc512a4d071a19eae0",
|
||||
"86da78cb59576328483a11c6ef74bc2b": "00d62fbdc8d5dec4c659005e116d0ba2ee63b547",
|
||||
"8ad82b1dc550319993a7d6c932b2656d": "9056e59b69c6f1160fdfb92a0773f17f6307f3ad",
|
||||
"a7470da305b78df13804a1e62c7bb41e": "660c7fe74de23ee604bea47673f84710bd34f075",
|
||||
@@ -208247,7 +208196,6 @@
|
||||
"083257a65f869a927d8f5a4534f46106": "36cc8f46014a321b7602e52c062ab23d7c256ccc",
|
||||
"95f294d24b5e97ceab96282f1284190f": "8dba66121f3bbc4b2bdf67722ef4c2b6b6b23ee6",
|
||||
"6f47c5daf33a759df3395db8ebe62ed1": "7b0bb79d38c573648de218645c6fb16449e156e0",
|
||||
"ec65dbfe7379cfac816b4d02259c0ca5": "72bfdf91fa11a86e2810a7c167cdcf11e20418a8",
|
||||
"75527903c6a793772b02807c5343f4c8": "456e74b0805d1931a7786d3d04673a3b02dd4dd3",
|
||||
"46dbe14f577b56ade221acf89631fd7b": "69253a2722f999ba4205f2e679d0b75e3b17109f",
|
||||
"5199b3892ba596a6bf8d0f930729157f": "9dc5783345f258d0c66c17028d39652daa234473",
|
||||
@@ -208255,7 +208203,6 @@
|
||||
"e910afbd441c5247227fb4a731d65799": "83d3c03dc1fc16f2a5b8bbf929d4cc21a078282c",
|
||||
"19498325f22381f551df06ce1b4cad4f": "01299427e4d789295c614752c8398261f006479b",
|
||||
"ad18e8fc265252917dd3698e268977d7": "59815234d228a7f390364c6d6380cc518ea63db1",
|
||||
"809868f006eebec4e4615dd8bc397ef7": "f7d0f1606c87019f00109ebdf0f260299f0a72da",
|
||||
"d91939cc76fb66f536476320987d0dc0": "8618516b26a0bc319ed174b66fd2d990eb5b74a3",
|
||||
"7547b120ce49d79d50addee5ea6fe38b": "fb33e36d016aedea36478eb4bb15b35ca1b7bb0c",
|
||||
"2c928cbd0410a7e7edae6b2808a1f566": "b478ce9d1343b8dd201c045f010bd1b64ba158ae",
|
||||
@@ -208520,7 +208467,6 @@
|
||||
"f4ae6809bd8c31573370e8da72514012": "e18b9aabbda0cc178436855ef8d04bce96533049",
|
||||
"bf5e583b038933c5f7bc4f0a6f70617c": "c549a2088bc9b678c8c79fa987ac5f4e6ff4772c",
|
||||
"602ea57b1d141e81b2a96f20a4b8098c": "2d9878fdd70979ecf6dd0da7652efd3797c26039",
|
||||
"dbf48fe7b36fab316a1682961178ffd7": "dceb4536fba7fb89452864fc512a4d071a19eae0",
|
||||
"ed4d4e8f81d9707dbf4931a786b6b56d": "a3d939af27fe0929e61b8e20d49ef58316c0a66c",
|
||||
"efa6392d3dd6e66abf94542159889e4c": "81c56a54bde692fb15fca4efe6db508b7d09bf6e",
|
||||
"b56eb507fac5ac64a7079514c02b8ca7": "cb0d4276f9947762af0841a8af75efa6cbe9109c",
|
||||
@@ -213973,9 +213919,7 @@
|
||||
"2e70ad008d4ec8549aada8002fdf42fb": "caa18ed04854a91e68f2d61a782560edd6373bbf",
|
||||
"b53d51edc7fc086685e31b811dc32aad": "34a81db03ab617fbfdd7f9b861692dd2ecd57b82",
|
||||
"5dea62f70439682a6cee16ba3823d11e": "6eddec30056cde7c664a0cf508dcad29353a12bb",
|
||||
"fc3c14208a929c386445635b19baa3f2": "5e2d50a3376dc6a0750505bec68626d39a5bd4c1",
|
||||
"de6da198a7359d1200c3eeb6df9c7eda": "40ecf6138c99a0aba775ef93240b295025a45500",
|
||||
"0525ad1f1381152f20bfb05775cf39cd": "bf2c73d668cd671df8adfe2ec3c7074e8ba9176a",
|
||||
"d66b0bccd743bd11eaca55bcaec925da": "c54f04b63ac6f04e0a0944ef260b5497e4494dbd",
|
||||
"44552702b05697a14ccbe2ca22ee7139": "47d2ec4b342649e4c391043ab915d4435f9d180d",
|
||||
"362e2dac5e4de13591c67fb5eceeb94a": "92ce9e739dcbfa0d6afb442620b71919d26bf283",
|
||||
@@ -247352,20 +247296,6 @@
|
||||
"b624812e3aa0ef2f75847f47907296e1f370cecd",
|
||||
"ca84ef31b6d48c6ab9530b743da373790a4f723b"
|
||||
],
|
||||
"GameIndex.yaml": [
|
||||
"71fc274cbe27f382afb0324024ebdc6f59aa666d",
|
||||
"72bfdf91fa11a86e2810a7c167cdcf11e20418a8",
|
||||
"2d9878fdd70979ecf6dd0da7652efd3797c26039",
|
||||
"997fdfb1c1873c1a43972b2984ac0da61fd47b14",
|
||||
"fca26d4f7c1fe0abf6534b470da2ee5ad461eba5",
|
||||
"58fb1f8ea6cf2d526904349640453e682fe388a1",
|
||||
"7efb2ed721cf0c0f0677324e09be311a8337e793",
|
||||
"57369df9ca82a460e596cea7835c5a0797dee668",
|
||||
"b22389650f6c0a1e276b48213fed8a9c1c6476ce",
|
||||
"6eddec30056cde7c664a0cf508dcad29353a12bb",
|
||||
"5e2d50a3376dc6a0750505bec68626d39a5bd4c1",
|
||||
"40ecf6138c99a0aba775ef93240b295025a45500"
|
||||
],
|
||||
"810-archiver.rom": [
|
||||
"8acfbc4a51909de382e33a221a82f0b8efc04794",
|
||||
"1eb8a3ee2366c605f7e47f8025a9e714ee9cf129"
|
||||
@@ -247681,6 +247611,9 @@
|
||||
"NotoColorEmoji-Regular.ttf": [
|
||||
"343f90558952049a0232faf6cf136fc2b8d4be84"
|
||||
],
|
||||
"RedumpDatabase.yaml": [
|
||||
"dceb4536fba7fb89452864fc512a4d071a19eae0"
|
||||
],
|
||||
"Roboto-Regular.ttf": [
|
||||
"00d62fbdc8d5dec4c659005e116d0ba2ee63b547",
|
||||
"d0860202be85147f3ce96d71aa88541d939a1aea"
|
||||
@@ -247779,12 +247712,6 @@
|
||||
"NotoSansTC-Regular.ttf": [
|
||||
"59815234d228a7f390364c6d6380cc518ea63db1"
|
||||
],
|
||||
"patches.zip": [
|
||||
"f7d0f1606c87019f00109ebdf0f260299f0a72da",
|
||||
"a3d939af27fe0929e61b8e20d49ef58316c0a66c",
|
||||
"2562e542d66db945ccf158d7a221c2a28e3b6be4",
|
||||
"541a74165496fb7ef35ce6ec03f711ebbe263e11"
|
||||
],
|
||||
"ffx_a_fsr1.h": [
|
||||
"8618516b26a0bc319ed174b66fd2d990eb5b74a3"
|
||||
],
|
||||
@@ -248561,9 +248488,21 @@
|
||||
"games.db": [
|
||||
"0e2ee74d1f9ebc3a6faf9dbaebbacc2ff053f4df"
|
||||
],
|
||||
"RedumpDatabase.yaml": [
|
||||
"dceb4536fba7fb89452864fc512a4d071a19eae0",
|
||||
"bf2c73d668cd671df8adfe2ec3c7074e8ba9176a"
|
||||
"GameIndex.yaml": [
|
||||
"2d9878fdd70979ecf6dd0da7652efd3797c26039",
|
||||
"997fdfb1c1873c1a43972b2984ac0da61fd47b14",
|
||||
"fca26d4f7c1fe0abf6534b470da2ee5ad461eba5",
|
||||
"58fb1f8ea6cf2d526904349640453e682fe388a1",
|
||||
"7efb2ed721cf0c0f0677324e09be311a8337e793",
|
||||
"57369df9ca82a460e596cea7835c5a0797dee668",
|
||||
"b22389650f6c0a1e276b48213fed8a9c1c6476ce",
|
||||
"6eddec30056cde7c664a0cf508dcad29353a12bb",
|
||||
"40ecf6138c99a0aba775ef93240b295025a45500"
|
||||
],
|
||||
"patches.zip": [
|
||||
"a3d939af27fe0929e61b8e20d49ef58316c0a66c",
|
||||
"2562e542d66db945ccf158d7a221c2a28e3b6be4",
|
||||
"541a74165496fb7ef35ce6ec03f711ebbe263e11"
|
||||
],
|
||||
"libopenglplugin.so": [
|
||||
"407f0fde89ac1a628fed0329072dfae1f8d304b1",
|
||||
@@ -283931,7 +283870,6 @@
|
||||
"feeddf41": "0c15d6753cf0ae2a1737b6e857e8e82ee135a697",
|
||||
"2b3e5a61": "56306e7234bcffd14b4f977d42d237c4ad9c12d5",
|
||||
"6b7152b1": "042ce2001fb438a0a34e0c5d0001a9d95d5516df",
|
||||
"5acc416a": "71fc274cbe27f382afb0324024ebdc6f59aa666d",
|
||||
"bd90e77d": "8acfbc4a51909de382e33a221a82f0b8efc04794",
|
||||
"196eed02": "ad28302b3bdd8aef667969fceb073f0845942017",
|
||||
"55dfc9ac": "7fad1628fd7da3b7a4bad6a7040f9fb4ed945d3a",
|
||||
@@ -284037,6 +283975,7 @@
|
||||
"84ad7d58": "0f37f2795ca071eb712d5243247675f140897c50",
|
||||
"1094a89e": "bd9262879f8a72ec610a9554b503d747194a0f49",
|
||||
"a5b357e6": "343f90558952049a0232faf6cf136fc2b8d4be84",
|
||||
"0496a60f": "dceb4536fba7fb89452864fc512a4d071a19eae0",
|
||||
"f49fbea6": "00d62fbdc8d5dec4c659005e116d0ba2ee63b547",
|
||||
"27922b13": "9056e59b69c6f1160fdfb92a0773f17f6307f3ad",
|
||||
"84d6cb99": "660c7fe74de23ee604bea47673f84710bd34f075",
|
||||
@@ -284061,7 +284000,6 @@
|
||||
"8216948a": "36cc8f46014a321b7602e52c062ab23d7c256ccc",
|
||||
"2c7a1b2a": "8dba66121f3bbc4b2bdf67722ef4c2b6b6b23ee6",
|
||||
"6e8b2b47": "7b0bb79d38c573648de218645c6fb16449e156e0",
|
||||
"1a8e4cf7": "72bfdf91fa11a86e2810a7c167cdcf11e20418a8",
|
||||
"92685e68": "456e74b0805d1931a7786d3d04673a3b02dd4dd3",
|
||||
"b02dadf9": "69253a2722f999ba4205f2e679d0b75e3b17109f",
|
||||
"96886a26": "9dc5783345f258d0c66c17028d39652daa234473",
|
||||
@@ -284069,7 +284007,6 @@
|
||||
"0fcd7543": "83d3c03dc1fc16f2a5b8bbf929d4cc21a078282c",
|
||||
"07694737": "01299427e4d789295c614752c8398261f006479b",
|
||||
"05eb51ca": "59815234d228a7f390364c6d6380cc518ea63db1",
|
||||
"5120eae7": "f7d0f1606c87019f00109ebdf0f260299f0a72da",
|
||||
"3a8aeca2": "8618516b26a0bc319ed174b66fd2d990eb5b74a3",
|
||||
"6743e2ad": "fb33e36d016aedea36478eb4bb15b35ca1b7bb0c",
|
||||
"ce9e57e4": "b478ce9d1343b8dd201c045f010bd1b64ba158ae",
|
||||
@@ -284334,7 +284271,6 @@
|
||||
"e1232465": "e18b9aabbda0cc178436855ef8d04bce96533049",
|
||||
"ca72899c": "c549a2088bc9b678c8c79fa987ac5f4e6ff4772c",
|
||||
"e8038ec9": "2d9878fdd70979ecf6dd0da7652efd3797c26039",
|
||||
"0496a60f": "dceb4536fba7fb89452864fc512a4d071a19eae0",
|
||||
"8820b708": "a3d939af27fe0929e61b8e20d49ef58316c0a66c",
|
||||
"ea1fd68a": "81c56a54bde692fb15fca4efe6db508b7d09bf6e",
|
||||
"13eed5a5": "cb0d4276f9947762af0841a8af75efa6cbe9109c",
|
||||
@@ -289787,9 +289723,7 @@
|
||||
"41391dd3": "caa18ed04854a91e68f2d61a782560edd6373bbf",
|
||||
"25f6212a": "34a81db03ab617fbfdd7f9b861692dd2ecd57b82",
|
||||
"0b805686": "6eddec30056cde7c664a0cf508dcad29353a12bb",
|
||||
"a7541e89": "5e2d50a3376dc6a0750505bec68626d39a5bd4c1",
|
||||
"a3e573a0": "40ecf6138c99a0aba775ef93240b295025a45500",
|
||||
"28709e73": "bf2c73d668cd671df8adfe2ec3c7074e8ba9176a",
|
||||
"d766f908": "c54f04b63ac6f04e0a0944ef260b5497e4494dbd",
|
||||
"2c3bcd32": "47d2ec4b342649e4c391043ab915d4435f9d180d",
|
||||
"0509cea5": "92ce9e739dcbfa0d6afb442620b71919d26bf283",
|
||||
@@ -302935,7 +302869,6 @@
|
||||
"68757d34c21e28cce68c38ec1cc0de37b9fb046eed0716036ba4f1d0df3bcc5e": "0c15d6753cf0ae2a1737b6e857e8e82ee135a697",
|
||||
"2a5448dd69dcd13c48c7b256e51907aa43bdc2ce2dde618a039c0b7cc009a57d": "56306e7234bcffd14b4f977d42d237c4ad9c12d5",
|
||||
"9e7fb1335e56027f150b7ff9b7b6cddd9c0844af19be6dbc17347e1882022038": "042ce2001fb438a0a34e0c5d0001a9d95d5516df",
|
||||
"e7c70faf16ce180e6a17c040dc2bb326c6fcca63e7fb3f83f992d65fbe480443": "71fc274cbe27f382afb0324024ebdc6f59aa666d",
|
||||
"b861b939d32cb2c1e672cb9c50668394f1d83d723fd5f331727a19c36001f5ad": "8acfbc4a51909de382e33a221a82f0b8efc04794",
|
||||
"5160ac22a55d6c82ee2304046b2b6eac13d153c0d0b1164bd693e93acc053623": "ad28302b3bdd8aef667969fceb073f0845942017",
|
||||
"acb8f9c481ffe762021f980c0cee42bd1a512ccd210686e6e0ac7d6e34f5a374": "7fad1628fd7da3b7a4bad6a7040f9fb4ed945d3a",
|
||||
@@ -303041,6 +302974,7 @@
|
||||
"dedbaf271c3257db2a02493a53616d1da78302c678575c9b0f4d254cdb1e4293": "0f37f2795ca071eb712d5243247675f140897c50",
|
||||
"c217cc125856b0ee9776208b8e0bf5f1a4f04561ff2fa644f690518f87f55942": "bd9262879f8a72ec610a9554b503d747194a0f49",
|
||||
"1ca0775b9dc92d8d959cd06dd773e2d4dacc97301ad81e6b5aa02cd5e7233a27": "343f90558952049a0232faf6cf136fc2b8d4be84",
|
||||
"755962450efd5effd2f3fe2f9a60c73d6cb3f9b4acea964abf5bb25a86ee25bc": "dceb4536fba7fb89452864fc512a4d071a19eae0",
|
||||
"797e35f7f5d6020a5c6ea13b42ecd668bcfb3bbc4baa0e74773527e5b6cb3174": "00d62fbdc8d5dec4c659005e116d0ba2ee63b547",
|
||||
"4d901b560f20f455b3c82c6616b17b72360956747cb35c3808aa370052fe8e32": "9056e59b69c6f1160fdfb92a0773f17f6307f3ad",
|
||||
"c4541eefde3f31e4216559ada91f52d1b7ec95b849f7e9afc7a05613a56c8bf2": "660c7fe74de23ee604bea47673f84710bd34f075",
|
||||
@@ -303065,7 +302999,6 @@
|
||||
"022c8b05d8f3e7e6046077a2d6ad2d3da1e7fa0e69c8321f3c556b9963be2477": "36cc8f46014a321b7602e52c062ab23d7c256ccc",
|
||||
"c749cd9513bd345b9f12ff46eac0ffbf4e54c0b0884921df97ca8297396e3fea": "8dba66121f3bbc4b2bdf67722ef4c2b6b6b23ee6",
|
||||
"ab829598e15b771112130cbafbd982736400690c11e3d7c81e3fc8c582c24f47": "7b0bb79d38c573648de218645c6fb16449e156e0",
|
||||
"61c77b789ccde856096f024e4a3eef6891e15ca59c127e0dd13e762a7d338a7e": "72bfdf91fa11a86e2810a7c167cdcf11e20418a8",
|
||||
"90f50de40af560ad55f2fcf460af8205f842b534b057bd8b8bcf7d3e10d3223a": "456e74b0805d1931a7786d3d04673a3b02dd4dd3",
|
||||
"11fa517464dbd568e855e449650a9004d1072c4a54ac1845aaafc462c0652b64": "69253a2722f999ba4205f2e679d0b75e3b17109f",
|
||||
"1edcc5695148a632b8a5516ba41a86edf01f06289c5c2e84a1afafe833eb25da": "9dc5783345f258d0c66c17028d39652daa234473",
|
||||
@@ -303073,7 +303006,6 @@
|
||||
"9db318b65ee9c575a43e7efd273dbdd1afef26e467eea3e1073a50e1a6595f6d": "83d3c03dc1fc16f2a5b8bbf929d4cc21a078282c",
|
||||
"ae82f4e2a55e1316a55bcc1d05e9555ce08d8bda07e893b486896b626fd852ff": "01299427e4d789295c614752c8398261f006479b",
|
||||
"6b137e2eb57a2d1e4cf391c886ab1b783a0e5ddb5c75254748bde00c15cb8ff5": "59815234d228a7f390364c6d6380cc518ea63db1",
|
||||
"8d95f1fba3acc83195bbac2d3e042b5f34c3c399856d9865e681d173f4cee609": "f7d0f1606c87019f00109ebdf0f260299f0a72da",
|
||||
"f60e2722fcd13989523b9164d776ab382b3692791767f3bf8bb19967f763f3fb": "8618516b26a0bc319ed174b66fd2d990eb5b74a3",
|
||||
"93c3922362ea7fc99cbcc698ca30c98de4f8c246d1fbb0b09e015ddef38ce3a5": "fb33e36d016aedea36478eb4bb15b35ca1b7bb0c",
|
||||
"3d5b9cef750ea8bedceef43c4c52215e8280abb81b5b8fa002007bb3f97f0506": "b478ce9d1343b8dd201c045f010bd1b64ba158ae",
|
||||
@@ -303338,7 +303270,6 @@
|
||||
"306b53ecfb182a504dd8a7446093c316387d2fd8dc350d0792ed1753fe0996cd": "e18b9aabbda0cc178436855ef8d04bce96533049",
|
||||
"cdefaf8efd47045f6820928eba84db5bed7557539328952b5f828315485e02ee": "c549a2088bc9b678c8c79fa987ac5f4e6ff4772c",
|
||||
"d5558a0e0aa033482b098cc8a6449e78137f5069beabf421ab60d9eccbb62d8f": "2d9878fdd70979ecf6dd0da7652efd3797c26039",
|
||||
"755962450efd5effd2f3fe2f9a60c73d6cb3f9b4acea964abf5bb25a86ee25bc": "dceb4536fba7fb89452864fc512a4d071a19eae0",
|
||||
"aa14a46908c1114e0c0f1c06accbfa7efc83c1768fb501699e043cbdb1af797f": "a3d939af27fe0929e61b8e20d49ef58316c0a66c",
|
||||
"817eb2a258e44bb730b94ab77b00cac2480c2e27449078949e2897bad3cb3bc3": "81c56a54bde692fb15fca4efe6db508b7d09bf6e",
|
||||
"53e5ddc6825ecde7b502acb5fae3a173ba6696ec5b8bb7445f378f7b0ad1beb8": "cb0d4276f9947762af0841a8af75efa6cbe9109c",
|
||||
@@ -308791,9 +308722,7 @@
|
||||
"6b61e27861c288a99ee9b4878862ed824173a037ebaca2bef0581017a85241e1": "caa18ed04854a91e68f2d61a782560edd6373bbf",
|
||||
"e8eed9831c39fc298be087ba11d620673134bc683b97ccc909c555d29cc1b207": "34a81db03ab617fbfdd7f9b861692dd2ecd57b82",
|
||||
"3e14a0d3db871704a9652a5d90d4ba62be2bceefbca6bb46da65275b09d4f78e": "6eddec30056cde7c664a0cf508dcad29353a12bb",
|
||||
"6531239315d759d0588985b31576696d2672c1a0e192633ea2d4f3d011bfdb37": "5e2d50a3376dc6a0750505bec68626d39a5bd4c1",
|
||||
"483face8f50f5e96d89528059522be3766b44f5e07e854bae1fa92ff0111d0a7": "40ecf6138c99a0aba775ef93240b295025a45500",
|
||||
"302b57fdd1f9472ef590789e4b347381cc8442a04a765c7eae3d3f2e4b06394c": "bf2c73d668cd671df8adfe2ec3c7074e8ba9176a",
|
||||
"dfd8035387a463722ad07af3c91934e0a685f2afa57e31d305f0602656d1edb1": "c54f04b63ac6f04e0a0944ef260b5497e4494dbd",
|
||||
"41de2047af8382988bfd568035ff26eec5f1cabc3efe773680546aa00a82857d": "47d2ec4b342649e4c391043ab915d4435f9d180d",
|
||||
"fc1b5e12e426b229fc4f114a4b8cbe6a5441f7e0ce6654abce382e677f1c051d": "92ce9e739dcbfa0d6afb442620b71919d26bf283",
|
||||
@@ -469596,18 +469525,6 @@
|
||||
"aethersx2/game_controller_db.txt": [
|
||||
"042ce2001fb438a0a34e0c5d0001a9d95d5516df"
|
||||
],
|
||||
"Other/aethersx2/resources/GameIndex.yaml": [
|
||||
"71fc274cbe27f382afb0324024ebdc6f59aa666d"
|
||||
],
|
||||
"aethersx2/resources/GameIndex.yaml": [
|
||||
"71fc274cbe27f382afb0324024ebdc6f59aa666d"
|
||||
],
|
||||
"resources/GameIndex.yaml": [
|
||||
"71fc274cbe27f382afb0324024ebdc6f59aa666d",
|
||||
"72bfdf91fa11a86e2810a7c167cdcf11e20418a8",
|
||||
"2d9878fdd70979ecf6dd0da7652efd3797c26039",
|
||||
"40ecf6138c99a0aba775ef93240b295025a45500"
|
||||
],
|
||||
"Other/altirra/.variants/810-archiver.rom.8080d51e": [
|
||||
"8acfbc4a51909de382e33a221a82f0b8efc04794"
|
||||
],
|
||||
@@ -470259,6 +470176,12 @@
|
||||
"armsx2/NotoColorEmoji-Regular.ttf": [
|
||||
"343f90558952049a0232faf6cf136fc2b8d4be84"
|
||||
],
|
||||
"Other/armsx2/RedumpDatabase.yaml": [
|
||||
"dceb4536fba7fb89452864fc512a4d071a19eae0"
|
||||
],
|
||||
"armsx2/RedumpDatabase.yaml": [
|
||||
"dceb4536fba7fb89452864fc512a4d071a19eae0"
|
||||
],
|
||||
"Other/armsx2/Roboto-Regular.ttf": [
|
||||
"00d62fbdc8d5dec4c659005e116d0ba2ee63b547"
|
||||
],
|
||||
@@ -470406,15 +470329,6 @@
|
||||
"armsx2/no-save.png": [
|
||||
"7b0bb79d38c573648de218645c6fb16449e156e0"
|
||||
],
|
||||
"Other/armsx2/pcsx2/resources/GameIndex.yaml": [
|
||||
"72bfdf91fa11a86e2810a7c167cdcf11e20418a8"
|
||||
],
|
||||
"armsx2/pcsx2/resources/GameIndex.yaml": [
|
||||
"72bfdf91fa11a86e2810a7c167cdcf11e20418a8"
|
||||
],
|
||||
"pcsx2/resources/GameIndex.yaml": [
|
||||
"72bfdf91fa11a86e2810a7c167cdcf11e20418a8"
|
||||
],
|
||||
"Other/armsx2/pcsx2/resources/fonts/NotoSansArabic-Regular.ttf": [
|
||||
"456e74b0805d1931a7786d3d04673a3b02dd4dd3"
|
||||
],
|
||||
@@ -470529,19 +470443,6 @@
|
||||
"fonts/NotoSansTC-Regular.ttf": [
|
||||
"59815234d228a7f390364c6d6380cc518ea63db1"
|
||||
],
|
||||
"Other/armsx2/pcsx2/resources/patches.zip": [
|
||||
"f7d0f1606c87019f00109ebdf0f260299f0a72da"
|
||||
],
|
||||
"armsx2/pcsx2/resources/patches.zip": [
|
||||
"f7d0f1606c87019f00109ebdf0f260299f0a72da"
|
||||
],
|
||||
"pcsx2/resources/patches.zip": [
|
||||
"f7d0f1606c87019f00109ebdf0f260299f0a72da"
|
||||
],
|
||||
"resources/patches.zip": [
|
||||
"f7d0f1606c87019f00109ebdf0f260299f0a72da",
|
||||
"a3d939af27fe0929e61b8e20d49ef58316c0a66c"
|
||||
],
|
||||
"Other/armsx2/pcsx2/resources/shaders/common/ffx_a_fsr1.h": [
|
||||
"8618516b26a0bc319ed174b66fd2d990eb5b74a3"
|
||||
],
|
||||
@@ -473120,15 +473021,9 @@
|
||||
"emucorex/resources/GameIndex.yaml": [
|
||||
"2d9878fdd70979ecf6dd0da7652efd3797c26039"
|
||||
],
|
||||
"Other/emucorex/resources/RedumpDatabase.yaml": [
|
||||
"dceb4536fba7fb89452864fc512a4d071a19eae0"
|
||||
],
|
||||
"emucorex/resources/RedumpDatabase.yaml": [
|
||||
"dceb4536fba7fb89452864fc512a4d071a19eae0"
|
||||
],
|
||||
"resources/RedumpDatabase.yaml": [
|
||||
"dceb4536fba7fb89452864fc512a4d071a19eae0",
|
||||
"bf2c73d668cd671df8adfe2ec3c7074e8ba9176a"
|
||||
"resources/GameIndex.yaml": [
|
||||
"2d9878fdd70979ecf6dd0da7652efd3797c26039",
|
||||
"40ecf6138c99a0aba775ef93240b295025a45500"
|
||||
],
|
||||
"Other/emucorex/resources/patches.zip": [
|
||||
"a3d939af27fe0929e61b8e20d49ef58316c0a66c"
|
||||
@@ -473136,6 +473031,9 @@
|
||||
"emucorex/resources/patches.zip": [
|
||||
"a3d939af27fe0929e61b8e20d49ef58316c0a66c"
|
||||
],
|
||||
"resources/patches.zip": [
|
||||
"a3d939af27fe0929e61b8e20d49ef58316c0a66c"
|
||||
],
|
||||
"Other/emucorex/resources/shaders/common/fxaa.fx": [
|
||||
"81c56a54bde692fb15fca4efe6db508b7d09bf6e"
|
||||
],
|
||||
@@ -544479,30 +544377,12 @@
|
||||
".variants/GameIndex.yaml": [
|
||||
"6eddec30056cde7c664a0cf508dcad29353a12bb"
|
||||
],
|
||||
"Sony/PlayStation 2/resources/.variants/GameIndex.yaml.fc3c1420": [
|
||||
"5e2d50a3376dc6a0750505bec68626d39a5bd4c1"
|
||||
],
|
||||
"PlayStation 2/resources/.variants/GameIndex.yaml.fc3c1420": [
|
||||
"5e2d50a3376dc6a0750505bec68626d39a5bd4c1"
|
||||
],
|
||||
"resources/.variants/GameIndex.yaml.fc3c1420": [
|
||||
"5e2d50a3376dc6a0750505bec68626d39a5bd4c1"
|
||||
],
|
||||
".variants/GameIndex.yaml.fc3c1420": [
|
||||
"5e2d50a3376dc6a0750505bec68626d39a5bd4c1"
|
||||
],
|
||||
"Sony/PlayStation 2/resources/GameIndex.yaml": [
|
||||
"40ecf6138c99a0aba775ef93240b295025a45500"
|
||||
],
|
||||
"PlayStation 2/resources/GameIndex.yaml": [
|
||||
"40ecf6138c99a0aba775ef93240b295025a45500"
|
||||
],
|
||||
"Sony/PlayStation 2/resources/RedumpDatabase.yaml": [
|
||||
"bf2c73d668cd671df8adfe2ec3c7074e8ba9176a"
|
||||
],
|
||||
"PlayStation 2/resources/RedumpDatabase.yaml": [
|
||||
"bf2c73d668cd671df8adfe2ec3c7074e8ba9176a"
|
||||
],
|
||||
"Sony/PlayStation 2/resources/sounds/achievements/message.wav": [
|
||||
"c54f04b63ac6f04e0a0944ef260b5497e4494dbd"
|
||||
],
|
||||
|
||||
@@ -148,9 +148,6 @@ files:
|
||||
path: resources/GameIndex.yaml
|
||||
required: false
|
||||
bundled: true
|
||||
sha1:
|
||||
- 997fdfb1c1873c1a43972b2984ac0da61fd47b14
|
||||
- 71fc274cbe27f382afb0324024ebdc6f59aa666d
|
||||
description: "Game database"
|
||||
note: "Read from the resources folder, keyed by serial, and carrying the per-game fixes, patches and settings overrides. Parsed into gamedb.cache in the cache folder and reparsed whenever the modification time moves."
|
||||
source_ref: "aethersx2/pcsx2/GameDatabase.cpp:35-36, aethersx2/pcsx2/GameDatabase.cpp:348-352, aethersx2/pcsx2/GameDatabase.cpp:523-535"
|
||||
|
||||
@@ -272,8 +272,6 @@ files:
|
||||
standalone_path: resources/GameIndex.yaml
|
||||
required: false
|
||||
bundled: true
|
||||
sha1: 72bfdf91fa11a86e2810a7c167cdcf11e20418a8
|
||||
size: 2690355
|
||||
description: "Game database"
|
||||
note: >-
|
||||
Read from the resources folder, keyed by serial, carrying the per-game
|
||||
@@ -299,8 +297,6 @@ files:
|
||||
standalone_path: resources/RedumpDatabase.yaml
|
||||
required: false
|
||||
bundled: true
|
||||
sha1: bf2c73d668cd671df8adfe2ec3c7074e8ba9176a
|
||||
size: 1917242
|
||||
description: "Disc track hash database"
|
||||
note: >-
|
||||
Loaded on the first hash lookup and used to tell the user whether a dump
|
||||
@@ -312,8 +308,6 @@ files:
|
||||
standalone_path: resources/patches.zip
|
||||
required: false
|
||||
bundled: true
|
||||
sha1: f7d0f1606c87019f00109ebdf0f260299f0a72da
|
||||
size: 2007224
|
||||
description: "Game patch archive"
|
||||
note: >-
|
||||
Per-game pnach entries matched on serial and disc CRC. The patches and
|
||||
@@ -793,8 +787,6 @@ files:
|
||||
standalone_path: resources/sounds/achievements/unlock.wav
|
||||
required: false
|
||||
bundled: true
|
||||
sha1: da328a0a3074ec86fee2d6741bfc2e5a1f68d063
|
||||
size: 202702
|
||||
description: "Achievement unlock sound"
|
||||
source_ref: "pcsx2/Achievements.cpp:69, pcsx2/Achievements.cpp:1278-1284, pcsx2/Achievements.cpp:1581"
|
||||
|
||||
|
||||
@@ -289,8 +289,6 @@ files:
|
||||
system: sony-playstation-2
|
||||
required: false
|
||||
bundled: true
|
||||
sha1: 2d9878fdd70979ecf6dd0da7652efd3797c26039
|
||||
size: 2697551
|
||||
category: game_data
|
||||
description: "per-title compatibility and fix database"
|
||||
note: >-
|
||||
@@ -305,8 +303,6 @@ files:
|
||||
system: sony-playstation-2
|
||||
required: false
|
||||
bundled: true
|
||||
sha1: dceb4536fba7fb89452864fc512a4d071a19eae0
|
||||
size: 1906463
|
||||
category: game_data
|
||||
description: "disc hash database"
|
||||
note: >-
|
||||
@@ -319,8 +315,6 @@ files:
|
||||
system: sony-playstation-2
|
||||
required: false
|
||||
bundled: true
|
||||
sha1: a3d939af27fe0929e61b8e20d49ef58316c0a66c
|
||||
size: 2091144
|
||||
category: game_data
|
||||
description: "built-in game patch archive"
|
||||
note: >-
|
||||
@@ -560,8 +554,6 @@ files:
|
||||
|
||||
- name: unlock.wav
|
||||
path: resources/sounds/achievements/unlock.wav
|
||||
sha1: da328a0a3074ec86fee2d6741bfc2e5a1f68d063
|
||||
size: 202702
|
||||
system: sony-playstation-2
|
||||
required: false
|
||||
description: "achievement unlock sound"
|
||||
|
||||
+1046
-1153
File diff suppressed because it is too large.
Load diff
+430
-443
File diff suppressed because it is too large.
Load diff
@@ -69,8 +69,6 @@ files:
|
||||
path: resources/GameIndex.yaml
|
||||
required: false
|
||||
bundled: true
|
||||
sha1: 5e2d50a3376dc6a0750505bec68626d39a5bd4c1
|
||||
size: 2683897
|
||||
category: game_data
|
||||
description: "per-title compatibility and fix database"
|
||||
source_ref: "pcsx2/GameDatabase.cpp:39, pcsx2/GameDatabase.cpp:1026-1035"
|
||||
@@ -80,8 +78,6 @@ files:
|
||||
path: resources/RedumpDatabase.yaml
|
||||
required: false
|
||||
bundled: true
|
||||
sha1: bf2c73d668cd671df8adfe2ec3c7074e8ba9176a
|
||||
size: 1917242
|
||||
category: game_data
|
||||
description: "disc hash database"
|
||||
source_ref: "pcsx2/GameDatabase.cpp:1133, pcsx2/GameDatabase.cpp:1188-1197"
|
||||
|
||||
@@ -59,14 +59,14 @@ files:
|
||||
# -- Fallback BIOS filenames (searched in order after scph550x) --
|
||||
|
||||
- name: "psxonpsp660.bin"
|
||||
description: "PSP embedded PS1 BIOS, filed as the Japanese BIOS"
|
||||
region: [japan]
|
||||
description: "PSP embedded PS1 BIOS (region detected at runtime)"
|
||||
region: [world]
|
||||
size: 524288
|
||||
required: false
|
||||
hle_fallback: true
|
||||
validation: [size]
|
||||
source_ref: "frontend/libretro.c:3864-3867 (listed_bios[3]), frontend/libretro.c:3654-3669 (try_use_bios region guess)"
|
||||
note: "The core reads the region out of the image: bytes 0x7ff51 are ' J' in this dump, so it takes the JP slot (' E' takes EU, anything else US), one image per slot, first found kept. The disc licence picks the slot at boot (Config.Bios[Config.PsxRegion]); a US or EU disc only reaches this file when its own slot is empty. Must be 512 KB."
|
||||
source_ref: "frontend/libretro.c:3864-3867 (listed_bios[3])"
|
||||
note: "Region detected from content at runtime. Must be 512 KB in libretro context."
|
||||
|
||||
- name: "scph101.bin"
|
||||
description: "SCPH-101 (v4.4 03-24-00 A) - PSone US"
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ param(
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$defaultInstallUrl = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
|
||||
$defaultInstallSha256 = "df0adf1fd5088ef9509429fb1d5720e5ca5e7bf46aff9ad0021851dce7349107"
|
||||
$defaultInstallSha256 = "60e18da18b5a9a43b18d37020b676ad47babd6d12c5314e8fcc1370e4864c4e5"
|
||||
$maximumInstallerBytes = 2MB
|
||||
$installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null }
|
||||
$temporary = $null
|
||||
|
||||
+10
-19
@@ -120,8 +120,7 @@ def _os_default_dests(os_type: str) -> dict[str, Path]:
|
||||
appdata = Path(os.environ.get("APPDATA", str(home / "AppData" / "Roaming")))
|
||||
return {
|
||||
"retroarch": appdata / "RetroArch" / "system",
|
||||
# RetroBat's installer defaults to C:\RetroBat (installer.iss).
|
||||
"retrobat": Path("C:/RetroBat/bios"),
|
||||
"retrobat": profile / "RetroBat" / "bios",
|
||||
"bizhawk": profile / "BizHawk" / "Firmware",
|
||||
}
|
||||
if os_type == "darwin":
|
||||
@@ -1295,7 +1294,7 @@ def download_files(
|
||||
def do_standalone_copies(
|
||||
manifest: dict, bios_path: Path, os_type: str,
|
||||
extra_dirs: dict[str, Path] | None = None,
|
||||
) -> tuple[int, int, int]:
|
||||
) -> tuple[int, int]:
|
||||
"""Copy BIOS files to standalone emulator directories.
|
||||
|
||||
Supports:
|
||||
@@ -1308,19 +1307,16 @@ def do_standalone_copies(
|
||||
default per-OS locations, for setups such as LaunchBox that keep their
|
||||
emulators outside them. The layout below the root is the same.
|
||||
|
||||
Returns (copied, skipped, failed): skipped is a target directory that
|
||||
does not exist, failed a copy the system refused (permission, disk
|
||||
full, read-only mount), which used to be counted as "dir not found".
|
||||
Returns (copied_count, skipped_count).
|
||||
"""
|
||||
from fnmatch import fnmatch
|
||||
|
||||
copies = manifest.get("standalone_copies", [])
|
||||
if not copies:
|
||||
return 0, 0, 0
|
||||
return 0, 0
|
||||
|
||||
copied = 0
|
||||
skipped = 0
|
||||
failed = 0
|
||||
|
||||
for entry in copies:
|
||||
# Note entries: print message if emulator detected
|
||||
@@ -1379,11 +1375,10 @@ def do_standalone_copies(
|
||||
try:
|
||||
shutil.copy2(src, dest)
|
||||
copied += 1
|
||||
except OSError as exc:
|
||||
print(f" ERROR: {dest}: {exc.strerror or exc}")
|
||||
failed += 1
|
||||
except OSError:
|
||||
skipped += 1
|
||||
|
||||
return copied, skipped, failed
|
||||
return copied, skipped
|
||||
|
||||
|
||||
def format_size(n: int) -> str:
|
||||
@@ -1743,15 +1738,11 @@ def main() -> None:
|
||||
print("\nStandalone emulators:")
|
||||
lb_root = launchbox_root(os_type)
|
||||
extra_dirs = launchbox_bios_dirs(lb_root) if lb_root else None
|
||||
copied, skipped, copy_failed = do_standalone_copies(
|
||||
copied, skipped = do_standalone_copies(
|
||||
manifest, bios_path, os_type, extra_dirs
|
||||
)
|
||||
if copied or skipped or copy_failed:
|
||||
print(
|
||||
f" {copied} copied, {skipped} skipped (dir not found), "
|
||||
f"{copy_failed} failed"
|
||||
)
|
||||
total_errors += copy_failed
|
||||
if copied or skipped:
|
||||
print(f" {copied} copied, {skipped} skipped (dir not found)")
|
||||
elif manifest.get("standalone_copies") and not args.check:
|
||||
print(
|
||||
"\nStandalone copies skipped "
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ esac
|
||||
TEMP_INSTALLER=""
|
||||
TEMP_DIRECTORY=""
|
||||
DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
|
||||
DEFAULT_INSTALL_SHA256="df0adf1fd5088ef9509429fb1d5720e5ca5e7bf46aff9ad0021851dce7349107"
|
||||
DEFAULT_INSTALL_SHA256="60e18da18b5a9a43b18d37020b676ad47babd6d12c5314e8fcc1370e4864c4e5"
|
||||
MAX_INSTALLER_BYTES=2097152
|
||||
|
||||
cleanup() {
|
||||
|
||||
+195
-15
@@ -5,7 +5,7 @@
|
||||
"platform": "batocera",
|
||||
"display_name": "Batocera",
|
||||
"version": "1.0",
|
||||
"generated": "2026-10-09T20:41:14Z",
|
||||
"generated": "2026-10-06T06:50:25Z",
|
||||
"base_destination": "bios",
|
||||
"detect": [
|
||||
{
|
||||
@@ -237,10 +237,10 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"total_files": 12808,
|
||||
"total_size": 8059515793,
|
||||
"pack_files": 12810,
|
||||
"pack_size": 8059515793,
|
||||
"total_files": 12826,
|
||||
"total_size": 8065338769,
|
||||
"pack_files": 12828,
|
||||
"pack_size": 8065338769,
|
||||
"total_omitted": 30,
|
||||
"omitted_files": [
|
||||
{
|
||||
@@ -3467,6 +3467,36 @@
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cd32.rom",
|
||||
"sha1": "3525be8887f79b5929e017b42380a79edfee542d",
|
||||
"sha256": "3586355514947b5ab48c8755dc004fab94f188b19e7bce491c8f9693499e830b",
|
||||
"size": 524288,
|
||||
"repo_path": "bios/Commodore/Amiga/amiga-os-310-cd32.rom",
|
||||
"cores": [
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cd32ext.rom",
|
||||
"sha1": "5bef3d628ce59cc02a66e6e4ae0da48f60e78f7f",
|
||||
"sha256": "d964826432559994ffe329f643fd4e42dbbff313362d9434945725d7119cae87",
|
||||
"size": 524288,
|
||||
"repo_path": "bios/Commodore/Amiga/amiga-ext-310-cd32.rom",
|
||||
"cores": [
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cdtv.rom",
|
||||
"sha1": "7ba40ffa17e500ed9fed041f3424bd81d9c907be",
|
||||
"sha256": "6e48bb768539e8dae352553e5fdfa860682edaf9e26b105fadd8e26103171765",
|
||||
"size": 262144,
|
||||
"repo_path": "bios/Commodore/Amiga/amiga-ext-130-cdtv.rom",
|
||||
"cores": [
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cdtvext.rom",
|
||||
"sha1": "7ba40ffa17e500ed9fed041f3424bd81d9c907be",
|
||||
@@ -40987,6 +41017,16 @@
|
||||
"clk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "Acorn/basic.rom",
|
||||
"sha1": "4a7393f3a45ea309f744441c16723e2ef447a281",
|
||||
"sha256": "45bd55dc0f6f0f8f1fe9e2481de7def206565eec8f600ba3068b849ca4132079",
|
||||
"size": 16384,
|
||||
"repo_path": "bios/Acorn/BBC Micro/basic2.rom",
|
||||
"cores": [
|
||||
"clk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "BBCMicro/dfs09.rom",
|
||||
"sha1": "5cc0f14b8f46855c70eaa653cca4ad079b458732",
|
||||
@@ -41567,6 +41607,16 @@
|
||||
"clk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "MSX/MSX.ROM",
|
||||
"sha1": "409e82adac40f6bdd18eb6c84e8b2fbdc7fb5498",
|
||||
"sha256": "999564a371dd2fdf7fbe8d853e82a68d557c27b7d87417639b2fa17704b83f78",
|
||||
"size": 32768,
|
||||
"repo_path": "bios/Microsoft/MSX/MSX.ROM",
|
||||
"cores": [
|
||||
"clk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "MSX/msx-japanese.rom",
|
||||
"sha1": "302afb5d8be26c758309ca3df611ae69cced2821",
|
||||
@@ -41607,6 +41657,26 @@
|
||||
"clk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "MSX/MSX2.ROM",
|
||||
"sha1": "6103b39f1e38d1aa2d84b1c3219c44f1abb5436e",
|
||||
"sha256": "4bc4ae85ca5f28246cd3e7b7e017d298ddd375603657f84ef2c7954bc2d9b919",
|
||||
"size": 32768,
|
||||
"repo_path": "bios/Microsoft/MSX/MSX2.ROM",
|
||||
"cores": [
|
||||
"clk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "MSX/MSX2EXT.ROM",
|
||||
"sha1": "5c1f9c7fb655e43d38e5dd1fcc6b942b2ff68b02",
|
||||
"sha256": "6c6f421a10c428d960b7ecc990f99af1c638147f747bddca7b0bf0e2ab738300",
|
||||
"size": 16384,
|
||||
"repo_path": "bios/Microsoft/MSX/MSX2EXT.ROM",
|
||||
"cores": [
|
||||
"clk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "MSX/fmpac.rom",
|
||||
"sha1": "9d789166e3caf28e4742fe933d962e99618c633d",
|
||||
@@ -84703,6 +84773,36 @@
|
||||
"mesen"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "dmg_boot.bin",
|
||||
"sha1": "1db57a1e8b6e4096f811587f9eab0c6675fd9755",
|
||||
"sha256": "6f64da4cecd7e54e2f928eb3e3ba7810a7a567d0d247cc71737d1771e073a916",
|
||||
"size": 256,
|
||||
"repo_path": "bios/Nintendo/Game Boy/dmg_boot.bin",
|
||||
"cores": [
|
||||
"mesen-s"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cgb_boot.bin",
|
||||
"sha1": "80e3ee82416c1a74827a5459fde32f04bf51a1f6",
|
||||
"sha256": "119470a9cf9f0d85f0c7ba5a6cadffeec07eb9862719086d1a9808189605e388",
|
||||
"size": 2304,
|
||||
"repo_path": "bios/Nintendo/Game Boy Color/cgb_boot.bin",
|
||||
"cores": [
|
||||
"mesen-s"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "Mupen64plus/IPL.n64",
|
||||
"sha1": "bf861922dcb78c316360e3e742f4f70ff63c9bc3",
|
||||
"sha256": "806400ec0df94b0755de6c5b8249d6b6a9866124c5ddbdac198bde22499bfb8b",
|
||||
"size": 4194304,
|
||||
"repo_path": "bios/Nintendo/64DD/64DD_IPL_v12_JPN.bin",
|
||||
"cores": [
|
||||
"mupen64plus_next"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "Mupen64plus/font.ttf",
|
||||
"sha1": "1d7f6fa0c34e3d50be52c16a0816bebac309fe41",
|
||||
@@ -89473,6 +89573,86 @@
|
||||
"px68k"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/n88.rom",
|
||||
"sha1": "3518193b8207bdebf22c1380c2db8c554baff329",
|
||||
"sha256": "d76f9118f9994eb391a0dc9c1fbfbfa2e9bc584a65da75f3ba7065d3c03cab76",
|
||||
"size": 32768,
|
||||
"repo_path": "bios/NEC/PC-98/n88.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/n88_0.rom",
|
||||
"sha1": "d1ae642aed4f0584eeb81ff50180db694e5101d4",
|
||||
"sha256": "8b584cd9143a670ddfce657ca65067c86f6be1db757a3085f48b1479ac6e8cf3",
|
||||
"size": 8192,
|
||||
"repo_path": "bios/NEC/PC-98/n88_0.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/n88_1.rom",
|
||||
"sha1": "4ae4d37409ff99411a623da9f6a44192170a854e",
|
||||
"sha256": "810936574cdf68849822a1570046091f0bad0590dee3635dbc89a4cc4e099555",
|
||||
"size": 8192,
|
||||
"repo_path": "bios/NEC/PC-98/n88_1.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/n88_2.rom",
|
||||
"sha1": "e94278682ef9e9bbb82201f72c50382748dcea2a",
|
||||
"sha256": "7d361b1bfd8523cd580bd9ca73902154556fbf45f60f0f539beef397d9462646",
|
||||
"size": 8192,
|
||||
"repo_path": "bios/NEC/PC-98/n88_2.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/n88_3.rom",
|
||||
"sha1": "af452aed79b072c4d17985830b7c5dca64d4b412",
|
||||
"sha256": "3dc431af6e0c724582a3aa49e5c22cb9cc99d8298a4cce500e62bbaf1bc4bffb",
|
||||
"size": 8192,
|
||||
"repo_path": "bios/NEC/PC-98/n88_3.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/n88n.rom",
|
||||
"sha1": "063609dd518c124a4fc9ba35d1bae35771666a34",
|
||||
"sha256": "4db0b25bf25fd063b08fde9e76d7504440cd322a8ab6b72c4ee7d8ea98ce0839",
|
||||
"size": 32768,
|
||||
"repo_path": "bios/NEC/PC-98/n88n.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/n88sub.rom",
|
||||
"sha1": "bb7103a0818850a039c67ff666a31ce49a8d516f",
|
||||
"sha256": "9dc63118728a171ce2ed437ad38ef280b8b7bcf4a5a95fedf18946501b010456",
|
||||
"size": 2048,
|
||||
"repo_path": "bios/NEC/PC-98/N88SUB.ROM",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/n88knj1.rom",
|
||||
"sha1": "82e11a177af6a5091dd67f50a2f4bafda84d6556",
|
||||
"sha256": "7608040cffb1951e5cc567abb63f75b5746777a1ba96196c1b75606b793bb4bb",
|
||||
"size": 131072,
|
||||
"repo_path": "bios/NEC/PC-88/KANJI1.ROM",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/n88knj2.rom",
|
||||
"sha1": "b86b932cb4ce34472e0fe0f01a4a4350e15dcb53",
|
||||
@@ -89493,6 +89673,16 @@
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/font.rom",
|
||||
"sha1": "84b90f69671d4b72e8f219e1fe7cd667e976cf7f",
|
||||
"sha256": "3227087599afd9304f260cdeb3bf1533e39e425cf444ceb3c24f55f561e71a0c",
|
||||
"size": 2048,
|
||||
"repo_path": "bios/NEC/PC-88/font.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/font2.rom",
|
||||
"sha1": "0a056b6d28b4b9e29a2d255bdaa25c24f176cf1e",
|
||||
@@ -126021,16 +126211,6 @@
|
||||
"fbneo"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "Acorn/basic.rom",
|
||||
"sha1": "4a7393f3a45ea309f744441c16723e2ef447a281",
|
||||
"sha256": "45bd55dc0f6f0f8f1fe9e2481de7def206565eec8f600ba3068b849ca4132079",
|
||||
"size": 16384,
|
||||
"repo_path": "bios/Acorn/BBC Micro/basic2.rom",
|
||||
"cores": [
|
||||
"clk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "Triforce/segaboot.gcm",
|
||||
"sha1": "7297a11be143011ceb75a0720f96855003415a5d",
|
||||
|
||||
+65
-5
@@ -5,7 +5,7 @@
|
||||
"platform": "bizhawk",
|
||||
"display_name": "BizHawk",
|
||||
"version": "1.0",
|
||||
"generated": "2026-10-09T20:41:14Z",
|
||||
"generated": "2026-10-06T10:11:17Z",
|
||||
"base_destination": "Firmware",
|
||||
"detect": [
|
||||
{
|
||||
@@ -20,10 +20,10 @@
|
||||
}
|
||||
],
|
||||
"standalone_copies": [],
|
||||
"total_files": 948,
|
||||
"total_size": 2585741346,
|
||||
"pack_files": 950,
|
||||
"pack_size": 2585741346,
|
||||
"total_files": 954,
|
||||
"total_size": 2588624930,
|
||||
"pack_files": 956,
|
||||
"pack_size": 2588624930,
|
||||
"total_omitted": 0,
|
||||
"omitted_files": [],
|
||||
"files": [
|
||||
@@ -7839,6 +7839,16 @@
|
||||
"puae"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "kick34005.A500",
|
||||
"sha1": "891e9a547772fe0c6c19b610baf8bc4ea7fcb785",
|
||||
"sha256": "ee05862d8102a08436ac4056da7d549db31625c7d47b24dfb7b3c9a5c113ca53",
|
||||
"size": 262144,
|
||||
"repo_path": "bios/Commodore/Amiga/Kickstart-v1.3-rev34.5-1987-Commodore-A500-A1000-A2000-CDTV.rom",
|
||||
"cores": [
|
||||
"puae"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "kick37175.A500",
|
||||
"sha1": "c5839f5cb98a7a8947065c3ed2f14f5f42e334a1",
|
||||
@@ -7849,6 +7859,16 @@
|
||||
"puae"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "kick37350.A600",
|
||||
"sha1": "02843c4253bbd29aba535b0aa3bd9a85034ecde4",
|
||||
"sha256": "8d57d6e9d976df42d91ff9d10a5eedaede1a00e3b4b34cad750ca5ff7229f28a",
|
||||
"size": 524288,
|
||||
"repo_path": "bios/Commodore/Amiga/amiga-os-205-a600.rom",
|
||||
"cores": [
|
||||
"puae"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "kick40063.A600",
|
||||
"sha1": "3b7f1493b27e212830f989f26ca76c02049f09ca",
|
||||
@@ -7869,6 +7889,16 @@
|
||||
"puae"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "kick40068.A1200",
|
||||
"sha1": "e21545723fe8374e91342617604f1b3d703094f1",
|
||||
"sha256": "6d43840d4099a74170ea0f0425b6257c3891ebcaa39c4d1840075a9ab22b5707",
|
||||
"size": 524288,
|
||||
"repo_path": "bios/Commodore/Amiga/amiga-os-310-a1200.rom",
|
||||
"cores": [
|
||||
"puae"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "kick39106.A4000",
|
||||
"sha1": "f0b4e9e29e12218c2d5bd7020e4e785297d91fd7",
|
||||
@@ -7879,6 +7909,16 @@
|
||||
"puae"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "kick40068.A4000",
|
||||
"sha1": "5fe04842d04a489720f0f4bb0e46948199406f49",
|
||||
"sha256": "71236ed62c85394a3ad7d0d8d65405bac7a10083e06ba6b82120dacc7a00fe9a",
|
||||
"size": 524288,
|
||||
"repo_path": "bios/Commodore/Amiga/amiga-os-310-a4000.rom",
|
||||
"cores": [
|
||||
"puae"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "kick34005.CDTV",
|
||||
"sha1": "7ba40ffa17e500ed9fed041f3424bd81d9c907be",
|
||||
@@ -7889,6 +7929,26 @@
|
||||
"puae"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "kick40060.CD32",
|
||||
"sha1": "3525be8887f79b5929e017b42380a79edfee542d",
|
||||
"sha256": "3586355514947b5ab48c8755dc004fab94f188b19e7bce491c8f9693499e830b",
|
||||
"size": 524288,
|
||||
"repo_path": "bios/Commodore/Amiga/amiga-os-310-cd32.rom",
|
||||
"cores": [
|
||||
"puae"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "kick40060.CD32.ext",
|
||||
"sha1": "5bef3d628ce59cc02a66e6e4ae0da48f60e78f7f",
|
||||
"sha256": "d964826432559994ffe329f643fd4e42dbbff313362d9434945725d7119cae87",
|
||||
"size": 524288,
|
||||
"repo_path": "bios/Commodore/Amiga/amiga-ext-310-cd32.rom",
|
||||
"cores": [
|
||||
"puae"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "rom.key",
|
||||
"sha1": "801bbab32e3d37386dd1636d4f1d6fa7e21d5874",
|
||||
|
||||
+15
-5
@@ -5,7 +5,7 @@
|
||||
"platform": "emudeck",
|
||||
"display_name": "EmuDeck",
|
||||
"version": "1.0",
|
||||
"generated": "2026-10-09T20:41:14Z",
|
||||
"generated": "2026-10-06T10:11:17Z",
|
||||
"base_destination": "bios",
|
||||
"detect": [
|
||||
{
|
||||
@@ -250,10 +250,10 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"total_files": 618,
|
||||
"total_size": 3472900342,
|
||||
"pack_files": 620,
|
||||
"pack_size": 3472900342,
|
||||
"total_files": 619,
|
||||
"total_size": 3473031414,
|
||||
"pack_files": 621,
|
||||
"pack_size": 3473031414,
|
||||
"total_omitted": 0,
|
||||
"omitted_files": [],
|
||||
"files": [
|
||||
@@ -2191,6 +2191,16 @@
|
||||
"eden"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "dc/dc_nvmem.bin",
|
||||
"sha1": "72c65f2a11ee7b72b77b9945187b7a332632e14a",
|
||||
"sha256": "15a9bc3aa59fa998c03bfe0b1cf6cb1958c1660e8e72b3d7195b653f44038407",
|
||||
"size": 131072,
|
||||
"repo_path": "bios/Sega/Dreamcast/dc_nvmem.bin",
|
||||
"cores": [
|
||||
"flycast"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "dc/font.bin",
|
||||
"sha1": "94c6ff701f79742ee23c0a028ac0be5aead77c73",
|
||||
|
||||
+80
-22
@@ -5,7 +5,7 @@
|
||||
"platform": "lakka",
|
||||
"display_name": "Lakka",
|
||||
"version": "1.0",
|
||||
"generated": "2026-10-09T20:41:14Z",
|
||||
"generated": "2026-10-06T06:50:25Z",
|
||||
"base_destination": "system",
|
||||
"detect": [
|
||||
{
|
||||
@@ -16,10 +16,10 @@
|
||||
}
|
||||
],
|
||||
"standalone_copies": [],
|
||||
"total_files": 5861,
|
||||
"total_size": 6398490526,
|
||||
"pack_files": 8504,
|
||||
"pack_size": 6439023690,
|
||||
"total_files": 5867,
|
||||
"total_size": 6399839286,
|
||||
"pack_files": 8510,
|
||||
"pack_size": 6440372450,
|
||||
"total_omitted": 0,
|
||||
"omitted_files": [],
|
||||
"files": [
|
||||
@@ -1319,6 +1319,14 @@
|
||||
"repo_path": "bios/NEC/PC-98/n88knj2.rom",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/N88SUB.ROM",
|
||||
"sha1": "bb7103a0818850a039c67ff666a31ce49a8d516f",
|
||||
"sha256": "9dc63118728a171ce2ed437ad38ef280b8b7bcf4a5a95fedf18946501b010456",
|
||||
"size": 2048,
|
||||
"repo_path": "bios/NEC/PC-98/N88SUB.ROM",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/N88EXT0.ROM",
|
||||
"sha1": "d239c26ad7ac5efac6e947b0e9549b1534aa970d",
|
||||
@@ -1859,10 +1867,10 @@
|
||||
},
|
||||
{
|
||||
"dest": "BS-X.bin",
|
||||
"sha1": "604556b2e62860af18db5a77f2956ebc75450020",
|
||||
"sha256": "3ce321496edc5d77038de2034eb3fb354d7724afd0bc7fd0319f3eb5d57b984d",
|
||||
"sha1": "4891d739a8a8b67923681bad4fb67edab2e90e50",
|
||||
"sha256": "a49827b45ff9ac9cf5b4658190e1428e59251bc82d8a63d8e9e0f71e439f008f",
|
||||
"size": 1048576,
|
||||
"repo_path": "bios/Nintendo/SNES/Satellaview_BS-X.sfc",
|
||||
"repo_path": "bios/Nintendo/SNES/BS-X.bin",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
@@ -4107,10 +4115,10 @@
|
||||
},
|
||||
{
|
||||
"dest": "pcsx2/resources/GameIndex.yaml",
|
||||
"sha1": "72bfdf91fa11a86e2810a7c167cdcf11e20418a8",
|
||||
"sha256": "61c77b789ccde856096f024e4a3eef6891e15ca59c127e0dd13e762a7d338a7e",
|
||||
"size": 2690355,
|
||||
"repo_path": "bios/Other/armsx2/pcsx2/resources/GameIndex.yaml",
|
||||
"sha1": "7efb2ed721cf0c0f0677324e09be311a8337e793",
|
||||
"sha256": "3bf3c54d6f16d88d3669be698179bb8c2fb981ee5ce71bcb952719429a838cc1",
|
||||
"size": 2693579,
|
||||
"repo_path": "bios/Other/yaps2/GameIndex.yaml",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
@@ -4261,6 +4269,16 @@
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cd32.rom",
|
||||
"sha1": "3525be8887f79b5929e017b42380a79edfee542d",
|
||||
"sha256": "3586355514947b5ab48c8755dc004fab94f188b19e7bce491c8f9693499e830b",
|
||||
"size": 524288,
|
||||
"repo_path": "bios/Commodore/Amiga/amiga-os-310-cd32.rom",
|
||||
"cores": [
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cd32ext.rom",
|
||||
"sha1": "5bef3d628ce59cc02a66e6e4ae0da48f60e78f7f",
|
||||
@@ -4271,6 +4289,16 @@
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cdtv.rom",
|
||||
"sha1": "7ba40ffa17e500ed9fed041f3424bd81d9c907be",
|
||||
"sha256": "6e48bb768539e8dae352553e5fdfa860682edaf9e26b105fadd8e26103171765",
|
||||
"size": 262144,
|
||||
"repo_path": "bios/Commodore/Amiga/amiga-ext-130-cdtv.rom",
|
||||
"cores": [
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cdtvext.rom",
|
||||
"sha1": "7ba40ffa17e500ed9fed041f3424bd81d9c907be",
|
||||
@@ -25511,6 +25539,16 @@
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "SufamiTurbo.sfc",
|
||||
"sha1": "ef86ea192eed03d5c413fdbbfd46043be1d7a127",
|
||||
"sha256": "edacb453da14f825f05d1134d6035f4bf034e55f7cfb97c70c4ee107eabc7342",
|
||||
"size": 262144,
|
||||
"repo_path": "bios/Nintendo/SuFami Turbo/STBIOS.bin",
|
||||
"cores": [
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "ymf288_adpcm_rom.bin",
|
||||
"sha1": "50b6c3e288eaa12ad275d4f323267bb72b0445df",
|
||||
@@ -25541,6 +25579,26 @@
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "[BIOS] Super CD-ROM System (Japan) (v3.0).pce",
|
||||
"sha1": "79f5ff55dd10187c7fd7b8daab0b3ffbd1f56a2c",
|
||||
"sha256": "e11527b3b96ce112a037138988ca72fd117a6b0779c2480d9e03eaebece3d9ce",
|
||||
"size": 262144,
|
||||
"repo_path": "bios/NEC/PC Engine/syscard3.pce",
|
||||
"cores": [
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "[BIOS] Games Express CD Card (Japan).pce",
|
||||
"sha1": "014881a959e045e00f4db8f52955200865d40280",
|
||||
"sha256": "4b86bb96a48a4ca8375fc0109631d0b1d64f255a03b01de70594d40788ba6c3d",
|
||||
"size": 32768,
|
||||
"repo_path": "bios/NEC/PC Engine/gecard.pce",
|
||||
"cores": [
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "Satellaview/BSX0120-0.bin",
|
||||
"sha1": "f51c52bb26f2546fb167ab1084a56457475ff0a0",
|
||||
@@ -53027,6 +53085,16 @@
|
||||
"qemu"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/font.rom",
|
||||
"sha1": "84b90f69671d4b72e8f219e1fe7cd667e976cf7f",
|
||||
"sha256": "3227087599afd9304f260cdeb3bf1533e39e425cf444ceb3c24f55f561e71a0c",
|
||||
"size": 2048,
|
||||
"repo_path": "bios/NEC/PC-88/font.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/font2.rom",
|
||||
"sha1": "0a056b6d28b4b9e29a2d255bdaa25c24f176cf1e",
|
||||
@@ -56529,16 +56597,6 @@
|
||||
"nekop2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/font.rom",
|
||||
"sha1": "84b90f69671d4b72e8f219e1fe7cd667e976cf7f",
|
||||
"sha256": "3227087599afd9304f260cdeb3bf1533e39e425cf444ceb3c24f55f561e71a0c",
|
||||
"size": 2048,
|
||||
"repo_path": "bios/NEC/PC-88/font.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "scummvm/extra/MT32_CONTROL.ROM",
|
||||
"sha1": "b083518fffb7f66b03c23b7eb4f868e62dc5a987",
|
||||
|
||||
+95
-5
@@ -5,7 +5,7 @@
|
||||
"platform": "recalbox",
|
||||
"display_name": "Recalbox",
|
||||
"version": "1.0",
|
||||
"generated": "2026-10-09T20:41:14Z",
|
||||
"generated": "2026-10-06T06:50:25Z",
|
||||
"base_destination": "bios",
|
||||
"detect": [
|
||||
{
|
||||
@@ -237,10 +237,10 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"total_files": 3220,
|
||||
"total_size": 4192446979,
|
||||
"pack_files": 3222,
|
||||
"pack_size": 4192446979,
|
||||
"total_files": 3229,
|
||||
"total_size": 4193661699,
|
||||
"pack_files": 3231,
|
||||
"pack_size": 4193661699,
|
||||
"total_omitted": 1,
|
||||
"omitted_files": [
|
||||
{
|
||||
@@ -3383,6 +3383,16 @@
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cd32.rom",
|
||||
"sha1": "3525be8887f79b5929e017b42380a79edfee542d",
|
||||
"sha256": "3586355514947b5ab48c8755dc004fab94f188b19e7bce491c8f9693499e830b",
|
||||
"size": 524288,
|
||||
"repo_path": "bios/Commodore/Amiga/amiga-os-310-cd32.rom",
|
||||
"cores": [
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cd32ext.rom",
|
||||
"sha1": "5bef3d628ce59cc02a66e6e4ae0da48f60e78f7f",
|
||||
@@ -3393,6 +3403,16 @@
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cdtv.rom",
|
||||
"sha1": "7ba40ffa17e500ed9fed041f3424bd81d9c907be",
|
||||
"sha256": "6e48bb768539e8dae352553e5fdfa860682edaf9e26b105fadd8e26103171765",
|
||||
"size": 262144,
|
||||
"repo_path": "bios/Commodore/Amiga/amiga-ext-130-cdtv.rom",
|
||||
"cores": [
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cdtvext.rom",
|
||||
"sha1": "7ba40ffa17e500ed9fed041f3424bd81d9c907be",
|
||||
@@ -4093,6 +4113,16 @@
|
||||
"beetle_saturn"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bk/BASIC10.ROM",
|
||||
"sha1": "af7eae90c398ec12942a408fa5ac2722e331738c",
|
||||
"sha256": "24be347356715352fcab480ed1559302d47d598255980a32150eba1431b462b8",
|
||||
"size": 24576,
|
||||
"repo_path": "bios/Elektronika/BK/BASIC10.ROM",
|
||||
"cores": [
|
||||
"bk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bk/TERAK.ROM",
|
||||
"sha1": "273a9933b68a290c5aedcd6d69faa7b1d22c0344",
|
||||
@@ -21423,6 +21453,16 @@
|
||||
"fceumm"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "dc/dc_nvmem.bin",
|
||||
"sha1": "72c65f2a11ee7b72b77b9945187b7a332632e14a",
|
||||
"sha256": "15a9bc3aa59fa998c03bfe0b1cf6cb1958c1660e8e72b3d7195b653f44038407",
|
||||
"size": 131072,
|
||||
"repo_path": "bios/Sega/Dreamcast/dc_nvmem.bin",
|
||||
"cores": [
|
||||
"flycast"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "dc/font.bin",
|
||||
"sha1": "94c6ff701f79742ee23c0a028ac0be5aead77c73",
|
||||
@@ -21823,6 +21863,16 @@
|
||||
"fuse"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "colecovision.rom",
|
||||
"sha1": "45bedc4cbdeac66c7df59e9e599195c778d86a92",
|
||||
"sha256": "990bf1956f10207d8781b619eb74f89b00d921c8d45c95c334c16c8cceca09ad",
|
||||
"size": 8192,
|
||||
"repo_path": "bios/Coleco/ColecoVision/BIOS.col",
|
||||
"cores": [
|
||||
"gearcoleco"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "eos.rom",
|
||||
"sha1": "ad3c20ef444f10af7ae8eb75c81e500d9b1bba3d",
|
||||
@@ -21943,6 +21993,16 @@
|
||||
"gsplus"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "c600.rom",
|
||||
"sha1": "d4181c9f046aafc3fb326b381baac809d9e38d16",
|
||||
"sha256": "de1e3e035878bab43d0af8fe38f5839c527e9548647036598ee6fe7ec74d2a7d",
|
||||
"size": 256,
|
||||
"repo_path": "bios/Apple/Apple II/AppleIIe_DiskII.rom",
|
||||
"cores": [
|
||||
"gsplus"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "howard.o",
|
||||
"sha1": "bdca7bbb647066cf7555ce5d555ce09de4ebc41b",
|
||||
@@ -27983,6 +28043,16 @@
|
||||
"px68k"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/n88sub.rom",
|
||||
"sha1": "bb7103a0818850a039c67ff666a31ce49a8d516f",
|
||||
"sha256": "9dc63118728a171ce2ed437ad38ef280b8b7bcf4a5a95fedf18946501b010456",
|
||||
"size": 2048,
|
||||
"repo_path": "bios/NEC/PC-98/N88SUB.ROM",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/n88knj2.rom",
|
||||
"sha1": "b86b932cb4ce34472e0fe0f01a4a4350e15dcb53",
|
||||
@@ -28863,6 +28933,26 @@
|
||||
"virtualjaguar"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "jagboot.rom",
|
||||
"sha1": "af643900fea84234e08b616a6d27f03f0a61db96",
|
||||
"sha256": "f0bd29599b3dfca7f5620c011256c1dc45c9d82c428a318f965c2ba3ded2e9f9",
|
||||
"size": 131072,
|
||||
"repo_path": "bios/Atari/Jaguar/boot.rom",
|
||||
"cores": [
|
||||
"virtualjaguar"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "jagboot_m.rom",
|
||||
"sha1": "af643900fea84234e08b616a6d27f03f0a61db96",
|
||||
"sha256": "f0bd29599b3dfca7f5620c011256c1dc45c9d82c428a318f965c2ba3ded2e9f9",
|
||||
"size": 131072,
|
||||
"repo_path": "bios/Atari/Jaguar/boot.rom",
|
||||
"cores": [
|
||||
"virtualjaguar"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "pinmame/roms/gts1.zip",
|
||||
"sha1": "d4b0c8277c187a92279e2d2b71fc91b72d581257",
|
||||
|
||||
+80
-22
@@ -5,7 +5,7 @@
|
||||
"platform": "retroarch",
|
||||
"display_name": "RetroArch",
|
||||
"version": "1.0",
|
||||
"generated": "2026-10-09T20:41:14Z",
|
||||
"generated": "2026-10-06T06:50:25Z",
|
||||
"base_destination": "system",
|
||||
"detect": [
|
||||
{
|
||||
@@ -255,10 +255,10 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"total_files": 5861,
|
||||
"total_size": 6398490526,
|
||||
"pack_files": 8504,
|
||||
"pack_size": 6439023690,
|
||||
"total_files": 5867,
|
||||
"total_size": 6399839286,
|
||||
"pack_files": 8510,
|
||||
"pack_size": 6440372450,
|
||||
"total_omitted": 0,
|
||||
"omitted_files": [],
|
||||
"files": [
|
||||
@@ -1558,6 +1558,14 @@
|
||||
"repo_path": "bios/NEC/PC-98/n88knj2.rom",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/N88SUB.ROM",
|
||||
"sha1": "bb7103a0818850a039c67ff666a31ce49a8d516f",
|
||||
"sha256": "9dc63118728a171ce2ed437ad38ef280b8b7bcf4a5a95fedf18946501b010456",
|
||||
"size": 2048,
|
||||
"repo_path": "bios/NEC/PC-98/N88SUB.ROM",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/N88EXT0.ROM",
|
||||
"sha1": "d239c26ad7ac5efac6e947b0e9549b1534aa970d",
|
||||
@@ -2098,10 +2106,10 @@
|
||||
},
|
||||
{
|
||||
"dest": "BS-X.bin",
|
||||
"sha1": "604556b2e62860af18db5a77f2956ebc75450020",
|
||||
"sha256": "3ce321496edc5d77038de2034eb3fb354d7724afd0bc7fd0319f3eb5d57b984d",
|
||||
"sha1": "4891d739a8a8b67923681bad4fb67edab2e90e50",
|
||||
"sha256": "a49827b45ff9ac9cf5b4658190e1428e59251bc82d8a63d8e9e0f71e439f008f",
|
||||
"size": 1048576,
|
||||
"repo_path": "bios/Nintendo/SNES/Satellaview_BS-X.sfc",
|
||||
"repo_path": "bios/Nintendo/SNES/BS-X.bin",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
@@ -4346,10 +4354,10 @@
|
||||
},
|
||||
{
|
||||
"dest": "pcsx2/resources/GameIndex.yaml",
|
||||
"sha1": "72bfdf91fa11a86e2810a7c167cdcf11e20418a8",
|
||||
"sha256": "61c77b789ccde856096f024e4a3eef6891e15ca59c127e0dd13e762a7d338a7e",
|
||||
"size": 2690355,
|
||||
"repo_path": "bios/Other/armsx2/pcsx2/resources/GameIndex.yaml",
|
||||
"sha1": "7efb2ed721cf0c0f0677324e09be311a8337e793",
|
||||
"sha256": "3bf3c54d6f16d88d3669be698179bb8c2fb981ee5ce71bcb952719429a838cc1",
|
||||
"size": 2693579,
|
||||
"repo_path": "bios/Other/yaps2/GameIndex.yaml",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
@@ -4500,6 +4508,16 @@
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cd32.rom",
|
||||
"sha1": "3525be8887f79b5929e017b42380a79edfee542d",
|
||||
"sha256": "3586355514947b5ab48c8755dc004fab94f188b19e7bce491c8f9693499e830b",
|
||||
"size": 524288,
|
||||
"repo_path": "bios/Commodore/Amiga/amiga-os-310-cd32.rom",
|
||||
"cores": [
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cd32ext.rom",
|
||||
"sha1": "5bef3d628ce59cc02a66e6e4ae0da48f60e78f7f",
|
||||
@@ -4510,6 +4528,16 @@
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cdtv.rom",
|
||||
"sha1": "7ba40ffa17e500ed9fed041f3424bd81d9c907be",
|
||||
"sha256": "6e48bb768539e8dae352553e5fdfa860682edaf9e26b105fadd8e26103171765",
|
||||
"size": 262144,
|
||||
"repo_path": "bios/Commodore/Amiga/amiga-ext-130-cdtv.rom",
|
||||
"cores": [
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cdtvext.rom",
|
||||
"sha1": "7ba40ffa17e500ed9fed041f3424bd81d9c907be",
|
||||
@@ -25750,6 +25778,16 @@
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "SufamiTurbo.sfc",
|
||||
"sha1": "ef86ea192eed03d5c413fdbbfd46043be1d7a127",
|
||||
"sha256": "edacb453da14f825f05d1134d6035f4bf034e55f7cfb97c70c4ee107eabc7342",
|
||||
"size": 262144,
|
||||
"repo_path": "bios/Nintendo/SuFami Turbo/STBIOS.bin",
|
||||
"cores": [
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "ymf288_adpcm_rom.bin",
|
||||
"sha1": "50b6c3e288eaa12ad275d4f323267bb72b0445df",
|
||||
@@ -25780,6 +25818,26 @@
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "[BIOS] Super CD-ROM System (Japan) (v3.0).pce",
|
||||
"sha1": "79f5ff55dd10187c7fd7b8daab0b3ffbd1f56a2c",
|
||||
"sha256": "e11527b3b96ce112a037138988ca72fd117a6b0779c2480d9e03eaebece3d9ce",
|
||||
"size": 262144,
|
||||
"repo_path": "bios/NEC/PC Engine/syscard3.pce",
|
||||
"cores": [
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "[BIOS] Games Express CD Card (Japan).pce",
|
||||
"sha1": "014881a959e045e00f4db8f52955200865d40280",
|
||||
"sha256": "4b86bb96a48a4ca8375fc0109631d0b1d64f255a03b01de70594d40788ba6c3d",
|
||||
"size": 32768,
|
||||
"repo_path": "bios/NEC/PC Engine/gecard.pce",
|
||||
"cores": [
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "Satellaview/BSX0120-0.bin",
|
||||
"sha1": "f51c52bb26f2546fb167ab1084a56457475ff0a0",
|
||||
@@ -53266,6 +53324,16 @@
|
||||
"qemu"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/font.rom",
|
||||
"sha1": "84b90f69671d4b72e8f219e1fe7cd667e976cf7f",
|
||||
"sha256": "3227087599afd9304f260cdeb3bf1533e39e425cf444ceb3c24f55f561e71a0c",
|
||||
"size": 2048,
|
||||
"repo_path": "bios/NEC/PC-88/font.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/font2.rom",
|
||||
"sha1": "0a056b6d28b4b9e29a2d255bdaa25c24f176cf1e",
|
||||
@@ -56768,16 +56836,6 @@
|
||||
"nekop2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/font.rom",
|
||||
"sha1": "84b90f69671d4b72e8f219e1fe7cd667e976cf7f",
|
||||
"sha256": "3227087599afd9304f260cdeb3bf1533e39e425cf444ceb3c24f55f561e71a0c",
|
||||
"size": 2048,
|
||||
"repo_path": "bios/NEC/PC-88/font.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "scummvm/extra/MT32_CONTROL.ROM",
|
||||
"sha1": "b083518fffb7f66b03c23b7eb4f868e62dc5a987",
|
||||
|
||||
+125
-10
@@ -5,14 +5,9 @@
|
||||
"platform": "retrobat",
|
||||
"display_name": "RetroBat",
|
||||
"version": "1.0",
|
||||
"generated": "2026-10-09T20:41:14Z",
|
||||
"generated": "2026-10-06T10:11:17Z",
|
||||
"base_destination": "bios",
|
||||
"detect": [
|
||||
{
|
||||
"os": "windows",
|
||||
"method": "path_exists",
|
||||
"path": "C:\\RetroBat\\bios"
|
||||
},
|
||||
{
|
||||
"os": "windows",
|
||||
"method": "path_exists",
|
||||
@@ -241,10 +236,10 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"total_files": 12397,
|
||||
"total_size": 8504927168,
|
||||
"pack_files": 12399,
|
||||
"pack_size": 8504927168,
|
||||
"total_files": 12409,
|
||||
"total_size": 8505437376,
|
||||
"pack_files": 12411,
|
||||
"pack_size": 8505437376,
|
||||
"total_omitted": 30,
|
||||
"omitted_files": [
|
||||
{
|
||||
@@ -41513,6 +41508,16 @@
|
||||
"clk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "MSX/MSX.ROM",
|
||||
"sha1": "409e82adac40f6bdd18eb6c84e8b2fbdc7fb5498",
|
||||
"sha256": "999564a371dd2fdf7fbe8d853e82a68d557c27b7d87417639b2fa17704b83f78",
|
||||
"size": 32768,
|
||||
"repo_path": "bios/Microsoft/MSX/MSX.ROM",
|
||||
"cores": [
|
||||
"clk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "MSX/msx-japanese.rom",
|
||||
"sha1": "302afb5d8be26c758309ca3df611ae69cced2821",
|
||||
@@ -41553,6 +41558,26 @@
|
||||
"clk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "MSX/MSX2.ROM",
|
||||
"sha1": "6103b39f1e38d1aa2d84b1c3219c44f1abb5436e",
|
||||
"sha256": "4bc4ae85ca5f28246cd3e7b7e017d298ddd375603657f84ef2c7954bc2d9b919",
|
||||
"size": 32768,
|
||||
"repo_path": "bios/Microsoft/MSX/MSX2.ROM",
|
||||
"cores": [
|
||||
"clk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "MSX/MSX2EXT.ROM",
|
||||
"sha1": "5c1f9c7fb655e43d38e5dd1fcc6b942b2ff68b02",
|
||||
"sha256": "6c6f421a10c428d960b7ecc990f99af1c638147f747bddca7b0bf0e2ab738300",
|
||||
"size": 16384,
|
||||
"repo_path": "bios/Microsoft/MSX/MSX2EXT.ROM",
|
||||
"cores": [
|
||||
"clk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "Oric/basic10.rom",
|
||||
"sha1": "333116e6884d85aaa4dfc7578a91cceeea66d016",
|
||||
@@ -58031,6 +58056,16 @@
|
||||
"fceumm"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "dc/dc_nvmem.bin",
|
||||
"sha1": "72c65f2a11ee7b72b77b9945187b7a332632e14a",
|
||||
"sha256": "15a9bc3aa59fa998c03bfe0b1cf6cb1958c1660e8e72b3d7195b653f44038407",
|
||||
"size": 131072,
|
||||
"repo_path": "bios/Sega/Dreamcast/dc_nvmem.bin",
|
||||
"cores": [
|
||||
"flycast"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "dc/font.bin",
|
||||
"sha1": "94c6ff701f79742ee23c0a028ac0be5aead77c73",
|
||||
@@ -58561,6 +58596,26 @@
|
||||
"genesis_plus_gx"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "ROM",
|
||||
"sha1": "e4fc7560b69d062cb2da5b1ffbe11cd1ca03cc37",
|
||||
"sha256": "34cd454c6201bfd26839d6ace2ff6b1231e09d6279e49b4890311756dc3825fb",
|
||||
"size": 131072,
|
||||
"repo_path": "bios/Apple/Apple IIGS/ROM",
|
||||
"cores": [
|
||||
"gsplus"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "c600.rom",
|
||||
"sha1": "d4181c9f046aafc3fb326b381baac809d9e38d16",
|
||||
"sha256": "de1e3e035878bab43d0af8fe38f5839c527e9548647036598ee6fe7ec74d2a7d",
|
||||
"size": 256,
|
||||
"repo_path": "bios/Apple/Apple II/AppleIIe_DiskII.rom",
|
||||
"cores": [
|
||||
"gsplus"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "gzdoom.pk3",
|
||||
"sha1": "810ed9968c5b3bdc2c348c9bc76e9f80fbe31308",
|
||||
@@ -86447,6 +86502,46 @@
|
||||
"px68k"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/n88_0.rom",
|
||||
"sha1": "d1ae642aed4f0584eeb81ff50180db694e5101d4",
|
||||
"sha256": "8b584cd9143a670ddfce657ca65067c86f6be1db757a3085f48b1479ac6e8cf3",
|
||||
"size": 8192,
|
||||
"repo_path": "bios/NEC/PC-98/n88_0.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/n88_1.rom",
|
||||
"sha1": "4ae4d37409ff99411a623da9f6a44192170a854e",
|
||||
"sha256": "810936574cdf68849822a1570046091f0bad0590dee3635dbc89a4cc4e099555",
|
||||
"size": 8192,
|
||||
"repo_path": "bios/NEC/PC-98/n88_1.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/n88_2.rom",
|
||||
"sha1": "e94278682ef9e9bbb82201f72c50382748dcea2a",
|
||||
"sha256": "7d361b1bfd8523cd580bd9ca73902154556fbf45f60f0f539beef397d9462646",
|
||||
"size": 8192,
|
||||
"repo_path": "bios/NEC/PC-98/n88_2.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/n88_3.rom",
|
||||
"sha1": "af452aed79b072c4d17985830b7c5dca64d4b412",
|
||||
"sha256": "3dc431af6e0c724582a3aa49e5c22cb9cc99d8298a4cce500e62bbaf1bc4bffb",
|
||||
"size": 8192,
|
||||
"repo_path": "bios/NEC/PC-98/n88_3.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/n88knj1.rom",
|
||||
"sha1": "82e11a177af6a5091dd67f50a2f4bafda84d6556",
|
||||
@@ -86477,6 +86572,16 @@
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/font.rom",
|
||||
"sha1": "84b90f69671d4b72e8f219e1fe7cd667e976cf7f",
|
||||
"sha256": "3227087599afd9304f260cdeb3bf1533e39e425cf444ceb3c24f55f561e71a0c",
|
||||
"size": 2048,
|
||||
"repo_path": "bios/NEC/PC-88/font.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/font2.rom",
|
||||
"sha1": "0a056b6d28b4b9e29a2d255bdaa25c24f176cf1e",
|
||||
@@ -120507,6 +120612,16 @@
|
||||
"virtualjaguar"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "jagboot.rom",
|
||||
"sha1": "af643900fea84234e08b616a6d27f03f0a61db96",
|
||||
"sha256": "f0bd29599b3dfca7f5620c011256c1dc45c9d82c428a318f965c2ba3ded2e9f9",
|
||||
"size": 131072,
|
||||
"repo_path": "bios/Atari/Jaguar/boot.rom",
|
||||
"cores": [
|
||||
"virtualjaguar"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "jagboot_m.rom",
|
||||
"sha1": "af643900fea84234e08b616a6d27f03f0a61db96",
|
||||
|
||||
+255
-95
@@ -5,7 +5,7 @@
|
||||
"platform": "retrodeck",
|
||||
"display_name": "RetroDECK",
|
||||
"version": "1.0",
|
||||
"generated": "2026-10-09T20:41:14Z",
|
||||
"generated": "2026-10-06T10:11:17Z",
|
||||
"base_destination": "",
|
||||
"detect": [
|
||||
{
|
||||
@@ -237,10 +237,10 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"total_files": 7791,
|
||||
"total_size": 8622202286,
|
||||
"pack_files": 7793,
|
||||
"pack_size": 8622202286,
|
||||
"total_files": 7807,
|
||||
"total_size": 8628195246,
|
||||
"pack_files": 7809,
|
||||
"pack_size": 8628195246,
|
||||
"total_omitted": 1,
|
||||
"omitted_files": [
|
||||
{
|
||||
@@ -16371,6 +16371,16 @@
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/cd32.rom",
|
||||
"sha1": "3525be8887f79b5929e017b42380a79edfee542d",
|
||||
"sha256": "3586355514947b5ab48c8755dc004fab94f188b19e7bce491c8f9693499e830b",
|
||||
"size": 524288,
|
||||
"repo_path": "bios/Commodore/Amiga/amiga-os-310-cd32.rom",
|
||||
"cores": [
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/cd32ext.rom",
|
||||
"sha1": "5bef3d628ce59cc02a66e6e4ae0da48f60e78f7f",
|
||||
@@ -16381,6 +16391,16 @@
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/cdtv.rom",
|
||||
"sha1": "7ba40ffa17e500ed9fed041f3424bd81d9c907be",
|
||||
"sha256": "6e48bb768539e8dae352553e5fdfa860682edaf9e26b105fadd8e26103171765",
|
||||
"size": 262144,
|
||||
"repo_path": "bios/Commodore/Amiga/amiga-ext-130-cdtv.rom",
|
||||
"cores": [
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/cdtvext.rom",
|
||||
"sha1": "7ba40ffa17e500ed9fed041f3424bd81d9c907be",
|
||||
@@ -18231,6 +18251,16 @@
|
||||
"clk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/MSX/MSX.ROM",
|
||||
"sha1": "409e82adac40f6bdd18eb6c84e8b2fbdc7fb5498",
|
||||
"sha256": "999564a371dd2fdf7fbe8d853e82a68d557c27b7d87417639b2fa17704b83f78",
|
||||
"size": 32768,
|
||||
"repo_path": "bios/Microsoft/MSX/MSX.ROM",
|
||||
"cores": [
|
||||
"clk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/MSX/msx-japanese.rom",
|
||||
"sha1": "302afb5d8be26c758309ca3df611ae69cced2821",
|
||||
@@ -18261,6 +18291,26 @@
|
||||
"clk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/MSX/MSX2.ROM",
|
||||
"sha1": "6103b39f1e38d1aa2d84b1c3219c44f1abb5436e",
|
||||
"sha256": "4bc4ae85ca5f28246cd3e7b7e017d298ddd375603657f84ef2c7954bc2d9b919",
|
||||
"size": 32768,
|
||||
"repo_path": "bios/Microsoft/MSX/MSX2.ROM",
|
||||
"cores": [
|
||||
"clk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/MSX/MSX2EXT.ROM",
|
||||
"sha1": "5c1f9c7fb655e43d38e5dd1fcc6b942b2ff68b02",
|
||||
"sha256": "6c6f421a10c428d960b7ecc990f99af1c638147f747bddca7b0bf0e2ab738300",
|
||||
"size": 16384,
|
||||
"repo_path": "bios/Microsoft/MSX/MSX2EXT.ROM",
|
||||
"cores": [
|
||||
"clk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/MSX/fmpac.rom",
|
||||
"sha1": "9d789166e3caf28e4742fe933d962e99618c633d",
|
||||
@@ -31789,6 +31839,16 @@
|
||||
"fceumm"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/dc/dc_nvmem.bin",
|
||||
"sha1": "72c65f2a11ee7b72b77b9945187b7a332632e14a",
|
||||
"sha256": "15a9bc3aa59fa998c03bfe0b1cf6cb1958c1660e8e72b3d7195b653f44038407",
|
||||
"size": 131072,
|
||||
"repo_path": "bios/Sega/Dreamcast/dc_nvmem.bin",
|
||||
"cores": [
|
||||
"flycast"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/dc/font.bin",
|
||||
"sha1": "94c6ff701f79742ee23c0a028ac0be5aead77c73",
|
||||
@@ -32509,6 +32569,26 @@
|
||||
"geolith"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/ROM",
|
||||
"sha1": "e4fc7560b69d062cb2da5b1ffbe11cd1ca03cc37",
|
||||
"sha256": "34cd454c6201bfd26839d6ace2ff6b1231e09d6279e49b4890311756dc3825fb",
|
||||
"size": 131072,
|
||||
"repo_path": "bios/Apple/Apple IIGS/ROM",
|
||||
"cores": [
|
||||
"gsplus"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/c600.rom",
|
||||
"sha1": "d4181c9f046aafc3fb326b381baac809d9e38d16",
|
||||
"sha256": "de1e3e035878bab43d0af8fe38f5839c527e9548647036598ee6fe7ec74d2a7d",
|
||||
"size": 256,
|
||||
"repo_path": "bios/Apple/Apple II/AppleIIe_DiskII.rom",
|
||||
"cores": [
|
||||
"gsplus"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/gzdoom.pk3",
|
||||
"sha1": "810ed9968c5b3bdc2c348c9bc76e9f80fbe31308",
|
||||
@@ -38169,6 +38249,16 @@
|
||||
"mesen"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/sgb_boot.bin",
|
||||
"sha1": "369e6eb5e0c975eaa52a4a3f6ee07b2a3c3c16de",
|
||||
"sha256": "b60d493a7944ccf74c81f1e7b6bf38c2c7029e296648ea0e74cb22b10dd1fcb8",
|
||||
"size": 256,
|
||||
"repo_path": "bios/Nintendo/Super Game Boy/sgb_boot.bin",
|
||||
"cores": [
|
||||
"mesen-s"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/dsp1.rom",
|
||||
"sha1": "188d471fefea71eb53f0ee7064697ff0971b1014",
|
||||
@@ -38249,6 +38339,16 @@
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/SufamiTurbo.sfc",
|
||||
"sha1": "ef86ea192eed03d5c413fdbbfd46043be1d7a127",
|
||||
"sha256": "edacb453da14f825f05d1134d6035f4bf034e55f7cfb97c70c4ee107eabc7342",
|
||||
"size": 262144,
|
||||
"repo_path": "bios/Nintendo/SuFami Turbo/STBIOS.bin",
|
||||
"cores": [
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/ymf288_adpcm_rom.bin",
|
||||
"sha1": "50b6c3e288eaa12ad275d4f323267bb72b0445df",
|
||||
@@ -38279,6 +38379,16 @@
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/[BIOS] Super CD-ROM System (Japan) (v3.0).pce",
|
||||
"sha1": "79f5ff55dd10187c7fd7b8daab0b3ffbd1f56a2c",
|
||||
"sha256": "e11527b3b96ce112a037138988ca72fd117a6b0779c2480d9e03eaebece3d9ce",
|
||||
"size": 262144,
|
||||
"repo_path": "bios/NEC/PC Engine/syscard3.pce",
|
||||
"cores": [
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/[BIOS] Games Express CD Card (Japan).pce",
|
||||
"sha1": "014881a959e045e00f4db8f52955200865d40280",
|
||||
@@ -59419,6 +59529,16 @@
|
||||
"mu"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/Mupen64plus/IPL.n64",
|
||||
"sha1": "bf861922dcb78c316360e3e742f4f70ff63c9bc3",
|
||||
"sha256": "806400ec0df94b0755de6c5b8249d6b6a9866124c5ddbdac198bde22499bfb8b",
|
||||
"size": 4194304,
|
||||
"repo_path": "bios/Nintendo/64DD/64DD_IPL_v12_JPN.bin",
|
||||
"cores": [
|
||||
"mupen64plus_next"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/Mupen64plus/font.ttf",
|
||||
"sha1": "1d7f6fa0c34e3d50be52c16a0816bebac309fe41",
|
||||
@@ -60229,6 +60349,76 @@
|
||||
"noods"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/np2kai/2608_bd.wav",
|
||||
"sha1": "0a56c142ef40cec50f3ee56a6e42d0029c9e2818",
|
||||
"sha256": "802fb0ce40878e98335fc56d11eff6382982e9f09ebc83055b0a469df13f7900",
|
||||
"size": 19192,
|
||||
"repo_path": "bios/NEC/PC-98/2608_bd.wav",
|
||||
"cores": [
|
||||
"np2kai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/np2kai/2608_sd.wav",
|
||||
"sha1": "3c79663ef74c0b0439d13351326eb1c52a657008",
|
||||
"sha256": "e5877cd1e4821cbbc9a450491a189032c82906c05f7fc9f2e48ee0d06b3ad18c",
|
||||
"size": 15558,
|
||||
"repo_path": "bios/NEC/PC-98/2608_sd.wav",
|
||||
"cores": [
|
||||
"np2kai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/np2kai/2608_top.wav",
|
||||
"sha1": "aa4a8f766a86b830687d5083fd3b9db0652f46fc",
|
||||
"sha256": "d7bcf2da99a09589096e9bcee98da43303c588cca4e89b1a263adace52038cd3",
|
||||
"size": 57016,
|
||||
"repo_path": "bios/NEC/PC-98/2608_top.wav",
|
||||
"cores": [
|
||||
"np2kai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/np2kai/2608_hh.wav",
|
||||
"sha1": "12f676cef249b82480b6f19c454e234b435ca7b6",
|
||||
"sha256": "216c81ff9a84af0dfbf21dd8df6827c25fca9fdeefe9bc8426f7a00d6ff2264e",
|
||||
"size": 36722,
|
||||
"repo_path": "bios/NEC/PC-98/2608_hh.wav",
|
||||
"cores": [
|
||||
"np2kai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/np2kai/2608_tom.wav",
|
||||
"sha1": "9513fb4a3f41e75a972a273a5104cbd834c1e2c5",
|
||||
"sha256": "8e2c9ccfa8caff03dbd0d945d7c1d4de15292817409fe6fabda07f008f97dbc6",
|
||||
"size": 23092,
|
||||
"repo_path": "bios/NEC/PC-98/2608_tom.wav",
|
||||
"cores": [
|
||||
"np2kai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/np2kai/2608_rim.wav",
|
||||
"sha1": "c65592330c9dd84011151daed52f9aec926b7e56",
|
||||
"sha256": "6f7d1996001c74452a9ca68b41a2767925b5b947c699096a8ad15bbc658af5a3",
|
||||
"size": 5288,
|
||||
"repo_path": "bios/NEC/PC-98/2608_rim.wav",
|
||||
"cores": [
|
||||
"np2kai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/np2kai/ide.rom",
|
||||
"sha1": "0877ffb4b4d1c18283468be3579b72ed8c22e3ac",
|
||||
"sha256": "c15220d1c448bfd3aa268ef8de41dce14a9b3ba90171aaae539d6d94721ff4e0",
|
||||
"size": 8192,
|
||||
"repo_path": "bios/NEC/PC-98/ide.rom",
|
||||
"cores": [
|
||||
"np2kai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/np2kai/scsi.rom",
|
||||
"sha1": "3d7166f05daad1b022fa04c2569e788580158095",
|
||||
@@ -65855,6 +66045,46 @@
|
||||
"qemu"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/quasi88/n88sub.rom",
|
||||
"sha1": "bb7103a0818850a039c67ff666a31ce49a8d516f",
|
||||
"sha256": "9dc63118728a171ce2ed437ad38ef280b8b7bcf4a5a95fedf18946501b010456",
|
||||
"size": 2048,
|
||||
"repo_path": "bios/NEC/PC-98/N88SUB.ROM",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/quasi88/n88knj2.rom",
|
||||
"sha1": "b86b932cb4ce34472e0fe0f01a4a4350e15dcb53",
|
||||
"sha256": "5c31df4b892e730d1ba7f80fd5583e838ae5583ba52408088da74b9ff422e5fe",
|
||||
"size": 131072,
|
||||
"repo_path": "bios/NEC/PC-98/n88knj2.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/quasi88/n88jisho.rom",
|
||||
"sha1": "deef0cc2a9734ba891a6d6c022aa70ffc66f783e",
|
||||
"sha256": "25773eaee3af617db08d26602d8f4cdc7d42ed946ca510d3b1d96fe1645af63c",
|
||||
"size": 524288,
|
||||
"repo_path": "bios/NEC/PC-98/n88jisho.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/quasi88/font.rom",
|
||||
"sha1": "84b90f69671d4b72e8f219e1fe7cd667e976cf7f",
|
||||
"sha256": "3227087599afd9304f260cdeb3bf1533e39e425cf444ceb3c24f55f561e71a0c",
|
||||
"size": 2048,
|
||||
"repo_path": "bios/NEC/PC-88/font.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/quasi88/font2.rom",
|
||||
"sha1": "0a056b6d28b4b9e29a2d255bdaa25c24f176cf1e",
|
||||
@@ -66317,16 +66547,6 @@
|
||||
"same_cdi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/sgb_boot.bin",
|
||||
"sha1": "369e6eb5e0c975eaa52a4a3f6ee07b2a3c3c16de",
|
||||
"sha256": "b60d493a7944ccf74c81f1e7b6bf38c2c7029e296648ea0e74cb22b10dd1fcb8",
|
||||
"size": 256,
|
||||
"repo_path": "bios/Nintendo/Super Game Boy/sgb_boot.bin",
|
||||
"cores": [
|
||||
"sameboy"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/agb_boot.bin",
|
||||
"sha1": "4ed1d95c9c682a78698f235ce9e9bb3b62bccf9f",
|
||||
@@ -67157,6 +67377,26 @@
|
||||
"sdlpal"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/SkyEmu/cgb_boot.bin",
|
||||
"sha1": "80e3ee82416c1a74827a5459fde32f04bf51a1f6",
|
||||
"sha256": "119470a9cf9f0d85f0c7ba5a6cadffeec07eb9862719086d1a9808189605e388",
|
||||
"size": 2304,
|
||||
"repo_path": "bios/Nintendo/Game Boy Color/cgb_boot.bin",
|
||||
"cores": [
|
||||
"skyemu"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/SkyEmu/dmg_rom.bin",
|
||||
"sha1": "4ed31ec6b0b175bb109c0eb5fd3d193da823339f",
|
||||
"sha256": "cf053eccb4ccafff9e67339d4e78e98dce7d1ed59be819d2a1ba2232c6fce1c7",
|
||||
"size": 256,
|
||||
"repo_path": "bios/Nintendo/Game Boy/GB_dmg.bin",
|
||||
"cores": [
|
||||
"skyemu"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/SkyEmu/nds7.bin",
|
||||
"sha1": "24f67bdea115a2c847c8813a262502ee1607b7df",
|
||||
@@ -71243,66 +71483,6 @@
|
||||
"np2kai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/np2kai/2608_bd.wav",
|
||||
"sha1": "0a56c142ef40cec50f3ee56a6e42d0029c9e2818",
|
||||
"sha256": "802fb0ce40878e98335fc56d11eff6382982e9f09ebc83055b0a469df13f7900",
|
||||
"size": 19192,
|
||||
"repo_path": "bios/NEC/PC-98/2608_bd.wav",
|
||||
"cores": [
|
||||
"np2kai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/np2kai/2608_sd.wav",
|
||||
"sha1": "3c79663ef74c0b0439d13351326eb1c52a657008",
|
||||
"sha256": "e5877cd1e4821cbbc9a450491a189032c82906c05f7fc9f2e48ee0d06b3ad18c",
|
||||
"size": 15558,
|
||||
"repo_path": "bios/NEC/PC-98/2608_sd.wav",
|
||||
"cores": [
|
||||
"np2kai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/np2kai/2608_top.wav",
|
||||
"sha1": "aa4a8f766a86b830687d5083fd3b9db0652f46fc",
|
||||
"sha256": "d7bcf2da99a09589096e9bcee98da43303c588cca4e89b1a263adace52038cd3",
|
||||
"size": 57016,
|
||||
"repo_path": "bios/NEC/PC-98/2608_top.wav",
|
||||
"cores": [
|
||||
"np2kai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/np2kai/2608_hh.wav",
|
||||
"sha1": "12f676cef249b82480b6f19c454e234b435ca7b6",
|
||||
"sha256": "216c81ff9a84af0dfbf21dd8df6827c25fca9fdeefe9bc8426f7a00d6ff2264e",
|
||||
"size": 36722,
|
||||
"repo_path": "bios/NEC/PC-98/2608_hh.wav",
|
||||
"cores": [
|
||||
"np2kai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/np2kai/2608_tom.wav",
|
||||
"sha1": "9513fb4a3f41e75a972a273a5104cbd834c1e2c5",
|
||||
"sha256": "8e2c9ccfa8caff03dbd0d945d7c1d4de15292817409fe6fabda07f008f97dbc6",
|
||||
"size": 23092,
|
||||
"repo_path": "bios/NEC/PC-98/2608_tom.wav",
|
||||
"cores": [
|
||||
"np2kai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/np2kai/2608_rim.wav",
|
||||
"sha1": "c65592330c9dd84011151daed52f9aec926b7e56",
|
||||
"sha256": "6f7d1996001c74452a9ca68b41a2767925b5b947c699096a8ad15bbc658af5a3",
|
||||
"size": 5288,
|
||||
"repo_path": "bios/NEC/PC-98/2608_rim.wav",
|
||||
"cores": [
|
||||
"np2kai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/PPSSPP/ppge_atlas.zim",
|
||||
"sha1": "30a33f3651f8e1b0967a5dfe6ff714baebcce41a",
|
||||
@@ -71433,16 +71613,6 @@
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/quasi88/n88jisho.rom",
|
||||
"sha1": "deef0cc2a9734ba891a6d6c022aa70ffc66f783e",
|
||||
"sha256": "25773eaee3af617db08d26602d8f4cdc7d42ed946ca510d3b1d96fe1645af63c",
|
||||
"size": 524288,
|
||||
"repo_path": "bios/NEC/PC-98/n88jisho.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/same_cdi/bios/cdimono1.zip",
|
||||
"sha1": "36771315484a1f57227e23278df91b84b5d7d3be",
|
||||
@@ -71463,16 +71633,6 @@
|
||||
"same_cdi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/SkyEmu/cgb_boot.bin",
|
||||
"sha1": "80e3ee82416c1a74827a5459fde32f04bf51a1f6",
|
||||
"sha256": "119470a9cf9f0d85f0c7ba5a6cadffeec07eb9862719086d1a9808189605e388",
|
||||
"size": 2304,
|
||||
"repo_path": "bios/Nintendo/Game Boy Color/cgb_boot.bin",
|
||||
"cores": [
|
||||
"skyemu"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "bios/SkyEmu/gba_bios.bin",
|
||||
"sha1": "300c20df6731a33952ded8c436f7f186d25d3492",
|
||||
|
||||
+120
-32
@@ -5,7 +5,7 @@
|
||||
"platform": "retropie",
|
||||
"display_name": "RetroPie",
|
||||
"version": "1.0",
|
||||
"generated": "2026-10-09T20:41:14Z",
|
||||
"generated": "2026-10-06T06:50:25Z",
|
||||
"base_destination": "BIOS",
|
||||
"detect": [
|
||||
{
|
||||
@@ -15,10 +15,10 @@
|
||||
}
|
||||
],
|
||||
"standalone_copies": [],
|
||||
"total_files": 9890,
|
||||
"total_size": 8732256943,
|
||||
"pack_files": 12533,
|
||||
"pack_size": 8772790107,
|
||||
"total_files": 9899,
|
||||
"total_size": 8731269966,
|
||||
"pack_files": 12542,
|
||||
"pack_size": 8771803130,
|
||||
"total_omitted": 8,
|
||||
"omitted_files": [
|
||||
{
|
||||
@@ -1399,6 +1399,14 @@
|
||||
"repo_path": "bios/NEC/PC-98/n88knj2.rom",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/N88SUB.ROM",
|
||||
"sha1": "bb7103a0818850a039c67ff666a31ce49a8d516f",
|
||||
"sha256": "9dc63118728a171ce2ed437ad38ef280b8b7bcf4a5a95fedf18946501b010456",
|
||||
"size": 2048,
|
||||
"repo_path": "bios/NEC/PC-98/N88SUB.ROM",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/N88EXT0.ROM",
|
||||
"sha1": "d239c26ad7ac5efac6e947b0e9549b1534aa970d",
|
||||
@@ -1939,10 +1947,10 @@
|
||||
},
|
||||
{
|
||||
"dest": "BS-X.bin",
|
||||
"sha1": "604556b2e62860af18db5a77f2956ebc75450020",
|
||||
"sha256": "3ce321496edc5d77038de2034eb3fb354d7724afd0bc7fd0319f3eb5d57b984d",
|
||||
"sha1": "4891d739a8a8b67923681bad4fb67edab2e90e50",
|
||||
"sha256": "a49827b45ff9ac9cf5b4658190e1428e59251bc82d8a63d8e9e0f71e439f008f",
|
||||
"size": 1048576,
|
||||
"repo_path": "bios/Nintendo/SNES/Satellaview_BS-X.sfc",
|
||||
"repo_path": "bios/Nintendo/SNES/BS-X.bin",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
@@ -4187,10 +4195,10 @@
|
||||
},
|
||||
{
|
||||
"dest": "pcsx2/resources/GameIndex.yaml",
|
||||
"sha1": "72bfdf91fa11a86e2810a7c167cdcf11e20418a8",
|
||||
"sha256": "61c77b789ccde856096f024e4a3eef6891e15ca59c127e0dd13e762a7d338a7e",
|
||||
"size": 2690355,
|
||||
"repo_path": "bios/Other/armsx2/pcsx2/resources/GameIndex.yaml",
|
||||
"sha1": "7efb2ed721cf0c0f0677324e09be311a8337e793",
|
||||
"sha256": "3bf3c54d6f16d88d3669be698179bb8c2fb981ee5ce71bcb952719429a838cc1",
|
||||
"size": 2693579,
|
||||
"repo_path": "bios/Other/yaps2/GameIndex.yaml",
|
||||
"cores": null
|
||||
},
|
||||
{
|
||||
@@ -4571,6 +4579,16 @@
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cd32.rom",
|
||||
"sha1": "3525be8887f79b5929e017b42380a79edfee542d",
|
||||
"sha256": "3586355514947b5ab48c8755dc004fab94f188b19e7bce491c8f9693499e830b",
|
||||
"size": 524288,
|
||||
"repo_path": "bios/Commodore/Amiga/amiga-os-310-cd32.rom",
|
||||
"cores": [
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cd32ext.rom",
|
||||
"sha1": "5bef3d628ce59cc02a66e6e4ae0da48f60e78f7f",
|
||||
@@ -4581,6 +4599,16 @@
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cdtv.rom",
|
||||
"sha1": "7ba40ffa17e500ed9fed041f3424bd81d9c907be",
|
||||
"sha256": "6e48bb768539e8dae352553e5fdfa860682edaf9e26b105fadd8e26103171765",
|
||||
"size": 262144,
|
||||
"repo_path": "bios/Commodore/Amiga/amiga-ext-130-cdtv.rom",
|
||||
"cores": [
|
||||
"amiberry"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cdtvext.rom",
|
||||
"sha1": "7ba40ffa17e500ed9fed041f3424bd81d9c907be",
|
||||
@@ -41945,6 +41973,16 @@
|
||||
"storage": "release",
|
||||
"release_asset": "easyrpg.soundfont"
|
||||
},
|
||||
{
|
||||
"dest": "system.rom",
|
||||
"sha1": "1f5f7013f18c08ff50d7942e76c4fbd782412414",
|
||||
"sha256": "863df0975a305110a7da4591b704e8f5dc7302f03c63603aa2dcd03e84c36ff9",
|
||||
"size": 65536,
|
||||
"repo_path": "bios/IBM/PC/ibmpcjr/bios.rom",
|
||||
"cores": [
|
||||
"dosbox-staging"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "IBM PC Cassette BASIC ROM",
|
||||
"sha1": "07449ebca18f979b9ab748582b736e402f2bf940",
|
||||
@@ -41985,6 +42023,16 @@
|
||||
"dosbox_pure"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "system/nds_bios_arm9.bin",
|
||||
"sha1": "bfaac75f101c135e32e2aaf541de6b1be4c8c62d",
|
||||
"sha256": "1693983a7707ae394786fa526c0552457888a51d4e410d715ef07acd5a540555",
|
||||
"size": 4096,
|
||||
"repo_path": "bios/Nintendo/DS/NDS_Bios9.bin",
|
||||
"cores": [
|
||||
"drastic"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "system/drastic_bios_arm9.bin",
|
||||
"sha1": "4dc29a1560e1cbd7afef1998f7a6c25c5db1cc3a",
|
||||
@@ -41995,6 +42043,16 @@
|
||||
"drastic"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "system/nds_bios_arm7.bin",
|
||||
"sha1": "24f67bdea115a2c847c8813a262502ee1607b7df",
|
||||
"sha256": "ba65f690eb04ec92db67c0e299e21ad71de087d6d5de8a9cb17a62eaab563c17",
|
||||
"size": 16384,
|
||||
"repo_path": "bios/Nintendo/DS/NDS_Bios7.bin",
|
||||
"cores": [
|
||||
"drastic"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "system/drastic_bios_arm7.bin",
|
||||
"sha1": "31ae916c85d012582c7cdf47de6943ac6778253b",
|
||||
@@ -42005,6 +42063,16 @@
|
||||
"drastic"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "system/nds_firmware.bin",
|
||||
"sha1": "87dae2500e889737af51f4a5b5845770a62482f5",
|
||||
"sha256": "330c59198faec64b40e96cc875b422d340fc9ab1a9e32e9bd9b7baa1dbe6ba7f",
|
||||
"size": 262144,
|
||||
"repo_path": "bios/Nintendo/DS/NDS_Lite_Firmware.bin",
|
||||
"cores": [
|
||||
"drastic"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "game_database.xml",
|
||||
"sha1": "b0ae5480c61bdb701b9a444a1a81c4970db06d77",
|
||||
@@ -62783,6 +62851,16 @@
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "SufamiTurbo.sfc",
|
||||
"sha1": "ef86ea192eed03d5c413fdbbfd46043be1d7a127",
|
||||
"sha256": "edacb453da14f825f05d1134d6035f4bf034e55f7cfb97c70c4ee107eabc7342",
|
||||
"size": 262144,
|
||||
"repo_path": "bios/Nintendo/SuFami Turbo/STBIOS.bin",
|
||||
"cores": [
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "ymf288_adpcm_rom.bin",
|
||||
"sha1": "50b6c3e288eaa12ad275d4f323267bb72b0445df",
|
||||
@@ -62813,6 +62891,26 @@
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "[BIOS] Super CD-ROM System (Japan) (v3.0).pce",
|
||||
"sha1": "79f5ff55dd10187c7fd7b8daab0b3ffbd1f56a2c",
|
||||
"sha256": "e11527b3b96ce112a037138988ca72fd117a6b0779c2480d9e03eaebece3d9ce",
|
||||
"size": 262144,
|
||||
"repo_path": "bios/NEC/PC Engine/syscard3.pce",
|
||||
"cores": [
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "[BIOS] Games Express CD Card (Japan).pce",
|
||||
"sha1": "014881a959e045e00f4db8f52955200865d40280",
|
||||
"sha256": "4b86bb96a48a4ca8375fc0109631d0b1d64f255a03b01de70594d40788ba6c3d",
|
||||
"size": 32768,
|
||||
"repo_path": "bios/NEC/PC Engine/gecard.pce",
|
||||
"cores": [
|
||||
"mesence"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "Satellaview/BSX0120-0.bin",
|
||||
"sha1": "f51c52bb26f2546fb167ab1084a56457475ff0a0",
|
||||
@@ -90569,6 +90667,16 @@
|
||||
"qemu"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/font.rom",
|
||||
"sha1": "84b90f69671d4b72e8f219e1fe7cd667e976cf7f",
|
||||
"sha256": "3227087599afd9304f260cdeb3bf1533e39e425cf444ceb3c24f55f561e71a0c",
|
||||
"size": 2048,
|
||||
"repo_path": "bios/NEC/PC-88/font.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/font2.rom",
|
||||
"sha1": "0a056b6d28b4b9e29a2d255bdaa25c24f176cf1e",
|
||||
@@ -95707,26 +95815,6 @@
|
||||
"nekop2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "resources/GameIndex.yaml",
|
||||
"sha1": "5e2d50a3376dc6a0750505bec68626d39a5bd4c1",
|
||||
"sha256": "6531239315d759d0588985b31576696d2672c1a0e192633ea2d4f3d011bfdb37",
|
||||
"size": 2683897,
|
||||
"repo_path": "bios/Sony/PlayStation 2/resources/.variants/GameIndex.yaml.fc3c1420",
|
||||
"cores": [
|
||||
"pcsx2"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "quasi88/font.rom",
|
||||
"sha1": "84b90f69671d4b72e8f219e1fe7cd667e976cf7f",
|
||||
"sha256": "3227087599afd9304f260cdeb3bf1533e39e425cf444ceb3c24f55f561e71a0c",
|
||||
"size": 2048,
|
||||
"repo_path": "bios/NEC/PC-88/font.rom",
|
||||
"cores": [
|
||||
"quasi88"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "scummvm/extra/MT32_CONTROL.ROM",
|
||||
"sha1": "b083518fffb7f66b03c23b7eb4f868e62dc5a987",
|
||||
|
||||
+35
-15
@@ -5,7 +5,7 @@
|
||||
"platform": "rocknix",
|
||||
"display_name": "ROCKNIX",
|
||||
"version": "1.0",
|
||||
"generated": "2026-10-09T20:41:14Z",
|
||||
"generated": "2026-10-06T06:50:25Z",
|
||||
"base_destination": "bios",
|
||||
"detect": [
|
||||
{
|
||||
@@ -17,10 +17,10 @@
|
||||
}
|
||||
],
|
||||
"standalone_copies": [],
|
||||
"total_files": 5802,
|
||||
"total_size": 6570592972,
|
||||
"pack_files": 5804,
|
||||
"pack_size": 6570592972,
|
||||
"total_files": 5804,
|
||||
"total_size": 6570724300,
|
||||
"pack_files": 5806,
|
||||
"pack_size": 6570724300,
|
||||
"total_omitted": 0,
|
||||
"omitted_files": [],
|
||||
"files": [
|
||||
@@ -15720,6 +15720,16 @@
|
||||
"fceumm"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "dc/dc_nvmem.bin",
|
||||
"sha1": "72c65f2a11ee7b72b77b9945187b7a332632e14a",
|
||||
"sha256": "15a9bc3aa59fa998c03bfe0b1cf6cb1958c1660e8e72b3d7195b653f44038407",
|
||||
"size": 131072,
|
||||
"repo_path": "bios/Sega/Dreamcast/dc_nvmem.bin",
|
||||
"cores": [
|
||||
"flycast"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "dc/font.bin",
|
||||
"sha1": "94c6ff701f79742ee23c0a028ac0be5aead77c73",
|
||||
@@ -53048,6 +53058,26 @@
|
||||
"sdlpal"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "SkyEmu/cgb_boot.bin",
|
||||
"sha1": "80e3ee82416c1a74827a5459fde32f04bf51a1f6",
|
||||
"sha256": "119470a9cf9f0d85f0c7ba5a6cadffeec07eb9862719086d1a9808189605e388",
|
||||
"size": 2304,
|
||||
"repo_path": "bios/Nintendo/Game Boy Color/cgb_boot.bin",
|
||||
"cores": [
|
||||
"skyemu"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "SkyEmu/dmg_rom.bin",
|
||||
"sha1": "4ed31ec6b0b175bb109c0eb5fd3d193da823339f",
|
||||
"sha256": "cf053eccb4ccafff9e67339d4e78e98dce7d1ed59be819d2a1ba2232c6fce1c7",
|
||||
"size": 256,
|
||||
"repo_path": "bios/Nintendo/Game Boy/GB_dmg.bin",
|
||||
"cores": [
|
||||
"skyemu"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "SkyEmu/nds7.bin",
|
||||
"sha1": "24f67bdea115a2c847c8813a262502ee1607b7df",
|
||||
@@ -56080,16 +56110,6 @@
|
||||
"kronos"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "SkyEmu/cgb_boot.bin",
|
||||
"sha1": "80e3ee82416c1a74827a5459fde32f04bf51a1f6",
|
||||
"sha256": "119470a9cf9f0d85f0c7ba5a6cadffeec07eb9862719086d1a9808189605e388",
|
||||
"size": 2304,
|
||||
"repo_path": "bios/Nintendo/Game Boy Color/cgb_boot.bin",
|
||||
"cores": [
|
||||
"skyemu"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "SkyEmu/gba_bios.bin",
|
||||
"sha1": "300c20df6731a33952ded8c436f7f186d25d3492",
|
||||
|
||||
+47
-538
@@ -5,7 +5,7 @@
|
||||
"platform": "romm",
|
||||
"display_name": "RomM",
|
||||
"version": "1.0",
|
||||
"generated": "2026-10-09T20:41:14Z",
|
||||
"generated": "2026-10-06T10:11:17Z",
|
||||
"base_destination": "bios",
|
||||
"detect": [
|
||||
{
|
||||
@@ -15,543 +15,12 @@
|
||||
}
|
||||
],
|
||||
"standalone_copies": [],
|
||||
"total_files": 1085,
|
||||
"total_size": 1582092326,
|
||||
"pack_files": 1087,
|
||||
"pack_size": 1582092326,
|
||||
"total_omitted": 53,
|
||||
"omitted_files": [
|
||||
{
|
||||
"dest": "CM32LN_CONTROL.ROM",
|
||||
"name": "CM32LN_CONTROL.ROM",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"DOSBox Pure"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "CM32L_CONTROL.ROM",
|
||||
"name": "CM32L_CONTROL.ROM",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"DOSBox Pure"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "CM32L_PCM.ROM",
|
||||
"name": "CM32L_PCM.ROM",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"DOSBox Pure"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "DOSBOX.SF2",
|
||||
"name": "DOSBOX.SF2",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"DOSBox Pure"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "JiffyDOS_1541-II.bin",
|
||||
"name": "JiffyDOS_1541-II.bin",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"VICE x128"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "JiffyDOS_1571_repl310654.bin",
|
||||
"name": "JiffyDOS_1571_repl310654.bin",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"VICE x128"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "JiffyDOS_1581.bin",
|
||||
"name": "JiffyDOS_1581.bin",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"VICE x128"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "JiffyDOS_C128.bin",
|
||||
"name": "JiffyDOS_C128.bin",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"VICE x128"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "JiffyDOS_C64.bin",
|
||||
"name": "JiffyDOS_C64.bin",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"VICE x128"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "JiffyDOS_SX-64.bin",
|
||||
"name": "JiffyDOS_SX-64.bin",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"VICE x64"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "MT32_CONTROL.ROM",
|
||||
"name": "MT32_CONTROL.ROM",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"DOSBox Pure"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "MT32_PCM.ROM",
|
||||
"name": "MT32_PCM.ROM",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"DOSBox Pure"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "[BIOS] Atari Jaguar CD (World).j64",
|
||||
"name": "[BIOS] Atari Jaguar CD (World).j64",
|
||||
"system": "atari-jaguar",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"Virtual Jaguar"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "[BIOS] Atari Jaguar Developer CD (World).j64",
|
||||
"name": "[BIOS] Atari Jaguar Developer CD (World).j64",
|
||||
"system": "atari-jaguar",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"Virtual Jaguar"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "acpsx.zip",
|
||||
"name": "acpsx.zip",
|
||||
"system": "zn1-acclaim",
|
||||
"required": true,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"MAME 2003"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "chargen-901460-02.bin",
|
||||
"name": "chargen-901460-02.bin",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"VICE xvic"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cpzn1.zip",
|
||||
"name": "cpzn1.zip",
|
||||
"system": "zn1-capcom",
|
||||
"required": true,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"MAME 2003"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cpzn2.zip",
|
||||
"name": "cpzn2.zip",
|
||||
"system": "zn2-capcom",
|
||||
"required": true,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"MAME 2003"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "cvs.zip",
|
||||
"name": "cvs.zip",
|
||||
"system": "century-cvs",
|
||||
"required": true,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"MAME 2003"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "fbalpha2012/hiscore.dat",
|
||||
"name": "hiscore.dat",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"FB Alpha 2012 CPS-1"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "jagboot.rom",
|
||||
"name": "jagboot.rom",
|
||||
"system": "atari-jaguar",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"Virtual Jaguar"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "jagboot_m.rom",
|
||||
"name": "jagboot_m.rom",
|
||||
"system": "atari-jaguar",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"Virtual Jaguar"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "kernal-901227-01.bin",
|
||||
"name": "kernal-901227-01.bin",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"VICE x64"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "kernal-901227-02.bin",
|
||||
"name": "kernal-901227-02.bin",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"VICE x64"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "kernal.901486-02.bin",
|
||||
"name": "kernal.901486-02.bin",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"VICE xvic"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "konamigx.zip",
|
||||
"name": "konamigx.zip",
|
||||
"system": "konami-gx",
|
||||
"required": true,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"MAME 2003"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "kvb1.wav",
|
||||
"name": "kvb1.wav",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"Stella 2014"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "kvb2.wav",
|
||||
"name": "kvb2.wav",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"Stella 2014"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "kvb3.wav",
|
||||
"name": "kvb3.wav",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"Stella 2014"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "kvs1.wav",
|
||||
"name": "kvs1.wav",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"Stella 2014"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "kvs2.wav",
|
||||
"name": "kvs2.wav",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"Stella 2014"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "kvs3.wav",
|
||||
"name": "kvs3.wav",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"Stella 2014"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "kvshared.wav",
|
||||
"name": "kvshared.wav",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"Stella 2014"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "mame2003-plus/cheat.dat",
|
||||
"name": "cheat.dat",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"MAME 2003-Plus"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "mame2003-plus/hiscore.dat",
|
||||
"name": "hiscore.dat",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"MAME 2003-Plus"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "mame2003-plus/history.dat",
|
||||
"name": "history.dat",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"MAME 2003-Plus"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "mame2003-plus/mameinfo.dat",
|
||||
"name": "mameinfo.dat",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"MAME 2003-Plus"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "mame2003/cheat.dat",
|
||||
"name": "cheat.dat",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"MAME 2003"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "mame2003/hiscore.dat",
|
||||
"name": "hiscore.dat",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"MAME 2003"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "mame2003/history.dat",
|
||||
"name": "history.dat",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"MAME 2003"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "mame2003/mameinfo.dat",
|
||||
"name": "mameinfo.dat",
|
||||
"system": "",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"MAME 2003"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "megaplay.zip",
|
||||
"name": "megaplay.zip",
|
||||
"system": "sega-megaplay",
|
||||
"required": true,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"MAME 2003"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "megatech.zip",
|
||||
"name": "megatech.zip",
|
||||
"system": "sega-megatech",
|
||||
"required": true,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"MAME 2003"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "nss.zip",
|
||||
"name": "nss.zip",
|
||||
"system": "nintendo-nss",
|
||||
"required": true,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"MAME 2003"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "playch10.zip",
|
||||
"name": "playch10.zip",
|
||||
"system": "nintendo-playch10",
|
||||
"required": true,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"MAME 2003"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "same_cdi/bios/cdi490a.zip",
|
||||
"name": "cdi490a.zip",
|
||||
"system": "cdi",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"SAME CDi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "same_cdi/bios/cdi910.zip",
|
||||
"name": "cdi910.zip",
|
||||
"system": "cdi",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"SAME CDi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "same_cdi/bios/cdimono1.zip",
|
||||
"name": "cdimono1.zip",
|
||||
"system": "cdi",
|
||||
"required": true,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"SAME CDi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "same_cdi/bios/cdimono2.zip",
|
||||
"name": "cdimono2.zip",
|
||||
"system": "cdi",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"SAME CDi"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "sncwgltd.zip",
|
||||
"name": "sncwgltd.zip",
|
||||
"system": "zn1-video-system",
|
||||
"required": false,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"MAME 2003"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "stvbios.zip",
|
||||
"name": "stvbios.zip",
|
||||
"system": "sega-stv",
|
||||
"required": true,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"MAME 2003"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "taitofx1.zip",
|
||||
"name": "taitofx1.zip",
|
||||
"system": "zn1-taito",
|
||||
"required": true,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"MAME 2003"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "tps.zip",
|
||||
"name": "tps.zip",
|
||||
"system": "zn1-tecmo",
|
||||
"required": true,
|
||||
"reason": "no_platform_slug",
|
||||
"cores": [
|
||||
"MAME 2003"
|
||||
]
|
||||
}
|
||||
],
|
||||
"total_files": 1089,
|
||||
"total_size": 1586368550,
|
||||
"pack_files": 1091,
|
||||
"pack_size": 1586368550,
|
||||
"total_omitted": 0,
|
||||
"omitted_files": [],
|
||||
"files": [
|
||||
{
|
||||
"dest": "3do/3do_arcade_saot.bin",
|
||||
@@ -4271,6 +3740,16 @@
|
||||
"clk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "acpc/MSX/MSX.ROM",
|
||||
"sha1": "409e82adac40f6bdd18eb6c84e8b2fbdc7fb5498",
|
||||
"sha256": "999564a371dd2fdf7fbe8d853e82a68d557c27b7d87417639b2fa17704b83f78",
|
||||
"size": 32768,
|
||||
"repo_path": "bios/Microsoft/MSX/MSX.ROM",
|
||||
"cores": [
|
||||
"clk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "acpc/MSX/msx-japanese.rom",
|
||||
"sha1": "302afb5d8be26c758309ca3df611ae69cced2821",
|
||||
@@ -4311,6 +3790,26 @@
|
||||
"clk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "acpc/MSX/MSX2.ROM",
|
||||
"sha1": "6103b39f1e38d1aa2d84b1c3219c44f1abb5436e",
|
||||
"sha256": "4bc4ae85ca5f28246cd3e7b7e017d298ddd375603657f84ef2c7954bc2d9b919",
|
||||
"size": 32768,
|
||||
"repo_path": "bios/Microsoft/MSX/MSX2.ROM",
|
||||
"cores": [
|
||||
"clk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "acpc/MSX/MSX2EXT.ROM",
|
||||
"sha1": "5c1f9c7fb655e43d38e5dd1fcc6b942b2ff68b02",
|
||||
"sha256": "6c6f421a10c428d960b7ecc990f99af1c638147f747bddca7b0bf0e2ab738300",
|
||||
"size": 16384,
|
||||
"repo_path": "bios/Microsoft/MSX/MSX2EXT.ROM",
|
||||
"cores": [
|
||||
"clk"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "acpc/MSX/fmpac.rom",
|
||||
"sha1": "9d789166e3caf28e4742fe933d962e99618c633d",
|
||||
@@ -5521,6 +5020,16 @@
|
||||
"minivmac"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "64dd/Mupen64plus/IPL.n64",
|
||||
"sha1": "bf861922dcb78c316360e3e742f4f70ff63c9bc3",
|
||||
"sha256": "806400ec0df94b0755de6c5b8249d6b6a9866124c5ddbdac198bde22499bfb8b",
|
||||
"size": 4194304,
|
||||
"repo_path": "bios/Nintendo/64DD/64DD_IPL_v12_JPN.bin",
|
||||
"cores": [
|
||||
"mupen64plus_next"
|
||||
]
|
||||
},
|
||||
{
|
||||
"dest": "64dd/Mupen64plus/font.ttf",
|
||||
"sha1": "1d7f6fa0c34e3d50be52c16a0816bebac309fe41",
|
||||
|
||||
@@ -805,10 +805,6 @@ platforms:
|
||||
target_source: null
|
||||
install:
|
||||
detect:
|
||||
# installer.iss: DefaultDirName=C:\RetroBat, the first place to look.
|
||||
- os: windows
|
||||
method: path_exists
|
||||
path: 'C:\RetroBat\bios'
|
||||
- os: windows
|
||||
method: path_exists
|
||||
path: '%USERPROFILE%\RetroBat\bios'
|
||||
|
||||
+37
-9
@@ -163,15 +163,22 @@ shared_groups:
|
||||
# QUASI88 searches "quasi88/" subfolder first
|
||||
# ref: quasi88-libretro/src/LIBRETRO/libretro.c:463 — snprintf(..., "%s%cquasi88%c%s", ...)
|
||||
quasi88:
|
||||
# Only what System.dat does not declare for nec-pc-88: the mkII SR
|
||||
# extension ROMs that match N88.ROM (MAME pc8801mk2sr: 710a63ec, c0bd2aa6,
|
||||
# af2b6efa, 7713c519). The core reads N88EXTn.ROM before n88_n.rom
|
||||
# (src/LIBRETRO/libretro.c:109-112), so they win over the n88_n.rom dumps
|
||||
# System.dat pairs with that main ROM, which come from other models (n88_0
|
||||
# 61984bab is the original PC-8801's). N88SUB.ROM, N88.ROM, N88N.ROM and
|
||||
# N88KNJ1.ROM are the native disk.rom, n88.rom, n88n.rom and n88knj1.rom
|
||||
# under the other spelling the core tries: aliases in the profile, not
|
||||
# files of their own here, or the pack carried disk.rom twice.
|
||||
- name: N88.ROM
|
||||
destination: quasi88/N88.ROM
|
||||
required: true
|
||||
md5: "4f984e04a99d56c4cfe36115415d6eb8"
|
||||
- name: N88SUB.ROM
|
||||
destination: quasi88/N88SUB.ROM
|
||||
required: true
|
||||
md5: "793f86784e5608352a5d7f03f03e0858"
|
||||
- name: N88N.ROM
|
||||
destination: quasi88/N88N.ROM
|
||||
required: true
|
||||
md5: "2ff07b8769367321128e03924af668a0"
|
||||
- name: N88KNJ1.ROM
|
||||
destination: quasi88/N88KNJ1.ROM
|
||||
required: true
|
||||
md5: "d81c6d5d7ad1a4bbbd6ae22a01257603"
|
||||
- name: N88EXT0.ROM
|
||||
destination: quasi88/N88EXT0.ROM
|
||||
required: true
|
||||
@@ -188,6 +195,27 @@ shared_groups:
|
||||
destination: quasi88/N88EXT3.ROM
|
||||
required: true
|
||||
md5: "fc4b76a402ba501e6ba6de4b3e8b4273"
|
||||
# N88EXT0-3.ROM above are the mkII SR extension ROMs that match N88.ROM
|
||||
# (MAME pc8801mk2sr: 710a63ec, c0bd2aa6, af2b6efa, 7713c519). The core
|
||||
# reads N88EXTn.ROM before n88_n.rom (src/LIBRETRO/libretro.c:109-112),
|
||||
# so they win over the n88_n.rom dumps System.dat pairs with that main ROM,
|
||||
# which come from other models (n88_0 61984bab is the original PC-8801's).
|
||||
- name: n88_0.rom
|
||||
destination: quasi88/n88_0.rom
|
||||
required: false
|
||||
md5: "e28fe3f520bea594350ea8fb00395370"
|
||||
- name: n88_1.rom
|
||||
destination: quasi88/n88_1.rom
|
||||
required: false
|
||||
md5: "a8e298da7ac947669bcb1ff25cee0a83"
|
||||
- name: n88_2.rom
|
||||
destination: quasi88/n88_2.rom
|
||||
required: false
|
||||
md5: "9d03154fd9abfc28c4e6d4dc705e6e23"
|
||||
- name: n88_3.rom
|
||||
destination: quasi88/n88_3.rom
|
||||
required: false
|
||||
md5: "e1791f8154f1cdf22b576a1a365b6e1f"
|
||||
|
||||
# Sinclair ZX Spectrum — Fuse core (Pentagon/Scorpion ROMs)
|
||||
# VERIFIED: fuse-libretro/src/compat/paths.c compat_get_next_path()
|
||||
|
||||
+24
-105
@@ -4,136 +4,55 @@
|
||||
"title": "RetroBIOS content database",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"schema_version",
|
||||
"generated_at",
|
||||
"total_files",
|
||||
"total_size",
|
||||
"files",
|
||||
"indexes"
|
||||
],
|
||||
"required": ["schema_version", "generated_at", "total_files", "total_size", "files", "indexes"],
|
||||
"properties": {
|
||||
"schema_version": {
|
||||
"const": 1
|
||||
},
|
||||
"generated_at": {
|
||||
"type": "string",
|
||||
"format": "date-time"
|
||||
},
|
||||
"total_files": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"total_size": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"bios_dir": {
|
||||
"type": "string",
|
||||
"description": "Directory the files were scanned from; _mame_clones.json sits beside it."
|
||||
},
|
||||
"schema_version": { "const": 1 },
|
||||
"generated_at": { "type": "string", "format": "date-time" },
|
||||
"total_files": { "type": "integer", "minimum": 0 },
|
||||
"total_size": { "type": "integer", "minimum": 0 },
|
||||
"files": {
|
||||
"type": "object",
|
||||
"propertyNames": {
|
||||
"pattern": "^[a-f0-9]{40}$"
|
||||
},
|
||||
"propertyNames": { "pattern": "^[a-f0-9]{40}$" },
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"path",
|
||||
"name",
|
||||
"size",
|
||||
"sha1",
|
||||
"md5",
|
||||
"sha256",
|
||||
"crc32",
|
||||
"adler32"
|
||||
],
|
||||
"required": ["path", "name", "size", "sha1", "md5", "sha256", "crc32", "adler32"],
|
||||
"properties": {
|
||||
"path": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"size": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"sha1": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-f0-9]{40}$"
|
||||
},
|
||||
"md5": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-f0-9]{32}$"
|
||||
},
|
||||
"sha256": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-f0-9]{64}$"
|
||||
},
|
||||
"crc32": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-f0-9]{8}$"
|
||||
},
|
||||
"adler32": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-f0-9]{8}$"
|
||||
},
|
||||
"provenance": {
|
||||
"type": "object"
|
||||
}
|
||||
"path": { "type": "string", "minLength": 1 },
|
||||
"name": { "type": "string", "minLength": 1 },
|
||||
"size": { "type": "integer", "minimum": 0 },
|
||||
"sha1": { "type": "string", "pattern": "^[a-f0-9]{40}$" },
|
||||
"md5": { "type": "string", "pattern": "^[a-f0-9]{32}$" },
|
||||
"sha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
||||
"crc32": { "type": "string", "pattern": "^[a-f0-9]{8}$" },
|
||||
"adler32": { "type": "string", "pattern": "^[a-f0-9]{8}$" },
|
||||
"provenance": { "type": "object" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"by_md5",
|
||||
"by_name",
|
||||
"by_crc32",
|
||||
"by_sha256",
|
||||
"by_path_suffix"
|
||||
],
|
||||
"required": ["by_md5", "by_name", "by_crc32", "by_sha256", "by_path_suffix"],
|
||||
"properties": {
|
||||
"by_md5": {
|
||||
"$ref": "#/$defs/hashIndex"
|
||||
},
|
||||
"by_crc32": {
|
||||
"$ref": "#/$defs/hashIndex"
|
||||
},
|
||||
"by_sha256": {
|
||||
"$ref": "#/$defs/hashIndex"
|
||||
},
|
||||
"by_name": {
|
||||
"$ref": "#/$defs/listIndex"
|
||||
},
|
||||
"by_path_suffix": {
|
||||
"$ref": "#/$defs/listIndex"
|
||||
}
|
||||
"by_md5": { "$ref": "#/$defs/hashIndex" },
|
||||
"by_crc32": { "$ref": "#/$defs/hashIndex" },
|
||||
"by_sha256": { "$ref": "#/$defs/hashIndex" },
|
||||
"by_name": { "$ref": "#/$defs/listIndex" },
|
||||
"by_path_suffix": { "$ref": "#/$defs/listIndex" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"hashIndex": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-f0-9]{40}$"
|
||||
}
|
||||
"additionalProperties": { "type": "string", "pattern": "^[a-f0-9]{40}$" }
|
||||
},
|
||||
"listIndex": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-f0-9]{40}$"
|
||||
},
|
||||
"items": { "type": "string", "pattern": "^[a-f0-9]{40}$" },
|
||||
"minItems": 1,
|
||||
"uniqueItems": true
|
||||
}
|
||||
|
||||
@@ -129,8 +129,7 @@
|
||||
"hash_mismatch",
|
||||
"not_found",
|
||||
"external",
|
||||
"user_provided",
|
||||
"no_platform_slug"
|
||||
"user_provided"
|
||||
]
|
||||
},
|
||||
"cores": {
|
||||
|
||||
+6
-30
@@ -43,18 +43,11 @@ def write_if_changed(path: str, content: str, normalize=None) -> bool:
|
||||
)
|
||||
if _strip_timestamps(before) == _strip_timestamps(after):
|
||||
return False
|
||||
write_text_atomic(path, content)
|
||||
return True
|
||||
|
||||
|
||||
def write_text_atomic(path: str, content: str) -> None:
|
||||
"""Write a whole file or nothing.
|
||||
|
||||
Truncate-then-write leaves a half-written artifact behind an interrupt:
|
||||
a partial database.json, manifest or README.md is committed-looking and
|
||||
silently wrong. The scratch file sits beside the target so the rename
|
||||
stays on one filesystem, which is what makes it atomic.
|
||||
"""
|
||||
# Truncate-then-write leaves a half-written artifact behind an interrupt,
|
||||
# and every generator in the repo funnels through here: a partial
|
||||
# database.json or README.md is committed-looking and silently wrong.
|
||||
# The scratch file sits beside the target so the rename stays on one
|
||||
# filesystem, which is what makes it atomic.
|
||||
directory = os.path.dirname(os.path.abspath(path))
|
||||
handle, scratch = tempfile.mkstemp(
|
||||
dir=directory, prefix=f".{os.path.basename(path)}.", suffix=".tmp"
|
||||
@@ -67,24 +60,7 @@ def write_text_atomic(path: str, content: str) -> None:
|
||||
with contextlib.suppress(OSError):
|
||||
os.unlink(scratch)
|
||||
raise
|
||||
|
||||
|
||||
def copy_file_atomic(source: str, path: str) -> None:
|
||||
"""Copy a file into place whole or not at all, metadata included."""
|
||||
import shutil
|
||||
|
||||
directory = os.path.dirname(os.path.abspath(path))
|
||||
handle, scratch = tempfile.mkstemp(
|
||||
dir=directory, prefix=f".{os.path.basename(path)}.", suffix=".tmp"
|
||||
)
|
||||
os.close(handle)
|
||||
try:
|
||||
shutil.copy2(source, scratch)
|
||||
os.replace(scratch, path)
|
||||
except BaseException:
|
||||
with contextlib.suppress(OSError):
|
||||
os.unlink(scratch)
|
||||
raise
|
||||
return True
|
||||
|
||||
def _strip_timestamps(text: str) -> str:
|
||||
"""Remove known timestamp patterns for content comparison."""
|
||||
|
||||
@@ -152,13 +152,12 @@ def print_report(report: dict) -> None:
|
||||
print(f"\nSummary: {', '.join(parts)}")
|
||||
|
||||
|
||||
def update_changed(report: dict) -> list[str]:
|
||||
"""Refresh entries that have changed; the keys whose refresh failed."""
|
||||
failed: list[str] = []
|
||||
def update_changed(report: dict) -> None:
|
||||
"""Refresh entries that have changed."""
|
||||
for e in report.get("entries", []):
|
||||
if e["status"] == "UPDATED" and e.get("key"):
|
||||
log.info("refreshing %s ...", e["key"])
|
||||
result = subprocess.run(
|
||||
subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
"scripts/refresh_data_dirs.py",
|
||||
@@ -168,10 +167,6 @@ def update_changed(report: dict) -> list[str]:
|
||||
],
|
||||
check=False,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
log.error("refresh of %s failed (exit %d)", e["key"], result.returncode)
|
||||
failed.append(e["key"])
|
||||
return failed
|
||||
|
||||
|
||||
def main() -> None:
|
||||
@@ -199,10 +194,7 @@ def main() -> None:
|
||||
print_report(report)
|
||||
|
||||
if args.update:
|
||||
failed = update_changed(report)
|
||||
if failed:
|
||||
print(f"ERROR: refresh failed for {', '.join(failed)}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
update_changed(report)
|
||||
|
||||
# Unreachable upstream means the freshness question was not answered, and
|
||||
# a zero exit says it was answered "fresh".
|
||||
|
||||
+40
-113
@@ -489,51 +489,6 @@ def _owner_tokens(file_entry: dict) -> set[str]:
|
||||
return tokens
|
||||
|
||||
|
||||
def _foreign_tree(path: str, owners: set[str]) -> bool:
|
||||
"""Whether a path lives in the tree of an emulator that is not the asker.
|
||||
|
||||
bios/Other/<emulator>/ holds what one emulator ships. A same-named file
|
||||
found there answers that emulator's entry, not another's: EmuCoreX's
|
||||
unlock.wav was served from NetherSX2's tree, and upstream PCSX2's
|
||||
GameIndex.yaml from EmuCoreX's, while the system directory held a copy.
|
||||
"""
|
||||
parts = path.split("/")
|
||||
if not owners or len(parts) < 4 or parts[0] != "bios" or parts[1] != "Other":
|
||||
return False
|
||||
return not (owners & _affinity_tokens(parts[2]))
|
||||
|
||||
|
||||
def _rank_tail_candidates(
|
||||
candidates: list[str], start: int, tail: str, files_db: dict, owners: set[str]
|
||||
) -> list[str]:
|
||||
"""Order the files a destination tail designates, the likeliest first.
|
||||
|
||||
A shortened tail found only in another emulator's tree is that
|
||||
emulator's asset, not evidence about this entry. A full tail claimed
|
||||
twice goes to the file whose path carries the least on top of the
|
||||
destination, then to the owner's own copy (super3's Games.xml was served
|
||||
to Supermodel-Dojo by index order alone), then to any copy outside
|
||||
another emulator's tree (upstream PCSX2 took EmuCoreX's GameIndex.yaml
|
||||
over the system directory's).
|
||||
"""
|
||||
def path_of(sha1: str) -> str:
|
||||
return files_db.get(sha1, {}).get("path", "")
|
||||
|
||||
if start:
|
||||
candidates = [h for h in candidates if not _foreign_tree(path_of(h), owners)]
|
||||
if len(candidates) < 2:
|
||||
return candidates
|
||||
depth = len(tail.split("/"))
|
||||
return sorted(
|
||||
candidates,
|
||||
key=lambda h: (
|
||||
len(path_of(h).split("/")) - depth,
|
||||
not owners & _affinity_tokens(path_of(h).rsplit("/", 1)[0]),
|
||||
_foreign_tree(path_of(h), owners),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def _by_affinity(paths: list[str], file_entry: dict, dest_hint: str) -> list[str]:
|
||||
"""Same-named candidates, the likeliest first.
|
||||
|
||||
@@ -552,15 +507,14 @@ def _by_affinity(paths: list[str], file_entry: dict, dest_hint: str) -> list[str
|
||||
owners = _owner_tokens(file_entry)
|
||||
sized = any(file_entry.get(k) for k in ("size", "min_size", "max_size"))
|
||||
|
||||
def score(path: str) -> tuple[int, bool, bool, int]:
|
||||
def score(path: str) -> tuple[int, bool, int]:
|
||||
fits = 0
|
||||
if sized and os.path.exists(path):
|
||||
fits = int(size_fits(file_entry, os.path.getsize(path)))
|
||||
tokens = _affinity_tokens(path.rsplit("/", 1)[0])
|
||||
# The owner's own copy ranks above any shared tree segment: NetherSX2's
|
||||
# shaders/common/ beat armsx2's own fxaa.fx on two directory tokens.
|
||||
# Another emulator's own tree ranks below everything else.
|
||||
return fits, bool(owners & tokens), not _foreign_tree(path, owners | wanted), len(wanted & tokens)
|
||||
return fits, bool(owners & tokens), len(wanted & tokens)
|
||||
|
||||
return sorted(paths, key=score, reverse=True)
|
||||
|
||||
@@ -762,14 +716,9 @@ def resolve_local_file(
|
||||
# stops describing this file and starts describing whichever emulator
|
||||
# happens to store the same asset tree.
|
||||
hint_parts = dest_hint.split("/")
|
||||
# The asking profile and the destination's own directories both name
|
||||
# whose tree a copy may come from.
|
||||
owners = _owner_tokens(file_entry) | _affinity_tokens("/".join(hint_parts[:-1]))
|
||||
for start in range(min(2, len(hint_parts) - 1)):
|
||||
tail = "/".join(hint_parts[start:])
|
||||
candidates = _rank_tail_candidates(
|
||||
by_path_suffix.get(tail, []), start, tail, files_db, owners
|
||||
)
|
||||
candidates = by_path_suffix.get(tail, [])
|
||||
if start and len(candidates) > 1:
|
||||
continue
|
||||
# The index names the file this destination designates. Not on
|
||||
@@ -780,6 +729,20 @@ def resolve_local_file(
|
||||
for h in candidates
|
||||
):
|
||||
own_file_absent = True
|
||||
if len(candidates) > 1:
|
||||
depth = len(tail.split("/"))
|
||||
# At equal depth the owner's own copy: super3's Games.xml was
|
||||
# served to Supermodel-Dojo by index order alone.
|
||||
owners = _owner_tokens(file_entry)
|
||||
candidates = sorted(
|
||||
candidates,
|
||||
key=lambda h: (
|
||||
len(files_db.get(h, {}).get("path", "").split("/")) - depth,
|
||||
not owners & _affinity_tokens(
|
||||
files_db.get(h, {}).get("path", "").rsplit("/", 1)[0]
|
||||
),
|
||||
),
|
||||
)
|
||||
for match_sha1 in candidates:
|
||||
if match_sha1 not in files_db:
|
||||
continue
|
||||
@@ -912,7 +875,7 @@ def resolve_local_file(
|
||||
|
||||
# MAME clone fallback: if a file was deduped, resolve via canonical
|
||||
if _depth < 3 and not has_strong_hash:
|
||||
clone_map = get_mame_clone_map(db.get("bios_dir"))
|
||||
clone_map = get_mame_clone_map()
|
||||
canonical = clone_map.get(name)
|
||||
if canonical and canonical != name:
|
||||
canonical_entry = {"name": canonical}
|
||||
@@ -960,34 +923,28 @@ def resolve_local_file(
|
||||
return None, "not_found"
|
||||
|
||||
|
||||
_mame_clone_map_cache: dict[str, dict[str, str]] = {}
|
||||
_mame_clone_map_cache: dict[str, str] | None = None
|
||||
|
||||
|
||||
def get_mame_clone_map(bios_dir: str | None = None) -> dict[str, str]:
|
||||
"""The MAME clone map (clone_name -> canonical_name) of a scanned tree.
|
||||
|
||||
dedup.py writes _mame_clones.json beside the tree it scanned, and the
|
||||
map of one tree says nothing about another: read from the repository
|
||||
root whatever ``--bios-dir`` said, an alternate tree's renamed clones
|
||||
fell out of its packs. Without a tree the repository's own is read.
|
||||
"""
|
||||
if bios_dir is None:
|
||||
bios_dir = os.path.join(
|
||||
os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "bios"
|
||||
)
|
||||
clone_path = os.path.join(os.path.dirname(os.path.abspath(bios_dir)), "_mame_clones.json")
|
||||
cached = _mame_clone_map_cache.get(clone_path)
|
||||
if cached is not None:
|
||||
return cached
|
||||
clone_map: dict[str, str] = {}
|
||||
def get_mame_clone_map() -> dict[str, str]:
|
||||
"""Load and cache the MAME clone map (clone_name -> canonical_name)."""
|
||||
global _mame_clone_map_cache
|
||||
if _mame_clone_map_cache is not None:
|
||||
return _mame_clone_map_cache
|
||||
clone_path = os.path.join(
|
||||
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
||||
"_mame_clones.json",
|
||||
)
|
||||
if os.path.exists(clone_path):
|
||||
with open(clone_path) as f:
|
||||
data = json.load(f)
|
||||
_mame_clone_map_cache = {}
|
||||
for canonical, info in data.items():
|
||||
for clone in info.get("clones", []):
|
||||
clone_map[clone] = canonical
|
||||
_mame_clone_map_cache[clone_path] = clone_map
|
||||
return clone_map
|
||||
_mame_clone_map_cache[clone] = canonical
|
||||
else:
|
||||
_mame_clone_map_cache = {}
|
||||
return _mame_clone_map_cache
|
||||
|
||||
|
||||
|
||||
@@ -1000,27 +957,6 @@ class ProfileSelectionError(ValueError):
|
||||
"""A named profile cannot answer for itself."""
|
||||
|
||||
|
||||
def profiles_for_systems(
|
||||
profiles: dict, system_ids: list[str], standalone: bool = False
|
||||
) -> list[str]:
|
||||
"""Profile keys that serve any of the systems, spelled either way.
|
||||
|
||||
Compared on the normalized id: stella2014 writes atari_2600 where every
|
||||
other profile writes atari-2600, and an exact match built two different
|
||||
packs under one file name, the second replacing the first.
|
||||
"""
|
||||
wanted = {_norm_system_id(sid) for sid in system_ids}
|
||||
matching = []
|
||||
for name, profile in sorted(profiles.items()):
|
||||
if profile.get("type") in ("launcher", "alias", "test"):
|
||||
continue
|
||||
if standalone and "standalone" not in profile.get("type", "libretro"):
|
||||
continue
|
||||
if wanted & {_norm_system_id(sid) for sid in profile.get("systems", [])}:
|
||||
matching.append(name)
|
||||
return matching
|
||||
|
||||
|
||||
def select_emulator_profiles(
|
||||
profile_names: list[str],
|
||||
all_profiles: dict,
|
||||
@@ -1709,24 +1645,17 @@ def list_emulator_profiles(emulators_dir: str, skip_aliases: bool = True) -> Non
|
||||
|
||||
|
||||
def list_system_ids(emulators_dir: str) -> None:
|
||||
"""Print available system IDs with emulator count.
|
||||
|
||||
Counted on the normalized id, as --system selects: listed per spelling,
|
||||
3do showed two emulators and panasonic-3do six for one system.
|
||||
"""
|
||||
"""Print available system IDs with emulator count."""
|
||||
profiles = load_emulator_profiles(emulators_dir)
|
||||
system_emus: dict[str, set[str]] = {}
|
||||
spelling: dict[str, str] = {}
|
||||
for name, p in sorted(profiles.items()):
|
||||
system_emus: dict[str, list[str]] = {}
|
||||
for name, p in profiles.items():
|
||||
if p.get("type") in ("alias", "test", "launcher"):
|
||||
continue
|
||||
for sys_id in p.get("systems", []):
|
||||
key = _norm_system_id(sys_id)
|
||||
system_emus.setdefault(key, set()).add(name)
|
||||
spelling.setdefault(key, sys_id)
|
||||
for key in sorted(system_emus, key=lambda k: spelling[k]):
|
||||
count = len(system_emus[key])
|
||||
print(f" {spelling[key]:35s} ({count} emulator{'s' if count > 1 else ''})")
|
||||
system_emus.setdefault(sys_id, []).append(name)
|
||||
for sys_id in sorted(system_emus):
|
||||
count = len(system_emus[sys_id])
|
||||
print(f" {sys_id:35s} ({count} emulator{'s' if count > 1 else ''})")
|
||||
|
||||
|
||||
def list_platform_system_ids(platform_name: str, platforms_dir: str) -> None:
|
||||
@@ -1802,8 +1731,6 @@ from ziptools import ( # noqa: E402,F401
|
||||
)
|
||||
from artifacts import ( # noqa: E402,F401
|
||||
write_if_changed,
|
||||
write_text_atomic,
|
||||
copy_file_atomic,
|
||||
ArtifactLockBusy,
|
||||
artifact_lock,
|
||||
hold_artifact_lock,
|
||||
|
||||
@@ -70,25 +70,6 @@ def load_platform_files(
|
||||
return declared, platform_data_dirs
|
||||
|
||||
|
||||
def declared_names(platforms_dir: str, platforms: list[str] | None, db: dict) -> set[str]:
|
||||
"""Every name the platforms declare, under every spelling the database knows.
|
||||
|
||||
The report judged "undeclared" per normalized system id, which does not
|
||||
bring bk and elektronika-bk, or nintendo-sgb and nintendo-super-game-boy,
|
||||
together: 646 entries whose exact name a platform declares were counted
|
||||
undeclared, while verify and the site read one flat set.
|
||||
"""
|
||||
from common import expand_platform_declared_names
|
||||
|
||||
names: set[str] = set()
|
||||
for platform_name in platforms or list_registered_platforms(
|
||||
platforms_dir, include_archived=True
|
||||
):
|
||||
config = load_platform_config(platform_name, platforms_dir)
|
||||
names.update(expand_platform_declared_names(config, db))
|
||||
return names
|
||||
|
||||
|
||||
def _build_supplemental_index(
|
||||
data_root: str = "data", bios_root: str = "bios"
|
||||
) -> set[str]:
|
||||
@@ -154,7 +135,6 @@ def _resolve_source(
|
||||
by_path_suffix: dict | None = None,
|
||||
file_entry: dict | None = None,
|
||||
db_files: dict | None = None,
|
||||
bios_dir: str | None = None,
|
||||
) -> str | None:
|
||||
"""Return the source category for a file, or None if not found.
|
||||
|
||||
@@ -197,7 +177,7 @@ def _resolve_source(
|
||||
return "bios"
|
||||
# bios/ under the canonical MAME set name, as resolve_local_file does:
|
||||
# a renamed archive is held once, under the name the dedup kept.
|
||||
canonical = get_mame_clone_map(bios_dir).get(fname)
|
||||
canonical = get_mame_clone_map().get(fname)
|
||||
if canonical and canonical != fname:
|
||||
if canonical in by_name and _name_hit(canonical):
|
||||
return "bios"
|
||||
@@ -289,7 +269,6 @@ def entry_source(f: dict, index: dict) -> str | None:
|
||||
continue
|
||||
source = _resolve_source(
|
||||
candidate, by_name, by_name_lower, data_names, by_path_suffix, f, db_files,
|
||||
index.get("bios_dir"),
|
||||
)
|
||||
if source is not None:
|
||||
return source
|
||||
@@ -534,7 +513,6 @@ def cross_reference(
|
||||
"db_files": db_files,
|
||||
"data_names": data_names,
|
||||
"all_declared": all_declared,
|
||||
"bios_dir": db.get("bios_dir"),
|
||||
}
|
||||
for emu_name, profile in profiles.items():
|
||||
_cross_reference_profile(
|
||||
@@ -633,13 +611,13 @@ def main():
|
||||
print("No emulator profiles found.", file=sys.stderr)
|
||||
return
|
||||
|
||||
selected = [args.platform] if args.platform else None
|
||||
declared, plat_data_dirs = load_platform_files(args.platforms_dir, selected)
|
||||
declared, plat_data_dirs = load_platform_files(
|
||||
args.platforms_dir, [args.platform] if args.platform else None
|
||||
)
|
||||
db = load_database(args.db)
|
||||
data_names = _build_supplemental_index()
|
||||
report = cross_reference(
|
||||
profiles, declared, db, plat_data_dirs, data_names,
|
||||
all_declared=declared_names(args.platforms_dir, selected, db),
|
||||
standalone_cores=standalone_cores,
|
||||
)
|
||||
|
||||
|
||||
@@ -62,29 +62,10 @@ class Exporter(BaseExporter):
|
||||
# The data block is a fraction of the file; the rest is the checker.
|
||||
return True
|
||||
|
||||
def _bios_files(self, files: list[NativeFile], before: dict | None = None) -> str:
|
||||
return ", ".join(self._bios_items(files, before))
|
||||
def _bios_files(self, files: list[NativeFile]) -> str:
|
||||
return ", ".join(self._bios_items(files))
|
||||
|
||||
@staticmethod
|
||||
def _runner_keys(item: dict | None) -> list[str]:
|
||||
"""The emulator and core checkBios reads through coreExists().
|
||||
|
||||
Not in our model, so they travel from the maintainer's own entry: a
|
||||
rewritten item without them made the checker treat the BIOS as
|
||||
wanted by every build (vectrex, tvc, macintosh, bk).
|
||||
"""
|
||||
return [
|
||||
f'"{key}": "{item[key]}"'
|
||||
for key in ("emulator", "core")
|
||||
if item and isinstance(item.get(key), str)
|
||||
]
|
||||
|
||||
def _bios_items(self, files: list[NativeFile], before: dict | None = None) -> list[str]:
|
||||
originals = {
|
||||
str(item.get("file", "")): item
|
||||
for item in (before or {}).get("biosFiles", [])
|
||||
if isinstance(item, dict)
|
||||
}
|
||||
def _bios_items(self, files: list[NativeFile]) -> list[str]:
|
||||
parts: list[str] = []
|
||||
for fe in files:
|
||||
# The platform states an unhashed file as an empty md5 rather
|
||||
@@ -99,25 +80,14 @@ class Exporter(BaseExporter):
|
||||
zipped = fe.native("zipped_file", "")
|
||||
if zipped:
|
||||
item.append(f'"zippedFile": "{zipped}"')
|
||||
item.extend(self._runner_keys(originals.get(path)))
|
||||
parts.append("{ " + ", ".join(item) + " }")
|
||||
return parts
|
||||
|
||||
@staticmethod
|
||||
def _own_entry(system: NativeSystem, before: dict | None) -> dict:
|
||||
"""The maintainer's entry for this system, out of the parsed pair."""
|
||||
inner = (before or {}).get(system.native_id)
|
||||
return inner if isinstance(inner, dict) else {}
|
||||
|
||||
def _entry_line(
|
||||
self, system: NativeSystem, files: list[NativeFile], before: dict | None = None
|
||||
) -> str:
|
||||
own = self._own_entry(system, before)
|
||||
runner = "".join(f"{key}, " for key in self._runner_keys(own))
|
||||
def _entry_line(self, system: NativeSystem, files: list[NativeFile]) -> str:
|
||||
return (
|
||||
f'{_INDENT}"{system.native_id}": '
|
||||
f'{{ "name": "{self.display_name(system)}", {runner}'
|
||||
f'"biosFiles": [ {self._bios_files(files, own)} ] }},'
|
||||
f'{{ "name": "{self.display_name(system)}", '
|
||||
f'"biosFiles": [ {self._bios_files(files)} ] }},'
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@@ -157,17 +127,14 @@ class Exporter(BaseExporter):
|
||||
) -> list[str]:
|
||||
"""One entry, keeping the layout and the notes it was written with."""
|
||||
trailing, leading = self._item_notes(original)
|
||||
before = self._parse_entry(original)
|
||||
own = self._own_entry(system, before)
|
||||
items = self._bios_items(files, own)
|
||||
items = self._bios_items(files)
|
||||
one_line = len(original) == 1 and not trailing and not leading
|
||||
if one_line:
|
||||
return [self._entry_line(system, files, before)]
|
||||
return [self._entry_line(system, files)]
|
||||
|
||||
runner = "".join(f"{key}, " for key in self._runner_keys(own))
|
||||
head = (
|
||||
f'{_INDENT}"{system.native_id}": '
|
||||
f'{{ "name": "{self.display_name(system)}", {runner}"biosFiles": ['
|
||||
f'{{ "name": "{self.display_name(system)}", "biosFiles": ['
|
||||
)
|
||||
pad = " " * (len(_INDENT) + 4)
|
||||
lines = [head]
|
||||
@@ -277,8 +244,8 @@ class Exporter(BaseExporter):
|
||||
continue
|
||||
written.add(key)
|
||||
original_lines = body[start:end + 1]
|
||||
replacement = self._entry_line(*pair)
|
||||
before = self._parse_entry(original_lines)
|
||||
replacement = self._entry_line(*pair, before)
|
||||
after = self._parse_entry([replacement])
|
||||
if before is not None and before == after:
|
||||
# Nothing changed: keep the maintainer's own lines, comments
|
||||
|
||||
@@ -82,15 +82,8 @@ class Exporter(BaseExporter):
|
||||
for fe in system.files:
|
||||
# A SHA1 BizHawk declares is BizHawk's own code, the authority
|
||||
# for its cores: the truth's MSX.rom came from FBNeo and would
|
||||
# have made MSXHawk refuse the dump it accepts. An entry the
|
||||
# database never declared (can_add refuses it) names no call
|
||||
# of its own either: by name it would rewrite the homonym of
|
||||
# another system.
|
||||
if (
|
||||
fe.platform is not None
|
||||
and fe.hash("sha1")
|
||||
and not _hash_values(fe.platform, "sha1")
|
||||
):
|
||||
# have made MSXHawk refuse the dump it accepts.
|
||||
if fe.hash("sha1") and not _hash_values(fe.platform or {}, "sha1"):
|
||||
seen.setdefault(fe.name.casefold(), []).append(fe)
|
||||
resolved: dict[str, NativeFile] = {}
|
||||
for name, entries in seen.items():
|
||||
|
||||
@@ -176,8 +176,8 @@ class Exporter(BaseExporter):
|
||||
return nested, "bios"
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def _merge(cls, existing: object, ours: list[OrderedDict]) -> list[OrderedDict]:
|
||||
@staticmethod
|
||||
def _merge(existing: object, ours: list[OrderedDict]) -> list[OrderedDict]:
|
||||
"""Correct the component's own list; never replace it.
|
||||
|
||||
Assigning our entries wholesale dropped every file RetroDECK declares
|
||||
@@ -206,9 +206,6 @@ class Exporter(BaseExporter):
|
||||
for system in _systems_of(entry):
|
||||
key = (str(entry.get("filename", "")), system)
|
||||
seen_keys[key] = seen_keys.get(key, 0) + 1
|
||||
# A hash no revision shares is a revision the platform does not
|
||||
# know: appended, not counted corrected against entries kept as
|
||||
# they were (the 64DD IPL declared twice, both contradicted).
|
||||
for entry in entries:
|
||||
keys = _matching_keys(entry, by_key)
|
||||
other_revision = (
|
||||
@@ -216,17 +213,18 @@ class Exporter(BaseExporter):
|
||||
and seen_keys.get(keys[0], 0) > 1
|
||||
and not _md5_set(entry.get("md5")) & _md5_set(by_key[keys[0]].get("md5"))
|
||||
)
|
||||
if other_revision or not keys:
|
||||
if other_revision:
|
||||
merged.append(OrderedDict(entry))
|
||||
corrected.update(keys)
|
||||
continue
|
||||
if not keys:
|
||||
merged.append(OrderedDict(entry))
|
||||
continue
|
||||
declared = entry.get("system")
|
||||
combined = OrderedDict(entry)
|
||||
# Only what the format says it corrects: the maintainer's
|
||||
# description and search paths are theirs (neogeo.zip lost its
|
||||
# four roms directories to a path that does not exist).
|
||||
combined.update(
|
||||
(field, value) for field, value in by_key[keys[0]].items()
|
||||
if field in cls.carries() and not (field == "system" and declared)
|
||||
if not (field == "system" and declared)
|
||||
)
|
||||
merged.append(combined)
|
||||
corrected.update(keys)
|
||||
|
||||
@@ -386,9 +386,6 @@ def main():
|
||||
"generated_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
||||
"total_files": len(files),
|
||||
"total_size": total_size,
|
||||
# The tree these paths belong to: dedup writes _mame_clones.json
|
||||
# beside it, and the resolver reads the map there, not at the repo root.
|
||||
"bios_dir": os.path.normpath(args.bios_dir),
|
||||
"files": files,
|
||||
"indexes": indexes,
|
||||
}
|
||||
|
||||
+26
-55
@@ -31,8 +31,6 @@ sys.path.insert(0, os.path.dirname(__file__))
|
||||
from common import resolve_platform_cores as _platform_cores
|
||||
from common import (
|
||||
apply_target_overrides,
|
||||
profiles_for_systems,
|
||||
write_text_atomic,
|
||||
artifact_lock,
|
||||
ArtifactLockBusy,
|
||||
build_target_cores_cache,
|
||||
@@ -725,13 +723,10 @@ def generate_pack(
|
||||
|
||||
preferred_entries: dict[str, int] = {}
|
||||
if source != "truth":
|
||||
# Decided among the declarations this pack keeps: a preferred entry
|
||||
# that --required-only drops must not silence its required twin, or
|
||||
# the destination leaves the pack without a trace (RetroDECK's four
|
||||
# Atari 5200 ROMs, optional under atari-400-800 and required under
|
||||
# atari-5200). No data directories: the choice is about the baseline.
|
||||
# A manifest lists the full set and carries no data directories, so
|
||||
# the preference is resolved with neither narrowing nor a cache.
|
||||
preferred_entries = _preferred_entries(
|
||||
pack_systems, db, bios_dir, base_dest, required_only,
|
||||
pack_systems, db, bios_dir, base_dest, False,
|
||||
zip_contents, None, offline,
|
||||
)
|
||||
|
||||
@@ -1561,7 +1556,16 @@ def generate_system_pack(
|
||||
) -> str | None:
|
||||
"""Generate a ZIP pack for all emulators supporting given system IDs."""
|
||||
profiles = load_emulator_profiles(emulators_dir)
|
||||
matching = profiles_for_systems(profiles, system_ids, standalone)
|
||||
matching = []
|
||||
for name, profile in sorted(profiles.items()):
|
||||
if profile.get("type") in ("launcher", "alias", "test"):
|
||||
continue
|
||||
emu_systems = set(profile.get("systems", []))
|
||||
if emu_systems & set(system_ids):
|
||||
ptype = profile.get("type", "libretro")
|
||||
if standalone and "standalone" not in ptype:
|
||||
continue
|
||||
matching.append(name)
|
||||
|
||||
if not matching:
|
||||
all_systems: set[str] = set()
|
||||
@@ -1583,9 +1587,10 @@ def generate_system_pack(
|
||||
)
|
||||
return None
|
||||
|
||||
# Named like the platform's --system tag: one spelling of a system is
|
||||
# one file name, and one selection.
|
||||
sys_display = "_".join(_system_display_name(sid) for sid in system_ids)
|
||||
# Use system-based ZIP name
|
||||
sys_display = "_".join(
|
||||
_name_part("_".join(w.title() for w in sid.split("-"))) for sid in system_ids
|
||||
)
|
||||
# Built in a scratch directory: under its emulator name in output_dir it
|
||||
# overwrote, then carried away, an --emulator pack already there.
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
@@ -1821,16 +1826,7 @@ def generate_md5_pack(
|
||||
if emulator_name and emulators_dir:
|
||||
profiles = load_emulator_profiles(emulators_dir, skip_aliases=False)
|
||||
if emulator_name in profiles:
|
||||
# The same gate as --emulator: a core with no standalone build
|
||||
# has no standalone layout, and a pack named for one would be
|
||||
# the libretro pack under a name that promises otherwise.
|
||||
try:
|
||||
(_name, profile), = select_emulator_profiles(
|
||||
[emulator_name], profiles, standalone
|
||||
)
|
||||
except ProfileSelectionError as exc:
|
||||
print(f"Error: {exc}", file=sys.stderr)
|
||||
return None
|
||||
profile = profiles[emulator_name]
|
||||
emu_display = profile.get("emulator", emulator_name)
|
||||
emu_pack_structure = profile.get("pack_structure")
|
||||
for fe in profile.get("files", []):
|
||||
@@ -2003,7 +1999,9 @@ def generate_target_manifests(targets_dir: str, output_dir: str) -> None:
|
||||
result[alias] = result[target_name]
|
||||
|
||||
out_path = Path(output_dir) / f"{yml_file.stem}.json"
|
||||
write_text_atomic(out_path, json.dumps(result, indent=2, sort_keys=True) + "\n")
|
||||
with open(out_path, "w") as f:
|
||||
json.dump(result, f, indent=2, sort_keys=True)
|
||||
f.write("\n")
|
||||
count += 1
|
||||
print(f" {yml_file.stem}: {len(result)} targets")
|
||||
print(f"Generated {count} target manifest(s) in {output_dir}")
|
||||
@@ -2105,7 +2103,8 @@ def _write_manifest_if_changed(path: str, manifest: dict) -> None:
|
||||
new_cmp = {k: v for k, v in manifest.items() if k != "generated"}
|
||||
if old_cmp == new_cmp:
|
||||
return # no content change, keep existing timestamp
|
||||
write_text_atomic(path, new_json)
|
||||
with open(path, "w") as f:
|
||||
f.write(new_json)
|
||||
|
||||
|
||||
def _run_manifest_mode(
|
||||
@@ -2298,11 +2297,6 @@ def _pack_label(source: str, required_only: bool) -> str:
|
||||
return f"[source={source}, required]" if required_only else f"[source={source}]"
|
||||
|
||||
|
||||
def _produced(split: bool, zip_path, zip_paths) -> bool:
|
||||
"""Whether the build wrote what the run asked for."""
|
||||
return bool(zip_paths if split else zip_path)
|
||||
|
||||
|
||||
def _run_platform_packs(
|
||||
args,
|
||||
groups,
|
||||
@@ -2337,8 +2331,6 @@ def _run_platform_packs(
|
||||
else:
|
||||
print(f"\nGenerating pack for {representative} {_pack_label(source, required_only)}...")
|
||||
|
||||
zip_path = None
|
||||
zip_paths: list = []
|
||||
try:
|
||||
tc = target_cores_cache.get(representative) if args.target else None
|
||||
if args.split:
|
||||
@@ -2407,12 +2399,6 @@ def _run_platform_packs(
|
||||
except (FileNotFoundError, OSError, yaml.YAMLError) as e:
|
||||
print(f" ERROR: {e}")
|
||||
failed.append(representative)
|
||||
else:
|
||||
# A builder that declined (no system left under the target)
|
||||
# produced nothing: the run must say so, not verify whatever
|
||||
# the output directory already held and exit 0.
|
||||
if not _produced(args.split, zip_path, zip_paths):
|
||||
failed.append(representative)
|
||||
|
||||
print("\nVerifying packs and generating manifests...")
|
||||
skip_conf = bool(system_filter or args.split)
|
||||
@@ -3092,18 +3078,6 @@ def _manifest_region_drops(
|
||||
)
|
||||
|
||||
|
||||
def _record_unplaceable(unplaceable: list[dict], record_omission) -> None:
|
||||
"""Held by the collection, nowhere to put it on this platform.
|
||||
|
||||
Said in the manifest, where RomM's DOSBox Pure ROMs were simply absent.
|
||||
"""
|
||||
for u in unplaceable:
|
||||
record_omission(
|
||||
u.get("path") or u.get("name", ""), u, u.get("system") or "",
|
||||
"no_platform_slug", [u.get("emulator", "")],
|
||||
)
|
||||
|
||||
|
||||
def generate_manifest(
|
||||
platform_name: str,
|
||||
platforms_dir: str,
|
||||
@@ -3213,10 +3187,10 @@ def generate_manifest(
|
||||
# constrained sibling claim it; without the same rule here the
|
||||
# manifest named whatever answered to the name, so the pack and the
|
||||
# installer disagreed about which file a destination means.
|
||||
# Decided among the declarations this manifest keeps, as the pack
|
||||
# does; a manifest carries no data directories.
|
||||
# A manifest lists the full set and carries no data directories, so
|
||||
# the preference is resolved with neither narrowing nor a cache.
|
||||
preferred_entries = _preferred_entries(
|
||||
pack_systems, db, bios_dir, base_dest, required_only,
|
||||
pack_systems, db, bios_dir, base_dest, False,
|
||||
zip_contents, None, offline,
|
||||
)
|
||||
for sys_id, system in sorted(pack_systems.items()):
|
||||
@@ -3345,7 +3319,6 @@ def generate_manifest(
|
||||
seen_lower.add(dedup_key.lower())
|
||||
|
||||
# Phase 2: core complement (emulator extras)
|
||||
unplaceable: list[dict] = []
|
||||
if source != "platform":
|
||||
core_files = _collect_emulator_extras(
|
||||
config,
|
||||
@@ -3356,11 +3329,9 @@ def generate_manifest(
|
||||
emu_profiles,
|
||||
target_cores=target_cores,
|
||||
include_all=(source == "truth"),
|
||||
unplaceable=unplaceable,
|
||||
)
|
||||
else:
|
||||
core_files = []
|
||||
_record_unplaceable(unplaceable, record_omission)
|
||||
total_size += _manifest_core_entries(
|
||||
core_files, config, db, bios_dir, base_dest, repo_root,
|
||||
zip_contents, offline, region_drops, case_insensitive,
|
||||
|
||||
+15
-54
@@ -134,42 +134,6 @@ def _map_emulator_to_slug(
|
||||
return sys_to_slug.get(target, "")
|
||||
return ""
|
||||
|
||||
def _slug_for(
|
||||
u: dict, profiles: dict, platform_systems: set, norm_map: dict, sys_to_slug: dict
|
||||
) -> str:
|
||||
"""The platform slug a report entry's emulator files under, or empty."""
|
||||
emu_name = u.get("profile") or u.get("emulator", "")
|
||||
profile = profiles.get(emu_name, {})
|
||||
if not profile:
|
||||
# The report names the display name where the key failed.
|
||||
profile = next(
|
||||
(pp for pp in profiles.values() if pp.get("emulator") == emu_name), {}
|
||||
)
|
||||
return _map_emulator_to_slug(profile, platform_systems, norm_map, sys_to_slug)
|
||||
|
||||
|
||||
def unplaceable_extras(config: dict, undeclared: list[dict], profiles: dict) -> set:
|
||||
"""Keys of the report entries a slug-based platform cannot place.
|
||||
|
||||
RomM files every BIOS under the slug of its system and knows no slug
|
||||
for DOS or for the arcade sets MAME 2003 reads; the builder skipped
|
||||
those entries and the report counted them in the pack.
|
||||
"""
|
||||
from common import _norm_system_id
|
||||
|
||||
is_slug_based, sys_to_slug = _detect_slug_structure(config)
|
||||
if not is_slug_based:
|
||||
return set()
|
||||
platform_systems = set(config.get("systems", {}).keys())
|
||||
norm_map = {_norm_system_id(sid): sid for sid in platform_systems}
|
||||
return {
|
||||
(u.get("emulator", ""), u.get("name", ""), u.get("path") or "")
|
||||
for u in undeclared
|
||||
if u.get("in_repo")
|
||||
and not _slug_for(u, profiles, platform_systems, norm_map, sys_to_slug)
|
||||
}
|
||||
|
||||
|
||||
def _agnostic_scan_extras(
|
||||
profiles: dict,
|
||||
relevant: set,
|
||||
@@ -448,18 +412,9 @@ def _collect_emulator_extras(
|
||||
emu_profiles: dict | None = None,
|
||||
target_cores: set[str] | None = None,
|
||||
include_all: bool = False,
|
||||
claimants: dict[tuple[str, str, str], str] | None = None,
|
||||
unplaceable: list[dict] | None = None,
|
||||
) -> list[dict]:
|
||||
"""Collect core requirement files from emulator profiles not in the platform pack.
|
||||
|
||||
``claimants`` receives the destination of every report entry that
|
||||
asked for one, the ones a sibling already took included: the region
|
||||
report withdraws by that key, and an entry without one stayed listed
|
||||
after the builder had withdrawn its destination. ``unplaceable``
|
||||
receives the entries a slug-based platform cannot place, so the
|
||||
manifest and the report can say so instead of counting them packed.
|
||||
|
||||
Uses the same system-overlap matching as verify.py cross-reference:
|
||||
- Matches emulators by shared system IDs with the platform
|
||||
- Filters mode: standalone, type: launcher, type: alias
|
||||
@@ -517,18 +472,25 @@ def _collect_emulator_extras(
|
||||
|
||||
# Slug-based platforms: prefix dest with system slug
|
||||
if is_slug_based:
|
||||
slug = _slug_for(u, profiles, platform_systems, norm_map, sys_to_slug)
|
||||
emu_name = u.get("profile") or u.get("emulator", "")
|
||||
profile = profiles.get(emu_name, {})
|
||||
# Try finding profile by display name if key lookup failed
|
||||
if not profile:
|
||||
for pn, pp in profiles.items():
|
||||
if pp.get("emulator") == emu_name:
|
||||
profile = pp
|
||||
break
|
||||
slug = _map_emulator_to_slug(
|
||||
profile,
|
||||
platform_systems,
|
||||
norm_map,
|
||||
sys_to_slug,
|
||||
)
|
||||
if not slug:
|
||||
if unplaceable is not None:
|
||||
unplaceable.append(u)
|
||||
continue
|
||||
continue # can't place without slug
|
||||
dest = f"{slug}/{dest}"
|
||||
|
||||
full_dest = f"{extras_prefix}/{dest}" if extras_prefix else dest
|
||||
if claimants is not None:
|
||||
claimants[(u.get("emulator", ""), u.get("name", ""), u.get("path") or "")] = (
|
||||
sanitize_pack_path(dest)
|
||||
)
|
||||
if full_dest in seen_dests:
|
||||
continue
|
||||
seen_dests.add(full_dest)
|
||||
@@ -719,7 +681,6 @@ def platform_region_groups(
|
||||
emu_profiles,
|
||||
target_cores=target_cores,
|
||||
include_all=include_all,
|
||||
claimants=extra_dests,
|
||||
)
|
||||
for extra in _kept(extras, required_only):
|
||||
dest = sanitize_pack_path(extra.get("destination", extra.get("name", "")))
|
||||
|
||||
+1
-5
@@ -198,13 +198,9 @@ def check_consistency(verify_output: str, pack_output: str) -> bool:
|
||||
f" {v_label}: verify {v_ok}/{v_total} == pack {p_ok}/{p_total} OK"
|
||||
)
|
||||
else:
|
||||
# The invariant runs both ways: a builder more optimistic
|
||||
# than the report ships files the report never vouched for.
|
||||
print(
|
||||
f" {v_label}: MISMATCH pack {p_ok}/{p_total} OK "
|
||||
f"> verify {v_ok}/{v_total}"
|
||||
f" {v_label}: verify {v_ok}/{v_total}, pack {p_ok}/{p_total} OK (pack resolves more)"
|
||||
)
|
||||
all_ok = False
|
||||
else:
|
||||
print(f" {v_label}: {v_ok}/{v_total} (no separate pack)")
|
||||
all_ok = False
|
||||
|
||||
@@ -88,13 +88,6 @@ def build_report(db: dict, snapshots: dict, members: dict | None = None) -> dict
|
||||
by_md5_size = {
|
||||
(entry.get("md5", ""), entry.get("size", 0)) for entry in by_sha1.values()
|
||||
}
|
||||
# Redump lists some dumps by crc32 alone (ps2-0101jd-20030110, DTL-H10100):
|
||||
# collected as a loose file it would stay MISSING for good without this.
|
||||
by_crc32_size = {
|
||||
(str(entry.get("crc32", "")).lower(), entry.get("size", 0))
|
||||
for entry in by_sha1.values()
|
||||
if entry.get("crc32")
|
||||
}
|
||||
|
||||
report = {}
|
||||
for source, snapshot in sorted(snapshots.items()):
|
||||
@@ -105,7 +98,6 @@ def build_report(db: dict, snapshots: dict, members: dict | None = None) -> dict
|
||||
if (
|
||||
entry.get("sha1") in by_sha1
|
||||
or (entry.get("md5"), entry.get("size")) in by_md5_size
|
||||
or (str(entry.get("crc32") or "").lower(), entry.get("size")) in by_crc32_size
|
||||
or _held_in_archive(entry, members)
|
||||
):
|
||||
matched += 1
|
||||
|
||||
+20
-18
@@ -19,7 +19,7 @@ from pathlib import Path
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||
|
||||
import region
|
||||
from common import load_database, load_emulator_profiles, resolve_local_file
|
||||
from common import load_emulator_profiles, load_database, parse_md5_list
|
||||
|
||||
# No-Intro filename tokens: full English territory names.
|
||||
NOINTRO = {
|
||||
@@ -100,21 +100,24 @@ def catalog_regions(db: dict) -> dict[str, tuple[set[str], str]]:
|
||||
return out
|
||||
|
||||
|
||||
def resolve_sha1(file_entry: dict, db: dict, by_path: dict | None = None) -> str | None:
|
||||
"""The repo SHA1 a profile entry resolves to, by the pack's own resolver.
|
||||
|
||||
A private resolver read sha1, then md5, then a lone name: entries that
|
||||
identify their file by path (Dolphin's three IPL.bin), by crc32
|
||||
(geargrafx) or by sha256 (mednafen) were never checked at all.
|
||||
"""
|
||||
if by_path is None:
|
||||
by_path = {entry.get("path", ""): sha1 for sha1, entry in db["files"].items()}
|
||||
path, status = resolve_local_file(
|
||||
file_entry, db, dest_hint=file_entry.get("path") or ""
|
||||
)
|
||||
if not path or status in ("not_found", "hash_mismatch"):
|
||||
return None
|
||||
return by_path.get(path)
|
||||
def resolve_sha1(file_entry: dict, db: dict) -> str | None:
|
||||
"""Resolve a profile file entry to a repo SHA1, or None when ambiguous."""
|
||||
files = db["files"]
|
||||
indexes = db["indexes"]
|
||||
raw_sha1 = file_entry.get("sha1")
|
||||
declared = raw_sha1 if isinstance(raw_sha1, list) else [raw_sha1]
|
||||
sha1_hits = [str(value).lower() for value in declared if value]
|
||||
sha1_hits = [value for value in sha1_hits if value in files]
|
||||
if len(sha1_hits) == 1:
|
||||
return sha1_hits[0]
|
||||
for md5 in parse_md5_list(file_entry.get("md5")):
|
||||
hit = indexes["by_md5"].get(md5)
|
||||
if hit:
|
||||
return hit
|
||||
hits = indexes["by_name"].get(file_entry.get("name", ""), [])
|
||||
if isinstance(hits, str):
|
||||
hits = [hits]
|
||||
return hits[0] if len(hits) == 1 else None
|
||||
|
||||
|
||||
def build_report(profiles: dict, db: dict) -> dict:
|
||||
@@ -127,7 +130,6 @@ def build_report(profiles: dict, db: dict) -> dict:
|
||||
for slugs, _name in catalog.values():
|
||||
vocabulary.update(slugs)
|
||||
|
||||
by_path = {entry.get("path", ""): sha1 for sha1, entry in db["files"].items()}
|
||||
for emu_name, profile in sorted(profiles.items()):
|
||||
if profile.get("type") in ("launcher", "alias"):
|
||||
continue
|
||||
@@ -135,7 +137,7 @@ def build_report(profiles: dict, db: dict) -> dict:
|
||||
if not isinstance(f, dict) or not f.get("region"):
|
||||
continue
|
||||
declared = region.normalize_declared(f["region"])
|
||||
sha1 = resolve_sha1(dict(f, source_profile=emu_name), db, by_path)
|
||||
sha1 = resolve_sha1(f, db)
|
||||
if not sha1 or sha1 not in catalog:
|
||||
unchecked += 1
|
||||
continue
|
||||
|
||||
@@ -24,7 +24,6 @@ import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
import split_pack
|
||||
from common import write_text_atomic
|
||||
|
||||
RECORD = "release.json"
|
||||
|
||||
@@ -125,7 +124,7 @@ def main() -> int:
|
||||
print(f"Error: {line}", file=sys.stderr)
|
||||
if mismatches:
|
||||
return 1
|
||||
write_text_atomic(str(args.output), json.dumps(record, indent=2) + "\n")
|
||||
args.output.write_text(json.dumps(record, indent=2) + "\n", encoding="utf-8")
|
||||
print(f"Wrote {args.output}")
|
||||
return 0
|
||||
|
||||
|
||||
@@ -19,10 +19,11 @@ from __future__ import annotations
|
||||
import argparse
|
||||
import hashlib
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, os.path.dirname(__file__))
|
||||
from common import copy_file_atomic, load_database
|
||||
from common import load_database
|
||||
|
||||
|
||||
def gitignored_paths(gitignore: str) -> set[str]:
|
||||
@@ -71,9 +72,7 @@ def restore(
|
||||
unsatisfied.append(path)
|
||||
continue
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
# Whole or absent: a scan running beside a plain copy hashed the
|
||||
# truncated file and published that in the database.
|
||||
copy_file_atomic(source, path)
|
||||
shutil.copy2(source, path)
|
||||
print(f"Restored: {path}")
|
||||
restored += 1
|
||||
print(f"Total: {restored} files restored")
|
||||
|
||||
+12
-59
@@ -28,7 +28,6 @@ from common import (
|
||||
resolve_local_file,
|
||||
runs_standalone,
|
||||
)
|
||||
from validation import outside_gap_scope
|
||||
|
||||
# A profile entry can prove a slot without declaring a hash: Dolphin names no
|
||||
# checksum for the GameCube boot ROM because its source names none, and the
|
||||
@@ -156,10 +155,10 @@ def profile_claims(
|
||||
for entry in profile.get("files") or []:
|
||||
if not isinstance(entry, dict):
|
||||
continue
|
||||
# The same rule as the gap report and the builder: an entry the
|
||||
# pack never carries (read from the save directory, a
|
||||
# placeholder, a filename-agnostic scan) arbitrates nothing.
|
||||
if outside_gap_scope(entry, is_standalone):
|
||||
entry_mode = entry.get("mode")
|
||||
if entry_mode == "standalone" and not is_standalone:
|
||||
continue
|
||||
if entry_mode == "libretro" and is_standalone:
|
||||
continue
|
||||
# A member of a romset is not a file at the BIOS root: the archive
|
||||
# holding it is what occupies a destination, and it is declared
|
||||
@@ -243,14 +242,10 @@ def find_conflicts(
|
||||
continue
|
||||
by_slot.setdefault(key, []).append(claim)
|
||||
|
||||
# A declaration that lists several accepted hashes is satisfied by any of
|
||||
# them: Recalbox names both scph1001.bin revisions, and mesence names
|
||||
# four BS-X dumps among which System.dat pins one. The resolver stops on
|
||||
# the first it finds, so the paths differ while both layers agree.
|
||||
disputed = {
|
||||
key: claims
|
||||
for key, claims in by_slot.items()
|
||||
if not any(_satisfies(c, by_dest[key], db) for c in claims)
|
||||
if all(c.local_path != by_dest[key].local_path for c in claims)
|
||||
}
|
||||
|
||||
return [
|
||||
@@ -460,51 +455,6 @@ def find_collisions(
|
||||
return collisions
|
||||
|
||||
|
||||
def _satisfies(profile: Claim, platform: Claim, db: dict) -> bool:
|
||||
"""Whether the two layers agree on what ships at this destination.
|
||||
|
||||
They agree when they resolve to the same file, when the platform's own
|
||||
hash list accepts the profile's file, or when the profile's accepts the
|
||||
platform's. A contradiction needs both lists to reject the other side.
|
||||
"""
|
||||
if profile.local_path == platform.local_path:
|
||||
return True
|
||||
if (
|
||||
profile.local_path
|
||||
and _lists_hashes(platform.entry)
|
||||
and _accepted_by_all(profile.local_path, [platform.entry], db)
|
||||
):
|
||||
return True
|
||||
return bool(
|
||||
platform.local_path
|
||||
and _lists_hashes(profile.entry)
|
||||
and _accepted_by_all(platform.local_path, [profile.entry], db)
|
||||
)
|
||||
|
||||
|
||||
def _lists_hashes(entry: dict) -> bool:
|
||||
"""Whether a declaration says anything about content at all."""
|
||||
return any(
|
||||
_declared_hashes(entry, field_name)
|
||||
for field_name in ("md5", "sha1", "sha256", "crc32")
|
||||
)
|
||||
|
||||
|
||||
def _declared_hashes(entry: dict, field_name: str) -> set[str]:
|
||||
"""Hashes an entry accepts for one field, declared as a string or a list."""
|
||||
value = entry.get(field_name)
|
||||
values = value if isinstance(value, list) else [value]
|
||||
# Recalbox writes its md5 list comma separated, and profiles do the
|
||||
# same for sha256 (mesence names four BS-X dumps in one string).
|
||||
return {
|
||||
part.strip().lower()
|
||||
for v in values
|
||||
if v
|
||||
for part in str(v).split(",")
|
||||
if part.strip()
|
||||
}
|
||||
|
||||
|
||||
def _accepted_by_all(path: str, entries: list[dict], db: dict) -> bool:
|
||||
"""Whether every declaration's own hashes accept the file at path."""
|
||||
record = next(
|
||||
@@ -520,10 +470,13 @@ def _accepted_by_all(path: str, entries: list[dict], db: dict) -> bool:
|
||||
):
|
||||
return False
|
||||
continue
|
||||
for field_name in ("md5", "sha1", "sha256", "crc32"):
|
||||
declared = _declared_hashes(entry, field_name)
|
||||
if declared and str(record.get(field_name, "")).lower() not in declared:
|
||||
return False
|
||||
md5s = parse_md5_list(entry.get("md5"))
|
||||
if md5s and str(record.get("md5", "")).lower() not in md5s:
|
||||
return False
|
||||
sha1 = entry.get("sha1")
|
||||
sha1s = [sha1] if isinstance(sha1, str) else list(sha1 or [])
|
||||
if sha1s and str(record.get("sha1", "")).lower() not in {h.lower() for h in sha1s}:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
|
||||
+37
-60
@@ -228,71 +228,36 @@ def validate_file(
|
||||
|
||||
|
||||
def get_changed_files() -> list[str]:
|
||||
"""BIOS files the branch changes against origin's main line.
|
||||
"""Get list of changed files in current PR/branch using git."""
|
||||
try:
|
||||
for base in ("main", "master", "v2"):
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["git", "diff", "--name-only", f"origin/{base}...HEAD"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
files = [
|
||||
f
|
||||
for f in result.stdout.strip().split("\n")
|
||||
if f.startswith("bios/")
|
||||
]
|
||||
if files:
|
||||
return files
|
||||
except subprocess.CalledProcessError:
|
||||
continue
|
||||
except (subprocess.CalledProcessError, OSError):
|
||||
pass
|
||||
|
||||
A base git cannot resolve is skipped; when none resolves the staged
|
||||
changes are read and said so. A git that fails outright is an error,
|
||||
never "nothing changed": a clone whose remote is not called origin
|
||||
used to answer that no BIOS had changed while the branch committed one.
|
||||
"""
|
||||
compared = False
|
||||
for base in ("main", "master", "v2"):
|
||||
result = subprocess.run(
|
||||
["git", "diff", "--name-only", f"origin/{base}...HEAD"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
continue
|
||||
compared = True
|
||||
files = [f for f in result.stdout.strip().split("\n") if f.startswith("bios/")]
|
||||
if files:
|
||||
return files
|
||||
if compared:
|
||||
return []
|
||||
print(
|
||||
"no origin/main, origin/master or origin/v2 to compare against; "
|
||||
"reading the staged changes only",
|
||||
file=sys.stderr,
|
||||
)
|
||||
result = subprocess.run(
|
||||
["git", "diff", "--cached", "--name-only"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
raise RuntimeError(f"git diff --cached failed: {result.stderr.strip()}")
|
||||
return [f for f in result.stdout.strip().split("\n") if f.startswith("bios/") and f]
|
||||
|
||||
|
||||
def _files_to_validate(args, parser) -> list:
|
||||
"""The files named on the command line, or the changed ones."""
|
||||
if not args.changed:
|
||||
if not args.files:
|
||||
parser.error("No files specified. Use --changed or provide file paths.")
|
||||
return args.files
|
||||
try:
|
||||
files = get_changed_files()
|
||||
except (RuntimeError, OSError) as exc:
|
||||
print(f"Error: {exc}", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
if not files:
|
||||
print("No changed BIOS files detected")
|
||||
return files
|
||||
|
||||
|
||||
def _load_database_if_any(path):
|
||||
try:
|
||||
return load_database(path)
|
||||
except FileNotFoundError:
|
||||
return None
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"WARNING: corrupt database.json: {e}", file=sys.stderr)
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Validate BIOS file contributions")
|
||||
parser.add_argument("files", nargs="*", help="Files to validate")
|
||||
@@ -307,11 +272,23 @@ def main():
|
||||
parser.add_argument("--json", action="store_true", help="Output as JSON")
|
||||
args = parser.parse_args()
|
||||
|
||||
files = _files_to_validate(args, parser)
|
||||
if not files:
|
||||
return
|
||||
files = args.files
|
||||
if args.changed:
|
||||
files = get_changed_files()
|
||||
if not files:
|
||||
print("No changed BIOS files detected")
|
||||
return
|
||||
|
||||
db = _load_database_if_any(args.db)
|
||||
if not files:
|
||||
parser.error("No files specified. Use --changed or provide file paths.")
|
||||
|
||||
try:
|
||||
db = load_database(args.db)
|
||||
except FileNotFoundError:
|
||||
db = None
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"WARNING: corrupt database.json: {e}", file=sys.stderr)
|
||||
db = None
|
||||
platform_hashes = load_platform_hashes(args.platforms_dir)
|
||||
|
||||
results = []
|
||||
|
||||
+21
-115
@@ -34,7 +34,6 @@ import slots
|
||||
|
||||
sys.path.insert(0, os.path.dirname(__file__))
|
||||
from common import (
|
||||
profiles_for_systems,
|
||||
PROFILE_IDENTITY_FIELDS,
|
||||
list_available_targets,
|
||||
list_platform_system_ids,
|
||||
@@ -85,7 +84,6 @@ from validation import (
|
||||
|
||||
DEFAULT_DB = "database.json"
|
||||
DEFAULT_PLATFORMS_DIR = "platforms"
|
||||
DEFAULT_BIOS_DIR = "bios"
|
||||
# The repository's platforms wherever the script runs from, for library calls
|
||||
# that pass no directory.
|
||||
_REPO_PLATFORMS = os.path.join(os.path.dirname(__file__), "..", "platforms")
|
||||
@@ -399,11 +397,8 @@ def _candidate_verdict(
|
||||
if outside_gap_scope(file_entry, is_standalone):
|
||||
return "skip"
|
||||
if not include_all:
|
||||
# A platform declaring any name the core answers to has met the
|
||||
# requirement: quasi88 reads n88sub.rom or disk.rom, and System.dat
|
||||
# names disk.rom. Without the aliases the pack carried the ROM twice.
|
||||
answers = {fname, file_entry.get("archive") or "", *file_entry.get("aliases", [])}
|
||||
if answers & declared_names:
|
||||
archive = file_entry.get("archive")
|
||||
if fname in declared_names or (archive and archive in declared_names):
|
||||
return "settled"
|
||||
return "keep"
|
||||
|
||||
@@ -720,75 +715,6 @@ def find_exclusion_notes(
|
||||
# Platform verification
|
||||
|
||||
|
||||
def _mark_unplaced(config: dict, undeclared: list[dict], profiles: dict) -> None:
|
||||
"""What the builder cannot place is not in the pack, held or not."""
|
||||
from packextras import unplaceable_extras
|
||||
|
||||
unplaced = unplaceable_extras(config, undeclared, profiles)
|
||||
for u in undeclared:
|
||||
if (u.get("emulator", ""), u.get("name", ""), u.get("path") or "") in unplaced:
|
||||
u["in_pack"] = False
|
||||
u["omitted"] = "no platform slug for its system"
|
||||
|
||||
|
||||
def _twin_index(
|
||||
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]]]:
|
||||
"""Which declaration the pack ships at each destination, and every declaration by id."""
|
||||
from generate_pack import _preferred_entries
|
||||
|
||||
preferred_entries = _preferred_entries(
|
||||
verify_systems, db, DEFAULT_BIOS_DIR, base_dest, False,
|
||||
zip_contents, data_dir_registry, True,
|
||||
)
|
||||
winners = {
|
||||
id(fe): (sid, fe)
|
||||
for sid, system in verify_systems.items()
|
||||
for fe in system.get("files", [])
|
||||
}
|
||||
return preferred_entries, winners
|
||||
|
||||
|
||||
def _resolve_declaration(
|
||||
file_entry: dict, sys_id: str, twins: tuple, db: dict, zip_contents: dict,
|
||||
data_dir_registry: dict | None, slot_overrides: dict, mode: str,
|
||||
platform_profiles: dict,
|
||||
) -> tuple[str | None, str, str | None]:
|
||||
"""Resolve a declaration to the file the pack ships at its destination.
|
||||
|
||||
When another declaration holds the destination, that one's file is
|
||||
returned and the evidence reset: the winner's hash match is not this
|
||||
declaration's, which is hashed against the shipped file.
|
||||
"""
|
||||
preferred_entries, winners, base_dest = twins
|
||||
bare = sanitize_pack_path(file_entry.get("destination", file_entry.get("name", "")))
|
||||
preferred = preferred_entries.get(f"{base_dest}/{bare}" if base_dest else bare)
|
||||
held_by, winner = (None, file_entry)
|
||||
if preferred is not None and preferred != id(file_entry):
|
||||
held_by, winner = winners[preferred]
|
||||
local_path, resolve_status = _resolve_for_platform(
|
||||
winner, held_by or sys_id, db, zip_contents, data_dir_registry,
|
||||
slot_overrides, mode, platform_profiles,
|
||||
)
|
||||
return local_path, "" if held_by else resolve_status, held_by
|
||||
|
||||
|
||||
def _twin_unmet(result: dict, held_by: str | None) -> bool:
|
||||
"""Mark a declaration unmet at a path another declaration holds.
|
||||
|
||||
The path is answered by the winner's file, which the pack counts once;
|
||||
this declaration's failure is reported, never folded into the
|
||||
destination's status.
|
||||
"""
|
||||
if held_by is None or result["status"] == Status.OK:
|
||||
return False
|
||||
result["discrepancy"] = (
|
||||
f"{result.get('name', '')} is not met at the path {held_by} holds"
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
def _resolve_for_platform(
|
||||
file_entry: dict,
|
||||
sys_id: str,
|
||||
@@ -904,7 +830,7 @@ def verify_platform(
|
||||
# pass reads. Grouping the platform files here and the core extras
|
||||
# there let the two answer differently on one request: the report kept
|
||||
# every core extra a region run withdraws from the pack.
|
||||
from packextras import platform_region_groups
|
||||
from generate_pack import platform_region_groups
|
||||
|
||||
region_groups, region_extra_dests = platform_region_groups(
|
||||
config,
|
||||
@@ -919,15 +845,6 @@ def verify_platform(
|
||||
region_groups, region_mod.build_region_index(profiles), regions
|
||||
)
|
||||
|
||||
# A destination two systems declare with two hashes ships one file, the
|
||||
# one the builder prefers. The other declaration is scored against that
|
||||
# file, as the frontend will score it after installation: RetroDECK's
|
||||
# xroar component hashes bios/disk.rom and finds the PC-88 ROM.
|
||||
base_dest = config.get("base_destination", "")
|
||||
preferred_entries, winners = _twin_index(
|
||||
verify_systems, db, base_dest, zip_contents, data_dir_registry
|
||||
)
|
||||
|
||||
for sys_id, system in verify_systems.items():
|
||||
for file_entry in system.get("files", []):
|
||||
if region_drops and (
|
||||
@@ -937,10 +854,9 @@ def verify_platform(
|
||||
in region_drops
|
||||
):
|
||||
continue
|
||||
local_path, resolve_status, held_by = _resolve_declaration(
|
||||
file_entry, sys_id, (preferred_entries, winners, base_dest),
|
||||
db, zip_contents, data_dir_registry, slot_overrides, mode,
|
||||
platform_profiles,
|
||||
local_path, resolve_status = _resolve_for_platform(
|
||||
file_entry, sys_id, db, zip_contents, data_dir_registry,
|
||||
slot_overrides, mode, platform_profiles,
|
||||
)
|
||||
destination = sanitize_pack_path(
|
||||
file_entry.get("destination", file_entry.get("name", ""))
|
||||
@@ -977,8 +893,6 @@ def verify_platform(
|
||||
validation_index,
|
||||
)
|
||||
details.append(result)
|
||||
if _twin_unmet(result, held_by):
|
||||
continue
|
||||
|
||||
# Aggregate by destination
|
||||
dest = file_entry.get("destination", file_entry.get("name", ""))
|
||||
@@ -1035,7 +949,6 @@ def verify_platform(
|
||||
)
|
||||
not in region_drops
|
||||
]
|
||||
_mark_unplaced(config, undeclared, profiles)
|
||||
exclusions = find_exclusion_notes(
|
||||
config, emulators_dir, emu_profiles, target_cores=target_cores
|
||||
)
|
||||
@@ -1135,10 +1048,6 @@ def _print_detail_entries(details: list[dict], seen: set[str], verbose: bool) ->
|
||||
"""Print UNTESTED, MISSING, and DISCREPANCY entries from verification details."""
|
||||
for d in details:
|
||||
if d["status"] == Status.UNTESTED:
|
||||
if d.get("discrepancy"):
|
||||
# Reported below with the ground for it: a declaration
|
||||
# unmet at a path another declaration holds.
|
||||
continue
|
||||
key = f"{d['system']}/{d['name']}"
|
||||
if key in seen:
|
||||
continue
|
||||
@@ -1207,27 +1116,17 @@ def _print_undeclared_entry(u: dict, prefix: str, verbose: bool) -> None:
|
||||
print(f" [{'+'.join(checks)}]")
|
||||
|
||||
|
||||
def _split_undeclared(undeclared: list[dict]) -> tuple[list[dict], list[dict], list[dict]]:
|
||||
"""Game data, firmware the pack places, firmware it holds but cannot place.
|
||||
|
||||
Everything that is not game data is firmware the core loads, archives
|
||||
included: a bios_zip sat in neither list, so a required one that was
|
||||
missing was never printed.
|
||||
"""
|
||||
game_data = [u for u in undeclared if u.get("category", "bios") == "game_data"]
|
||||
firmware = [u for u in undeclared if u.get("category", "bios") != "game_data"]
|
||||
unplaced = [u for u in firmware if u["in_repo"] and not u.get("in_pack", True)]
|
||||
placed = [u for u in firmware if u.get("in_pack", True)]
|
||||
return game_data, placed, unplaced
|
||||
|
||||
|
||||
def _print_undeclared_section(result: dict, verbose: bool) -> None:
|
||||
"""Print cross-reference section for undeclared files used by cores."""
|
||||
undeclared = result.get("undeclared_files", [])
|
||||
if not undeclared:
|
||||
return
|
||||
|
||||
game_data, bios_files, unplaced = _split_undeclared(undeclared)
|
||||
# Everything that is not game data is firmware the core loads, archives
|
||||
# included: a bios_zip sat in neither list, so a required one that was
|
||||
# missing was never printed.
|
||||
game_data = [u for u in undeclared if u.get("category", "bios") == "game_data"]
|
||||
bios_files = [u for u in undeclared if u.get("category", "bios") != "game_data"]
|
||||
|
||||
req_not_in_repo = [
|
||||
u
|
||||
@@ -1250,8 +1149,6 @@ def _print_undeclared_section(result: dict, verbose: bool) -> None:
|
||||
print(
|
||||
f" Core files: {core_in_pack} in pack, {core_missing_req} required missing, {core_missing_opt} optional missing"
|
||||
)
|
||||
if unplaced:
|
||||
print(f" Core files held but not packed: {len(unplaced)} ({unplaced[0]['omitted']})")
|
||||
|
||||
for u in req_not_in_repo:
|
||||
_print_undeclared_entry(u, "MISSING (required)", verbose)
|
||||
@@ -1665,7 +1562,16 @@ def verify_system(
|
||||
) -> dict:
|
||||
"""Verify files for all emulators supporting given system IDs."""
|
||||
profiles = load_emulator_profiles(emulators_dir)
|
||||
matching = profiles_for_systems(profiles, system_ids, standalone)
|
||||
matching = []
|
||||
for name, profile in sorted(profiles.items()):
|
||||
if profile.get("type") in ("launcher", "alias", "test"):
|
||||
continue
|
||||
emu_systems = set(profile.get("systems", []))
|
||||
if emu_systems & set(system_ids):
|
||||
ptype = profile.get("type", "libretro")
|
||||
if standalone and "standalone" not in ptype:
|
||||
continue # skip non-standalone in standalone mode
|
||||
matching.append(name)
|
||||
|
||||
if not matching:
|
||||
all_systems: set[str] = set()
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
"""Generated artifacts are written whole or not at all.
|
||||
|
||||
write_if_changed got the scratch-and-rename treatment, but the install
|
||||
manifests, the target manifests and release.json were still truncated at
|
||||
their final name, and restore_large_files copied an asset straight to its
|
||||
path in bios/: a scan running beside the copy hashed the truncated file.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from unittest import mock
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
||||
|
||||
import artifacts # noqa: E402
|
||||
|
||||
|
||||
class WholeOrNothing(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self._tmp = tempfile.TemporaryDirectory()
|
||||
self.dir = Path(self._tmp.name)
|
||||
|
||||
def tearDown(self):
|
||||
self._tmp.cleanup()
|
||||
|
||||
def test_an_interrupted_write_keeps_the_previous_file(self):
|
||||
target = self.dir / "manifest.json"
|
||||
target.write_text("{}")
|
||||
with mock.patch("os.replace", side_effect=OSError("cut")), self.assertRaises(OSError):
|
||||
artifacts.write_text_atomic(str(target), "{\"files\": []}")
|
||||
self.assertEqual(target.read_text(), "{}")
|
||||
self.assertEqual(os.listdir(self.dir), ["manifest.json"])
|
||||
|
||||
def test_an_interrupted_copy_leaves_no_file(self):
|
||||
source = self.dir / "asset.bin"
|
||||
source.write_bytes(b"x" * 1000)
|
||||
target = self.dir / "bios" / "asset.bin"
|
||||
target.parent.mkdir()
|
||||
with mock.patch("shutil.copy2", side_effect=OSError("cut")), self.assertRaises(OSError):
|
||||
artifacts.copy_file_atomic(str(source), str(target))
|
||||
self.assertEqual(os.listdir(target.parent), [])
|
||||
|
||||
|
||||
class EveryWriterGoesThroughIt(unittest.TestCase):
|
||||
def test_manifests_record_and_restore_use_the_atomic_helpers(self):
|
||||
scripts = REPO_ROOT / "scripts"
|
||||
for name, helper in (
|
||||
("generate_pack.py", "write_text_atomic("),
|
||||
("release_record.py", "write_text_atomic("),
|
||||
("restore_large_files.py", "copy_file_atomic("),
|
||||
):
|
||||
source = (scripts / name).read_text(encoding="utf-8")
|
||||
with self.subTest(script=name):
|
||||
self.assertIn(helper, source)
|
||||
pack = (scripts / "generate_pack.py").read_text(encoding="utf-8")
|
||||
self.assertNotIn('with open(path, "w") as f:\n f.write(new_json)', pack)
|
||||
self.assertNotIn("json.dump(result, f", pack)
|
||||
self.assertNotIn("args.output.write_text(", (scripts / "release_record.py").read_text())
|
||||
self.assertNotIn("shutil.copy2(source, path)", (scripts / "restore_large_files.py").read_text())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -482,20 +482,6 @@ class PipelineRegressions(unittest.TestCase):
|
||||
with contextlib.redirect_stdout(io.StringIO()):
|
||||
self.assertFalse(pipeline.check_consistency(verify, pack))
|
||||
|
||||
def test_a_pack_more_optimistic_than_verify_is_a_failure(self):
|
||||
"""The invariant held one way only: a pack counting more OK files
|
||||
than the report passed as 'pack resolves more'."""
|
||||
verify = "DupTest: 0/1 OK, 1 untested [md5]"
|
||||
pack = "\n".join(
|
||||
[
|
||||
"Generating pack for DupTest [source=full]...",
|
||||
" x/DupTest_1_BIOS_Pack.zip: 1 files packed (1 baseline + 0 from cores), "
|
||||
"1/1 files OK [md5]",
|
||||
]
|
||||
)
|
||||
with contextlib.redirect_stdout(io.StringIO()):
|
||||
self.assertFalse(pipeline.check_consistency(verify, pack))
|
||||
|
||||
def test_nothing_parsed_is_not_consistent(self):
|
||||
"""A changed output format must not pass with no platform compared."""
|
||||
with contextlib.redirect_stdout(io.StringIO()):
|
||||
@@ -939,37 +925,17 @@ class RegionRegressions(unittest.TestCase):
|
||||
)
|
||||
self.assertNotEqual(north_america, europe)
|
||||
|
||||
def test_region_audit_resolves_the_way_the_pack_does(self):
|
||||
"""A private resolver read sha1, md5 and a lone name: an entry
|
||||
identified by its path (Dolphin's three IPL.bin) was never checked."""
|
||||
import hashlib # noqa: PLC0415
|
||||
|
||||
with tempfile.TemporaryDirectory(dir=TMP_ROOT) as directory:
|
||||
previous = os.getcwd()
|
||||
os.chdir(directory)
|
||||
self.addCleanup(os.chdir, previous)
|
||||
files, shas = {}, {}
|
||||
for rel, payload in (("bios/GC/USA/IPL.bin", b"usa"), ("bios/GC/JAP/IPL.bin", b"jap")):
|
||||
Path(rel).parent.mkdir(parents=True, exist_ok=True)
|
||||
Path(rel).write_bytes(payload)
|
||||
sha1 = hashlib.sha1(payload).hexdigest()
|
||||
shas[rel] = sha1
|
||||
files[sha1] = {"path": rel, "name": "IPL.bin", "size": len(payload), "sha1": sha1,
|
||||
"md5": hashlib.md5(payload).hexdigest(),
|
||||
"sha256": hashlib.sha256(payload).hexdigest(), "crc32": "00000001"}
|
||||
from scripts import generate_db # noqa: PLC0415
|
||||
|
||||
db = {"files": files, "indexes": generate_db.build_indexes(files, {})}
|
||||
by_md5 = {e["md5"]: s for s, e in files.items()}
|
||||
self.assertEqual(
|
||||
region_audit.resolve_sha1({"name": "IPL.bin", "md5": [files[shas["bios/GC/USA/IPL.bin"]]["md5"]]}, db),
|
||||
shas["bios/GC/USA/IPL.bin"],
|
||||
)
|
||||
self.assertEqual(
|
||||
region_audit.resolve_sha1({"name": "IPL.bin", "path": "GC/JAP/IPL.bin"}, db),
|
||||
shas["bios/GC/JAP/IPL.bin"],
|
||||
)
|
||||
self.assertTrue(by_md5)
|
||||
def test_region_audit_accepts_list_valued_md5(self):
|
||||
sha1 = "a" * 40
|
||||
md5 = "b" * 32
|
||||
db = {
|
||||
"files": {sha1: {}},
|
||||
"indexes": {"by_md5": {md5: sha1}, "by_name": {}},
|
||||
}
|
||||
self.assertEqual(
|
||||
region_audit.resolve_sha1({"name": "bios.bin", "md5": [md5]}, db),
|
||||
sha1,
|
||||
)
|
||||
|
||||
|
||||
class ArchiveSecurityRegressions(unittest.TestCase):
|
||||
@@ -1325,10 +1291,7 @@ class EveryManifestEntryIsFetchable(unittest.TestCase):
|
||||
|
||||
def test_an_unresolvable_file_is_recorded_as_omitted(self):
|
||||
"""The reason must be one install.py knows how to report."""
|
||||
allowed = {
|
||||
"hash_mismatch", "not_found", "external", "user_provided",
|
||||
"no_platform_slug",
|
||||
}
|
||||
allowed = {"hash_mismatch", "not_found", "external", "user_provided"}
|
||||
for path in sorted((ROOT / "install").glob("*.json")):
|
||||
with self.subTest(manifest=path.name):
|
||||
data = json.loads(path.read_text())
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
"""The command line report judges "undeclared" on the flat declared set.
|
||||
|
||||
Per normalized system id, bk and elektronika-bk never met, nor nintendo-sgb
|
||||
and nintendo-super-game-boy: 646 entries whose exact name a platform
|
||||
declares were counted undeclared, while verify and the site read one flat
|
||||
set, enriched with every spelling the database knows.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
||||
|
||||
from common import load_database, load_emulator_profiles # noqa: E402
|
||||
from cross_reference import ( # noqa: E402
|
||||
_build_supplemental_index,
|
||||
cross_reference,
|
||||
declared_names,
|
||||
load_platform_files,
|
||||
)
|
||||
|
||||
|
||||
class FlatDeclaredSet(unittest.TestCase):
|
||||
def test_a_name_declared_under_another_system_id_is_not_a_gap(self):
|
||||
if not (REPO_ROOT / "database.json").is_file():
|
||||
self.skipTest("no database.json")
|
||||
db = load_database(str(REPO_ROOT / "database.json"))
|
||||
platforms = str(REPO_ROOT / "platforms")
|
||||
profiles = {k: v for k, v in load_emulator_profiles(str(REPO_ROOT / "emulators")).items() if k == "bk"}
|
||||
if not profiles:
|
||||
self.skipTest("no bk profile")
|
||||
declared, data_dirs = load_platform_files(platforms, ["batocera"])
|
||||
names = _build_supplemental_index()
|
||||
per_system = cross_reference(profiles, declared, db, data_dirs, names)
|
||||
flat = cross_reference(
|
||||
profiles, declared, db, data_dirs, names,
|
||||
all_declared=declared_names(platforms, ["batocera"], db),
|
||||
)
|
||||
gaps = {g["name"] for g in flat["bk"]["gap_details"]}
|
||||
self.assertNotIn("MONIT10.ROM", gaps)
|
||||
self.assertLess(flat["bk"]["gaps"], per_system["bk"]["gaps"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -271,21 +271,5 @@ class PathPriority(unittest.TestCase):
|
||||
self.assertFalse(dedup._in_nodedup_dir("bios/Sony/PlayStation/scph.bin"))
|
||||
|
||||
|
||||
class TheNoDedupListIsPinned(unittest.TestCase):
|
||||
"""Every directory the dedup must leave alone, with the reason written
|
||||
beside it in dedup.py. Only RPG Maker and ScummVM were tested; a merge
|
||||
that dropped c64-emu would have the name step serve the C128 keymap to
|
||||
three machines again, with the suite green."""
|
||||
|
||||
def test_every_tree_read_by_path_stays_undeduplicated(self):
|
||||
from scripts.dedup import NODEDUP_DIRS # noqa: PLC0415
|
||||
|
||||
expected = {
|
||||
"RPG Maker", "ScummVM", "tzdata", "c64-emu", "PinMAME", "BBK",
|
||||
"C-Dogs SDL", "Ikemen GO", "Theme Hospital", "TheXTech", "Softdisk",
|
||||
}
|
||||
self.assertEqual(expected - set(NODEDUP_DIRS), set())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
+2
-2
@@ -3833,7 +3833,7 @@ class TestE2E(unittest.TestCase):
|
||||
}
|
||||
],
|
||||
}
|
||||
copied, skipped, _ = do_standalone_copies(manifest, bios_dir, "linux")
|
||||
copied, skipped = do_standalone_copies(manifest, bios_dir, "linux")
|
||||
self.assertEqual(copied, 1)
|
||||
self.assertEqual(skipped, 1)
|
||||
self.assertTrue((yuzu_dir / "prod.keys").exists())
|
||||
@@ -6356,7 +6356,7 @@ struct BurnDriver BurnDrvneogeo = {
|
||||
|
||||
db = self._gap_db("v4bios.zip", "a" * 40, "465c4e1c")
|
||||
original = cross_reference.get_mame_clone_map
|
||||
cross_reference.get_mame_clone_map = lambda *_a: {"bctvidbs.zip": "v4bios.zip"}
|
||||
cross_reference.get_mame_clone_map = lambda: {"bctvidbs.zip": "v4bios.zip"}
|
||||
try:
|
||||
entry = {"name": "bctvidbs.zip", "required": True}
|
||||
self.assertEqual(self._gap_source(entry, db), "bios")
|
||||
|
||||
@@ -355,76 +355,5 @@ class RetroPieProposals(unittest.TestCase):
|
||||
self.assertIsNone(RetroPie._insertion_point(alternatives))
|
||||
self.assertIsNotNone(RetroPie._insertion_point(enumeration))
|
||||
|
||||
|
||||
class BizHawkRewritesOnlyWhatItDeclares(unittest.TestCase):
|
||||
def test_a_truth_only_homonym_does_not_rewrite_a_declared_call(self):
|
||||
"""opera's panafz10-norsa.bin, absent from BizHawk's list, would have
|
||||
rewritten the 3DO call of the same name by index on the name alone."""
|
||||
sha_bizhawk, sha_other = "4" * 40, "e" * 40
|
||||
declared = NativeFile("bios.bin", "bios.bin", "SYSB", platform={"sha1": sha_bizhawk})
|
||||
truth_only = NativeFile("bios.bin", "bios.bin", "sys-a", truth={"sha1": sha_other})
|
||||
exporter = BizHawk()
|
||||
source = f'File("{sha_bizhawk.upper()}", 10, "bios.bin")\n'
|
||||
produced = exporter.render(
|
||||
{"SYSB": NativeSystem("SYSB", files=[declared]),
|
||||
"sys-a": NativeSystem("sys-a", files=[truth_only])},
|
||||
None, {exporter.native_filename(): source},
|
||||
)
|
||||
self.assertIn(sha_bizhawk.upper(), produced[exporter.native_filename()])
|
||||
self.assertNotIn(sha_other.upper(), produced[exporter.native_filename()])
|
||||
|
||||
|
||||
class RetroDeckCorrectsOnlyWhatItCarries(unittest.TestCase):
|
||||
def test_paths_and_description_stay_the_maintainers(self):
|
||||
"""neogeo.zip lost its four search directories to a path that does
|
||||
not exist, and 26 descriptions were rewritten."""
|
||||
from collections import OrderedDict # noqa: PLC0415
|
||||
|
||||
existing = [{"filename": "neogeo.zip", "system": "neogeo", "md5": A,
|
||||
"paths": ["$roms_path/neogeo", "$roms_path/fbneo", "$bios_path"],
|
||||
"description": "Neo Geo BIOS"}]
|
||||
ours = [OrderedDict([("filename", "neogeo.zip"), ("md5", B), ("system", "neogeo"),
|
||||
("description", "generated"), ("paths", "$bios_path/roms/neogeo")])]
|
||||
merged = RetroDeck._merge(existing, ours)
|
||||
self.assertEqual(merged[0]["md5"], B)
|
||||
self.assertEqual(merged[0]["paths"], ["$roms_path/neogeo", "$roms_path/fbneo", "$bios_path"])
|
||||
self.assertEqual(merged[0]["description"], "Neo Geo BIOS")
|
||||
|
||||
def test_a_hash_no_revision_shares_is_appended(self):
|
||||
"""IPL.n64 declared twice, both contradicted: counted corrected,
|
||||
written nowhere."""
|
||||
from collections import OrderedDict # noqa: PLC0415
|
||||
|
||||
existing = [{"filename": "IPL.n64", "system": "n64dd", "md5": A},
|
||||
{"filename": "IPL.n64", "system": "n64dd", "md5": B}]
|
||||
ours = [OrderedDict([("filename", "IPL.n64"), ("md5", C), ("system", "n64dd")])]
|
||||
merged = RetroDeck._merge(existing, ours)
|
||||
self.assertEqual([e["md5"] for e in merged], [A, B, C])
|
||||
|
||||
|
||||
class BatoceraKeepsTheRunnerKeys(unittest.TestCase):
|
||||
def test_emulator_and_core_travel_with_a_rewritten_entry(self):
|
||||
"""checkBios skips a BIOS whose emulator or core is not installed;
|
||||
rewritten without the keys, vectrex's MAME BIOS was wanted by every
|
||||
build."""
|
||||
from exporter.batocera_exporter import Exporter as Batocera # noqa: PLC0415
|
||||
|
||||
fe = NativeFile("bios.bin", "bios.bin", "vectrex", platform={"md5": A},
|
||||
truth={"md5": B}, corrections=["md5"])
|
||||
fe.native_data = {"native_path": "bios/bios.bin"}
|
||||
system = NativeSystem("vectrex", files=[fe])
|
||||
original = (
|
||||
' "vectrex": { "name": "Vectrex", "emulator": "libretro", "core": "mame", '
|
||||
'"biosFiles": [ { "md5": "' + A + '", "file": "bios/bios.bin", '
|
||||
'"emulator": "libretro", "core": "mame" } ] },'
|
||||
)
|
||||
exporter = Batocera()
|
||||
line = exporter._entry_line(system, [fe], exporter._parse_entry([original]))
|
||||
parsed = exporter._parse_entry([line])["vectrex"]
|
||||
self.assertEqual((parsed["emulator"], parsed["core"]), ("libretro", "mame"))
|
||||
self.assertEqual(parsed["biosFiles"][0]["core"], "mame")
|
||||
self.assertEqual(parsed["biosFiles"][0]["md5"], B)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -56,21 +56,5 @@ class ExtractTargets(unittest.TestCase):
|
||||
self.assertNotIn('" | RetroDECK | `~/retrodeck/` |"', site)
|
||||
|
||||
|
||||
class ThePackGuideNamesTheInstallerDefault(unittest.TestCase):
|
||||
"""The pack README, the registry and install.py each wrote RetroBat's
|
||||
default folder by hand, and two of the three said the home directory
|
||||
where the installer (installer.iss) says C:\\RetroBat."""
|
||||
|
||||
def test_retrobat_guide_matches_the_registry(self):
|
||||
registry = load_platform_registry(PLATFORMS)
|
||||
first = next(
|
||||
rule["path"] for rule in registry["retrobat"]["install"]["detect"]
|
||||
if rule["os"] == "windows" and rule["method"] == "path_exists"
|
||||
)
|
||||
guide = (REPO_ROOT / "scripts" / "packreadme.py").read_text(encoding="utf-8")
|
||||
self.assertEqual(first, "C:\\RetroBat\\bios")
|
||||
self.assertIn("(default: C:\\\\RetroBat\\\\bios\\\\)", guide)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -1,72 +0,0 @@
|
||||
"""A file in another emulator's tree does not answer for this one.
|
||||
|
||||
bios/Other/<emulator>/ holds what that emulator ships. The path step took a
|
||||
shortened tail that only NetherSX2's tree answered as proof for EmuCoreX's
|
||||
unlock.wav, and broke a full-tail tie between EmuCoreX's GameIndex.yaml and
|
||||
the system directory's copy by index order, serving a fork's game database
|
||||
to upstream PCSX2.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
||||
|
||||
import generate_db # noqa: E402
|
||||
from common import resolve_local_file # noqa: E402
|
||||
|
||||
|
||||
class ForeignTree(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self._tmp = tempfile.TemporaryDirectory()
|
||||
self._cwd = os.getcwd()
|
||||
os.chdir(self._tmp.name)
|
||||
files = {}
|
||||
for rel, payload in (
|
||||
("bios/Other/forkemu/resources/GameIndex.yaml", b"fork db"),
|
||||
("bios/Sony/PlayStation 2/resources/GameIndex.yaml", b"system copy"),
|
||||
("bios/Other/forkemu/resources/sounds/unlock.wav", b"fork sound"),
|
||||
):
|
||||
path = Path(rel)
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_bytes(payload)
|
||||
sha1 = hashlib.sha1(payload).hexdigest()
|
||||
files[sha1] = {
|
||||
"path": rel, "name": path.name, "size": len(payload), "sha1": sha1,
|
||||
"md5": hashlib.md5(payload).hexdigest(),
|
||||
"sha256": hashlib.sha256(payload).hexdigest(), "crc32": "00000001",
|
||||
}
|
||||
self.db = {"files": files, "indexes": generate_db.build_indexes(files, {})}
|
||||
|
||||
def tearDown(self):
|
||||
os.chdir(self._cwd)
|
||||
self._tmp.cleanup()
|
||||
|
||||
def _resolve(self, owner: str, path: str):
|
||||
entry = {"name": path.rsplit("/", 1)[-1], "path": path, "source_profile": owner}
|
||||
return resolve_local_file(entry, self.db, {}, dest_hint=path)
|
||||
|
||||
def test_a_full_tail_tie_goes_to_the_system_directory(self):
|
||||
local, status = self._resolve("pcsx2", "resources/GameIndex.yaml")
|
||||
self.assertEqual(status, "path_exact")
|
||||
self.assertTrue(local.startswith("bios/Sony/"), local)
|
||||
|
||||
def test_the_fork_still_gets_its_own_copy(self):
|
||||
local, status = self._resolve("forkemu", "resources/GameIndex.yaml")
|
||||
self.assertEqual(status, "path_exact")
|
||||
self.assertTrue(local.startswith("bios/Other/forkemu/"), local)
|
||||
|
||||
def test_a_shortened_tail_in_a_foreign_tree_is_not_path_evidence(self):
|
||||
_local, status = self._resolve("otheremu", "assets/sounds/unlock.wav")
|
||||
self.assertNotEqual(status, "path_exact")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
+7
-71
@@ -218,15 +218,6 @@ class TestDefaultsFollowTheRegistry(unittest.TestCase):
|
||||
with self.subTest(platform=name, os=os_type):
|
||||
self.assertIn(install._default_dest(os_type, name), candidates)
|
||||
checked += 1
|
||||
# The installer's own default comes first in the registry and
|
||||
# is the one the pack guide and the installer name.
|
||||
first = next(
|
||||
(self._expand(rule["path"]) for rule in (entry.get("install") or {}).get("detect", [])
|
||||
if rule.get("os") == "windows" and rule.get("method") == "path_exists"),
|
||||
None,
|
||||
)
|
||||
if name == "retrobat":
|
||||
self.assertEqual(install._default_dest("windows", name), first)
|
||||
self.assertGreater(checked, 10)
|
||||
|
||||
|
||||
@@ -798,7 +789,7 @@ class TestStandaloneCopiesExtraDirs(unittest.TestCase):
|
||||
(bios / "ps2-0230a.bin").write_text("rom")
|
||||
extra = Path(tempfile.mkdtemp()) / "bios"
|
||||
extra.mkdir()
|
||||
copied, _, _ = install.do_standalone_copies(
|
||||
copied, _ = install.do_standalone_copies(
|
||||
self._manifest(), bios, "windows", {"pcsx2": extra}
|
||||
)
|
||||
self.assertEqual(copied, 1)
|
||||
@@ -810,7 +801,7 @@ class TestStandaloneCopiesExtraDirs(unittest.TestCase):
|
||||
(bios / "GC" / "USA" / "IPL.bin").write_text("ipl")
|
||||
user = Path(tempfile.mkdtemp()) / "User"
|
||||
(user / "GC" / "USA").mkdir(parents=True)
|
||||
copied, _, _ = install.do_standalone_copies(
|
||||
copied, _ = install.do_standalone_copies(
|
||||
self._manifest(), bios, "windows", {"dolphin": user}
|
||||
)
|
||||
self.assertEqual(copied, 1)
|
||||
@@ -820,64 +811,10 @@ class TestStandaloneCopiesExtraDirs(unittest.TestCase):
|
||||
bios = Path(tempfile.mkdtemp())
|
||||
(bios / "ps2-0230a.bin").write_text("rom")
|
||||
self.assertEqual(
|
||||
install.do_standalone_copies(self._manifest(), bios, "windows"), (0, 0, 0)
|
||||
install.do_standalone_copies(self._manifest(), bios, "windows"), (0, 0)
|
||||
)
|
||||
|
||||
|
||||
class InstallerStaysOnPython38AndTheStdlib(unittest.TestCase):
|
||||
"""install.py runs where the bootstraps let it run: Python 3.8, stdlib only.
|
||||
|
||||
The suite runs on 3.12 with pyyaml installed, so a removeprefix() or an
|
||||
import of yaml kept every test green and died at the user's first run,
|
||||
after the bootstrap had told them 3.8 was enough.
|
||||
"""
|
||||
|
||||
SOURCE = (REPO_ROOT / "install.py").read_text(encoding="utf-8")
|
||||
|
||||
def test_the_grammar_is_python_38(self):
|
||||
import ast # noqa: PLC0415
|
||||
|
||||
ast.parse(self.SOURCE, filename="install.py", feature_version=(3, 8))
|
||||
|
||||
def test_every_import_is_standard_library(self):
|
||||
import ast # noqa: PLC0415
|
||||
import sys # noqa: PLC0415
|
||||
|
||||
tree = ast.parse(self.SOURCE)
|
||||
imported = set()
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, ast.Import):
|
||||
imported.update(alias.name.split(".")[0] for alias in node.names)
|
||||
elif isinstance(node, ast.ImportFrom) and node.level == 0 and node.module:
|
||||
imported.add(node.module.split(".")[0])
|
||||
self.assertEqual(sorted(imported - set(sys.stdlib_module_names)), [])
|
||||
|
||||
def test_no_api_newer_than_38(self):
|
||||
newer = (".removeprefix(", ".removesuffix(", "zoneinfo", "graphlib",
|
||||
"functools.cache(", ".bit_count(", "math.lcm(", "math.nextafter(")
|
||||
used = [api for api in newer if api in self.SOURCE]
|
||||
self.assertEqual(used, [])
|
||||
|
||||
|
||||
class ARefusedCopyIsAnError(unittest.TestCase):
|
||||
"""A PermissionError on a standalone copy was counted as "dir not found"
|
||||
and the run ended on Done with exit code 0."""
|
||||
|
||||
def test_a_refused_copy_is_counted_failed_not_skipped(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
bios = Path(tmp, "bios")
|
||||
bios.mkdir()
|
||||
(bios / "prod.keys").write_bytes(b"k")
|
||||
target = Path(tmp, "keys")
|
||||
target.mkdir()
|
||||
manifest = {"standalone_copies": [
|
||||
{"file": "prod.keys", "targets": {"linux": [str(target)]}}
|
||||
]}
|
||||
with unittest.mock.patch("shutil.copy2", side_effect=PermissionError(13, "denied")):
|
||||
result = install.do_standalone_copies(manifest, bios, "linux")
|
||||
self.assertEqual(result, (0, 0, 1))
|
||||
|
||||
|
||||
class TargetFlagIsAppliedOrRefused(unittest.TestCase):
|
||||
"""A filter the installer cannot apply must stop it, not be ignored.
|
||||
|
||||
@@ -907,12 +844,11 @@ class TargetFlagIsAppliedOrRefused(unittest.TestCase):
|
||||
for name in ("install/targets/retroarch.json",):
|
||||
path = REPO_ROOT / name
|
||||
if not path.is_file():
|
||||
self.skipTest(f"{name} not generated yet")
|
||||
continue
|
||||
targets = json.loads(path.read_text())
|
||||
for alias in ("switch", "ps2", "psp"):
|
||||
# An alias the builder accepts and the manifest lacks is a
|
||||
# documented word that fails on the installer's side.
|
||||
self.assertIn(alias, targets, f"builder accepts --target {alias}")
|
||||
if alias not in targets:
|
||||
continue
|
||||
with self.subTest(alias=alias):
|
||||
resolved = load_target_config(
|
||||
"retroarch", alias, str(REPO_ROOT / "platforms")
|
||||
@@ -1071,7 +1007,7 @@ class TestStandaloneCopyTargetsAreUntrusted(unittest.TestCase):
|
||||
outside.write_text("untouched")
|
||||
(target / "boot.bin").symlink_to(outside)
|
||||
|
||||
copied, skipped, _ = install.do_standalone_copies(
|
||||
copied, skipped = install.do_standalone_copies(
|
||||
self._manifest(str(target)), bios, "linux"
|
||||
)
|
||||
self.assertEqual(copied, 0)
|
||||
|
||||
@@ -48,25 +48,13 @@ def _registered_platforms() -> list[str]:
|
||||
return list_registered_platforms(PLATFORMS_DIR, include_archived=True)
|
||||
|
||||
|
||||
def _profiles_newer_than(path: str, platform_name: str) -> list[str]:
|
||||
"""Profiles of this platform's emulators modified after the pack was built.
|
||||
|
||||
A profile the platform never resolves cannot have changed its pack: a
|
||||
comment edited in any of 500 profiles used to disarm the integrity
|
||||
check of every pack on disk.
|
||||
"""
|
||||
sys.path.insert(0, os.path.join(REPO_ROOT, "scripts"))
|
||||
from common import load_emulator_profiles, load_platform_config, resolve_platform_cores
|
||||
|
||||
def _profiles_newer_than(path: str) -> list[str]:
|
||||
"""Profiles modified after the pack was built."""
|
||||
built = os.path.getmtime(path)
|
||||
config = load_platform_config(platform_name, PLATFORMS_DIR)
|
||||
profiles = load_emulator_profiles(EMULATORS_DIR)
|
||||
relevant = resolve_platform_cores(config, profiles)
|
||||
return [
|
||||
os.path.basename(p)
|
||||
for p in glob.glob(os.path.join(EMULATORS_DIR, "*.yml"))
|
||||
if os.path.splitext(os.path.basename(p))[0] in relevant
|
||||
and os.path.getmtime(p) > built
|
||||
if os.path.getmtime(p) > built
|
||||
]
|
||||
|
||||
|
||||
@@ -99,7 +87,7 @@ class PackIntegrityTest(unittest.TestCase):
|
||||
# goes red depends on how far along that build is.
|
||||
if "is in use by another run" in (result.stdout + result.stderr):
|
||||
self.skipTest(f"dist/ is being written; {platform_name} not verifiable")
|
||||
newer = _profiles_newer_than(pack, platform_name)
|
||||
newer = _profiles_newer_than(pack)
|
||||
if newer:
|
||||
self.skipTest(
|
||||
f"{platform_name} pack predates {len(newer)} profile(s) "
|
||||
|
||||
@@ -353,20 +353,6 @@ class TestProvenanceReport(unittest.TestCase):
|
||||
report["redump"]["covered_dats"], ["Sony - PlayStation - BIOS Images"]
|
||||
)
|
||||
|
||||
def test_a_loose_file_matches_by_crc32_and_size(self):
|
||||
"""Redump lists DTL-H10100's image by crc32 alone; collected as a
|
||||
loose file it stayed MISSING for good."""
|
||||
db = {"files": {"f" * 40: {"name": "ps2-0101jd.bin", "size": 4194304,
|
||||
"md5": "e" * 32, "crc32": "E9836A0C"}}}
|
||||
snapshots = {"redump": {"source": "redump", "imported_at": "2026-10-09",
|
||||
"dats": {"PS2 BIOS": "2026-10-01"},
|
||||
"entries": [{"name": "ps2-0101jd.bin", "size": 4194304,
|
||||
"crc32": "e9836a0c", "md5": "", "sha1": "",
|
||||
"dat": "PS2 BIOS"}]}}
|
||||
report = build_report(db, snapshots)
|
||||
self.assertEqual(report["redump"]["matched"], 1)
|
||||
self.assertEqual(report["redump"]["missing"], [])
|
||||
|
||||
def test_uncovered_dat_entries_are_out_of_scope(self):
|
||||
"""Entries from a DAT the collection never matches are not targets.
|
||||
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
"""A step that could not do its job says so with its exit code.
|
||||
|
||||
validate_pr --changed answered "No changed BIOS files detected" and exited
|
||||
0 when git itself had failed (no origin, or no repository), and
|
||||
check_buildbot_system --update exited 0 after a refresh it launched had
|
||||
failed, the cache left at the old version.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from unittest import mock
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
||||
|
||||
import check_buildbot_system # noqa: E402
|
||||
|
||||
|
||||
class GitThatCannotAnswer(unittest.TestCase):
|
||||
def test_outside_a_repository_is_an_error_not_an_empty_change(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
proc = subprocess.run(
|
||||
[sys.executable, str(REPO_ROOT / "scripts" / "validate_pr.py"), "--changed"],
|
||||
cwd=tmp, capture_output=True, text=True, timeout=60, check=False,
|
||||
)
|
||||
self.assertEqual(proc.returncode, 2, proc.stdout + proc.stderr)
|
||||
self.assertNotIn("No changed BIOS files detected", proc.stdout)
|
||||
|
||||
def test_a_clone_without_origin_reads_the_index_and_says_so(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
subprocess.run(["git", "init", "-q", tmp], check=True)
|
||||
Path(tmp, "bios").mkdir()
|
||||
Path(tmp, "bios", "x.bin").write_bytes(b"x")
|
||||
subprocess.run(["git", "-C", tmp, "add", "bios/x.bin"], check=True)
|
||||
proc = subprocess.run(
|
||||
[sys.executable, "-c", (
|
||||
f"import sys; sys.path.insert(0, {str(REPO_ROOT / 'scripts')!r}); "
|
||||
"import validate_pr; print(validate_pr.get_changed_files())"
|
||||
)],
|
||||
cwd=tmp, capture_output=True, text=True, timeout=60, check=False,
|
||||
)
|
||||
self.assertEqual(proc.returncode, 0, proc.stderr)
|
||||
self.assertIn("bios/x.bin", proc.stdout)
|
||||
self.assertIn("staged changes only", proc.stderr)
|
||||
|
||||
|
||||
class RefreshFailuresAreFailures(unittest.TestCase):
|
||||
def test_a_failed_refresh_is_returned_and_exits_nonzero(self):
|
||||
report = {"entries": [{"status": "UPDATED", "key": "dolphin-sys"},
|
||||
{"status": "OK", "key": "ppsspp-assets"}]}
|
||||
with mock.patch.object(
|
||||
check_buildbot_system.subprocess, "run",
|
||||
return_value=subprocess.CompletedProcess([], 1),
|
||||
):
|
||||
self.assertEqual(check_buildbot_system.update_changed(report), ["dolphin-sys"])
|
||||
source = (REPO_ROOT / "scripts" / "check_buildbot_system.py").read_text(encoding="utf-8")
|
||||
self.assertIn("failed = update_changed(report)", source)
|
||||
self.assertIn("sys.exit(1)", source[source.index("failed = update_changed(report)"):])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
+6
-22
@@ -436,32 +436,26 @@ class TestVerifyModesHonourRegion(unittest.TestCase):
|
||||
"""
|
||||
|
||||
def _run(self, *args: str) -> str:
|
||||
"""The report, from a run that succeeded.
|
||||
|
||||
A run that crashed produced an empty stdout, different from the
|
||||
plain report, and the test read the difference as narrowing.
|
||||
"""
|
||||
import subprocess
|
||||
|
||||
repo = os.path.join(os.path.dirname(__file__), "..")
|
||||
completed = subprocess.run(
|
||||
return subprocess.run(
|
||||
[sys.executable, "scripts/verify.py", *args],
|
||||
capture_output=True, text=True, cwd=repo, timeout=900, check=False,
|
||||
)
|
||||
self.assertEqual(completed.returncode, 0, completed.stderr[-2000:])
|
||||
self.assertTrue(completed.stdout.strip(), "an empty report is not a report")
|
||||
return completed.stdout
|
||||
capture_output=True, text=True, cwd=repo, timeout=900,
|
||||
).stdout
|
||||
|
||||
def test_emulator_mode_narrows(self):
|
||||
plain = self._run("--emulator", "duckstation")
|
||||
filtered = self._run("--emulator", "duckstation", "--region", "us")
|
||||
if "duckstation" not in plain:
|
||||
self.skipTest("duckstation profile not present")
|
||||
filtered = self._run("--emulator", "duckstation", "--region", "us")
|
||||
self.assertNotEqual(plain, filtered)
|
||||
|
||||
def test_system_mode_narrows(self):
|
||||
plain = self._run("--system", "sony-playstation")
|
||||
filtered = self._run("--system", "sony-playstation", "--region", "us")
|
||||
if not plain.strip():
|
||||
self.skipTest("system not present")
|
||||
self.assertNotEqual(plain, filtered)
|
||||
|
||||
|
||||
@@ -582,16 +576,6 @@ class TestReportAndBuilderNarrowTogether(unittest.TestCase):
|
||||
"or emulator_region_drops",
|
||||
)
|
||||
|
||||
def test_the_verifier_reads_the_grouping_where_it_lives(self):
|
||||
"""Imported through the builder's CLI module, the verdict of a
|
||||
--region run depended on that module compiling, and seven scripts
|
||||
entered the site workflow's trigger paths for one function."""
|
||||
source = (Path(__file__).resolve().parent.parent / "scripts" / "verify.py").read_text(
|
||||
encoding="utf-8"
|
||||
)
|
||||
self.assertNotIn("from generate_pack import platform_region_groups", source)
|
||||
self.assertIn("from packextras import platform_region_groups", source)
|
||||
|
||||
def test_emulator_report_keeps_what_the_emulator_pack_keeps(self):
|
||||
"""O2EM: --region us left the Videopac BIOSes in the pack, not in the report."""
|
||||
repo = Path(__file__).resolve().parent.parent
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
"""A required declaration is never silenced by its optional twin.
|
||||
|
||||
RetroDECK declares the four Atari 5200 ROMs optional under atari-400-800 and
|
||||
required under atari-5200, same md5. The preferred declaration for a shared
|
||||
destination was chosen over the full set, then applied to a loop that had
|
||||
already dropped the optional entries: under --required-only the preferred
|
||||
(optional) entry was skipped for being optional and the required one for not
|
||||
being preferred, and the destination left the pack, the status counts and
|
||||
the manifest without a trace.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
||||
|
||||
import common # noqa: E402
|
||||
import generate_db # noqa: E402
|
||||
import generate_pack as builder # noqa: E402
|
||||
|
||||
|
||||
class RequiredTwins(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self._tmp = tempfile.TemporaryDirectory()
|
||||
self.root = Path(self._tmp.name)
|
||||
self.bios = self.root / "bios" / "Atari"
|
||||
self.platforms = self.root / "platforms"
|
||||
self.emulators = self.root / "emulators"
|
||||
for directory in (self.bios, self.platforms, self.emulators):
|
||||
directory.mkdir(parents=True)
|
||||
payload = b"atari os"
|
||||
(self.bios / "ATARIOSB.ROM").write_bytes(payload)
|
||||
md5 = hashlib.md5(payload).hexdigest()
|
||||
sha1 = hashlib.sha1(payload).hexdigest()
|
||||
files = {
|
||||
sha1: {
|
||||
"path": str(self.bios / "ATARIOSB.ROM"), "name": "ATARIOSB.ROM",
|
||||
"size": len(payload), "sha1": sha1, "md5": md5,
|
||||
"sha256": hashlib.sha256(payload).hexdigest(), "crc32": "00000008",
|
||||
}
|
||||
}
|
||||
self.db = {"files": files, "indexes": generate_db.build_indexes(files, {})}
|
||||
platform = {
|
||||
"platform": "Twins",
|
||||
"verification_mode": "md5",
|
||||
"base_destination": "bios",
|
||||
"cores": [],
|
||||
"systems": {
|
||||
# Sorted first: the optional declaration is the preferred one.
|
||||
"atari-400-800": {"files": [
|
||||
{"name": "ATARIOSB.ROM", "destination": "ATARIOSB.ROM",
|
||||
"md5": md5, "required": False},
|
||||
]},
|
||||
"atari-5200": {"files": [
|
||||
{"name": "ATARIOSB.ROM", "destination": "ATARIOSB.ROM",
|
||||
"md5": md5, "required": True},
|
||||
]},
|
||||
},
|
||||
}
|
||||
(self.platforms / "twins.yml").write_text(yaml.dump(platform))
|
||||
(self.platforms / "_registry.yml").write_text(
|
||||
yaml.dump({"platforms": {"twins": {"status": "active"}}})
|
||||
)
|
||||
common._platform_config_cache.clear()
|
||||
common._emulator_profiles_cache.clear()
|
||||
|
||||
def tearDown(self):
|
||||
common._platform_config_cache.clear()
|
||||
self._tmp.cleanup()
|
||||
|
||||
def test_the_required_only_pack_carries_the_required_file(self):
|
||||
out = self.root / "dist"
|
||||
out.mkdir()
|
||||
zip_path = builder.generate_pack(
|
||||
"twins", str(self.platforms), self.db, str(self.root / "bios"), str(out),
|
||||
emulators_dir=str(self.emulators), emu_profiles={},
|
||||
required_only=True, offline=True,
|
||||
)
|
||||
with zipfile.ZipFile(zip_path) as archive:
|
||||
self.assertIn("ATARIOSB.ROM", archive.namelist())
|
||||
|
||||
def test_the_required_only_manifest_lists_it(self):
|
||||
manifest = builder.generate_manifest(
|
||||
"twins", str(self.platforms), self.db, str(self.root / "bios"),
|
||||
str(self.platforms / "_registry.yml"),
|
||||
emulators_dir=str(self.emulators), emu_profiles={},
|
||||
required_only=True, offline=True,
|
||||
)
|
||||
listed = {entry["dest"] for entry in manifest["files"]}
|
||||
self.assertIn("ATARIOSB.ROM", listed)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -1,96 +0,0 @@
|
||||
"""A shared group adds files, never a second name for a native one.
|
||||
|
||||
The quasi88 group declared N88SUB.ROM beside the disk.rom System.dat
|
||||
already names for nec-pc-88, same bytes: the RetroArch pack carried the
|
||||
2 KB ROM twice under quasi88/, and the group read as twelve required files
|
||||
where four did the work. Alternative names are aliases in the profile.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
||||
|
||||
from common import list_registered_platforms, load_platform_config # noqa: E402
|
||||
|
||||
PLATFORMS = str(REPO_ROOT / "platforms")
|
||||
|
||||
|
||||
def _md5s(entry: dict) -> set[str]:
|
||||
raw = entry.get("md5") or ""
|
||||
values = raw if isinstance(raw, list) else str(raw).split(",")
|
||||
return {v.strip().lower() for v in values if v and v.strip()}
|
||||
|
||||
|
||||
class SharedGroupsAddOnly(unittest.TestCase):
|
||||
def test_no_shared_file_repeats_a_native_file_of_the_same_directory(self):
|
||||
shared = yaml.safe_load((REPO_ROOT / "platforms" / "_shared.yml").read_text())
|
||||
groups = shared.get("shared_groups") or {}
|
||||
repeated = []
|
||||
for platform in list_registered_platforms(PLATFORMS, include_archived=True):
|
||||
config = load_platform_config(platform, PLATFORMS)
|
||||
for system in config.get("systems", {}).values():
|
||||
included = [
|
||||
fe for group in system.get("includes", []) for fe in groups.get(group, [])
|
||||
]
|
||||
if not included:
|
||||
continue
|
||||
group_keys = {
|
||||
(fe.get("name"), fe.get("destination", fe.get("name"))) for fe in included
|
||||
}
|
||||
native: dict[tuple[str, str], str] = {}
|
||||
for fe in system.get("files", []):
|
||||
if (fe.get("name"), fe.get("destination", fe.get("name"))) in group_keys:
|
||||
continue
|
||||
dest = fe.get("destination") or fe.get("name", "")
|
||||
directory = dest.rsplit("/", 1)[0] if "/" in dest else ""
|
||||
for md5 in _md5s(fe):
|
||||
native.setdefault((directory, md5), fe.get("name", ""))
|
||||
for fe in included:
|
||||
dest = fe.get("destination") or fe.get("name", "")
|
||||
directory = dest.rsplit("/", 1)[0] if "/" in dest else ""
|
||||
for md5 in _md5s(fe):
|
||||
other = native.get((directory, md5))
|
||||
if other and other.lower() != fe.get("name", "").lower():
|
||||
repeated.append(f"{platform}:{fe['name']} = {other}")
|
||||
self.assertEqual(repeated, [])
|
||||
|
||||
class ADeclaredAliasSettlesTheRequirement(unittest.TestCase):
|
||||
"""quasi88 reads n88sub.rom or disk.rom; System.dat names disk.rom. The
|
||||
gap pass compared the entry's name alone and the pack carried the same
|
||||
2 KB ROM a second time as quasi88/n88sub.rom."""
|
||||
|
||||
def test_a_platform_naming_an_alias_has_met_the_entry(self):
|
||||
import os # noqa: PLC0415
|
||||
import tempfile # noqa: PLC0415
|
||||
|
||||
from common import _emulator_profiles_cache # noqa: PLC0415
|
||||
from verify import find_undeclared_files # noqa: PLC0415
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
Path(tmp, "emulators").mkdir()
|
||||
Path(tmp, "emulators", "quasi88.yml").write_text(
|
||||
"emulator: QUASI88\ntype: libretro\nsystems: [nec-pc-88]\ncores: [quasi88]\n"
|
||||
"files:\n - name: n88sub.rom\n aliases: [N88SUB.ROM, disk.rom]\n"
|
||||
" required: true\n"
|
||||
)
|
||||
_emulator_profiles_cache.clear()
|
||||
self.addCleanup(_emulator_profiles_cache.clear)
|
||||
config = {"platform": "P", "verification_mode": "existence", "cores": "all_libretro",
|
||||
"systems": {"nec-pc-88": {"files": [{"name": "disk.rom", "destination": "quasi88/disk.rom"}]}}}
|
||||
db = {"files": {}, "indexes": {"by_name": {}, "by_md5": {}, "by_crc32": {}, "by_path_suffix": {}}}
|
||||
previous = os.getcwd()
|
||||
os.chdir(tmp)
|
||||
self.addCleanup(os.chdir, previous)
|
||||
undeclared = find_undeclared_files(config, "emulators", db, data_names=set())
|
||||
self.assertEqual([u["name"] for u in undeclared], [])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -157,46 +157,6 @@ class TestConflicts(unittest.TestCase):
|
||||
conflicts = slots.find_conflicts(self._config("m" * 32), profile, REGIONS_DB)
|
||||
self.assertEqual(conflicts, [])
|
||||
|
||||
def test_a_platform_list_naming_the_profile_file_is_agreement(self):
|
||||
"""Recalbox names both scph1001.bin revisions; the resolver stops on
|
||||
the first, the profile pins the other, and --strict reported a
|
||||
contradiction nobody has to correct."""
|
||||
conflicts = slots.find_conflicts(
|
||||
self._config(f"{'m' * 32},{'n' * 32}"),
|
||||
self._profile("GC/JAP/IPL.bin"),
|
||||
REGIONS_DB,
|
||||
)
|
||||
self.assertEqual(conflicts, [])
|
||||
|
||||
def test_a_profile_list_naming_the_shipped_file_is_agreement(self):
|
||||
"""mesence accepts four BS-X dumps, one of which System.dat pins;
|
||||
the slot read as contested and the pack swapped the catalogued
|
||||
dump for an uncatalogued one."""
|
||||
profile = {
|
||||
"mesence": {
|
||||
"files": [
|
||||
{"name": "IPL.bin", "path": "GC/JAP/IPL.bin",
|
||||
"md5": [ "n" * 32, "m" * 32 ]},
|
||||
]
|
||||
}
|
||||
}
|
||||
conflicts = slots.find_conflicts(self._config("m" * 32), profile, REGIONS_DB)
|
||||
self.assertEqual(conflicts, [])
|
||||
|
||||
def test_an_entry_the_pack_never_carries_claims_nothing(self):
|
||||
"""A file read from the save directory, declared with the sha1 of
|
||||
another dump, arbitrated the platform's file off its own path."""
|
||||
profile = {
|
||||
"core": {
|
||||
"files": [
|
||||
{"name": "IPL.bin", "path": "GC/JAP/IPL.bin", "md5": "n" * 32,
|
||||
"load_from": "save_dir"},
|
||||
]
|
||||
}
|
||||
}
|
||||
conflicts = slots.find_conflicts(self._config("m" * 32), profile, REGIONS_DB)
|
||||
self.assertEqual(conflicts, [])
|
||||
|
||||
def test_a_name_only_resolution_asserts_nothing(self):
|
||||
# No hash and no path that the repository carries: the entry is
|
||||
# answered by filename alone and cannot contradict anything.
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
"""One system, two spellings, one pack.
|
||||
|
||||
stella2014 and stella2023 write atari_2600 where every other profile writes
|
||||
atari-2600. --system matched the spelling exactly and named the ZIP from
|
||||
it, so the two spellings built two different packs under one file name,
|
||||
the second replacing the first; --from-md5 --emulator X --standalone
|
||||
accepted a libretro-only core and named a standalone pack that was the
|
||||
libretro pack.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
||||
|
||||
import common # noqa: E402
|
||||
import generate_pack as builder # noqa: E402
|
||||
|
||||
|
||||
class SpellingsAgree(unittest.TestCase):
|
||||
def test_both_spellings_select_the_same_profiles(self):
|
||||
profiles = common.load_emulator_profiles(str(REPO_ROOT / "emulators"))
|
||||
dashed = common.profiles_for_systems(profiles, ["atari-2600"])
|
||||
underscored = common.profiles_for_systems(profiles, ["atari_2600"])
|
||||
self.assertEqual(dashed, underscored)
|
||||
self.assertIn("stella2014", dashed)
|
||||
self.assertIn("gopher2600", dashed)
|
||||
|
||||
def test_the_pack_name_normalizes_the_spelling(self):
|
||||
self.assertEqual(
|
||||
builder._system_display_name("atari_2600"), builder._system_display_name("atari-2600")
|
||||
)
|
||||
self.assertEqual(builder._system_display_name("msxturboR"), builder._system_display_name("msxturbor"))
|
||||
|
||||
|
||||
class TheListingCountsLikeTheSelection(unittest.TestCase):
|
||||
def test_one_system_is_listed_once_with_every_profile(self):
|
||||
import contextlib # noqa: PLC0415
|
||||
import io # noqa: PLC0415
|
||||
|
||||
out = io.StringIO()
|
||||
with contextlib.redirect_stdout(out):
|
||||
common.list_system_ids(str(REPO_ROOT / "emulators"))
|
||||
lines = {line.split()[0]: line for line in out.getvalue().splitlines() if line.strip()}
|
||||
profiles = common.load_emulator_profiles(str(REPO_ROOT / "emulators"))
|
||||
for spellings in (("3do", "panasonic-3do"), ("atari-2600", "atari_2600")):
|
||||
listed = [s for s in spellings if s in lines]
|
||||
self.assertEqual(len(listed), 1, f"{spellings}: one line per system")
|
||||
count = len(common.profiles_for_systems(profiles, [spellings[0]]))
|
||||
self.assertIn(f"({count} emulator", lines[listed[0]])
|
||||
|
||||
|
||||
class StandaloneNeedsAStandaloneBuild(unittest.TestCase):
|
||||
def test_a_custom_pack_refuses_it_for_a_libretro_core(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
Path(tmp, "emulators").mkdir()
|
||||
Path(tmp, "emulators", "handy.yml").write_text(
|
||||
"emulator: Handy\ntype: libretro\nsystems: [atari-lynx]\nfiles:\n - name: lynxboot.img\n"
|
||||
)
|
||||
common._emulator_profiles_cache.clear()
|
||||
self.addCleanup(common._emulator_profiles_cache.clear)
|
||||
db = {"files": {}, "indexes": {"by_md5": {}, "by_crc32": {}, "by_name": {}}}
|
||||
result = builder.generate_md5_pack(
|
||||
[("md5", "d8f1206299c48946e6ec5ef96d014eaa")], db, tmp, tmp,
|
||||
emulator_name="handy", emulators_dir=str(Path(tmp, "emulators")), standalone=True,
|
||||
)
|
||||
self.assertIsNone(result)
|
||||
self.assertEqual([p for p in os.listdir(tmp) if p.endswith(".zip")], [])
|
||||
|
||||
|
||||
class ADeclinedBuildIsAFailure(unittest.TestCase):
|
||||
def test_a_target_that_removes_every_requested_system_exits_nonzero(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
proc = subprocess.run(
|
||||
[sys.executable, "scripts/generate_pack.py", "--platform", "retroarch",
|
||||
"--system", "sony-playstation-2", "--target", "switch", "--offline",
|
||||
"--output-dir", tmp],
|
||||
capture_output=True, text=True, cwd=str(REPO_ROOT), timeout=600, check=False,
|
||||
)
|
||||
self.assertNotEqual(proc.returncode, 0, proc.stdout + proc.stderr)
|
||||
self.assertNotIn("Traceback", proc.stderr, proc.stderr)
|
||||
self.assertEqual([p for p in os.listdir(tmp) if p.endswith(".zip")], [])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -1,83 +0,0 @@
|
||||
"""A destination two systems declare with two hashes ships one file.
|
||||
|
||||
RetroDECK declares bios/disk.rom for pc88 (the N88SUB ROM) and for coco (the
|
||||
CoCo disk ROM), and bios/cdibios.zip for two components with two md5. The
|
||||
pack holds one file per path; verify resolved each declaration to its own
|
||||
file and counted both OK, so three green tools described a pack whose
|
||||
frontend hashes bios/disk.rom for xroar and finds the PC-88 ROM.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
||||
|
||||
import common # noqa: E402
|
||||
import generate_db # noqa: E402
|
||||
from verify import verify_platform # noqa: E402
|
||||
|
||||
|
||||
class TwinDeclarations(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self._tmp = tempfile.TemporaryDirectory()
|
||||
self._cwd = os.getcwd()
|
||||
os.chdir(self._tmp.name)
|
||||
files, self.md5 = {}, {}
|
||||
for rel, payload in (
|
||||
("bios/NEC/PC-98/N88SUB.ROM", b"pc88 disk rom"),
|
||||
("bios/Tandy/CoCo/disk.rom", b"coco disk rom"),
|
||||
):
|
||||
path = Path(rel)
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_bytes(payload)
|
||||
sha1 = hashlib.sha1(payload).hexdigest()
|
||||
self.md5[rel] = hashlib.md5(payload).hexdigest()
|
||||
files[sha1] = {
|
||||
"path": rel, "name": path.name, "size": len(payload), "sha1": sha1,
|
||||
"md5": self.md5[rel], "sha256": hashlib.sha256(payload).hexdigest(),
|
||||
"crc32": "00000001",
|
||||
}
|
||||
self.db = {"files": files, "indexes": generate_db.build_indexes(files, {})}
|
||||
self.emulators = Path(self._tmp.name) / "emulators"
|
||||
self.emulators.mkdir()
|
||||
common._emulator_profiles_cache.clear()
|
||||
|
||||
def tearDown(self):
|
||||
common._emulator_profiles_cache.clear()
|
||||
os.chdir(self._cwd)
|
||||
self._tmp.cleanup()
|
||||
|
||||
def test_the_losing_declaration_is_scored_against_the_shipped_file(self):
|
||||
config = {
|
||||
"platform": "Twins",
|
||||
"verification_mode": "md5",
|
||||
"base_destination": "bios",
|
||||
"cores": [],
|
||||
"systems": {
|
||||
"pc88": {"files": [{"name": "N88SUB.ROM", "destination": "disk.rom",
|
||||
"md5": self.md5["bios/NEC/PC-98/N88SUB.ROM"]}]},
|
||||
"coco": {"files": [{"name": "disk.rom", "destination": "disk.rom",
|
||||
"md5": self.md5["bios/Tandy/CoCo/disk.rom"]}]},
|
||||
},
|
||||
}
|
||||
report = verify_platform(
|
||||
config, self.db, str(self.emulators), {}, supplemental_names=set()
|
||||
)
|
||||
by_system = {d["system"]: d for d in report["details"] if d["name"] in ("N88SUB.ROM", "disk.rom")}
|
||||
statuses = sorted(d["status"] for d in by_system.values())
|
||||
self.assertEqual(statuses.count("ok"), 1, by_system)
|
||||
loser = next(d for d in by_system.values() if d["status"] != "ok")
|
||||
self.assertIn("is not met at the path", loser.get("discrepancy", ""))
|
||||
# The path counts once, for the file it ships; the loser is a detail.
|
||||
self.assertEqual(report["status_counts"], {"ok": 1})
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -1,141 +0,0 @@
|
||||
"""A core file the platform cannot place is not in the pack.
|
||||
|
||||
RomM files every BIOS under the slug of its system and knows no slug for
|
||||
DOS or for the arcade sets MAME 2003 reads. The builder skipped those
|
||||
entries without a word; the report counted them "in pack" and the manifest
|
||||
listed them neither in files nor in omitted_files.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
||||
|
||||
import common # noqa: E402
|
||||
import generate_db # noqa: E402
|
||||
import generate_pack as builder # noqa: E402
|
||||
from packextras import platform_region_groups # noqa: E402
|
||||
from verify import verify_platform # noqa: E402
|
||||
|
||||
PROFILE = """\
|
||||
emulator: Dosbox
|
||||
type: libretro
|
||||
systems: [dos]
|
||||
cores: [dosbox]
|
||||
files:
|
||||
- name: MT32_CONTROL.ROM
|
||||
required: false
|
||||
"""
|
||||
|
||||
|
||||
class SlugPlatform(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self._tmp = tempfile.TemporaryDirectory()
|
||||
self._cwd = os.getcwd()
|
||||
os.chdir(self._tmp.name)
|
||||
self.emulators = Path("emulators")
|
||||
self.platforms = Path("platforms")
|
||||
for d in (self.emulators, self.platforms, Path("bios/Roland"), Path("bios/Sony")):
|
||||
d.mkdir(parents=True)
|
||||
files = {}
|
||||
for rel, payload in (("bios/Roland/MT32_CONTROL.ROM", b"mt32"), ("bios/Sony/scph5501.bin", b"psx")):
|
||||
Path(rel).write_bytes(payload)
|
||||
sha1 = hashlib.sha1(payload).hexdigest()
|
||||
files[sha1] = {
|
||||
"path": rel, "name": Path(rel).name, "size": len(payload), "sha1": sha1,
|
||||
"md5": hashlib.md5(payload).hexdigest(),
|
||||
"sha256": hashlib.sha256(payload).hexdigest(), "crc32": "00000001",
|
||||
}
|
||||
self.db = {"files": files, "indexes": generate_db.build_indexes(files, {})}
|
||||
self.config = {
|
||||
"platform": "Slugs",
|
||||
"verification_mode": "md5",
|
||||
"base_destination": "bios",
|
||||
"cores": ["dosbox"],
|
||||
"systems": {
|
||||
"psx": {"files": [{"name": "scph5501.bin", "destination": "psx/scph5501.bin",
|
||||
"md5": hashlib.md5(b"psx").hexdigest()}]},
|
||||
"ps2": {"files": [{"name": "scph.bin", "destination": "ps2/scph.bin",
|
||||
"md5": "0" * 32}]},
|
||||
"snes": {"files": [{"name": "s.bin", "destination": "snes/s.bin", "md5": "1" * 32}]},
|
||||
},
|
||||
}
|
||||
(self.platforms / "slugs.yml").write_text(yaml.dump(self.config))
|
||||
(self.platforms / "_registry.yml").write_text(
|
||||
yaml.dump({"platforms": {"slugs": {"status": "active"}}})
|
||||
)
|
||||
(self.emulators / "dosbox.yml").write_text(PROFILE)
|
||||
common._platform_config_cache.clear()
|
||||
common._emulator_profiles_cache.clear()
|
||||
self.profiles = common.load_emulator_profiles("emulators")
|
||||
|
||||
def tearDown(self):
|
||||
common._platform_config_cache.clear()
|
||||
common._emulator_profiles_cache.clear()
|
||||
os.chdir(self._cwd)
|
||||
self._tmp.cleanup()
|
||||
|
||||
def test_the_report_does_not_count_it_packed(self):
|
||||
report = verify_platform(
|
||||
self.config, self.db, "emulators", self.profiles, supplemental_names=set()
|
||||
)
|
||||
entry = next(u for u in report["undeclared_files"] if u["name"] == "MT32_CONTROL.ROM")
|
||||
self.assertTrue(entry["in_repo"])
|
||||
self.assertFalse(entry.get("in_pack", True))
|
||||
|
||||
def test_the_manifest_says_why_it_is_absent(self):
|
||||
manifest = builder.generate_manifest(
|
||||
"slugs", "platforms", self.db, "bios", "platforms/_registry.yml",
|
||||
emulators_dir="emulators", emu_profiles=self.profiles, offline=True,
|
||||
)
|
||||
names = {entry["dest"] for entry in manifest["files"]}
|
||||
self.assertFalse(any("MT32_CONTROL" in n for n in names))
|
||||
omitted = [o for o in manifest["omitted_files"] if o["name"] == "MT32_CONTROL.ROM"]
|
||||
self.assertEqual([o["reason"] for o in omitted], ["no_platform_slug"])
|
||||
|
||||
|
||||
class RegionKeysForEveryClaimant(unittest.TestCase):
|
||||
"""Two profiles claiming one destination: the second, deduplicated by
|
||||
the collector, had no key in the region map, and the report kept it
|
||||
after the builder withdrew the destination."""
|
||||
|
||||
def test_both_claimants_are_keyed(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
previous = os.getcwd()
|
||||
os.chdir(tmp)
|
||||
self.addCleanup(os.chdir, previous)
|
||||
Path("emulators").mkdir()
|
||||
Path("bios").mkdir()
|
||||
Path("bios/jp.bin").write_bytes(b"jp")
|
||||
sha1 = hashlib.sha1(b"jp").hexdigest()
|
||||
files = {sha1: {"path": "bios/jp.bin", "name": "jp.bin", "size": 2, "sha1": sha1,
|
||||
"md5": hashlib.md5(b"jp").hexdigest(),
|
||||
"sha256": hashlib.sha256(b"jp").hexdigest(), "crc32": "00000001"}}
|
||||
db = {"files": files, "indexes": generate_db.build_indexes(files, {})}
|
||||
for name, extra in (("one", " system: sys\n"), ("two", "")):
|
||||
Path(f"emulators/{name}.yml").write_text(
|
||||
f"emulator: {name}\ntype: libretro\nsystems: [sys]\ncores: [{name}]\n"
|
||||
f"files:\n - name: jp.bin\n region: [japan]\n{extra}"
|
||||
)
|
||||
common._emulator_profiles_cache.clear()
|
||||
profiles = common.load_emulator_profiles("emulators")
|
||||
config = {"platform": "P", "verification_mode": "existence", "cores": ["one", "two"],
|
||||
"systems": {"sys": {"files": []}}}
|
||||
_groups, extra_dests = platform_region_groups(
|
||||
config, config["systems"], "emulators", db, "", profiles
|
||||
)
|
||||
claimants = {key[0] for key, dest in extra_dests.items() if dest == "jp.bin"}
|
||||
self.assertEqual(claimants, {"one", "two"}, extra_dests)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user