mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-10 12:03:53 -06:00
fix: lay out both builds of a dual-mode emulator
Batocera, RetroBat, Recalbox and ROCKNIX offer some emulators both as a libretro core and standalone. Filed as standalone only, the libretro layout was dropped: libretro Dolphin had no dolphin-emu/Sys/GC files. Platforms now name dual-mode emulators and each gets both layouts.
This commit is contained in:
1 parent
2b535525ef
commit
3c856bbc10
23 files changed
+1367
-478
No files matched your search
+134
-4
@@ -241,10 +241,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"total_files": 12899,
|
"total_files": 12907,
|
||||||
"total_size": 9279360834,
|
"total_size": 9288027834,
|
||||||
"pack_files": 12901,
|
"pack_files": 12909,
|
||||||
"pack_size": 9279360834,
|
"pack_size": 9288027834,
|
||||||
"total_omitted": 395,
|
"total_omitted": 395,
|
||||||
"omitted_files": [
|
"omitted_files": [
|
||||||
{
|
{
|
||||||
@@ -128447,6 +128447,62 @@
|
|||||||
"storage": "release",
|
"storage": "release",
|
||||||
"release_asset": "demo00.pk4"
|
"release_asset": "demo00.pk4"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"dest": "dolphin-emu/Sys/GC/dsp_rom.bin",
|
||||||
|
"sha1": "3c6cc6e04fdd0b2a392d7a6ed769455444846be7",
|
||||||
|
"sha256": "49d987ee1eab29a157425b82d54516957a81e1bac247c8834e494642605c3e8c",
|
||||||
|
"size": 8192,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/.variants/dsp_rom.bin.3c6cc6e0",
|
||||||
|
"cores": [
|
||||||
|
"dolphin"
|
||||||
|
],
|
||||||
|
"systems": [
|
||||||
|
"nintendo-gamecube",
|
||||||
|
"triforce"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "dolphin-emu/Sys/GC/dsp_coef.bin",
|
||||||
|
"sha1": "c116d867ba001dcd6bf6d399ff4bf38d340f556c",
|
||||||
|
"sha256": "a1e269cfdc6b82d5a55baab5a0bbbb3b9f953ad92729e0d5ec50dbc2746a2c47",
|
||||||
|
"size": 4096,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/.variants/dsp_coef.bin.c116d867",
|
||||||
|
"cores": [
|
||||||
|
"dolphin"
|
||||||
|
],
|
||||||
|
"systems": [
|
||||||
|
"nintendo-gamecube",
|
||||||
|
"triforce"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "dolphin-emu/Sys/GC/font_western.bin",
|
||||||
|
"sha1": "483a17ff25b68a8f18cae691da99f16beefbe370",
|
||||||
|
"sha256": "4ad991be2b0aa305f09b90a79fc50f57833b30b008890b5ef1336cc3d9d0bae0",
|
||||||
|
"size": 6478,
|
||||||
|
"repo_path": "bios/Other/dolphin-mmjr/font_western.bin",
|
||||||
|
"cores": [
|
||||||
|
"dolphin"
|
||||||
|
],
|
||||||
|
"systems": [
|
||||||
|
"nintendo-gamecube",
|
||||||
|
"triforce"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "dolphin-emu/Sys/GC/font_japanese.bin",
|
||||||
|
"sha1": "7fbbedcc7a1ae00414a4e9e967109cc78be1fe3b",
|
||||||
|
"sha256": "38f9f59d505bc4e2d86b0196706195f33ad72b7fe9d029cf263072cde19d044f",
|
||||||
|
"size": 259626,
|
||||||
|
"repo_path": "bios/Other/dolphin-mmjr/font_japanese.bin",
|
||||||
|
"cores": [
|
||||||
|
"dolphin"
|
||||||
|
],
|
||||||
|
"systems": [
|
||||||
|
"nintendo-gamecube",
|
||||||
|
"triforce"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"dest": "GC/dsp_rom.bin",
|
"dest": "GC/dsp_rom.bin",
|
||||||
"sha1": "3c6cc6e04fdd0b2a392d7a6ed769455444846be7",
|
"sha1": "3c6cc6e04fdd0b2a392d7a6ed769455444846be7",
|
||||||
@@ -140832,6 +140888,80 @@
|
|||||||
"mame"
|
"mame"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"dest": "dolphin-emu/Sys/GC/USA/IPL.bin",
|
||||||
|
"sha1": "ef9194ab4804aa0aa8540d846caf291b28331165",
|
||||||
|
"sha256": "7c833f95839c226699108b93ab6acbe772788bfa3f05132b8ced5e78f5bc9a26",
|
||||||
|
"size": 2097152,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/GC/USA/IPL.bin",
|
||||||
|
"cores": [
|
||||||
|
"dolphin"
|
||||||
|
],
|
||||||
|
"systems": [
|
||||||
|
"nintendo-gamecube",
|
||||||
|
"triforce"
|
||||||
|
],
|
||||||
|
"regions": [
|
||||||
|
"north-america"
|
||||||
|
],
|
||||||
|
"region_groups": [
|
||||||
|
"Dolphin:file:dolphin-emu/Sys/GC/USA/IPL.bin"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "dolphin-emu/Sys/GC/EUR/IPL.bin",
|
||||||
|
"sha1": "80b8744ff5e43585392f55546bd03a673d11ef5f",
|
||||||
|
"sha256": "85ffcbc834238eb24c87fce27ba7b438cc53a77db013a0a7c59c78e0af08cb84",
|
||||||
|
"size": 2097152,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/GC/EUR/IPL.bin",
|
||||||
|
"cores": [
|
||||||
|
"dolphin"
|
||||||
|
],
|
||||||
|
"systems": [
|
||||||
|
"nintendo-gamecube",
|
||||||
|
"triforce"
|
||||||
|
],
|
||||||
|
"regions": [
|
||||||
|
"europe",
|
||||||
|
"oceania"
|
||||||
|
],
|
||||||
|
"region_groups": [
|
||||||
|
"Dolphin:file:dolphin-emu/Sys/GC/EUR/IPL.bin"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "dolphin-emu/Sys/GC/JAP/IPL.bin",
|
||||||
|
"sha1": "a1837968288253ed541f2b11440b68f5a9b33875",
|
||||||
|
"sha256": "5b2f2617cc63f1861a1f4ae44bb135caf10e202b5b9b79271daaf60f8caaf37d",
|
||||||
|
"size": 2097152,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/GC/JAP/IPL.bin",
|
||||||
|
"cores": [
|
||||||
|
"dolphin"
|
||||||
|
],
|
||||||
|
"systems": [
|
||||||
|
"nintendo-gamecube",
|
||||||
|
"triforce"
|
||||||
|
],
|
||||||
|
"regions": [
|
||||||
|
"japan"
|
||||||
|
],
|
||||||
|
"region_groups": [
|
||||||
|
"Dolphin:file:dolphin-emu/Sys/GC/JAP/IPL.bin"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "dolphin-emu/Sys/Triforce/segaboot.gcm",
|
||||||
|
"sha1": "7297a11be143011ceb75a0720f96855003415a5d",
|
||||||
|
"sha256": "9a2eee93f5ed961aea4b12a318e2b4a54fa69a31239c1378b8bef6ea02e90391",
|
||||||
|
"size": 2097152,
|
||||||
|
"repo_path": "bios/Sega/Triforce/segaboot.gcm",
|
||||||
|
"cores": [
|
||||||
|
"dolphin"
|
||||||
|
],
|
||||||
|
"systems": [
|
||||||
|
"triforce"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"dest": "Triforce/segaboot.gcm",
|
"dest": "Triforce/segaboot.gcm",
|
||||||
"sha1": "7297a11be143011ceb75a0720f96855003415a5d",
|
"sha1": "7297a11be143011ceb75a0720f96855003415a5d",
|
||||||
|
|||||||
+124
-4
@@ -239,10 +239,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"total_files": 3418,
|
"total_files": 3426,
|
||||||
"total_size": 4183759111,
|
"total_size": 4192426111,
|
||||||
"pack_files": 3420,
|
"pack_files": 3428,
|
||||||
"pack_size": 4183759111,
|
"pack_size": 4192426111,
|
||||||
"total_omitted": 9,
|
"total_omitted": 9,
|
||||||
"omitted_files": [
|
"omitted_files": [
|
||||||
{
|
{
|
||||||
@@ -33254,6 +33254,68 @@
|
|||||||
"corsixth"
|
"corsixth"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"dest": "dolphin-emu/Sys/GC/dsp_rom.bin",
|
||||||
|
"sha1": "3c6cc6e04fdd0b2a392d7a6ed769455444846be7",
|
||||||
|
"sha256": "49d987ee1eab29a157425b82d54516957a81e1bac247c8834e494642605c3e8c",
|
||||||
|
"size": 8192,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/.variants/dsp_rom.bin.3c6cc6e0",
|
||||||
|
"cores": [
|
||||||
|
"dolphin"
|
||||||
|
],
|
||||||
|
"systems": [
|
||||||
|
"nintendo-gamecube"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "dolphin-emu/Sys/GC/dsp_coef.bin",
|
||||||
|
"sha1": "c116d867ba001dcd6bf6d399ff4bf38d340f556c",
|
||||||
|
"sha256": "a1e269cfdc6b82d5a55baab5a0bbbb3b9f953ad92729e0d5ec50dbc2746a2c47",
|
||||||
|
"size": 4096,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/.variants/dsp_coef.bin.c116d867",
|
||||||
|
"cores": [
|
||||||
|
"dolphin"
|
||||||
|
],
|
||||||
|
"systems": [
|
||||||
|
"nintendo-gamecube"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "dolphin-emu/Sys/GC/font_western.bin",
|
||||||
|
"sha1": "483a17ff25b68a8f18cae691da99f16beefbe370",
|
||||||
|
"sha256": "4ad991be2b0aa305f09b90a79fc50f57833b30b008890b5ef1336cc3d9d0bae0",
|
||||||
|
"size": 6478,
|
||||||
|
"repo_path": "bios/Other/dolphin-mmjr/font_western.bin",
|
||||||
|
"cores": [
|
||||||
|
"dolphin"
|
||||||
|
],
|
||||||
|
"systems": [
|
||||||
|
"nintendo-gamecube"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "dolphin-emu/Sys/GC/font_japanese.bin",
|
||||||
|
"sha1": "7fbbedcc7a1ae00414a4e9e967109cc78be1fe3b",
|
||||||
|
"sha256": "38f9f59d505bc4e2d86b0196706195f33ad72b7fe9d029cf263072cde19d044f",
|
||||||
|
"size": 259626,
|
||||||
|
"repo_path": "bios/Other/dolphin-mmjr/font_japanese.bin",
|
||||||
|
"cores": [
|
||||||
|
"dolphin"
|
||||||
|
],
|
||||||
|
"systems": [
|
||||||
|
"nintendo-gamecube"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "dolphin-emu/Sys/Triforce/segaboot.gcm",
|
||||||
|
"sha1": "7297a11be143011ceb75a0720f96855003415a5d",
|
||||||
|
"sha256": "9a2eee93f5ed961aea4b12a318e2b4a54fa69a31239c1378b8bef6ea02e90391",
|
||||||
|
"size": 2097152,
|
||||||
|
"repo_path": "bios/Sega/Triforce/segaboot.gcm",
|
||||||
|
"cores": [
|
||||||
|
"dolphin"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"dest": "GC/dsp_rom.bin",
|
"dest": "GC/dsp_rom.bin",
|
||||||
"sha1": "3c6cc6e04fdd0b2a392d7a6ed769455444846be7",
|
"sha1": "3c6cc6e04fdd0b2a392d7a6ed769455444846be7",
|
||||||
@@ -38601,6 +38663,64 @@
|
|||||||
"nintendo-ds"
|
"nintendo-ds"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"dest": "dolphin-emu/Sys/GC/USA/IPL.bin",
|
||||||
|
"sha1": "ef9194ab4804aa0aa8540d846caf291b28331165",
|
||||||
|
"sha256": "7c833f95839c226699108b93ab6acbe772788bfa3f05132b8ced5e78f5bc9a26",
|
||||||
|
"size": 2097152,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/GC/USA/IPL.bin",
|
||||||
|
"cores": [
|
||||||
|
"dolphin"
|
||||||
|
],
|
||||||
|
"systems": [
|
||||||
|
"nintendo-gamecube"
|
||||||
|
],
|
||||||
|
"regions": [
|
||||||
|
"north-america"
|
||||||
|
],
|
||||||
|
"region_groups": [
|
||||||
|
"Dolphin:file:dolphin-emu/Sys/GC/USA/IPL.bin"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "dolphin-emu/Sys/GC/EUR/IPL.bin",
|
||||||
|
"sha1": "80b8744ff5e43585392f55546bd03a673d11ef5f",
|
||||||
|
"sha256": "85ffcbc834238eb24c87fce27ba7b438cc53a77db013a0a7c59c78e0af08cb84",
|
||||||
|
"size": 2097152,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/GC/EUR/IPL.bin",
|
||||||
|
"cores": [
|
||||||
|
"dolphin"
|
||||||
|
],
|
||||||
|
"systems": [
|
||||||
|
"nintendo-gamecube"
|
||||||
|
],
|
||||||
|
"regions": [
|
||||||
|
"europe",
|
||||||
|
"oceania"
|
||||||
|
],
|
||||||
|
"region_groups": [
|
||||||
|
"Dolphin:file:dolphin-emu/Sys/GC/EUR/IPL.bin"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "dolphin-emu/Sys/GC/JAP/IPL.bin",
|
||||||
|
"sha1": "a1837968288253ed541f2b11440b68f5a9b33875",
|
||||||
|
"sha256": "5b2f2617cc63f1861a1f4ae44bb135caf10e202b5b9b79271daaf60f8caaf37d",
|
||||||
|
"size": 2097152,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/GC/JAP/IPL.bin",
|
||||||
|
"cores": [
|
||||||
|
"dolphin"
|
||||||
|
],
|
||||||
|
"systems": [
|
||||||
|
"nintendo-gamecube"
|
||||||
|
],
|
||||||
|
"regions": [
|
||||||
|
"japan"
|
||||||
|
],
|
||||||
|
"region_groups": [
|
||||||
|
"Dolphin:file:dolphin-emu/Sys/GC/JAP/IPL.bin"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"dest": "GC/USA/IPL.bin",
|
"dest": "GC/USA/IPL.bin",
|
||||||
"sha1": "ef9194ab4804aa0aa8540d846caf291b28331165",
|
"sha1": "ef9194ab4804aa0aa8540d846caf291b28331165",
|
||||||
|
|||||||
+157
-4
@@ -245,10 +245,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"total_files": 7740,
|
"total_files": 7751,
|
||||||
"total_size": 7988832663,
|
"total_size": 7997666672,
|
||||||
"pack_files": 7742,
|
"pack_files": 7753,
|
||||||
"pack_size": 7988832663,
|
"pack_size": 7997666672,
|
||||||
"total_omitted": 135,
|
"total_omitted": 135,
|
||||||
"omitted_files": [
|
"omitted_files": [
|
||||||
{
|
{
|
||||||
@@ -70316,6 +70316,36 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"dest": "supermodel/Games.xml",
|
||||||
|
"sha1": "fa334ab5ba6366bd28168860c913e6365b9fec96",
|
||||||
|
"sha256": "275832cdb077c22883192f5aaeb8f76547f4dce786370f10b2d54c46a8ed3c5e",
|
||||||
|
"size": 148364,
|
||||||
|
"repo_path": "bios/Sega/Model 3/supermodel/Games.xml",
|
||||||
|
"cores": [
|
||||||
|
"supermodel"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "supermodel/Supermodel.ini",
|
||||||
|
"sha1": "cbda2b3fa65e37c5c2824a4630f95f145533c360",
|
||||||
|
"sha256": "3c910e7aa207ea55d1946fedaf12718115c21be5e67abf35b1220080c6c67919",
|
||||||
|
"size": 9421,
|
||||||
|
"repo_path": "bios/Sega/Model 3/supermodel/Supermodel.ini",
|
||||||
|
"cores": [
|
||||||
|
"supermodel"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "supermodel/Music.xml",
|
||||||
|
"sha1": "b44ed959fe2948a03d81b98e67ca32f2b4c37e78",
|
||||||
|
"sha256": "cf7555ca86e8640ee5bbb8e0c0897b073fc86647a93eda10b05065227610478c",
|
||||||
|
"size": 9224,
|
||||||
|
"repo_path": "bios/Sega/Model 3/Config/Music.xml",
|
||||||
|
"cores": [
|
||||||
|
"supermodel"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"dest": "Config/Games.xml",
|
"dest": "Config/Games.xml",
|
||||||
"sha1": "6871a47de90ca67f9ff8e4e280416851a8164563",
|
"sha1": "6871a47de90ca67f9ff8e4e280416851a8164563",
|
||||||
@@ -74985,6 +75015,116 @@
|
|||||||
"storage": "release",
|
"storage": "release",
|
||||||
"release_asset": "demo00.pk4"
|
"release_asset": "demo00.pk4"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"dest": "dolphin-emu/Sys/GC/USA/IPL.bin",
|
||||||
|
"sha1": "ef9194ab4804aa0aa8540d846caf291b28331165",
|
||||||
|
"sha256": "7c833f95839c226699108b93ab6acbe772788bfa3f05132b8ced5e78f5bc9a26",
|
||||||
|
"size": 2097152,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/GC/USA/IPL.bin",
|
||||||
|
"cores": [
|
||||||
|
"dolphin"
|
||||||
|
],
|
||||||
|
"systems": [
|
||||||
|
"triforce"
|
||||||
|
],
|
||||||
|
"regions": [
|
||||||
|
"north-america"
|
||||||
|
],
|
||||||
|
"region_groups": [
|
||||||
|
"Dolphin:file:dolphin-emu/Sys/GC/USA/IPL.bin"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "dolphin-emu/Sys/GC/EUR/IPL.bin",
|
||||||
|
"sha1": "80b8744ff5e43585392f55546bd03a673d11ef5f",
|
||||||
|
"sha256": "85ffcbc834238eb24c87fce27ba7b438cc53a77db013a0a7c59c78e0af08cb84",
|
||||||
|
"size": 2097152,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/GC/EUR/IPL.bin",
|
||||||
|
"cores": [
|
||||||
|
"dolphin"
|
||||||
|
],
|
||||||
|
"systems": [
|
||||||
|
"triforce"
|
||||||
|
],
|
||||||
|
"regions": [
|
||||||
|
"europe",
|
||||||
|
"oceania"
|
||||||
|
],
|
||||||
|
"region_groups": [
|
||||||
|
"Dolphin:file:dolphin-emu/Sys/GC/EUR/IPL.bin"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "dolphin-emu/Sys/GC/JAP/IPL.bin",
|
||||||
|
"sha1": "a1837968288253ed541f2b11440b68f5a9b33875",
|
||||||
|
"sha256": "5b2f2617cc63f1861a1f4ae44bb135caf10e202b5b9b79271daaf60f8caaf37d",
|
||||||
|
"size": 2097152,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/GC/JAP/IPL.bin",
|
||||||
|
"cores": [
|
||||||
|
"dolphin"
|
||||||
|
],
|
||||||
|
"systems": [
|
||||||
|
"triforce"
|
||||||
|
],
|
||||||
|
"regions": [
|
||||||
|
"japan"
|
||||||
|
],
|
||||||
|
"region_groups": [
|
||||||
|
"Dolphin:file:dolphin-emu/Sys/GC/JAP/IPL.bin"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "dolphin-emu/Sys/GC/dsp_rom.bin",
|
||||||
|
"sha1": "3c6cc6e04fdd0b2a392d7a6ed769455444846be7",
|
||||||
|
"sha256": "49d987ee1eab29a157425b82d54516957a81e1bac247c8834e494642605c3e8c",
|
||||||
|
"size": 8192,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/.variants/dsp_rom.bin.3c6cc6e0",
|
||||||
|
"cores": [
|
||||||
|
"dolphin"
|
||||||
|
],
|
||||||
|
"systems": [
|
||||||
|
"triforce"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "dolphin-emu/Sys/GC/dsp_coef.bin",
|
||||||
|
"sha1": "c116d867ba001dcd6bf6d399ff4bf38d340f556c",
|
||||||
|
"sha256": "a1e269cfdc6b82d5a55baab5a0bbbb3b9f953ad92729e0d5ec50dbc2746a2c47",
|
||||||
|
"size": 4096,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/.variants/dsp_coef.bin.c116d867",
|
||||||
|
"cores": [
|
||||||
|
"dolphin"
|
||||||
|
],
|
||||||
|
"systems": [
|
||||||
|
"triforce"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "dolphin-emu/Sys/GC/font_western.bin",
|
||||||
|
"sha1": "483a17ff25b68a8f18cae691da99f16beefbe370",
|
||||||
|
"sha256": "4ad991be2b0aa305f09b90a79fc50f57833b30b008890b5ef1336cc3d9d0bae0",
|
||||||
|
"size": 6478,
|
||||||
|
"repo_path": "bios/Other/dolphin-mmjr/font_western.bin",
|
||||||
|
"cores": [
|
||||||
|
"dolphin"
|
||||||
|
],
|
||||||
|
"systems": [
|
||||||
|
"triforce"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "dolphin-emu/Sys/GC/font_japanese.bin",
|
||||||
|
"sha1": "7fbbedcc7a1ae00414a4e9e967109cc78be1fe3b",
|
||||||
|
"sha256": "38f9f59d505bc4e2d86b0196706195f33ad72b7fe9d029cf263072cde19d044f",
|
||||||
|
"size": 259626,
|
||||||
|
"repo_path": "bios/Other/dolphin-mmjr/font_japanese.bin",
|
||||||
|
"cores": [
|
||||||
|
"dolphin"
|
||||||
|
],
|
||||||
|
"systems": [
|
||||||
|
"triforce"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"dest": "GC/USA/IPL.bin",
|
"dest": "GC/USA/IPL.bin",
|
||||||
"sha1": "ef9194ab4804aa0aa8540d846caf291b28331165",
|
"sha1": "ef9194ab4804aa0aa8540d846caf291b28331165",
|
||||||
@@ -87723,6 +87863,19 @@
|
|||||||
"mame2016"
|
"mame2016"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"dest": "dolphin-emu/Sys/Triforce/segaboot.gcm",
|
||||||
|
"sha1": "7297a11be143011ceb75a0720f96855003415a5d",
|
||||||
|
"sha256": "9a2eee93f5ed961aea4b12a318e2b4a54fa69a31239c1378b8bef6ea02e90391",
|
||||||
|
"size": 2097152,
|
||||||
|
"repo_path": "bios/Sega/Triforce/segaboot.gcm",
|
||||||
|
"cores": [
|
||||||
|
"dolphin"
|
||||||
|
],
|
||||||
|
"systems": [
|
||||||
|
"triforce"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"dest": "GBA/gba_bios.bin",
|
"dest": "GBA/gba_bios.bin",
|
||||||
"sha1": "300c20df6731a33952ded8c436f7f186d25d3492",
|
"sha1": "300c20df6731a33952ded8c436f7f186d25d3492",
|
||||||
|
|||||||
+258
-73
@@ -17,10 +17,10 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"standalone_copies": [],
|
"standalone_copies": [],
|
||||||
"total_files": 6115,
|
"total_files": 6132,
|
||||||
"total_size": 7848090128,
|
"total_size": 7913710008,
|
||||||
"pack_files": 6117,
|
"pack_files": 6134,
|
||||||
"pack_size": 7848090128,
|
"pack_size": 7913710008,
|
||||||
"total_omitted": 49,
|
"total_omitted": 49,
|
||||||
"omitted_files": [
|
"omitted_files": [
|
||||||
{
|
{
|
||||||
@@ -1964,6 +1964,7 @@
|
|||||||
"size": 4194304,
|
"size": 4194304,
|
||||||
"repo_path": "bios/Sony/PlayStation 2/ps2-0230a-20080220.bin",
|
"repo_path": "bios/Sony/PlayStation 2/ps2-0230a-20080220.bin",
|
||||||
"cores": [
|
"cores": [
|
||||||
|
"armsx2",
|
||||||
"yaps2"
|
"yaps2"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -2879,6 +2880,46 @@
|
|||||||
"cannonball"
|
"cannonball"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"dest": "Cemu/resources/sharedFonts/CafeCn.ttf",
|
||||||
|
"sha1": "2d95227b9b3eaf1c74d9293ecd0d190ec7ffbbe3",
|
||||||
|
"sha256": "6afd751a9bb76bc788efb50a138c6f3765624ee84d8d8203be04d871ade40a6a",
|
||||||
|
"size": 9664448,
|
||||||
|
"repo_path": "bios/Nintendo/Wii U/resources/sharedFonts/CafeCn.ttf",
|
||||||
|
"cores": [
|
||||||
|
"cemu"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "Cemu/resources/sharedFonts/CafeKr.ttf",
|
||||||
|
"sha1": "f0ef38fcc6133c24f4f38ff9b5c06b37e970d3cc",
|
||||||
|
"sha256": "c3c326f6ce1899e391dc4996b10a0c30558c5b8f36dfbaa418976862b3e2dffb",
|
||||||
|
"size": 5626464,
|
||||||
|
"repo_path": "bios/Nintendo/Wii U/resources/sharedFonts/CafeKr.ttf",
|
||||||
|
"cores": [
|
||||||
|
"cemu"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "Cemu/resources/sharedFonts/CafeStd.ttf",
|
||||||
|
"sha1": "da0d4bcc3b13339179bee946eba16adfd731b4d3",
|
||||||
|
"sha256": "59e2494727b8e7a7bbf48c8749f2dbc07d438c9fabeaa9847a08ec690f9888a1",
|
||||||
|
"size": 5363768,
|
||||||
|
"repo_path": "bios/Nintendo/Wii U/resources/sharedFonts/CafeStd.ttf",
|
||||||
|
"cores": [
|
||||||
|
"cemu"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "Cemu/resources/sharedFonts/CafeTw.ttf",
|
||||||
|
"sha1": "8ec796192ebc710f23ab6bd55453cae523b70444",
|
||||||
|
"sha256": "c8f9e16345dbc3b6e165fe12435486a748e730ebe6002b5d713efe73d171d51c",
|
||||||
|
"size": 6593548,
|
||||||
|
"repo_path": "bios/Nintendo/Wii U/resources/sharedFonts/CafeTw.ttf",
|
||||||
|
"cores": [
|
||||||
|
"cemu"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"dest": "resources/sharedFonts/CafeCn.ttf",
|
"dest": "resources/sharedFonts/CafeCn.ttf",
|
||||||
"sha1": "2d95227b9b3eaf1c74d9293ecd0d190ec7ffbbe3",
|
"sha1": "2d95227b9b3eaf1c74d9293ecd0d190ec7ffbbe3",
|
||||||
@@ -47546,6 +47587,36 @@
|
|||||||
"spmp8000emu"
|
"spmp8000emu"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"dest": "supermodel/Games.xml",
|
||||||
|
"sha1": "fa334ab5ba6366bd28168860c913e6365b9fec96",
|
||||||
|
"sha256": "275832cdb077c22883192f5aaeb8f76547f4dce786370f10b2d54c46a8ed3c5e",
|
||||||
|
"size": 148364,
|
||||||
|
"repo_path": "bios/Sega/Model 3/supermodel/Games.xml",
|
||||||
|
"cores": [
|
||||||
|
"supermodel"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "supermodel/Supermodel.ini",
|
||||||
|
"sha1": "cbda2b3fa65e37c5c2824a4630f95f145533c360",
|
||||||
|
"sha256": "3c910e7aa207ea55d1946fedaf12718115c21be5e67abf35b1220080c6c67919",
|
||||||
|
"size": 9421,
|
||||||
|
"repo_path": "bios/Sega/Model 3/supermodel/Supermodel.ini",
|
||||||
|
"cores": [
|
||||||
|
"supermodel"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "supermodel/Music.xml",
|
||||||
|
"sha1": "b44ed959fe2948a03d81b98e67ca32f2b4c37e78",
|
||||||
|
"sha256": "cf7555ca86e8640ee5bbb8e0c0897b073fc86647a93eda10b05065227610478c",
|
||||||
|
"size": 9224,
|
||||||
|
"repo_path": "bios/Sega/Model 3/Config/Music.xml",
|
||||||
|
"cores": [
|
||||||
|
"supermodel"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"dest": "Config/Games.xml",
|
"dest": "Config/Games.xml",
|
||||||
"sha1": "6871a47de90ca67f9ff8e4e280416851a8164563",
|
"sha1": "6871a47de90ca67f9ff8e4e280416851a8164563",
|
||||||
@@ -50323,6 +50394,90 @@
|
|||||||
"cdi2015"
|
"cdi2015"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"dest": "Cemu/keys.txt",
|
||||||
|
"sha1": "9edc52be45201ec99f6e4ceb5dc6abfc633c4eae",
|
||||||
|
"sha256": "c5b9ee2a14cf6a0effb3b2f0f4b93be75823da80ebf5f0ca6a0f2be8b5aa5bfd",
|
||||||
|
"size": 449507,
|
||||||
|
"repo_path": "bios/Nintendo/Wii U/keys.txt",
|
||||||
|
"cores": [
|
||||||
|
"cemu"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "Cemu/otp.bin",
|
||||||
|
"sha1": "aa4ebc7f94c22b04945b6c9232547ada10b71f6f",
|
||||||
|
"sha256": "a12993f7fe069098ab6fe1cdb43099a1dfb568223bef08ed5cb43ec93d9ff2de",
|
||||||
|
"size": 1024,
|
||||||
|
"repo_path": "bios/Nintendo/Wii U/otp.bin",
|
||||||
|
"cores": [
|
||||||
|
"cemu"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "Cemu/seeprom.bin",
|
||||||
|
"sha1": "d08ffcf19945b63c7baf35d9965a9cd6bf61332b",
|
||||||
|
"sha256": "eab425e6e65430232ec87a776d352f8fe1be4905df3a6bdaa0941c03e20069bc",
|
||||||
|
"size": 512,
|
||||||
|
"repo_path": "bios/Nintendo/Wii U/seeprom.bin",
|
||||||
|
"cores": [
|
||||||
|
"cemu"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "Cemu/shareddata.bin",
|
||||||
|
"sha1": "cd34a0ee9b7a8cbb9c9eae34739704a9dd4dd413",
|
||||||
|
"sha256": "662bc6c693c88040531fbe51186ce203e188d1c67ad788b2013b3c91ce595420",
|
||||||
|
"size": 33554432,
|
||||||
|
"repo_path": "bios/Nintendo/Wii U/shareddata.bin",
|
||||||
|
"cores": [
|
||||||
|
"cemu"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "Cemu/cafeLibs/snd_user.rpl",
|
||||||
|
"sha1": "4e0ff91f1881203e404cd41ec34725b43eec5cf2",
|
||||||
|
"sha256": "45b0dbe8926a263d86bc4e77084815e58336b9b437196d48bc3da3c5b2b2675e",
|
||||||
|
"size": 84032,
|
||||||
|
"repo_path": "bios/Nintendo/Wii U/cafeLibs/snd_user.rpl",
|
||||||
|
"cores": [
|
||||||
|
"cemu"
|
||||||
|
],
|
||||||
|
"executable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "Cemu/cafeLibs/snduser2.rpl",
|
||||||
|
"sha1": "38eac5a4695e777b25c8f8a472f36e35c5a88fc4",
|
||||||
|
"sha256": "ce928ad53d8497b6626ef3893cc0427c9ea36f0a63e82e2530179afa354eb15e",
|
||||||
|
"size": 111360,
|
||||||
|
"repo_path": "bios/Nintendo/Wii U/cafeLibs/snduser2.rpl",
|
||||||
|
"cores": [
|
||||||
|
"cemu"
|
||||||
|
],
|
||||||
|
"executable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "Cemu/cafeLibs/swkbd.rpl",
|
||||||
|
"sha1": "4d88ddd169da56de721e4aa9e213a421f7221b4b",
|
||||||
|
"sha256": "94532b2639819e121ba69d75f5eabdf00988f87609c9ab9afe5ba1ca01eef4ea",
|
||||||
|
"size": 1116288,
|
||||||
|
"repo_path": "bios/Nintendo/Wii U/cafeLibs/swkbd.rpl",
|
||||||
|
"cores": [
|
||||||
|
"cemu"
|
||||||
|
],
|
||||||
|
"executable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "Cemu/cafeLibs/erreula.rpl",
|
||||||
|
"sha1": "8c04ae8fea6e6d7431d9be8599377eca2dd690da",
|
||||||
|
"sha256": "f259a75dba800856afdda263664c6f8563b59692d5694e64d763654550d294fc",
|
||||||
|
"size": 790080,
|
||||||
|
"repo_path": "bios/Nintendo/Wii U/cafeLibs/erreula.rpl",
|
||||||
|
"cores": [
|
||||||
|
"cemu"
|
||||||
|
],
|
||||||
|
"executable": true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"dest": "keys.txt",
|
"dest": "keys.txt",
|
||||||
"sha1": "9edc52be45201ec99f6e4ceb5dc6abfc633c4eae",
|
"sha1": "9edc52be45201ec99f6e4ceb5dc6abfc633c4eae",
|
||||||
@@ -50464,6 +50619,72 @@
|
|||||||
"microsoft-xbox"
|
"microsoft-xbox"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"dest": "dolphin-emu/Sys/GC/dsp_rom.bin",
|
||||||
|
"sha1": "3c6cc6e04fdd0b2a392d7a6ed769455444846be7",
|
||||||
|
"sha256": "49d987ee1eab29a157425b82d54516957a81e1bac247c8834e494642605c3e8c",
|
||||||
|
"size": 8192,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/.variants/dsp_rom.bin.3c6cc6e0",
|
||||||
|
"cores": [
|
||||||
|
"dolphin",
|
||||||
|
"ishiiruka"
|
||||||
|
],
|
||||||
|
"systems": [
|
||||||
|
"nintendo-gamecube"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "dolphin-emu/Sys/GC/dsp_coef.bin",
|
||||||
|
"sha1": "c116d867ba001dcd6bf6d399ff4bf38d340f556c",
|
||||||
|
"sha256": "a1e269cfdc6b82d5a55baab5a0bbbb3b9f953ad92729e0d5ec50dbc2746a2c47",
|
||||||
|
"size": 4096,
|
||||||
|
"repo_path": "bios/Nintendo/GameCube/.variants/dsp_coef.bin.c116d867",
|
||||||
|
"cores": [
|
||||||
|
"dolphin",
|
||||||
|
"ishiiruka"
|
||||||
|
],
|
||||||
|
"systems": [
|
||||||
|
"nintendo-gamecube"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "dolphin-emu/Sys/GC/font_western.bin",
|
||||||
|
"sha1": "483a17ff25b68a8f18cae691da99f16beefbe370",
|
||||||
|
"sha256": "4ad991be2b0aa305f09b90a79fc50f57833b30b008890b5ef1336cc3d9d0bae0",
|
||||||
|
"size": 6478,
|
||||||
|
"repo_path": "bios/Other/dolphin-mmjr/font_western.bin",
|
||||||
|
"cores": [
|
||||||
|
"dolphin",
|
||||||
|
"ishiiruka"
|
||||||
|
],
|
||||||
|
"systems": [
|
||||||
|
"nintendo-gamecube"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "dolphin-emu/Sys/GC/font_japanese.bin",
|
||||||
|
"sha1": "7fbbedcc7a1ae00414a4e9e967109cc78be1fe3b",
|
||||||
|
"sha256": "38f9f59d505bc4e2d86b0196706195f33ad72b7fe9d029cf263072cde19d044f",
|
||||||
|
"size": 259626,
|
||||||
|
"repo_path": "bios/Other/dolphin-mmjr/font_japanese.bin",
|
||||||
|
"cores": [
|
||||||
|
"dolphin",
|
||||||
|
"ishiiruka"
|
||||||
|
],
|
||||||
|
"systems": [
|
||||||
|
"nintendo-gamecube"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "dolphin-emu/Sys/Triforce/segaboot.gcm",
|
||||||
|
"sha1": "7297a11be143011ceb75a0720f96855003415a5d",
|
||||||
|
"sha256": "9a2eee93f5ed961aea4b12a318e2b4a54fa69a31239c1378b8bef6ea02e90391",
|
||||||
|
"size": 2097152,
|
||||||
|
"repo_path": "bios/Sega/Triforce/segaboot.gcm",
|
||||||
|
"cores": [
|
||||||
|
"dolphin"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"dest": "GC/dsp_rom.bin",
|
"dest": "GC/dsp_rom.bin",
|
||||||
"sha1": "3c6cc6e04fdd0b2a392d7a6ed769455444846be7",
|
"sha1": "3c6cc6e04fdd0b2a392d7a6ed769455444846be7",
|
||||||
@@ -53367,58 +53588,6 @@
|
|||||||
"hypseus-singe"
|
"hypseus-singe"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"dest": "dolphin-emu/Sys/GC/dsp_rom.bin",
|
|
||||||
"sha1": "3c6cc6e04fdd0b2a392d7a6ed769455444846be7",
|
|
||||||
"sha256": "49d987ee1eab29a157425b82d54516957a81e1bac247c8834e494642605c3e8c",
|
|
||||||
"size": 8192,
|
|
||||||
"repo_path": "bios/Nintendo/GameCube/.variants/dsp_rom.bin.3c6cc6e0",
|
|
||||||
"cores": [
|
|
||||||
"ishiiruka"
|
|
||||||
],
|
|
||||||
"systems": [
|
|
||||||
"nintendo-gamecube"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dest": "dolphin-emu/Sys/GC/dsp_coef.bin",
|
|
||||||
"sha1": "c116d867ba001dcd6bf6d399ff4bf38d340f556c",
|
|
||||||
"sha256": "a1e269cfdc6b82d5a55baab5a0bbbb3b9f953ad92729e0d5ec50dbc2746a2c47",
|
|
||||||
"size": 4096,
|
|
||||||
"repo_path": "bios/Nintendo/GameCube/.variants/dsp_coef.bin.c116d867",
|
|
||||||
"cores": [
|
|
||||||
"ishiiruka"
|
|
||||||
],
|
|
||||||
"systems": [
|
|
||||||
"nintendo-gamecube"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dest": "dolphin-emu/Sys/GC/font_western.bin",
|
|
||||||
"sha1": "483a17ff25b68a8f18cae691da99f16beefbe370",
|
|
||||||
"sha256": "4ad991be2b0aa305f09b90a79fc50f57833b30b008890b5ef1336cc3d9d0bae0",
|
|
||||||
"size": 6478,
|
|
||||||
"repo_path": "bios/Other/dolphin-mmjr/font_western.bin",
|
|
||||||
"cores": [
|
|
||||||
"ishiiruka"
|
|
||||||
],
|
|
||||||
"systems": [
|
|
||||||
"nintendo-gamecube"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"dest": "dolphin-emu/Sys/GC/font_japanese.bin",
|
|
||||||
"sha1": "7fbbedcc7a1ae00414a4e9e967109cc78be1fe3b",
|
|
||||||
"sha256": "38f9f59d505bc4e2d86b0196706195f33ad72b7fe9d029cf263072cde19d044f",
|
|
||||||
"size": 259626,
|
|
||||||
"repo_path": "bios/Other/dolphin-mmjr/font_japanese.bin",
|
|
||||||
"cores": [
|
|
||||||
"ishiiruka"
|
|
||||||
],
|
|
||||||
"systems": [
|
|
||||||
"nintendo-gamecube"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"dest": "jazz2/Source/AnimsSw.j2a",
|
"dest": "jazz2/Source/AnimsSw.j2a",
|
||||||
"sha1": "133392a2c638d17796c6f510cf94ec391cc4c407",
|
"sha1": "133392a2c638d17796c6f510cf94ec391cc4c407",
|
||||||
@@ -61993,7 +62162,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"dest": "eeprom.bin",
|
"dest": "xbox_eeprom.bin",
|
||||||
"sha1": "ff3e7eaf715fe5612e46fc984d686ed3b115baef",
|
"sha1": "ff3e7eaf715fe5612e46fc984d686ed3b115baef",
|
||||||
"sha256": "e652203e6d91140729c29588b18ff8838896cdeb9a3100fbf8d20763af27c9d2",
|
"sha256": "e652203e6d91140729c29588b18ff8838896cdeb9a3100fbf8d20763af27c9d2",
|
||||||
"size": 256,
|
"size": 256,
|
||||||
@@ -62031,6 +62200,19 @@
|
|||||||
"microsoft-xbox"
|
"microsoft-xbox"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"dest": "eeprom.bin",
|
||||||
|
"sha1": "ff3e7eaf715fe5612e46fc984d686ed3b115baef",
|
||||||
|
"sha256": "e652203e6d91140729c29588b18ff8838896cdeb9a3100fbf8d20763af27c9d2",
|
||||||
|
"size": 256,
|
||||||
|
"repo_path": "bios/Microsoft/Xbox/xbox_eeprom.bin",
|
||||||
|
"cores": [
|
||||||
|
"xemu"
|
||||||
|
],
|
||||||
|
"systems": [
|
||||||
|
"microsoft-xbox"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"dest": "cdb/cdb_rom.bin",
|
"dest": "cdb/cdb_rom.bin",
|
||||||
"sha1": "b3c20fbe57cd2eb595e9edac86817e5948dccae4",
|
"sha1": "b3c20fbe57cd2eb595e9edac86817e5948dccae4",
|
||||||
@@ -62692,19 +62874,6 @@
|
|||||||
"microsoft-xbox"
|
"microsoft-xbox"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"dest": "GBA/gba_bios.bin",
|
|
||||||
"sha1": "300c20df6731a33952ded8c436f7f186d25d3492",
|
|
||||||
"sha256": "fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570",
|
|
||||||
"size": 16384,
|
|
||||||
"repo_path": "bios/Nintendo/Game Boy Advance/GBA_bios.rom",
|
|
||||||
"cores": [
|
|
||||||
"dolphin"
|
|
||||||
],
|
|
||||||
"systems": [
|
|
||||||
"nintendo-gamecube"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"dest": "dolphin-emu/Sys/GC/USA/IPL.bin",
|
"dest": "dolphin-emu/Sys/GC/USA/IPL.bin",
|
||||||
"sha1": "ef9194ab4804aa0aa8540d846caf291b28331165",
|
"sha1": "ef9194ab4804aa0aa8540d846caf291b28331165",
|
||||||
@@ -62712,6 +62881,7 @@
|
|||||||
"size": 2097152,
|
"size": 2097152,
|
||||||
"repo_path": "bios/Nintendo/GameCube/GC/USA/IPL.bin",
|
"repo_path": "bios/Nintendo/GameCube/GC/USA/IPL.bin",
|
||||||
"cores": [
|
"cores": [
|
||||||
|
"dolphin",
|
||||||
"ishiiruka"
|
"ishiiruka"
|
||||||
],
|
],
|
||||||
"systems": [
|
"systems": [
|
||||||
@@ -62721,7 +62891,7 @@
|
|||||||
"north-america"
|
"north-america"
|
||||||
],
|
],
|
||||||
"region_groups": [
|
"region_groups": [
|
||||||
"Ishiiruka:file:dolphin-emu/Sys/GC/USA/IPL.bin"
|
"Dolphin:file:dolphin-emu/Sys/GC/USA/IPL.bin"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -62731,6 +62901,7 @@
|
|||||||
"size": 2097152,
|
"size": 2097152,
|
||||||
"repo_path": "bios/Nintendo/GameCube/GC/EUR/IPL.bin",
|
"repo_path": "bios/Nintendo/GameCube/GC/EUR/IPL.bin",
|
||||||
"cores": [
|
"cores": [
|
||||||
|
"dolphin",
|
||||||
"ishiiruka"
|
"ishiiruka"
|
||||||
],
|
],
|
||||||
"systems": [
|
"systems": [
|
||||||
@@ -62741,7 +62912,7 @@
|
|||||||
"oceania"
|
"oceania"
|
||||||
],
|
],
|
||||||
"region_groups": [
|
"region_groups": [
|
||||||
"Ishiiruka:file:dolphin-emu/Sys/GC/EUR/IPL.bin"
|
"Dolphin:file:dolphin-emu/Sys/GC/EUR/IPL.bin"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -62751,6 +62922,7 @@
|
|||||||
"size": 2097152,
|
"size": 2097152,
|
||||||
"repo_path": "bios/Nintendo/GameCube/GC/JAP/IPL.bin",
|
"repo_path": "bios/Nintendo/GameCube/GC/JAP/IPL.bin",
|
||||||
"cores": [
|
"cores": [
|
||||||
|
"dolphin",
|
||||||
"ishiiruka"
|
"ishiiruka"
|
||||||
],
|
],
|
||||||
"systems": [
|
"systems": [
|
||||||
@@ -62760,7 +62932,20 @@
|
|||||||
"japan"
|
"japan"
|
||||||
],
|
],
|
||||||
"region_groups": [
|
"region_groups": [
|
||||||
"Ishiiruka:file:dolphin-emu/Sys/GC/JAP/IPL.bin"
|
"Dolphin:file:dolphin-emu/Sys/GC/JAP/IPL.bin"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"dest": "GBA/gba_bios.bin",
|
||||||
|
"sha1": "300c20df6731a33952ded8c436f7f186d25d3492",
|
||||||
|
"sha256": "fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570",
|
||||||
|
"size": 16384,
|
||||||
|
"repo_path": "bios/Nintendo/Game Boy Advance/GBA_bios.rom",
|
||||||
|
"cores": [
|
||||||
|
"dolphin"
|
||||||
|
],
|
||||||
|
"systems": [
|
||||||
|
"nintendo-gamecube"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -5803,3 +5803,17 @@ standalone_cores:
|
|||||||
- xvic
|
- xvic
|
||||||
- ymir
|
- ymir
|
||||||
- yquake2
|
- yquake2
|
||||||
|
dual_mode_cores:
|
||||||
|
- applewin
|
||||||
|
- dolphin
|
||||||
|
- easyrpg
|
||||||
|
- flycast
|
||||||
|
- hatari
|
||||||
|
- mame
|
||||||
|
- melonds
|
||||||
|
- pcsx2
|
||||||
|
- play
|
||||||
|
- ppsspp
|
||||||
|
- scummvm
|
||||||
|
- stella
|
||||||
|
- tic80
|
||||||
@@ -262,6 +262,16 @@ standalone_cores:
|
|||||||
- xroar
|
- xroar
|
||||||
- xscpu64
|
- xscpu64
|
||||||
- xvic
|
- xvic
|
||||||
|
dual_mode_cores:
|
||||||
|
- amiberry
|
||||||
|
- dolphin
|
||||||
|
- hatari
|
||||||
|
- mupen64plus
|
||||||
|
- pcsx2
|
||||||
|
- pcsx_rearmed
|
||||||
|
- ppsspp
|
||||||
|
- rb5000
|
||||||
|
- scummvm
|
||||||
systems:
|
systems:
|
||||||
commodore-amiga:
|
commodore-amiga:
|
||||||
files:
|
files:
|
||||||
|
|||||||
@@ -421,6 +421,27 @@ standalone_cores:
|
|||||||
- zesarux
|
- zesarux
|
||||||
- zinc
|
- zinc
|
||||||
- zquestclassic
|
- zquestclassic
|
||||||
|
dual_mode_cores:
|
||||||
|
- amiberry
|
||||||
|
- applewin
|
||||||
|
- azahar
|
||||||
|
- desmume
|
||||||
|
- dolphin
|
||||||
|
- fbneo
|
||||||
|
- flycast
|
||||||
|
- hatari
|
||||||
|
- kronos
|
||||||
|
- melonds
|
||||||
|
- mesen
|
||||||
|
- mgba
|
||||||
|
- pcsx2
|
||||||
|
- ppsspp
|
||||||
|
- scummvm
|
||||||
|
- snes9x
|
||||||
|
- stella
|
||||||
|
- supermodel
|
||||||
|
- yabasanshiro
|
||||||
|
- ymir
|
||||||
systems:
|
systems:
|
||||||
3do:
|
3do:
|
||||||
files:
|
files:
|
||||||
|
|||||||
@@ -134,6 +134,70 @@ standalone_cores:
|
|||||||
- xemu-sa
|
- xemu-sa
|
||||||
- yabasanshiro
|
- yabasanshiro
|
||||||
- yabasanshiro-sa
|
- yabasanshiro-sa
|
||||||
|
dual_mode_cores:
|
||||||
|
- aethersx2
|
||||||
|
- aethersx2-sa
|
||||||
|
- amiberry
|
||||||
|
- ares
|
||||||
|
- ares-sa
|
||||||
|
- armsx2
|
||||||
|
- armsx2-sa
|
||||||
|
- azahar
|
||||||
|
- azahar-sa
|
||||||
|
- bigpemu
|
||||||
|
- bigpemu-sa
|
||||||
|
- cemu
|
||||||
|
- cemu-sa
|
||||||
|
- daedalusx64
|
||||||
|
- daedalusx64-sa
|
||||||
|
- dolphin
|
||||||
|
- dolphin-sa
|
||||||
|
- drastic
|
||||||
|
- drastic-sa
|
||||||
|
- dsperate
|
||||||
|
- dsperate-sa
|
||||||
|
- duckstation
|
||||||
|
- duckstation-sa
|
||||||
|
- flycast
|
||||||
|
- flycast-sa
|
||||||
|
- gopher64
|
||||||
|
- gopher64-sa
|
||||||
|
- gzdoom
|
||||||
|
- gzdoom-sa
|
||||||
|
- hatari
|
||||||
|
- hatarisa
|
||||||
|
- heroic
|
||||||
|
- hypseus-singe
|
||||||
|
- mednafen
|
||||||
|
- melonds
|
||||||
|
- melonds-sa
|
||||||
|
- mupen64plus
|
||||||
|
- mupen64plus-sa
|
||||||
|
- nanoboyadvance
|
||||||
|
- nanoboyadvance-sa
|
||||||
|
- openbor
|
||||||
|
- pico-8
|
||||||
|
- ppsspp
|
||||||
|
- ppsspp-sa
|
||||||
|
- rpcs3
|
||||||
|
- rpcs3-sa
|
||||||
|
- scummvm
|
||||||
|
- scummvm-sa
|
||||||
|
- skyemu
|
||||||
|
- skyemu-sa
|
||||||
|
- steam
|
||||||
|
- supermodel
|
||||||
|
- supermodel-sa
|
||||||
|
- touchhle
|
||||||
|
- touchhle-sa
|
||||||
|
- vice
|
||||||
|
- vice-sa
|
||||||
|
- vita3k
|
||||||
|
- vita3k-sa
|
||||||
|
- xemu
|
||||||
|
- xemu-sa
|
||||||
|
- yabasanshiro
|
||||||
|
- yabasanshiro-sa
|
||||||
systems:
|
systems:
|
||||||
microsoft-xbox:
|
microsoft-xbox:
|
||||||
files:
|
files:
|
||||||
|
|||||||
@@ -45,6 +45,14 @@
|
|||||||
},
|
},
|
||||||
"uniqueItems": true
|
"uniqueItems": true
|
||||||
},
|
},
|
||||||
|
"dual_mode_cores": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"uniqueItems": true,
|
||||||
|
"description": "Emulators of standalone_cores the platform also launches as a libretro core: both layouts get their files"
|
||||||
|
},
|
||||||
"homepage": {
|
"homepage": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "uri",
|
"format": "uri",
|
||||||
|
|||||||
@@ -1861,6 +1861,7 @@ def group_identical_platforms(
|
|||||||
emulators = [
|
emulators = [
|
||||||
cores if isinstance(cores, str) else sorted(map(str, cores or [])),
|
cores if isinstance(cores, str) else sorted(map(str, cores or [])),
|
||||||
sorted(map(str, config.get("standalone_cores") or [])),
|
sorted(map(str, config.get("standalone_cores") or [])),
|
||||||
|
sorted(map(str, config.get("dual_mode_cores") or [])),
|
||||||
# One archive cannot serve a filesystem that folds case and one
|
# One archive cannot serve a filesystem that folds case and one
|
||||||
# that does not: Lakka keeps bios.col beside BIOS.col.
|
# that does not: Lakka keeps bios.col beside BIOS.col.
|
||||||
bool(config.get("case_insensitive_fs")),
|
bool(config.get("case_insensitive_fs")),
|
||||||
@@ -2004,6 +2005,44 @@ def runs_standalone(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Marks, in a platform's standalone set, an emulator it also launches as a
|
||||||
|
# libretro core. No core name starts with it.
|
||||||
|
DUAL_MODE_PREFIX = "dual:"
|
||||||
|
|
||||||
|
|
||||||
|
def standalone_set(config: dict) -> set[str]:
|
||||||
|
"""The emulators a platform launches standalone, with its dual-mode ones.
|
||||||
|
|
||||||
|
``dual_mode_cores`` names those it offers both ways (Batocera lists
|
||||||
|
libretro:dolphin and standalone dolphin for GameCube). They travel in the
|
||||||
|
same set, tagged, so every reader of standalone_cores sees them.
|
||||||
|
"""
|
||||||
|
names = {str(c) for c in config.get("standalone_cores") or []}
|
||||||
|
names |= {DUAL_MODE_PREFIX + str(c) for c in config.get("dual_mode_cores") or []}
|
||||||
|
return names
|
||||||
|
|
||||||
|
|
||||||
|
def platform_layouts(
|
||||||
|
emu_name: str, profile: dict, standalone_cores: set[str]
|
||||||
|
) -> list[tuple[dict, bool]]:
|
||||||
|
"""The layouts a platform gives an emulator: (profile, standalone?) each.
|
||||||
|
|
||||||
|
One, libretro or standalone, for most. Both for an emulator the platform
|
||||||
|
launches either way: laid out standalone only, Batocera's libretro
|
||||||
|
Dolphin found dolphin-emu/Sys/GC/ empty. Each layout keeps its own rules
|
||||||
|
(what the build reads from the system directory, where it reads it).
|
||||||
|
"""
|
||||||
|
if not runs_standalone(emu_name, profile, standalone_cores):
|
||||||
|
return [(profile, False)]
|
||||||
|
dual = {c.removeprefix(DUAL_MODE_PREFIX) for c in standalone_cores
|
||||||
|
if c.startswith(DUAL_MODE_PREFIX)}
|
||||||
|
if "libretro" in str(profile.get("type", "")) and dual & {
|
||||||
|
emu_name, *(str(c) for c in profile.get("cores", []))
|
||||||
|
}:
|
||||||
|
return [(profile, False), (profile, True)]
|
||||||
|
return [(profile, True)]
|
||||||
|
|
||||||
|
|
||||||
# What identifies a profile entry's content, carried unchanged wherever the
|
# What identifies a profile entry's content, carried unchanged wherever the
|
||||||
# entry travels (cross-reference report, core extras, second-pass copies).
|
# entry travels (cross-reference report, core extras, second-pass copies).
|
||||||
# Each copy once kept its own subset: np2kai's bios.rom lost min_size and
|
# Each copy once kept its own subset: np2kai's bios.rom lost min_size and
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ from common import (
|
|||||||
require_platform,
|
require_platform,
|
||||||
require_yaml,
|
require_yaml,
|
||||||
resolve_local_file,
|
resolve_local_file,
|
||||||
runs_standalone,
|
platform_layouts,
|
||||||
|
standalone_set,
|
||||||
select_emulator_profiles,
|
select_emulator_profiles,
|
||||||
)
|
)
|
||||||
from validation import declaration_settles, outside_gap_scope
|
from validation import declaration_settles, outside_gap_scope
|
||||||
@@ -502,7 +503,10 @@ def _cross_reference_profile(
|
|||||||
|
|
||||||
# The build a platform runs decides which entries it reads, as in
|
# The build a platform runs decides which entries it reads, as in
|
||||||
# find_undeclared_files: without a platform, the libretro build.
|
# find_undeclared_files: without a platform, the libretro build.
|
||||||
is_standalone = runs_standalone(emu_name, profile, standalone_cores or set())
|
layouts = platform_layouts(emu_name, profile, standalone_cores or set())
|
||||||
|
# Each file under each layout the platform gives the emulator: both, for
|
||||||
|
# one it launches as a libretro core and standalone.
|
||||||
|
layout_files = [(f, standalone) for view, standalone in layouts for f in view.get("files", [])]
|
||||||
gaps = []
|
gaps = []
|
||||||
covered = []
|
covered = []
|
||||||
unsourceable_list: list[dict] = []
|
unsourceable_list: list[dict] = []
|
||||||
@@ -516,7 +520,7 @@ def _cross_reference_profile(
|
|||||||
for f in emu_files:
|
for f in emu_files:
|
||||||
versions.setdefault(slot_key(f), []).append(f)
|
versions.setdefault(slot_key(f), []).append(f)
|
||||||
|
|
||||||
for f in emu_files:
|
for f, is_standalone in layout_files:
|
||||||
fname = f.get("name", "")
|
fname = f.get("name", "")
|
||||||
# The destination the running build reads, as find_undeclared_files
|
# The destination the running build reads, as find_undeclared_files
|
||||||
# takes it: Batocera's standalone Cemu reads keys.txt at
|
# takes it: Batocera's standalone Cemu reads keys.txt at
|
||||||
@@ -527,6 +531,9 @@ def _cross_reference_profile(
|
|||||||
else:
|
else:
|
||||||
effective_path = f.get("path") or fname
|
effective_path = f.get("path") or fname
|
||||||
seen_key = slot_key(f)
|
seen_key = slot_key(f)
|
||||||
|
if len(layouts) > 1:
|
||||||
|
# One file read at one path by both builds counts once.
|
||||||
|
seen_key = (effective_path.casefold(), seen_key)
|
||||||
if not fname or seen_key in seen_files:
|
if not fname or seen_key in seen_files:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -820,7 +827,7 @@ def main() -> None:
|
|||||||
config = load_platform_config(args.platform, args.platforms_dir)
|
config = load_platform_config(args.platform, args.platforms_dir)
|
||||||
relevant = resolve_platform_cores(config, profiles, target_cores=target_cores)
|
relevant = resolve_platform_cores(config, profiles, target_cores=target_cores)
|
||||||
profiles = {k: v for k, v in profiles.items() if k in relevant}
|
profiles = {k: v for k, v in profiles.items() if k in relevant}
|
||||||
standalone_cores = set(map(str, config.get("standalone_cores", [])))
|
standalone_cores = standalone_set(config)
|
||||||
|
|
||||||
if not profiles:
|
if not profiles:
|
||||||
if args.emulator:
|
if args.emulator:
|
||||||
|
|||||||
+158
-157
@@ -15,7 +15,8 @@ from common import load_emulator_profiles
|
|||||||
from common import resolution_is_hash_exact
|
from common import resolution_is_hash_exact
|
||||||
from common import resolve_local_file
|
from common import resolve_local_file
|
||||||
from common import resolve_platform_cores
|
from common import resolve_platform_cores
|
||||||
from common import runs_standalone
|
from common import platform_layouts
|
||||||
|
from common import standalone_set as platform_standalone_set
|
||||||
from common import sanitize_pack_path
|
from common import sanitize_pack_path
|
||||||
from common import size_fits
|
from common import size_fits
|
||||||
from validation import read_from_system_dir
|
from validation import read_from_system_dir
|
||||||
@@ -290,96 +291,96 @@ def _agnostic_scan_extras(
|
|||||||
agnostic_votes.get(directory + "/", 0) + 1
|
agnostic_votes.get(directory + "/", 0) + 1
|
||||||
)
|
)
|
||||||
|
|
||||||
standalone = runs_standalone(emu_name, profile, standalone_set or set())
|
for profile, standalone in platform_layouts(emu_name, profile, standalone_set or set()):
|
||||||
for f in profile.get("files", []):
|
for f in profile.get("files", []):
|
||||||
if not is_profile_agnostic and not f.get("agnostic"):
|
if not is_profile_agnostic and not f.get("agnostic"):
|
||||||
continue
|
continue
|
||||||
fname = f.get("name", "")
|
fname = f.get("name", "")
|
||||||
if not fname:
|
if not fname:
|
||||||
continue
|
continue
|
||||||
# A file of the build the platform does not run seeds nothing:
|
# A file of the build the platform does not run seeds nothing:
|
||||||
# armsx2's proverb.zip is fetched by the Android app into its own
|
# armsx2's proverb.zip is fetched by the Android app into its own
|
||||||
# arcade/ folder, and the libretro packs carried it at system/.
|
# arcade/ folder, and the libretro packs carried it at system/.
|
||||||
mode = f.get("mode")
|
mode = f.get("mode")
|
||||||
if (mode == "standalone" and not standalone) or (
|
if (mode == "standalone" and not standalone) or (
|
||||||
mode == "libretro" and standalone
|
mode == "libretro" and standalone
|
||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
# An agnostic BIOS mode says the BIOS filename is free, not that
|
# An agnostic BIOS mode says the BIOS filename is free, not that
|
||||||
# every file the emulator loads is. A free filename still has a
|
# every file the emulator loads is. A free filename still has a
|
||||||
# known shape, and that shape is what identifies a candidate: an
|
# known shape, and that shape is what identifies a candidate: an
|
||||||
# entry declaring no size takes the whole directory, which is how
|
# entry declaring no size takes the whole directory, which is how
|
||||||
# the flag icons of an Android package walked into a PS2 pack.
|
# the flag icons of an Android package walked into a PS2 pack.
|
||||||
if f.get("category", "bios") != "bios":
|
if f.get("category", "bios") != "bios":
|
||||||
continue
|
continue
|
||||||
if not (f.get("size") or f.get("min_size") or f.get("max_size")):
|
if not (f.get("size") or f.get("min_size") or f.get("max_size")):
|
||||||
continue
|
continue
|
||||||
# The free name is the BIOS directory's: a shader, a sound or a
|
# The free name is the BIOS directory's: a shader, a sound or a
|
||||||
# font the emulator reads from its resources tree has a fixed
|
# font the emulator reads from its resources tree has a fixed
|
||||||
# name and must not seed a scan (yaps2 flattened 189 of them to
|
# name and must not seed a scan (yaps2 flattened 189 of them to
|
||||||
# the root of the RetroArch pack).
|
# the root of the RetroArch pack).
|
||||||
bios_dir = profile.get("bios_directory")
|
bios_dir = profile.get("bios_directory")
|
||||||
fpath = f.get("path") or ""
|
fpath = f.get("path") or ""
|
||||||
if bios_dir and "/" in fpath and not fpath.startswith(bios_dir):
|
if bios_dir and "/" in fpath and not fpath.startswith(bios_dir):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
path_prefix = resolved_dirs.get(id(f), "")
|
path_prefix = resolved_dirs.get(id(f), "")
|
||||||
if not path_prefix:
|
if not path_prefix:
|
||||||
continue
|
continue
|
||||||
# A name matching one file identifies it. A name matching several
|
# A name matching one file identifies it. A name matching several
|
||||||
# identifies nothing on its own, so the profile's other files have
|
# identifies nothing on its own, so the profile's other files have
|
||||||
# to agree on the directory before the scan walks it.
|
# to agree on the directory before the scan walks it.
|
||||||
ambiguous = id(f) in named_only and len(by_name.get(fname, [])) > 1
|
ambiguous = id(f) in named_only and len(by_name.get(fname, [])) > 1
|
||||||
if ambiguous and agnostic_votes.get(path_prefix, 0) < 2:
|
if ambiguous and agnostic_votes.get(path_prefix, 0) < 2:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Scan DB for all files under this prefix matching size
|
# Scan DB for all files under this prefix matching size
|
||||||
for sha1, entry in files_db.items():
|
for sha1, entry in files_db.items():
|
||||||
path = entry.get("path", "")
|
path = entry.get("path", "")
|
||||||
if not path.startswith(path_prefix):
|
if not path.startswith(path_prefix):
|
||||||
continue
|
continue
|
||||||
if not size_fits(f, entry.get("size", 0)):
|
if not size_fits(f, entry.get("size", 0)):
|
||||||
continue
|
continue
|
||||||
scan_name = entry.get("name", "")
|
scan_name = entry.get("name", "")
|
||||||
if not scan_name:
|
if not scan_name:
|
||||||
continue
|
continue
|
||||||
# Into the directory the seed entry names, not the root:
|
# Into the directory the seed entry names, not the root:
|
||||||
# yaps2 reads its BIOS from pcsx2/bios/.
|
# yaps2 reads its BIOS from pcsx2/bios/.
|
||||||
seed = f.get("path") or ""
|
seed = f.get("path") or ""
|
||||||
if standalone:
|
if standalone:
|
||||||
seed = f.get("standalone_path") or seed
|
seed = f.get("standalone_path") or seed
|
||||||
folder = seed.rsplit("/", 1)[0] if "/" in seed else ""
|
folder = seed.rsplit("/", 1)[0] if "/" in seed else ""
|
||||||
dest = f"{folder}/{scan_name}" if folder else scan_name
|
dest = f"{folder}/{scan_name}" if folder else scan_name
|
||||||
full_dest = f"{extras_prefix}/{dest}" if extras_prefix else dest
|
full_dest = f"{extras_prefix}/{dest}" if extras_prefix else dest
|
||||||
if full_dest in seen_dests and (taken is None or full_dest not in taken):
|
if full_dest in seen_dests and (taken is None or full_dest not in taken):
|
||||||
continue
|
continue
|
||||||
extra = {
|
extra = {
|
||||||
"name": scan_name,
|
"name": scan_name,
|
||||||
"destination": dest,
|
"destination": dest,
|
||||||
# The scan already holds the entry it selected, so it
|
# The scan already holds the entry it selected, so it
|
||||||
# names it by content. Emitting the filename alone
|
# names it by content. Emitting the filename alone
|
||||||
# sent the packing step back to a by-name lookup, and
|
# sent the packing step back to a by-name lookup, and
|
||||||
# three PS2 resources came back from another
|
# three PS2 resources came back from another
|
||||||
# emulator's copy of the same filename.
|
# emulator's copy of the same filename.
|
||||||
"sha1": sha1,
|
"sha1": sha1,
|
||||||
"required": False,
|
"required": False,
|
||||||
"hle_fallback": False,
|
"hle_fallback": False,
|
||||||
"source_emulator": profile.get("emulator", emu_name),
|
"source_emulator": profile.get("emulator", emu_name),
|
||||||
"source_profile": emu_name,
|
"source_profile": emu_name,
|
||||||
"source_system": f.get("system"),
|
"source_system": f.get("system"),
|
||||||
"source_systems": list(profile.get("systems", [])),
|
"source_systems": list(profile.get("systems", [])),
|
||||||
"region": f.get("region"),
|
"region": f.get("region"),
|
||||||
"variant_group": f.get("variant_group"),
|
"variant_group": f.get("variant_group"),
|
||||||
"agnostic_scan": True,
|
"agnostic_scan": True,
|
||||||
}
|
}
|
||||||
extra["claim_order"] = (4, 0, emu_name)
|
extra["claim_order"] = (4, 0, emu_name)
|
||||||
if not _take(extra, full_dest, seen_dests, taken):
|
if not _take(extra, full_dest, seen_dests, taken):
|
||||||
continue
|
continue
|
||||||
extras.append(extra)
|
extras.append(extra)
|
||||||
extras.extend(_companion_extras(
|
extras.extend(_companion_extras(
|
||||||
_companions(path, scan_name, _companion_extensions(profile, fname), files_db),
|
_companions(path, scan_name, _companion_extensions(profile, fname), files_db),
|
||||||
folder, extras_prefix, seen_dests, profile, emu_name, f, taken,
|
folder, extras_prefix, seen_dests, profile, emu_name, f, taken,
|
||||||
))
|
))
|
||||||
return extras
|
return extras
|
||||||
|
|
||||||
|
|
||||||
@@ -701,7 +702,7 @@ def _collect_emulator_extras(
|
|||||||
return extras
|
return extras
|
||||||
|
|
||||||
relevant = resolve_platform_cores(config, profiles, target_cores=target_cores)
|
relevant = resolve_platform_cores(config, profiles, target_cores=target_cores)
|
||||||
standalone_set = {str(c) for c in config.get("standalone_cores", [])}
|
standalone_set = platform_standalone_set(config)
|
||||||
by_name = db.get("indexes", {}).get("by_name", {})
|
by_name = db.get("indexes", {}).get("by_name", {})
|
||||||
by_path_suffix = db.get("indexes", {}).get("by_path_suffix", {})
|
by_path_suffix = db.get("indexes", {}).get("by_path_suffix", {})
|
||||||
|
|
||||||
@@ -716,73 +717,73 @@ def _collect_emulator_extras(
|
|||||||
continue
|
continue
|
||||||
if emu_name not in relevant:
|
if emu_name not in relevant:
|
||||||
continue
|
continue
|
||||||
is_standalone = runs_standalone(emu_name, profile, standalone_set)
|
for profile, is_standalone in platform_layouts(emu_name, profile, standalone_set):
|
||||||
for f in profile.get("files", []):
|
for f in profile.get("files", []):
|
||||||
fname = f.get("name", "")
|
fname = f.get("name", "")
|
||||||
if not fname:
|
if not fname:
|
||||||
continue
|
continue
|
||||||
# Only duplicate files already covered at another destination.
|
# Only duplicate files already covered at another destination.
|
||||||
# An unsourceable entry is a file no collection holds: the name
|
# An unsourceable entry is a file no collection holds: the name
|
||||||
# covered elsewhere belongs to another program's file.
|
# covered elsewhere belongs to another program's file.
|
||||||
if fname not in covered_names or f.get("unsourceable"):
|
if fname not in covered_names or f.get("unsourceable"):
|
||||||
continue
|
continue
|
||||||
# A member of an archive is read inside it, never as a loose
|
# A member of an archive is read inside it, never as a loose
|
||||||
# file; the archive itself is placed by the passes above.
|
# file; the archive itself is placed by the passes above.
|
||||||
if f.get("archive"):
|
if f.get("archive"):
|
||||||
continue
|
continue
|
||||||
# No path: the core reads it from nowhere a pack reaches
|
# No path: the core reads it from nowhere a pack reaches
|
||||||
# (Dolphin's codehandler.bin ships in its Sys tree).
|
# (Dolphin's codehandler.bin ships in its Sys tree).
|
||||||
if "path" in f and f["path"] is None:
|
if "path" in f and f["path"] is None:
|
||||||
continue
|
continue
|
||||||
file_mode = f.get("mode")
|
file_mode = f.get("mode")
|
||||||
if file_mode == "standalone" and not is_standalone:
|
if file_mode == "standalone" and not is_standalone:
|
||||||
continue
|
continue
|
||||||
if file_mode == "libretro" and is_standalone:
|
if file_mode == "libretro" and is_standalone:
|
||||||
continue
|
continue
|
||||||
if not read_from_system_dir(f, is_standalone):
|
if not read_from_system_dir(f, is_standalone):
|
||||||
continue
|
continue
|
||||||
if is_standalone:
|
if is_standalone:
|
||||||
raw = f.get("standalone_path") or f.get("path") or fname
|
raw = f.get("standalone_path") or f.get("path") or fname
|
||||||
else:
|
else:
|
||||||
raw = f.get("path") or fname
|
raw = f.get("path") or fname
|
||||||
dest = f"{raw}{fname}" if raw.endswith("/") else raw
|
dest = f"{raw}{fname}" if raw.endswith("/") else raw
|
||||||
# A bare name is a destination too: the core reads it at the root
|
# A bare name is a destination too: the core reads it at the root
|
||||||
# of the directory core files go to, which the platform may not
|
# of the directory core files go to, which the platform may not
|
||||||
# declare (Recalbox has its System Cards under pcenginecd/ only,
|
# declare (Recalbox has its System Cards under pcenginecd/ only,
|
||||||
# geargrafx reads them from the root).
|
# geargrafx reads them from the root).
|
||||||
full_dest = f"{extras_prefix}/{dest}" if extras_prefix else dest
|
full_dest = f"{extras_prefix}/{dest}" if extras_prefix else dest
|
||||||
if full_dest in seen_dests and full_dest not in taken:
|
if full_dest in seen_dests and full_dest not in taken:
|
||||||
continue
|
continue
|
||||||
# Check file exists in repo or data dirs
|
# Check file exists in repo or data dirs
|
||||||
if not (
|
if not (
|
||||||
by_name.get(fname)
|
by_name.get(fname)
|
||||||
or by_name.get(dest.rsplit("/", 1)[-1])
|
or by_name.get(dest.rsplit("/", 1)[-1])
|
||||||
or by_path_suffix.get(dest)
|
or by_path_suffix.get(dest)
|
||||||
):
|
):
|
||||||
continue
|
continue
|
||||||
extra = {
|
extra = {
|
||||||
"name": fname,
|
"name": fname,
|
||||||
"destination": dest,
|
"destination": dest,
|
||||||
"required": f.get("required", False),
|
"required": f.get("required", False),
|
||||||
"hle_fallback": f.get("hle_fallback", False),
|
"hle_fallback": f.get("hle_fallback", False),
|
||||||
"source_emulator": profile.get("emulator", emu_name),
|
"source_emulator": profile.get("emulator", emu_name),
|
||||||
"source_profile": emu_name,
|
"source_profile": emu_name,
|
||||||
"source_system": f.get("system"),
|
"source_system": f.get("system"),
|
||||||
"source_systems": list(profile.get("systems", [])),
|
"source_systems": list(profile.get("systems", [])),
|
||||||
"region": f.get("region"),
|
"region": f.get("region"),
|
||||||
"variant_group": f.get("variant_group"),
|
"variant_group": f.get("variant_group"),
|
||||||
}
|
}
|
||||||
# The same identity the first pass carries: without it the copy
|
# The same identity the first pass carries: without it the copy
|
||||||
# resolves on its name alone and a same-named file of another
|
# resolves on its name alone and a same-named file of another
|
||||||
# game fills the slot the profile marks as not held.
|
# game fills the slot the profile marks as not held.
|
||||||
for identity in PROFILE_IDENTITY_FIELDS:
|
for identity in PROFILE_IDENTITY_FIELDS:
|
||||||
if f.get(identity) not in (None, "", []):
|
if f.get(identity) not in (None, "", []):
|
||||||
extra[identity] = f[identity]
|
extra[identity] = f[identity]
|
||||||
if f.get("contents"):
|
if f.get("contents"):
|
||||||
extra["contents"] = f["contents"]
|
extra["contents"] = f["contents"]
|
||||||
extra["claim_order"] = (2, 0, emu_name)
|
extra["claim_order"] = (2, 0, emu_name)
|
||||||
if _take(extra, full_dest, seen_dests, taken):
|
if _take(extra, full_dest, seen_dests, taken):
|
||||||
extras.append(extra)
|
extras.append(extra)
|
||||||
|
|
||||||
extras.extend(
|
extras.extend(
|
||||||
_archive_prefix_extras(
|
_archive_prefix_extras(
|
||||||
|
|||||||
@@ -97,3 +97,15 @@ def yaml_load(stream: str | TextIO) -> Any:
|
|||||||
if yaml is None:
|
if yaml is None:
|
||||||
return require_yaml() # exits with the install hint
|
return require_yaml() # exits with the install hint
|
||||||
return yaml.load(stream, Loader=_YAML_LOADER)
|
return yaml.load(stream, Loader=_YAML_LOADER)
|
||||||
|
|
||||||
|
|
||||||
|
def yaml_load_upstream(stream: str | TextIO) -> Any:
|
||||||
|
"""Parse a third party's YAML the way its own PyYAML code reads it.
|
||||||
|
|
||||||
|
Our files refuse a repeated key; a platform's file is data we transcribe,
|
||||||
|
and its code keeps the last value. Batocera 43.1's configgen-defaults.yml
|
||||||
|
names rtcw's core twice, and refusing it stopped the rescrape.
|
||||||
|
"""
|
||||||
|
if yaml is None:
|
||||||
|
return require_yaml()
|
||||||
|
return yaml.load(stream, Loader=getattr(yaml, "CSafeLoader", None) or yaml.SafeLoader)
|
||||||
@@ -18,6 +18,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
|
|
||||||
from common import yaml_load
|
from common import yaml_load
|
||||||
|
from safeparse import yaml_load_upstream
|
||||||
|
|
||||||
from .base_scraper import (
|
from .base_scraper import (
|
||||||
BaseScraper,
|
BaseScraper,
|
||||||
@@ -98,7 +99,7 @@ def es_systems_emulators(text: str) -> tuple[set[str], set[str]]:
|
|||||||
reading it alone left DOSBox Staging, melonDS DS and nine other built
|
reading it alone left DOSBox Staging, melonDS DS and nine other built
|
||||||
cores outside the pack.
|
cores outside the pack.
|
||||||
"""
|
"""
|
||||||
data = yaml_load(text)
|
data = yaml_load_upstream(text)
|
||||||
if not isinstance(data, dict):
|
if not isinstance(data, dict):
|
||||||
raise ValueError("es_systems.yml is not a mapping")
|
raise ValueError("es_systems.yml is not a mapping")
|
||||||
cores: set[str] = set()
|
cores: set[str] = set()
|
||||||
@@ -126,6 +127,20 @@ def es_systems_emulators(text: str) -> tuple[set[str], set[str]]:
|
|||||||
return cores, standalone
|
return cores, standalone
|
||||||
|
|
||||||
|
|
||||||
|
def es_systems_libretro(text: str) -> set[str]:
|
||||||
|
"""The cores es_systems.yml offers under `libretro:` for some system."""
|
||||||
|
data = yaml_load_upstream(text)
|
||||||
|
if not isinstance(data, dict):
|
||||||
|
raise ValueError("es_systems.yml is not a mapping")
|
||||||
|
found: set[str] = set()
|
||||||
|
for system in data.values():
|
||||||
|
emulators = system.get("emulators") if isinstance(system, dict) else None
|
||||||
|
libretro = emulators.get("libretro") if isinstance(emulators, dict) else None
|
||||||
|
if isinstance(libretro, dict):
|
||||||
|
found.update(str(core) for core, spec in libretro.items() if isinstance(spec, dict))
|
||||||
|
return found
|
||||||
|
|
||||||
|
|
||||||
SYSTEM_SLUG_MAP = {
|
SYSTEM_SLUG_MAP = {
|
||||||
"advision": "entex-advision",
|
"advision": "entex-advision",
|
||||||
"amiga500": "commodore-amiga",
|
"amiga500": "commodore-amiga",
|
||||||
@@ -253,11 +268,12 @@ class Scraper(BaseScraper):
|
|||||||
self.tag = fetch_stable_tag()
|
self.tag = fetch_stable_tag()
|
||||||
super().__init__(url=SOURCE_URL.format(tag=self.tag))
|
super().__init__(url=SOURCE_URL.format(tag=self.tag))
|
||||||
|
|
||||||
def _fetch_cores(self) -> tuple[list[str], list[str]]:
|
def _fetch_cores(self) -> tuple[list[str], list[str], list[str]]:
|
||||||
"""Extract core names and standalone cores from configgen-defaults.yml.
|
"""Extract core names and standalone cores from configgen-defaults.yml.
|
||||||
|
|
||||||
Returns (all_cores, standalone_cores) where standalone_cores are
|
Returns (all_cores, standalone_cores, dual_mode_cores): standalone
|
||||||
those with emulator != "libretro".
|
cores are those with emulator != "libretro", dual-mode ones those
|
||||||
|
es_systems.yml also offers under libretro (dolphin for GameCube).
|
||||||
"""
|
"""
|
||||||
url = CONFIGGEN_DEFAULTS_URL.format(tag=self.tag)
|
url = CONFIGGEN_DEFAULTS_URL.format(tag=self.tag)
|
||||||
try:
|
try:
|
||||||
@@ -271,10 +287,10 @@ class Scraper(BaseScraper):
|
|||||||
raise ConnectionError(
|
raise ConnectionError(
|
||||||
f"Failed to fetch {url}: {e}"
|
f"Failed to fetch {url}: {e}"
|
||||||
) from e
|
) from e
|
||||||
data = yaml_load(raw)
|
data = yaml_load_upstream(raw)
|
||||||
cores, standalone = es_systems_emulators(
|
es_systems = self._fetch_text(ES_SYSTEMS_URL.format(tag=self.tag))
|
||||||
self._fetch_text(ES_SYSTEMS_URL.format(tag=self.tag))
|
cores, standalone = es_systems_emulators(es_systems)
|
||||||
)
|
libretro = es_systems_libretro(es_systems)
|
||||||
if not cores:
|
if not cores:
|
||||||
raise RuntimeError("es_systems.yml names no emulator")
|
raise RuntimeError("es_systems.yml names no emulator")
|
||||||
for system, cfg in data.items():
|
for system, cfg in data.items():
|
||||||
@@ -289,7 +305,8 @@ class Scraper(BaseScraper):
|
|||||||
if core and core != emulator:
|
if core and core != emulator:
|
||||||
standalone.add(core)
|
standalone.add(core)
|
||||||
dropped = _not_cores()
|
dropped = _not_cores()
|
||||||
return sorted(cores - dropped), sorted(standalone - dropped)
|
dual = (standalone & libretro) - dropped
|
||||||
|
return sorted(cores - dropped), sorted(standalone - dropped), sorted(dual)
|
||||||
|
|
||||||
def _fetch_text(self, url: str) -> str:
|
def _fetch_text(self, url: str) -> str:
|
||||||
req = urllib.request.Request(url, headers={"User-Agent": "retrobios-scraper/1.0"})
|
req = urllib.request.Request(url, headers={"User-Agent": "retrobios-scraper/1.0"})
|
||||||
@@ -449,7 +466,7 @@ class Scraper(BaseScraper):
|
|||||||
|
|
||||||
systems[req.system]["files"].append(requirement_entry(req))
|
systems[req.system]["files"].append(requirement_entry(req))
|
||||||
|
|
||||||
cores, standalone = self._fetch_cores()
|
cores, standalone, dual = self._fetch_cores()
|
||||||
result = {
|
result = {
|
||||||
"platform": "Batocera",
|
"platform": "Batocera",
|
||||||
"version": self.tag.removeprefix("batocera-"),
|
"version": self.tag.removeprefix("batocera-"),
|
||||||
@@ -463,6 +480,9 @@ class Scraper(BaseScraper):
|
|||||||
}
|
}
|
||||||
if standalone:
|
if standalone:
|
||||||
result["standalone_cores"] = standalone
|
result["standalone_cores"] = standalone
|
||||||
|
if dual:
|
||||||
|
# Launched both ways: each layout gets its files.
|
||||||
|
result["dual_mode_cores"] = dual
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -153,6 +153,18 @@ def _core_names(values: dict[str, str]) -> tuple[set[str], set[str]]:
|
|||||||
return names, set(names)
|
return names, set(names)
|
||||||
|
|
||||||
|
|
||||||
|
def system_ini_libretro(text: str) -> set[str]:
|
||||||
|
"""The libretro cores one Recalbox system.ini offers."""
|
||||||
|
import re
|
||||||
|
|
||||||
|
return {
|
||||||
|
values.get("core", "")
|
||||||
|
for section, values in _ini_sections(text).items()
|
||||||
|
if re.fullmatch(r"core\.\d+(\.[\w-]+)?", section)
|
||||||
|
and values.get("emulator") == "libretro" and values.get("core")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def system_ini_cores(text: str) -> tuple[set[str], set[str]]:
|
def system_ini_cores(text: str) -> tuple[set[str], set[str]]:
|
||||||
"""The cores one Recalbox system.ini offers, and those run standalone.
|
"""The cores one Recalbox system.ini offers, and those run standalone.
|
||||||
|
|
||||||
@@ -227,8 +239,9 @@ class Scraper(BaseScraper):
|
|||||||
raise RuntimeError(f"no system.ini at {self.tag} for {', '.join(missing[:5])}")
|
raise RuntimeError(f"no system.ini at {self.tag} for {', '.join(missing[:5])}")
|
||||||
return [texts[path] for path in paths]
|
return [texts[path] for path in paths]
|
||||||
|
|
||||||
def _fetch_cores(self) -> tuple[list[str], list[str]]:
|
def _fetch_cores(self) -> tuple[list[str], list[str], list[str]]:
|
||||||
"""Every core Recalbox's systems offer, and those it runs standalone.
|
"""Every core Recalbox's systems offer, those it runs standalone, and
|
||||||
|
those it offers both as a libretro core and standalone.
|
||||||
|
|
||||||
Read from each system.ini of recalbox-romfs2 at the scraped tag: the
|
Read from each system.ini of recalbox-romfs2 at the scraped tag: the
|
||||||
systems ES lists and the cores a user can pick for each.
|
systems ES lists and the cores a user can pick for each.
|
||||||
@@ -250,13 +263,15 @@ class Scraper(BaseScraper):
|
|||||||
raise RuntimeError(f"no Recalbox system found at {self.tag}")
|
raise RuntimeError(f"no Recalbox system found at {self.tag}")
|
||||||
cores: set[str] = set()
|
cores: set[str] = set()
|
||||||
standalone: set[str] = set()
|
standalone: set[str] = set()
|
||||||
|
libretro: set[str] = set()
|
||||||
for text in self._system_inis(systems):
|
for text in self._system_inis(systems):
|
||||||
found, alone = system_ini_cores(text)
|
found, alone = system_ini_cores(text)
|
||||||
cores |= found
|
cores |= found
|
||||||
standalone |= alone
|
standalone |= alone
|
||||||
|
libretro |= system_ini_libretro(text)
|
||||||
if not cores:
|
if not cores:
|
||||||
raise RuntimeError(f"no core in Recalbox's system.ini files at {self.tag}")
|
raise RuntimeError(f"no core in Recalbox's system.ini files at {self.tag}")
|
||||||
return sorted(cores), sorted(standalone)
|
return sorted(cores), sorted(standalone), sorted(libretro & standalone)
|
||||||
|
|
||||||
def fetch_requirements(self) -> list[BiosRequirement]:
|
def fetch_requirements(self) -> list[BiosRequirement]:
|
||||||
"""Parse es_bios.xml and return BIOS requirements."""
|
"""Parse es_bios.xml and return BIOS requirements."""
|
||||||
@@ -350,7 +365,7 @@ class Scraper(BaseScraper):
|
|||||||
|
|
||||||
systems[req.system]["files"].append(requirement_entry(req))
|
systems[req.system]["files"].append(requirement_entry(req))
|
||||||
|
|
||||||
cores, standalone = self._fetch_cores()
|
cores, standalone, dual = self._fetch_cores()
|
||||||
return {
|
return {
|
||||||
"platform": "Recalbox",
|
"platform": "Recalbox",
|
||||||
"version": self.tag,
|
"version": self.tag,
|
||||||
@@ -361,6 +376,8 @@ class Scraper(BaseScraper):
|
|||||||
"verification_mode": "md5",
|
"verification_mode": "md5",
|
||||||
"cores": cores,
|
"cores": cores,
|
||||||
"standalone_cores": standalone,
|
"standalone_cores": standalone,
|
||||||
|
# Launched both ways: each layout gets its files.
|
||||||
|
**({"dual_mode_cores": dual} if dual else {}),
|
||||||
"systems": systems,
|
"systems": systems,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,21 @@ ES_SYSTEMS_URL = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def es_systems_dual(text: str) -> set[str]:
|
||||||
|
"""Emulators es_systems.cfg offers both as a libretro core and standalone."""
|
||||||
|
from common import parse_untrusted_xml
|
||||||
|
|
||||||
|
libretro: set[str] = set()
|
||||||
|
standalone: set[str] = set()
|
||||||
|
for emulator in parse_untrusted_xml(text, "es_systems.cfg").iter("emulator"):
|
||||||
|
name = (emulator.get("name") or "").strip()
|
||||||
|
if name == "libretro":
|
||||||
|
libretro |= {(core.text or "").strip() for core in emulator.iter("core")} - {""}
|
||||||
|
elif name:
|
||||||
|
standalone.add(name)
|
||||||
|
return libretro & standalone
|
||||||
|
|
||||||
|
|
||||||
def es_systems_cores(text: str) -> tuple[set[str], set[str]]:
|
def es_systems_cores(text: str) -> tuple[set[str], set[str]]:
|
||||||
"""Every core RetroBat's ES offers, and the emulators it runs standalone.
|
"""Every core RetroBat's ES offers, and the emulators it runs standalone.
|
||||||
|
|
||||||
@@ -207,7 +222,9 @@ class Scraper(BaseScraper):
|
|||||||
req = urllib.request.Request(es_url, headers={"User-Agent": "retrobios-scraper/1.0"})
|
req = urllib.request.Request(es_url, headers={"User-Agent": "retrobios-scraper/1.0"})
|
||||||
try:
|
try:
|
||||||
with urllib.request.urlopen(req, timeout=30) as resp:
|
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||||
cores, standalone = es_systems_cores(resp.read().decode("utf-8"))
|
es_text = resp.read().decode("utf-8")
|
||||||
|
cores, standalone = es_systems_cores(es_text)
|
||||||
|
dual = es_systems_dual(es_text)
|
||||||
except urllib.error.URLError as e:
|
except urllib.error.URLError as e:
|
||||||
raise ConnectionError(f"Failed to fetch {es_url}: {e}") from e
|
raise ConnectionError(f"Failed to fetch {es_url}: {e}") from e
|
||||||
if not cores:
|
if not cores:
|
||||||
@@ -223,6 +240,8 @@ class Scraper(BaseScraper):
|
|||||||
"verification_mode": "md5",
|
"verification_mode": "md5",
|
||||||
"cores": sorted(cores),
|
"cores": sorted(cores),
|
||||||
"standalone_cores": sorted(standalone),
|
"standalone_cores": sorted(standalone),
|
||||||
|
# Launched both ways: each layout gets its files.
|
||||||
|
**({"dual_mode_cores": sorted(dual)} if dual else {}),
|
||||||
"systems": systems,
|
"systems": systems,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -236,6 +236,9 @@ class Scraper(BaseScraper):
|
|||||||
# emulators ROCKNIX packages beside it.
|
# emulators ROCKNIX packages beside it.
|
||||||
"cores": ["retroarch", *standalone],
|
"cores": ["retroarch", *standalone],
|
||||||
"standalone_cores": standalone,
|
"standalone_cores": standalone,
|
||||||
|
# RetroArch carries every libretro core beside them, so an
|
||||||
|
# emulator with a libretro build is launched both ways.
|
||||||
|
"dual_mode_cores": list(standalone),
|
||||||
"systems": systems,
|
"systems": systems,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ from datetime import datetime, timezone
|
|||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from common import yaml_load
|
from safeparse import yaml_load_upstream
|
||||||
|
|
||||||
from . import BaseTargetScraper
|
from . import BaseTargetScraper
|
||||||
|
|
||||||
@@ -244,7 +244,7 @@ def _parse_es_systems(text: str) -> dict[str, list[str]]:
|
|||||||
<core_name>: {requireAnyOf: [BR2_PACKAGE_FOO]}
|
<core_name>: {requireAnyOf: [BR2_PACKAGE_FOO]}
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
data = yaml_load(text)
|
data = yaml_load_upstream(text)
|
||||||
except yaml.YAMLError:
|
except yaml.YAMLError:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|||||||
+58
-57
@@ -32,7 +32,8 @@ from common import (
|
|||||||
resolution_is_hash_exact,
|
resolution_is_hash_exact,
|
||||||
resolve_local_file,
|
resolve_local_file,
|
||||||
resolve_platform_cores,
|
resolve_platform_cores,
|
||||||
runs_standalone,
|
platform_layouts,
|
||||||
|
standalone_set as platform_standalone_set,
|
||||||
variant_base_name,
|
variant_base_name,
|
||||||
)
|
)
|
||||||
from packresolve import preferred_entries
|
from packresolve import preferred_entries
|
||||||
@@ -202,63 +203,63 @@ def profile_claims(
|
|||||||
# carries its scan candidates, never its named entries.
|
# carries its scan candidates, never its named entries.
|
||||||
if profile.get("bios_mode") == "agnostic":
|
if profile.get("bios_mode") == "agnostic":
|
||||||
continue
|
continue
|
||||||
is_standalone = runs_standalone(emu_name, profile, standalone_cores)
|
for profile, is_standalone in platform_layouts(emu_name, profile, standalone_cores):
|
||||||
claims.extend(
|
claims.extend(
|
||||||
_archive_claims(
|
_archive_claims(
|
||||||
emu_name, profile, is_standalone, db, base_dest, placement,
|
emu_name, profile, is_standalone, db, base_dest, placement,
|
||||||
zip_contents, data_dir_registry,
|
zip_contents, data_dir_registry,
|
||||||
)
|
|
||||||
)
|
|
||||||
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):
|
|
||||||
continue
|
|
||||||
# A member of a romset is not a file at the BIOS root: the archive
|
|
||||||
# holding it occupies the destination (_archive_claims). Claiming
|
|
||||||
# the member put FBNeo's msx.zip contents loose at the root.
|
|
||||||
if entry.get("archive"):
|
|
||||||
continue
|
|
||||||
dest = (
|
|
||||||
(entry.get("standalone_path") or entry.get("path"))
|
|
||||||
if is_standalone
|
|
||||||
else entry.get("path")
|
|
||||||
) or entry.get("name") or ""
|
|
||||||
if not dest:
|
|
||||||
continue
|
|
||||||
if placement is None:
|
|
||||||
full = f"{base_dest}/{dest}" if base_dest else dest
|
|
||||||
else:
|
|
||||||
placed = placement.place(
|
|
||||||
{"profile": emu_name, "system": entry.get("system")}, dest
|
|
||||||
)
|
)
|
||||||
if placed is None:
|
)
|
||||||
|
for entry in profile.get("files") or []:
|
||||||
|
if not isinstance(entry, dict):
|
||||||
continue
|
continue
|
||||||
full = placed[1]
|
# The same rule as the gap report and the builder: an entry the
|
||||||
# The owner's own copy is the one the builder ships.
|
# pack never carries (read from the save directory, a
|
||||||
local, status = resolve_local_file(
|
# placeholder, a filename-agnostic scan) arbitrates nothing.
|
||||||
{**entry, "source_profile": emu_name},
|
if outside_gap_scope(entry, is_standalone):
|
||||||
db,
|
continue
|
||||||
zip_contents,
|
# A member of a romset is not a file at the BIOS root: the archive
|
||||||
dest_hint=dest,
|
# holding it occupies the destination (_archive_claims). Claiming
|
||||||
data_dir_registry=data_dir_registry,
|
# the member put FBNeo's msx.zip contents loose at the root.
|
||||||
)
|
if entry.get("archive"):
|
||||||
if status == "name_exact" and _size_the_code_reads(entry, local):
|
continue
|
||||||
status = SIZE_EXACT
|
dest = (
|
||||||
claims.append(
|
(entry.get("standalone_path") or entry.get("path"))
|
||||||
Claim(
|
if is_standalone
|
||||||
origin="profile",
|
else entry.get("path")
|
||||||
destination=full,
|
) or entry.get("name") or ""
|
||||||
name=entry.get("name", ""),
|
if not dest:
|
||||||
emulator=emu_name,
|
continue
|
||||||
entry=entry,
|
if placement is None:
|
||||||
local_path=local,
|
full = f"{base_dest}/{dest}" if base_dest else dest
|
||||||
status=status,
|
else:
|
||||||
|
placed = placement.place(
|
||||||
|
{"profile": emu_name, "system": entry.get("system")}, dest
|
||||||
|
)
|
||||||
|
if placed is None:
|
||||||
|
continue
|
||||||
|
full = placed[1]
|
||||||
|
# The owner's own copy is the one the builder ships.
|
||||||
|
local, status = resolve_local_file(
|
||||||
|
{**entry, "source_profile": emu_name},
|
||||||
|
db,
|
||||||
|
zip_contents,
|
||||||
|
dest_hint=dest,
|
||||||
|
data_dir_registry=data_dir_registry,
|
||||||
|
)
|
||||||
|
if status == "name_exact" and _size_the_code_reads(entry, local):
|
||||||
|
status = SIZE_EXACT
|
||||||
|
claims.append(
|
||||||
|
Claim(
|
||||||
|
origin="profile",
|
||||||
|
destination=full,
|
||||||
|
name=entry.get("name", ""),
|
||||||
|
emulator=emu_name,
|
||||||
|
entry=entry,
|
||||||
|
local_path=local,
|
||||||
|
status=status,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
return claims
|
return claims
|
||||||
|
|
||||||
|
|
||||||
@@ -524,7 +525,7 @@ def slot_choices(
|
|||||||
platform_profiles,
|
platform_profiles,
|
||||||
db,
|
db,
|
||||||
config.get("base_destination", ""),
|
config.get("base_destination", ""),
|
||||||
{str(c) for c in config.get("standalone_cores", [])},
|
platform_standalone_set(config),
|
||||||
zip_contents,
|
zip_contents,
|
||||||
data_dir_registry,
|
data_dir_registry,
|
||||||
)
|
)
|
||||||
@@ -821,7 +822,7 @@ def scan_platform(
|
|||||||
relevant,
|
relevant,
|
||||||
db,
|
db,
|
||||||
config.get("base_destination", ""),
|
config.get("base_destination", ""),
|
||||||
{str(c) for c in config.get("standalone_cores", [])},
|
platform_standalone_set(config),
|
||||||
build_zip_contents_index(db),
|
build_zip_contents_index(db),
|
||||||
load_data_dir_registry(platforms_dir),
|
load_data_dir_registry(platforms_dir),
|
||||||
)
|
)
|
||||||
|
|||||||
+12
-5
@@ -8,7 +8,12 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from common import _norm_system_id, resolve_platform_cores, runs_standalone
|
from common import (
|
||||||
|
_norm_system_id,
|
||||||
|
platform_layouts,
|
||||||
|
resolve_platform_cores,
|
||||||
|
standalone_set as platform_standalone_set,
|
||||||
|
)
|
||||||
from hashing import parse_md5_list
|
from hashing import parse_md5_list
|
||||||
from validation import filter_files_by_mode, read_from_system_dir
|
from validation import filter_files_by_mode, read_from_system_dir
|
||||||
|
|
||||||
@@ -395,7 +400,7 @@ def generate_platform_truth(
|
|||||||
# The layout rule verify and the pack builder apply: truth guessed its
|
# The layout rule verify and the pack builder apply: truth guessed its
|
||||||
# own from the profile type where the platform names no standalone
|
# own from the profile type where the platform names no standalone
|
||||||
# emulator, and kept files the pack never carries.
|
# emulator, and kept files the pack never carries.
|
||||||
standalone_set = {str(c) for c in config.get("standalone_cores") or []}
|
standalone_set = platform_standalone_set(config)
|
||||||
|
|
||||||
resolved = resolve_platform_cores(config, profiles, target_cores)
|
resolved = resolve_platform_cores(config, profiles, target_cores)
|
||||||
|
|
||||||
@@ -449,9 +454,11 @@ def generate_platform_truth(
|
|||||||
continue
|
continue
|
||||||
cores_profiled.add(emu_name)
|
cores_profiled.add(emu_name)
|
||||||
|
|
||||||
filtered = _archives_not_members(_system_dir_files(
|
filtered = _archives_not_members([
|
||||||
profile, runs_standalone(emu_name, profile, standalone_set)
|
fe
|
||||||
))
|
for view, standalone in platform_layouts(emu_name, profile, standalone_set)
|
||||||
|
for fe in _system_dir_files(view, standalone)
|
||||||
|
])
|
||||||
|
|
||||||
on_platform = {
|
on_platform = {
|
||||||
_map_sys_id(sid) for sid in profile.get("systems", [])
|
_map_sys_id(sid) for sid in profile.get("systems", [])
|
||||||
|
|||||||
+154
-152
@@ -65,7 +65,9 @@ from common import (
|
|||||||
resolve_local_file,
|
resolve_local_file,
|
||||||
ProfileSelectionError,
|
ProfileSelectionError,
|
||||||
resolve_platform_cores,
|
resolve_platform_cores,
|
||||||
|
platform_layouts,
|
||||||
runs_standalone,
|
runs_standalone,
|
||||||
|
standalone_set as platform_standalone_set,
|
||||||
sanitize_pack_path,
|
sanitize_pack_path,
|
||||||
select_emulator_profiles,
|
select_emulator_profiles,
|
||||||
)
|
)
|
||||||
@@ -483,7 +485,7 @@ def find_undeclared_files(
|
|||||||
)
|
)
|
||||||
|
|
||||||
relevant = resolve_platform_cores(config, profiles, target_cores=target_cores)
|
relevant = resolve_platform_cores(config, profiles, target_cores=target_cores)
|
||||||
standalone_set = set(str(c) for c in config.get("standalone_cores", []))
|
standalone_set = platform_standalone_set(config)
|
||||||
undeclared = []
|
undeclared = []
|
||||||
seen_files: set[tuple] = set()
|
seen_files: set[tuple] = set()
|
||||||
# Key -> the entry reported for it. A later profile reading the same
|
# Key -> the entry reported for it. A later profile reading the same
|
||||||
@@ -503,166 +505,166 @@ def find_undeclared_files(
|
|||||||
if profile.get("bios_mode") == "agnostic":
|
if profile.get("bios_mode") == "agnostic":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
is_standalone = runs_standalone(emu_name, profile, standalone_set)
|
for profile, is_standalone in platform_layouts(emu_name, profile, standalone_set):
|
||||||
|
|
||||||
for f in expand_directory_entries(
|
for f in expand_directory_entries(
|
||||||
profile.get("files", []), db, is_standalone
|
profile.get("files", []), db, is_standalone
|
||||||
):
|
):
|
||||||
fname = f.get("name", "")
|
fname = f.get("name", "")
|
||||||
# One destination rule for the dedup key and for the entry: a
|
# One destination rule for the dedup key and for the entry: a
|
||||||
# standalone build without standalone_path falls back on path.
|
# standalone build without standalone_path falls back on path.
|
||||||
# The key used to skip that fallback, so clk's Acorn/basic.rom and
|
# The key used to skip that fallback, so clk's Acorn/basic.rom and
|
||||||
# Electron/basic.rom both keyed as "basic.rom" under Batocera's
|
# Electron/basic.rom both keyed as "basic.rom" under Batocera's
|
||||||
# standalone clk and the second was dropped.
|
# standalone clk and the second was dropped.
|
||||||
if is_standalone:
|
if is_standalone:
|
||||||
dest = f.get("standalone_path") or f.get("path") or fname
|
dest = f.get("standalone_path") or f.get("path") or fname
|
||||||
else:
|
else:
|
||||||
dest = f.get("path") or fname
|
dest = f.get("path") or fname
|
||||||
raw_regions = f.get("region") or []
|
raw_regions = f.get("region") or []
|
||||||
region_key = tuple(
|
region_key = tuple(
|
||||||
str(value) for value in (
|
str(value) for value in (
|
||||||
raw_regions if isinstance(raw_regions, list) else [raw_regions]
|
raw_regions if isinstance(raw_regions, list) else [raw_regions]
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
# Same-name requirements at different paths, for different systems,
|
||||||
# Same-name requirements at different paths, for different systems,
|
# or in distinct variant groups are not interchangeable.
|
||||||
# or in distinct variant groups are not interchangeable.
|
seen_key = (
|
||||||
seen_key = (
|
fname,
|
||||||
fname,
|
f.get("archive"),
|
||||||
f.get("archive"),
|
dest,
|
||||||
dest,
|
|
||||||
f.get("system"),
|
|
||||||
f.get("variant_group"),
|
|
||||||
region_key,
|
|
||||||
)
|
|
||||||
if not fname:
|
|
||||||
continue
|
|
||||||
first = reported.get(seen_key)
|
|
||||||
duplicate = seen_key in seen_files
|
|
||||||
if duplicate and (first is None or first["profile"] == emu_name):
|
|
||||||
continue
|
|
||||||
verdict = _candidate_verdict(
|
|
||||||
f, fname, is_standalone, include_all, declared_names, dest
|
|
||||||
)
|
|
||||||
if verdict == "settled":
|
|
||||||
seen_files.add(seen_key)
|
|
||||||
continue
|
|
||||||
if verdict == "skip":
|
|
||||||
continue
|
|
||||||
|
|
||||||
archive = f.get("archive")
|
|
||||||
seen_files.add(seen_key)
|
|
||||||
|
|
||||||
# Archived files are grouped by archive
|
|
||||||
if archive:
|
|
||||||
archive_key = (
|
|
||||||
archive,
|
|
||||||
f.get("system"),
|
f.get("system"),
|
||||||
f.get("variant_group"),
|
f.get("variant_group"),
|
||||||
region_key,
|
region_key,
|
||||||
is_standalone,
|
|
||||||
)
|
)
|
||||||
if archive_key not in archive_entries:
|
if not fname:
|
||||||
in_repo = _name_in_index(
|
continue
|
||||||
archive, by_name, by_path_suffix, data_names,
|
first = reported.get(seen_key)
|
||||||
by_name_lower,
|
duplicate = seen_key in seen_files
|
||||||
)
|
if duplicate and (first is None or first["profile"] == emu_name):
|
||||||
archive_entries[archive_key] = {
|
continue
|
||||||
"profile": emu_name,
|
verdict = _candidate_verdict(
|
||||||
"emulator": profile.get("emulator", emu_name),
|
f, fname, is_standalone, include_all, declared_names, dest
|
||||||
"systems": list(profile.get("systems", [])),
|
)
|
||||||
"system": f.get("system"),
|
if verdict == "settled":
|
||||||
"region": f.get("region"),
|
seen_files.add(seen_key)
|
||||||
"variant_group": f.get("variant_group"),
|
continue
|
||||||
"name": archive,
|
if verdict == "skip":
|
||||||
"archive": archive,
|
continue
|
||||||
"path": archive,
|
|
||||||
"required": False,
|
|
||||||
"hle_fallback": False,
|
|
||||||
"category": f.get("category", "bios"),
|
|
||||||
"in_repo": in_repo,
|
|
||||||
"note": "",
|
|
||||||
"checks": [],
|
|
||||||
"source_ref": None,
|
|
||||||
"expected": {},
|
|
||||||
"archive_file_count": 0,
|
|
||||||
"archive_required_count": 0,
|
|
||||||
"members": archive_members(archive, profile.get("files", [])),
|
|
||||||
}
|
|
||||||
entry = archive_entries[archive_key]
|
|
||||||
entry["archive_file_count"] += 1
|
|
||||||
if f.get("required", False):
|
|
||||||
entry["archive_required_count"] += 1
|
|
||||||
entry["required"] = True
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Resolution: storage flag, then name, then path basename
|
archive = f.get("archive")
|
||||||
storage = f.get("storage", "")
|
seen_files.add(seen_key)
|
||||||
if storage in ("release", "large_file"):
|
|
||||||
in_repo = True
|
# Archived files are grouped by archive
|
||||||
elif shipped_on_mismatch and (f.get("md5") or f.get("sha1")):
|
if archive:
|
||||||
# The entry states what its content should be and the builder
|
archive_key = (
|
||||||
# drops a copy that contradicts it, so content decides here
|
archive,
|
||||||
# too. A name match would not do: generic names collide across
|
f.get("system"),
|
||||||
# systems, and answering yes on one describes a pack that will
|
f.get("variant_group"),
|
||||||
# not contain the file.
|
region_key,
|
||||||
_lp, _st = resolve_local_file(f, db, dest_hint=dest)
|
is_standalone,
|
||||||
in_repo = _lp is not None and _st != "hash_mismatch"
|
|
||||||
else:
|
|
||||||
in_repo = _name_in_index(
|
|
||||||
fname, by_name, by_path_suffix, data_names, by_name_lower,
|
|
||||||
)
|
|
||||||
if not in_repo and dest != fname:
|
|
||||||
path_base = dest.rsplit("/", 1)[-1]
|
|
||||||
in_repo = _name_in_index(
|
|
||||||
path_base, by_name, by_path_suffix, data_names,
|
|
||||||
by_name_lower,
|
|
||||||
)
|
)
|
||||||
if not in_repo:
|
if archive_key not in archive_entries:
|
||||||
# Hash fallback: the repo may hold the content under a
|
in_repo = _name_in_index(
|
||||||
# different filename (exos21.rom vs exos21.bin).
|
archive, by_name, by_path_suffix, data_names,
|
||||||
#
|
by_name_lower,
|
||||||
# A copy contradicting the declared hash counts as held
|
)
|
||||||
# only where the builder would ship it. Under existence
|
archive_entries[archive_key] = {
|
||||||
# the frontend never opens the file, so the pack carries
|
"profile": emu_name,
|
||||||
# it and reports the divergence; under a digest mode the
|
"emulator": profile.get("emulator", emu_name),
|
||||||
# frontend would reject it, the builder leaves it out, and
|
"systems": list(profile.get("systems", [])),
|
||||||
# calling it available here would describe a pack that
|
"system": f.get("system"),
|
||||||
# does not contain it.
|
"region": f.get("region"),
|
||||||
|
"variant_group": f.get("variant_group"),
|
||||||
|
"name": archive,
|
||||||
|
"archive": archive,
|
||||||
|
"path": archive,
|
||||||
|
"required": False,
|
||||||
|
"hle_fallback": False,
|
||||||
|
"category": f.get("category", "bios"),
|
||||||
|
"in_repo": in_repo,
|
||||||
|
"note": "",
|
||||||
|
"checks": [],
|
||||||
|
"source_ref": None,
|
||||||
|
"expected": {},
|
||||||
|
"archive_file_count": 0,
|
||||||
|
"archive_required_count": 0,
|
||||||
|
"members": archive_members(archive, profile.get("files", [])),
|
||||||
|
}
|
||||||
|
entry = archive_entries[archive_key]
|
||||||
|
entry["archive_file_count"] += 1
|
||||||
|
if f.get("required", False):
|
||||||
|
entry["archive_required_count"] += 1
|
||||||
|
entry["required"] = True
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Resolution: storage flag, then name, then path basename
|
||||||
|
storage = f.get("storage", "")
|
||||||
|
if storage in ("release", "large_file"):
|
||||||
|
in_repo = True
|
||||||
|
elif shipped_on_mismatch and (f.get("md5") or f.get("sha1")):
|
||||||
|
# The entry states what its content should be and the builder
|
||||||
|
# drops a copy that contradicts it, so content decides here
|
||||||
|
# too. A name match would not do: generic names collide across
|
||||||
|
# systems, and answering yes on one describes a pack that will
|
||||||
|
# not contain the file.
|
||||||
_lp, _st = resolve_local_file(f, db, dest_hint=dest)
|
_lp, _st = resolve_local_file(f, db, dest_hint=dest)
|
||||||
in_repo = _st != "not_found" and _lp is not None
|
in_repo = _lp is not None and _st != "hash_mismatch"
|
||||||
if in_repo and _st == "hash_mismatch" and shipped_on_mismatch:
|
else:
|
||||||
in_repo = False
|
in_repo = _name_in_index(
|
||||||
|
fname, by_name, by_path_suffix, data_names, by_name_lower,
|
||||||
|
)
|
||||||
|
if not in_repo and dest != fname:
|
||||||
|
path_base = dest.rsplit("/", 1)[-1]
|
||||||
|
in_repo = _name_in_index(
|
||||||
|
path_base, by_name, by_path_suffix, data_names,
|
||||||
|
by_name_lower,
|
||||||
|
)
|
||||||
|
if not in_repo:
|
||||||
|
# Hash fallback: the repo may hold the content under a
|
||||||
|
# different filename (exos21.rom vs exos21.bin).
|
||||||
|
#
|
||||||
|
# A copy contradicting the declared hash counts as held
|
||||||
|
# only where the builder would ship it. Under existence
|
||||||
|
# the frontend never opens the file, so the pack carries
|
||||||
|
# it and reports the divergence; under a digest mode the
|
||||||
|
# frontend would reject it, the builder leaves it out, and
|
||||||
|
# calling it available here would describe a pack that
|
||||||
|
# does not contain it.
|
||||||
|
_lp, _st = resolve_local_file(f, db, dest_hint=dest)
|
||||||
|
in_repo = _st != "not_found" and _lp is not None
|
||||||
|
if in_repo and _st == "hash_mismatch" and shipped_on_mismatch:
|
||||||
|
in_repo = False
|
||||||
|
|
||||||
checks = _parse_validation(f.get("validation"))
|
checks = _parse_validation(f.get("validation"))
|
||||||
entry = {
|
entry = {
|
||||||
"profile": emu_name,
|
"profile": emu_name,
|
||||||
"emulator": profile.get("emulator", emu_name),
|
"emulator": profile.get("emulator", emu_name),
|
||||||
"systems": list(profile.get("systems", [])),
|
"systems": list(profile.get("systems", [])),
|
||||||
"system": f.get("system"),
|
"system": f.get("system"),
|
||||||
"region": f.get("region"),
|
"region": f.get("region"),
|
||||||
"variant_group": f.get("variant_group"),
|
"variant_group": f.get("variant_group"),
|
||||||
"name": fname,
|
"name": fname,
|
||||||
"path": dest,
|
"path": dest,
|
||||||
"required": f.get("required", False),
|
"required": f.get("required", False),
|
||||||
"hle_fallback": f.get("hle_fallback", False),
|
"hle_fallback": f.get("hle_fallback", False),
|
||||||
"bundled": f.get("bundled", False),
|
"bundled": f.get("bundled", False),
|
||||||
"category": f.get("category", "bios"),
|
"category": f.get("category", "bios"),
|
||||||
"in_repo": in_repo,
|
"in_repo": in_repo,
|
||||||
"note": f.get("note", ""),
|
"note": f.get("note", ""),
|
||||||
"checks": sorted(checks) if checks else [],
|
"checks": sorted(checks) if checks else [],
|
||||||
"source_ref": f.get("source_ref"),
|
"source_ref": f.get("source_ref"),
|
||||||
"expected": _build_expected(f, checks),
|
"expected": _build_expected(f, checks),
|
||||||
**_identity_of(f),
|
**_identity_of(f),
|
||||||
"sha1": f.get("sha1"),
|
"sha1": f.get("sha1"),
|
||||||
"md5": f.get("md5"),
|
"md5": f.get("md5"),
|
||||||
}
|
}
|
||||||
if f.get("contents"):
|
if f.get("contents"):
|
||||||
entry["contents"] = f["contents"]
|
entry["contents"] = f["contents"]
|
||||||
if duplicate:
|
if duplicate:
|
||||||
first.setdefault("other_claims", []).append(entry)
|
first.setdefault("other_claims", []).append(entry)
|
||||||
continue
|
continue
|
||||||
reported[seen_key] = entry
|
reported[seen_key] = entry
|
||||||
undeclared.append(entry)
|
undeclared.append(entry)
|
||||||
|
|
||||||
# Append grouped archive entries
|
# Append grouped archive entries
|
||||||
for entry in sorted(archive_entries.values(), key=lambda e: e["name"]):
|
for entry in sorted(archive_entries.values(), key=lambda e: e["name"]):
|
||||||
@@ -731,7 +733,7 @@ def find_exclusion_notes(
|
|||||||
|
|
||||||
# Count standalone-only files -but only report as excluded if the
|
# Count standalone-only files -but only report as excluded if the
|
||||||
# platform does NOT use this emulator in standalone mode
|
# platform does NOT use this emulator in standalone mode
|
||||||
standalone_set = set(str(c) for c in config.get("standalone_cores", []))
|
standalone_set = platform_standalone_set(config)
|
||||||
is_standalone = runs_standalone(emu_name, profile, standalone_set)
|
is_standalone = runs_standalone(emu_name, profile, standalone_set)
|
||||||
if not is_standalone:
|
if not is_standalone:
|
||||||
standalone_files = [
|
standalone_files = [
|
||||||
|
|||||||
@@ -66,5 +66,61 @@ class TheTargetsNameTheStandaloneEmulator(unittest.TestCase):
|
|||||||
self.assertEqual(mapping["BR2_PACKAGE_LIBRETRO_DOSBOX_STAGING"], ["dosbox_staging"])
|
self.assertEqual(mapping["BR2_PACKAGE_LIBRETRO_DOSBOX_STAGING"], ["dosbox_staging"])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class AnEmulatorLaunchedBothWaysGetsBothLayouts(unittest.TestCase):
|
||||||
|
"""es_systems.yml offers libretro:dolphin and standalone dolphin for
|
||||||
|
GameCube. Filed as standalone only, the pack gave libretro Dolphin no
|
||||||
|
dolphin-emu/Sys/GC/ files: no IPL, no DSP ROM."""
|
||||||
|
|
||||||
|
def test_the_scraper_names_dual_mode_emulators(self):
|
||||||
|
from scripts.scraper.batocera_scraper import es_systems_emulators, es_systems_libretro
|
||||||
|
|
||||||
|
text = ES_SYSTEMS + (
|
||||||
|
"gamecube:\n emulators:\n libretro:\n dolphin: { requireAnyOf: [X] }\n"
|
||||||
|
" dolphin:\n dolphin: { requireAnyOf: [Y] }\n"
|
||||||
|
)
|
||||||
|
_cores, standalone = es_systems_emulators(text)
|
||||||
|
self.assertEqual(standalone & es_systems_libretro(text), {"dolphin"})
|
||||||
|
|
||||||
|
def test_the_layouts_are_both(self):
|
||||||
|
from common import DUAL_MODE_PREFIX, platform_layouts
|
||||||
|
|
||||||
|
profile = {"type": "standalone + libretro", "cores": ["dolphin"], "files": []}
|
||||||
|
both = platform_layouts("dolphin", profile, {"dolphin", DUAL_MODE_PREFIX + "dolphin"})
|
||||||
|
self.assertEqual([standalone for _view, standalone in both], [False, True])
|
||||||
|
only = platform_layouts("dolphin", profile, {"dolphin"})
|
||||||
|
self.assertEqual([standalone for _view, standalone in only], [True])
|
||||||
|
|
||||||
|
def test_the_batocera_manifest_carries_both(self):
|
||||||
|
import json
|
||||||
|
|
||||||
|
manifest = json.loads((REPO_ROOT / "install" / "batocera.json").read_text())
|
||||||
|
dests = {entry["dest"] for entry in manifest["files"]}
|
||||||
|
self.assertIn("dolphin-emu/Sys/GC/USA/IPL.bin", dests)
|
||||||
|
self.assertIn("GC/USA/IPL.bin", dests)
|
||||||
|
|
||||||
|
def test_retrobat_and_recalbox_name_theirs(self):
|
||||||
|
from scripts.scraper.recalbox_scraper import system_ini_cores, system_ini_libretro
|
||||||
|
from scripts.scraper.retrobat_scraper import es_systems_dual
|
||||||
|
|
||||||
|
cfg = ("<systemList><system><emulators>"
|
||||||
|
"<emulator name=\"libretro\"><cores><core>dolphin</core></cores></emulator>"
|
||||||
|
"<emulator name=\"dolphin\"/></emulators></system></systemList>")
|
||||||
|
self.assertEqual(es_systems_dual(cfg), {"dolphin"})
|
||||||
|
ini = "[core.0]\nemulator = libretro\ncore = dolphin\n[core.1]\nemulator = dolphin\ncore = dolphin\n"
|
||||||
|
_cores, standalone = system_ini_cores(ini)
|
||||||
|
self.assertEqual(system_ini_libretro(ini) & standalone, {"dolphin"})
|
||||||
|
|
||||||
|
def test_upstream_yaml_is_read_as_its_code_reads_it(self):
|
||||||
|
"""configgen-defaults.yml names rtcw's core twice; the strict loader
|
||||||
|
stopped the rescrape, Batocera's PyYAML keeps the last value."""
|
||||||
|
from safeparse import yaml_load_upstream
|
||||||
|
|
||||||
|
self.assertEqual(
|
||||||
|
yaml_load_upstream("rtcw:\n core: iortcw\n core: taradino\n"),
|
||||||
|
{"rtcw": {"core": "taradino"}},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
@@ -203,7 +203,7 @@ class RecalboxReadsItsSystemsInBatches(unittest.TestCase):
|
|||||||
scraper = recalbox_scraper.Scraper.__new__(recalbox_scraper.Scraper)
|
scraper = recalbox_scraper.Scraper.__new__(recalbox_scraper.Scraper)
|
||||||
scraper.tag = "10.1.1"
|
scraper.tag = "10.1.1"
|
||||||
with mock.patch.object(recalbox_scraper, "fetch_bytes", fetch):
|
with mock.patch.object(recalbox_scraper, "fetch_bytes", fetch):
|
||||||
cores, _standalone = scraper._fetch_cores()
|
cores, _standalone, _dual = scraper._fetch_cores()
|
||||||
self.assertEqual([len(q) for q in queries], [50, 50, 20])
|
self.assertEqual([len(q) for q in queries], [50, 50, 20])
|
||||||
self.assertEqual(len(cores), 120)
|
self.assertEqual(len(cores), 120)
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user