mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-04-19 23:32:36 -05:00
Compare commits
14 Commits
5cbd461a97
...
f1855641c5
| Author | SHA1 | Date | |
|---|---|---|---|
| f1855641c5 | |||
| 2666ebd9b7 | |||
| 22a1e7caf4 | |||
| 67186448a2 | |||
| 70891314d3 | |||
| 97b9900f62 | |||
| a1aa97a70e | |||
| 97b1c2c08a | |||
| 0624e9d87e | |||
| 3ded72f72b | |||
| 94a28f5459 | |||
| 837ac80cca | |||
| 43cb7a9884 | |||
| 020ff148c2 |
@@ -111,4 +111,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|||||||
|
|
||||||
This repository provides BIOS files for personal backup and archival purposes.
|
This repository provides BIOS files for personal backup and archival purposes.
|
||||||
|
|
||||||
*Auto-generated on 2026-03-27T22:52:26Z*
|
*Auto-generated on 2026-03-28T08:23:13Z*
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<database version="1.0" conformance="loose">
|
<database version="1.0" conformance="loose">
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="NES-PAL" dump="ok" crc="001388B3" sha1="4BCD36C05FCAF45C74001257C65AFB7EC5FA53D7">
|
<cartridge system="NES-PAL" dump="ok" crc="001388B3" sha1="4BCD36C05FCAF45C74001257C65AFB7EC5FA53D7">
|
||||||
@@ -69,6 +69,18 @@
|
|||||||
</board>
|
</board>
|
||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
|
<game>
|
||||||
|
<peripherals>
|
||||||
|
<device type="fourplayer" />
|
||||||
|
</peripherals>
|
||||||
|
<cartridge system="NES-NTSC" dump="ok" crc="00D2CB22" sha1="CCD60DBC65EC004956E972E116BDD114E8818E3E">
|
||||||
|
<board type="NES-NROM-256" mapper="0">
|
||||||
|
<prg size="32k" />
|
||||||
|
<chr size="8k" />
|
||||||
|
<pad h="1" v="0" />
|
||||||
|
</board>
|
||||||
|
</cartridge>
|
||||||
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="NES-NTSC" dump="ok" crc="00E95D86" sha1="8957393A397DB102BCE5A64B4D85384D1F2E5D20">
|
<cartridge system="NES-NTSC" dump="ok" crc="00E95D86" sha1="8957393A397DB102BCE5A64B4D85384D1F2E5D20">
|
||||||
<board type="NES-UNROM" mapper="2">
|
<board type="NES-UNROM" mapper="2">
|
||||||
@@ -767,6 +779,15 @@
|
|||||||
</board>
|
</board>
|
||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
|
<game>
|
||||||
|
<cartridge system="Famicom" dump="unknown" crc="084F61CD" sha1="44BC6C4E8B3F6C635281B4C05382E8F316D8269E">
|
||||||
|
<board type="NAMCOT-3301" mapper="0">
|
||||||
|
<prg size="8k" />
|
||||||
|
<chr size="8k" />
|
||||||
|
<pad h="0" v="1" />
|
||||||
|
</board>
|
||||||
|
</cartridge>
|
||||||
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="NES-NTSC" dump="ok" crc="085DE7C9" sha1="93339F07696CE1B98F1272613067482A02F47B95">
|
<cartridge system="NES-NTSC" dump="ok" crc="085DE7C9" sha1="93339F07696CE1B98F1272613067482A02F47B95">
|
||||||
<board type="NES-SLROM" mapper="1">
|
<board type="NES-SLROM" mapper="1">
|
||||||
@@ -1043,7 +1064,7 @@
|
|||||||
<device type="zapper" />
|
<device type="zapper" />
|
||||||
</peripherals>
|
</peripherals>
|
||||||
<cartridge system="Famicom" dump="unknown" crc="0AFB395E" sha1="CFFAC7D2ECB18A28C36E0E90A6682DFE5BA6E3D1">
|
<cartridge system="Famicom" dump="unknown" crc="0AFB395E" sha1="CFFAC7D2ECB18A28C36E0E90A6682DFE5BA6E3D1">
|
||||||
<board mapper="5">
|
<board type="HVC-ELROM" mapper="5">
|
||||||
<prg size="128k" />
|
<prg size="128k" />
|
||||||
<chr size="128k" />
|
<chr size="128k" />
|
||||||
</board>
|
</board>
|
||||||
@@ -2018,6 +2039,16 @@
|
|||||||
</board>
|
</board>
|
||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
|
<game>
|
||||||
|
<peripherals>
|
||||||
|
<device type="fourplayer" />
|
||||||
|
</peripherals>
|
||||||
|
<cartridge system="NES-NTSC" dump="ok" crc="1394DED0" sha1="B1C6A700A9F3B73666018E46515D376F06B8E9C2">
|
||||||
|
<board type="NES-UNROM" mapper="2">
|
||||||
|
<prg size="64k" />
|
||||||
|
</board>
|
||||||
|
</cartridge>
|
||||||
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="NES-NTSC" dump="ok" crc="1394F57E" sha1="FD9079CB5E8479EB06D93C2AE5175BFCE871746A">
|
<cartridge system="NES-NTSC" dump="ok" crc="1394F57E" sha1="FD9079CB5E8479EB06D93C2AE5175BFCE871746A">
|
||||||
<board type="NES-SEROM" mapper="1">
|
<board type="NES-SEROM" mapper="1">
|
||||||
@@ -2835,9 +2866,20 @@
|
|||||||
</board>
|
</board>
|
||||||
</arcade>
|
</arcade>
|
||||||
</game>
|
</game>
|
||||||
|
<game>
|
||||||
|
<peripherals>
|
||||||
|
<device type="zapper" />
|
||||||
|
</peripherals>
|
||||||
|
<cartridge system="NES-NTSC" dump="unknown" crc="1CA9C322" sha1="17869C4F55461D50E134CC3A4D15B89E7CAF8DE3">
|
||||||
|
<board mapper="258">
|
||||||
|
<prg size="128k" />
|
||||||
|
<chr size="128k" />
|
||||||
|
</board>
|
||||||
|
</cartridge>
|
||||||
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="Famicom" dump="unknown" crc="1CED086F" sha1="46C0B521B3C595409C05972388909CCB0D5F6369">
|
<cartridge system="Famicom" dump="unknown" crc="1CED086F" sha1="46C0B521B3C595409C05972388909CCB0D5F6369">
|
||||||
<board mapper="5">
|
<board type="HVC-ETROM" mapper="5">
|
||||||
<prg size="256k" />
|
<prg size="256k" />
|
||||||
<chr size="128k" />
|
<chr size="128k" />
|
||||||
<wram size="8k" />
|
<wram size="8k" />
|
||||||
@@ -3086,6 +3128,17 @@
|
|||||||
</board>
|
</board>
|
||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
|
<game>
|
||||||
|
<peripherals>
|
||||||
|
<device type="zapper" />
|
||||||
|
</peripherals>
|
||||||
|
<cartridge system="NES-NTSC" dump="ok" crc="1F14123C" sha1="9B1B8354B7449FDDA4176C93BCE7660D47F66019">
|
||||||
|
<board mapper="4">
|
||||||
|
<prg size="32k" />
|
||||||
|
<chr size="16k" />
|
||||||
|
</board>
|
||||||
|
</cartridge>
|
||||||
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="Famicom" dump="ok" crc="1F2D9DB7" sha1="544203A8304A7922A46579512665C743527CA1E6">
|
<cartridge system="Famicom" dump="ok" crc="1F2D9DB7" sha1="544203A8304A7922A46579512665C743527CA1E6">
|
||||||
<board type="HVC-NROM-256" mapper="0">
|
<board type="HVC-NROM-256" mapper="0">
|
||||||
@@ -4803,6 +4856,17 @@
|
|||||||
</board>
|
</board>
|
||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
|
<game>
|
||||||
|
<peripherals>
|
||||||
|
<device type="fourplayer" />
|
||||||
|
</peripherals>
|
||||||
|
<cartridge system="NES-NTSC" dump="ok" crc="2DA5ECE0" sha1="F3554E45D3261157653643C23A378C0295A5F893">
|
||||||
|
<board type="NES-NROM-256" mapper="0">
|
||||||
|
<prg size="32k" />
|
||||||
|
<chr size="8k" />
|
||||||
|
</board>
|
||||||
|
</cartridge>
|
||||||
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="Famicom" dump="unknown" crc="2DB7C31E" sha1="9BF95EEB404F103422E06214566C7D918ED4DC79">
|
<cartridge system="Famicom" dump="unknown" crc="2DB7C31E" sha1="9BF95EEB404F103422E06214566C7D918ED4DC79">
|
||||||
<board mapper="1">
|
<board mapper="1">
|
||||||
@@ -4939,6 +5003,18 @@
|
|||||||
</board>
|
</board>
|
||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
|
<game>
|
||||||
|
<cartridge system="Famicom" dump="ok" crc="2EA914FA" sha1="54F6E9C7F59D7A1B961F694716F5D4967BB55AA8">
|
||||||
|
<board type="KONAMI-VRC-4" mapper="25">
|
||||||
|
<prg size="128k" />
|
||||||
|
<chr size="128k" />
|
||||||
|
<chip type="Konami VRC IV">
|
||||||
|
<pin number="3" function="PRG A2" />
|
||||||
|
<pin number="4" function="PRG A3" />
|
||||||
|
</chip>
|
||||||
|
</board>
|
||||||
|
</cartridge>
|
||||||
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="Famicom" dump="unknown" crc="2EBF2E0D" sha1="0E37A2766280D73F2921567348A8D360707A5924">
|
<cartridge system="Famicom" dump="unknown" crc="2EBF2E0D" sha1="0E37A2766280D73F2921567348A8D360707A5924">
|
||||||
<board mapper="1">
|
<board mapper="1">
|
||||||
@@ -5064,6 +5140,20 @@
|
|||||||
</board>
|
</board>
|
||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
|
<game>
|
||||||
|
<peripherals>
|
||||||
|
<device type="turbofile" />
|
||||||
|
</peripherals>
|
||||||
|
<cartridge system="NES-NTSC" dump="ok" crc="3046C4D5" sha1="1AF51255F2837484974DA44650B186333472C7B2">
|
||||||
|
<board type="NES-SXROM" mapper="1">
|
||||||
|
<prg size="512k" />
|
||||||
|
<vram size="8k" />
|
||||||
|
<wram size="32k" battery="1" />
|
||||||
|
<pad h="1" v="0" />
|
||||||
|
<chip type="MMC1" />
|
||||||
|
</board>
|
||||||
|
</cartridge>
|
||||||
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="NES-PAL" dump="ok" crc="304FA926" sha1="EFFE8CCAA78F94F061B142042557B478B4B213EE">
|
<cartridge system="NES-PAL" dump="ok" crc="304FA926" sha1="EFFE8CCAA78F94F061B142042557B478B4B213EE">
|
||||||
<board type="NES-NROM-128" mapper="0">
|
<board type="NES-NROM-128" mapper="0">
|
||||||
@@ -6706,6 +6796,19 @@
|
|||||||
</board>
|
</board>
|
||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
|
<game>
|
||||||
|
<peripherals>
|
||||||
|
<device type="fourplayer" />
|
||||||
|
</peripherals>
|
||||||
|
<cartridge system="NES-NTSC" dump="ok" crc="40E1F09E" sha1="2B44E45C621D52C882C50BEF7A2F9B4C93DDF908">
|
||||||
|
<board mapper="2">
|
||||||
|
<prg size="64k" />
|
||||||
|
<vram size="8k" />
|
||||||
|
<wram size="8k" />
|
||||||
|
<pad h="1" v="0" />
|
||||||
|
</board>
|
||||||
|
</cartridge>
|
||||||
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="NES-NTSC" dump="ok" crc="40ED2A9D" sha1="D4E9126D02C9923C3871FD352248F41298498D4E">
|
<cartridge system="NES-NTSC" dump="ok" crc="40ED2A9D" sha1="D4E9126D02C9923C3871FD352248F41298498D4E">
|
||||||
<board type="NES-SEROM" mapper="1">
|
<board type="NES-SEROM" mapper="1">
|
||||||
@@ -7062,6 +7165,9 @@
|
|||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
<game>
|
<game>
|
||||||
|
<peripherals>
|
||||||
|
<device type="pachinko" />
|
||||||
|
</peripherals>
|
||||||
<cartridge system="Famicom" dump="unknown" crc="44F92026" sha1="9266BE2FD5D0C712FE7BF873D32AE50506A9B277">
|
<cartridge system="Famicom" dump="unknown" crc="44F92026" sha1="9266BE2FD5D0C712FE7BF873D32AE50506A9B277">
|
||||||
<board mapper="1">
|
<board mapper="1">
|
||||||
<prg size="128k" />
|
<prg size="128k" />
|
||||||
@@ -7963,6 +8069,18 @@
|
|||||||
</board>
|
</board>
|
||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
|
<game>
|
||||||
|
<peripherals>
|
||||||
|
<device type="fourplayer" />
|
||||||
|
</peripherals>
|
||||||
|
<cartridge system="NES-NTSC" dump="ok" crc="4E6B9078" sha1="93FF8CEC778771C7200F785798E0D1599EE8FEB5">
|
||||||
|
<board type="NES-NROM-256" mapper="0">
|
||||||
|
<prg size="32k" />
|
||||||
|
<chr size="8k" />
|
||||||
|
<pad h="0" v="1" />
|
||||||
|
</board>
|
||||||
|
</cartridge>
|
||||||
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="Famicom" dump="unknown" crc="4E7729FF" sha1="5FA23F88432006DCF6874EA36E9E7DA8934427BE">
|
<cartridge system="Famicom" dump="unknown" crc="4E7729FF" sha1="5FA23F88432006DCF6874EA36E9E7DA8934427BE">
|
||||||
<board mapper="182">
|
<board mapper="182">
|
||||||
@@ -8144,6 +8262,18 @@
|
|||||||
</board>
|
</board>
|
||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
|
<game>
|
||||||
|
<peripherals>
|
||||||
|
<device type="fourplayer" />
|
||||||
|
</peripherals>
|
||||||
|
<cartridge system="Famicom" dump="ok" crc="4FF17864" sha1="5119F1D6B67C5E44D63BA1E7080A6FE17623415C">
|
||||||
|
<board type="NES-SLROM" mapper="1">
|
||||||
|
<prg size="128k" />
|
||||||
|
<chr size="128k" />
|
||||||
|
<chip type="MMC1B2" />
|
||||||
|
</board>
|
||||||
|
</cartridge>
|
||||||
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="Famicom" dump="unknown" crc="4FFD17F0" sha1="27CB8AEAF0EA97A6C69D3D90BC056C5EB61695F6">
|
<cartridge system="Famicom" dump="unknown" crc="4FFD17F0" sha1="27CB8AEAF0EA97A6C69D3D90BC056C5EB61695F6">
|
||||||
<board mapper="194">
|
<board mapper="194">
|
||||||
@@ -8648,6 +8778,15 @@
|
|||||||
</board>
|
</board>
|
||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
|
<game>
|
||||||
|
<cartridge system="Famicom" dump="ok" crc="54C6FE75" sha1="7C30C244C36C9D1556C79458F06FC46C786028C6">
|
||||||
|
<board type="JALECO-JF-22-SRAM" mapper="75">
|
||||||
|
<prg size="256k" />
|
||||||
|
<chr size="128k" />
|
||||||
|
<wram size="8k" battery="1" />
|
||||||
|
</board>
|
||||||
|
</cartridge>
|
||||||
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="NES-PAL-B" dump="ok" crc="54E43C57" sha1="1F6072AE901F3D3530ADCD3C136178E3C7354990">
|
<cartridge system="NES-PAL-B" dump="ok" crc="54E43C57" sha1="1F6072AE901F3D3530ADCD3C136178E3C7354990">
|
||||||
<board type="NES-TLROM" mapper="4">
|
<board type="NES-TLROM" mapper="4">
|
||||||
@@ -8958,6 +9097,19 @@
|
|||||||
</board>
|
</board>
|
||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
|
<game>
|
||||||
|
<peripherals>
|
||||||
|
<device type="fourplayer" />
|
||||||
|
</peripherals>
|
||||||
|
<cartridge system="NES-NTSC" dump="ok" crc="58094016" sha1="8354E33F44156C42C7013EBBEACF378BFA6FD6B2">
|
||||||
|
<board mapper="4">
|
||||||
|
<prg size="64k" />
|
||||||
|
<chr size="128k" />
|
||||||
|
<wram size="8k" battery="1" />
|
||||||
|
<pad h="0" v="1" />
|
||||||
|
</board>
|
||||||
|
</cartridge>
|
||||||
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="NES-NTSC" dump="unknown" crc="58152B42" sha1="1E49BDA9CEF18F6F5C2DA34910487713D364AA68">
|
<cartridge system="NES-NTSC" dump="unknown" crc="58152B42" sha1="1E49BDA9CEF18F6F5C2DA34910487713D364AA68">
|
||||||
<board mapper="79">
|
<board mapper="79">
|
||||||
@@ -9318,6 +9470,18 @@
|
|||||||
</board>
|
</board>
|
||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
|
<game>
|
||||||
|
<peripherals>
|
||||||
|
<device type="fourplayer" />
|
||||||
|
</peripherals>
|
||||||
|
<cartridge system="NES-NTSC" dump="ok" crc="5B8D4378" sha1="7194D3DB031C9342BB473A4ADE364DB1631B5EC9">
|
||||||
|
<board mapper="34">
|
||||||
|
<prg size="32k" />
|
||||||
|
<vram size="12k" />
|
||||||
|
<wram size="8k" battery="1" />
|
||||||
|
</board>
|
||||||
|
</cartridge>
|
||||||
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="NES-NTSC" dump="ok" crc="5BB62688" sha1="D6615439A90FC68758C4149F0CBBE6D1331451F3">
|
<cartridge system="NES-NTSC" dump="ok" crc="5BB62688" sha1="D6615439A90FC68758C4149F0CBBE6D1331451F3">
|
||||||
<board type="NES-DEROM" mapper="206">
|
<board type="NES-DEROM" mapper="206">
|
||||||
@@ -10079,6 +10243,16 @@
|
|||||||
</board>
|
</board>
|
||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
|
<game>
|
||||||
|
<cartridge system="NES-NTSC" dump="ok" crc="633AFE6F" sha1="2F29F3DC724027FAD926BC9D4470A481884E42A5">
|
||||||
|
<board type="NES-HKROM" mapper="4">
|
||||||
|
<prg size="32k" />
|
||||||
|
<chr size="8k" />
|
||||||
|
<pad h="0" v="1" />
|
||||||
|
<chip type="MMC6B" battery="0" />
|
||||||
|
</board>
|
||||||
|
</cartridge>
|
||||||
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="Famicom" dump="unknown" crc="63469396" sha1="C6FEF52264372FAB620D1E5EE6A3E60E46262775">
|
<cartridge system="Famicom" dump="unknown" crc="63469396" sha1="C6FEF52264372FAB620D1E5EE6A3E60E46262775">
|
||||||
<board mapper="1">
|
<board mapper="1">
|
||||||
@@ -10126,7 +10300,7 @@
|
|||||||
</game>
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="Famicom" dump="unknown" crc="6396B988" sha1="B326D1984D5D369BC168028AD7672D2EFC2ECDDB">
|
<cartridge system="Famicom" dump="unknown" crc="6396B988" sha1="B326D1984D5D369BC168028AD7672D2EFC2ECDDB">
|
||||||
<board mapper="5">
|
<board type="HVC-ETROM" mapper="5">
|
||||||
<prg size="256k" />
|
<prg size="256k" />
|
||||||
<chr size="128k" />
|
<chr size="128k" />
|
||||||
<wram size="8k" />
|
<wram size="8k" />
|
||||||
@@ -10248,6 +10422,19 @@
|
|||||||
</board>
|
</board>
|
||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
|
<game>
|
||||||
|
<cartridge system="Famicom" dump="unknown" crc="64AFD592" sha1="618AC6835B96BB5EBFC57DD3B828FAFBB0E0FC7D">
|
||||||
|
<board type="KONAMI-VRC-4" mapper="23">
|
||||||
|
<prg size="128k" />
|
||||||
|
<chr size="128k" />
|
||||||
|
<wram size="8k" battery="1" />
|
||||||
|
<chip type="Konami VRC IV">
|
||||||
|
<pin number="3" function="PRG A3" />
|
||||||
|
<pin number="4" function="PRG A2" />
|
||||||
|
</chip>
|
||||||
|
</board>
|
||||||
|
</cartridge>
|
||||||
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="NES-NTSC" dump="ok" crc="64B710D2" sha1="2875F130DAC4C13FFB1D2FDB655A89AED7FEB44A">
|
<cartridge system="NES-NTSC" dump="ok" crc="64B710D2" sha1="2875F130DAC4C13FFB1D2FDB655A89AED7FEB44A">
|
||||||
<board type="NES-UNROM" mapper="2">
|
<board type="NES-UNROM" mapper="2">
|
||||||
@@ -10833,6 +11020,18 @@
|
|||||||
</board>
|
</board>
|
||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
|
<game>
|
||||||
|
<peripherals>
|
||||||
|
<device type="fourplayer" />
|
||||||
|
</peripherals>
|
||||||
|
<cartridge system="Famicom" dump="unknown" crc="69977C9E" sha1="C43D5F049F4F7862E6DECCA7500C0C23E349AF9F">
|
||||||
|
<board mapper="0">
|
||||||
|
<prg size="32k" />
|
||||||
|
<chr size="8k" />
|
||||||
|
<pad h="0" v="1" />
|
||||||
|
</board>
|
||||||
|
</cartridge>
|
||||||
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="NES-NTSC" dump="ok" crc="6997F5E1" sha1="4C8716C4651973B5F6811D6CA9A0F1E2C4E26FA3">
|
<cartridge system="NES-NTSC" dump="ok" crc="6997F5E1" sha1="4C8716C4651973B5F6811D6CA9A0F1E2C4E26FA3">
|
||||||
<board type="NES-CNROM" mapper="3">
|
<board type="NES-CNROM" mapper="3">
|
||||||
@@ -11327,7 +11526,7 @@
|
|||||||
</game>
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="Famicom" dump="unknown" crc="6F4E4312" sha1="99CF6CA63B173A2B86125F16BBE11885EF1AC377">
|
<cartridge system="Famicom" dump="unknown" crc="6F4E4312" sha1="99CF6CA63B173A2B86125F16BBE11885EF1AC377">
|
||||||
<board mapper="5">
|
<board type="HVC-EWROM" mapper="5">
|
||||||
<prg size="512k" />
|
<prg size="512k" />
|
||||||
<chr size="256k" />
|
<chr size="256k" />
|
||||||
<wram size="32k" battery="1" />
|
<wram size="32k" battery="1" />
|
||||||
@@ -11744,6 +11943,17 @@
|
|||||||
</board>
|
</board>
|
||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
|
<game>
|
||||||
|
<peripherals>
|
||||||
|
<device type="fourplayer" />
|
||||||
|
</peripherals>
|
||||||
|
<cartridge system="NES-PAL" dump="ok" crc="73298C87" sha1="16151504C01A3E89C7893A87567B0F31DC651D96">
|
||||||
|
<board type="PAL-ZZ" mapper="37">
|
||||||
|
<prg size="256k" />
|
||||||
|
<chr size="256k" />
|
||||||
|
</board>
|
||||||
|
</cartridge>
|
||||||
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<arcade system="Playchoice-10" dump="unknown" crc="732B0675" sha1="B50E2DCF63E724F3FE8E5ADED50F32AA95775676">
|
<arcade system="Playchoice-10" dump="unknown" crc="732B0675" sha1="B50E2DCF63E724F3FE8E5ADED50F32AA95775676">
|
||||||
<board mapper="1">
|
<board mapper="1">
|
||||||
@@ -12063,6 +12273,15 @@
|
|||||||
</board>
|
</board>
|
||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
|
<game>
|
||||||
|
<cartridge system="Famicom" dump="unknown" crc="76A6A813" sha1="0FE9120FD5ADC2790B0B9E8FADD136F9C66A709F">
|
||||||
|
<board type="NAMCOT-3301" mapper="0">
|
||||||
|
<prg size="8k" />
|
||||||
|
<chr size="8k" />
|
||||||
|
<pad h="0" v="1" />
|
||||||
|
</board>
|
||||||
|
</cartridge>
|
||||||
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="NES-PAL" dump="ok" crc="76C161E3" sha1="0711BC8D0BF42A0829391C2320393A0D3DF2DD1F">
|
<cartridge system="NES-PAL" dump="ok" crc="76C161E3" sha1="0711BC8D0BF42A0829391C2320393A0D3DF2DD1F">
|
||||||
<board type="NES-SGROM" mapper="1">
|
<board type="NES-SGROM" mapper="1">
|
||||||
@@ -13195,6 +13414,18 @@
|
|||||||
</board>
|
</board>
|
||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
|
<game>
|
||||||
|
<cartridge system="Famicom" dump="unknown" crc="8281C50F" sha1="DDAB9E627E9F7B7DB068B120CF857D51B2A935C9">
|
||||||
|
<board type="KONAMI-VRC-4" mapper="23">
|
||||||
|
<prg size="128k" />
|
||||||
|
<chr size="128k" />
|
||||||
|
<chip type="Konami VRC IV">
|
||||||
|
<pin number="3" function="PRG A3" />
|
||||||
|
<pin number="4" function="PRG A2" />
|
||||||
|
</chip>
|
||||||
|
</board>
|
||||||
|
</cartridge>
|
||||||
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="Famicom" dump="unknown" crc="828F8F1F" sha1="9DC376442DB43C7786230AEEB54D5D643A4104E6">
|
<cartridge system="Famicom" dump="unknown" crc="828F8F1F" sha1="9DC376442DB43C7786230AEEB54D5D643A4104E6">
|
||||||
<board mapper="1">
|
<board mapper="1">
|
||||||
@@ -13580,6 +13811,15 @@
|
|||||||
</board>
|
</board>
|
||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
|
<game>
|
||||||
|
<cartridge system="Famicom" dump="ok" crc="8589652D" sha1="0CC7ED7F5D7EE0959EE3724C3AF06EF8DF397C59">
|
||||||
|
<board type="NANJING" mapper="163">
|
||||||
|
<prg size="2048k" />
|
||||||
|
<wram size="8k" battery="1" />
|
||||||
|
<pad h="0" v="1" />
|
||||||
|
</board>
|
||||||
|
</cartridge>
|
||||||
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="NES-NTSC" dump="ok" crc="8593E5AD" sha1="84D2B96C2821FDC246DD876932F4E1752DF1CA73">
|
<cartridge system="NES-NTSC" dump="ok" crc="8593E5AD" sha1="84D2B96C2821FDC246DD876932F4E1752DF1CA73">
|
||||||
<board type="NES-TLROM" mapper="4">
|
<board type="NES-TLROM" mapper="4">
|
||||||
@@ -14365,6 +14605,18 @@
|
|||||||
</board>
|
</board>
|
||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
|
<game>
|
||||||
|
<peripherals>
|
||||||
|
<device type="fourplayer" />
|
||||||
|
</peripherals>
|
||||||
|
<cartridge system="NES-NTSC" dump="ok" crc="8C29D397" sha1="69612DDE41A2C52A802D9768D7C2942572939867">
|
||||||
|
<board type="NES-NROM-256" mapper="0">
|
||||||
|
<prg size="32k" />
|
||||||
|
<chr size="8k" />
|
||||||
|
<pad h="0" v="1" />
|
||||||
|
</board>
|
||||||
|
</cartridge>
|
||||||
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<peripherals>
|
<peripherals>
|
||||||
<device type="turbofile" />
|
<device type="turbofile" />
|
||||||
@@ -14829,6 +15081,17 @@
|
|||||||
</board>
|
</board>
|
||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
|
<game>
|
||||||
|
<peripherals>
|
||||||
|
<device type="fourplayer" />
|
||||||
|
</peripherals>
|
||||||
|
<cartridge system="NES-NTSC" dump="ok" crc="90D2E9F0" sha1="2801AADD9D0308CF2C9069A2BB76242ECA5B1501">
|
||||||
|
<board type="NES-NROM-256" mapper="0">
|
||||||
|
<prg size="32k" />
|
||||||
|
<chr size="8k" />
|
||||||
|
</board>
|
||||||
|
</cartridge>
|
||||||
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="NES-NTSC" dump="ok" crc="90D68A43" sha1="7698CE7AE3B83F12518169ECFEEE4D76D643C842">
|
<cartridge system="NES-NTSC" dump="ok" crc="90D68A43" sha1="7698CE7AE3B83F12518169ECFEEE4D76D643C842">
|
||||||
<board type="NES-CNROM" mapper="3">
|
<board type="NES-CNROM" mapper="3">
|
||||||
@@ -15735,6 +15998,18 @@
|
|||||||
</board>
|
</board>
|
||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
|
<game>
|
||||||
|
<peripherals>
|
||||||
|
<device type="fourplayer" />
|
||||||
|
</peripherals>
|
||||||
|
<cartridge system="NES-NTSC" dump="ok" crc="9908C6C9" sha1="0A98AB1B7D069A00ACA5D1EB5975C181B2F79E5A">
|
||||||
|
<board type="NES-CNROM" mapper="3">
|
||||||
|
<prg size="32k" />
|
||||||
|
<chr size="32k" />
|
||||||
|
<pad h="0" v="1" />
|
||||||
|
</board>
|
||||||
|
</cartridge>
|
||||||
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="NES-NTSC" dump="ok" crc="990985C0" sha1="AA08F65D6333448F088D8DCE32F3895662B577DE">
|
<cartridge system="NES-NTSC" dump="ok" crc="990985C0" sha1="AA08F65D6333448F088D8DCE32F3895662B577DE">
|
||||||
<board type="NES-SLROM" mapper="1">
|
<board type="NES-SLROM" mapper="1">
|
||||||
@@ -15981,6 +16256,9 @@
|
|||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
<game>
|
<game>
|
||||||
|
<peripherals>
|
||||||
|
<device type="pachinko" />
|
||||||
|
</peripherals>
|
||||||
<cartridge system="Famicom" dump="unknown" crc="9B3C5124" sha1="A96BFC7B51E2F7FF69F42B024CC9FB85CA9A943D">
|
<cartridge system="Famicom" dump="unknown" crc="9B3C5124" sha1="A96BFC7B51E2F7FF69F42B024CC9FB85CA9A943D">
|
||||||
<board mapper="4">
|
<board mapper="4">
|
||||||
<prg size="256k" />
|
<prg size="256k" />
|
||||||
@@ -16633,6 +16911,19 @@
|
|||||||
</board>
|
</board>
|
||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
|
<game>
|
||||||
|
<peripherals>
|
||||||
|
<device type="fourplayer" />
|
||||||
|
</peripherals>
|
||||||
|
<cartridge system="NES-NTSC" dump="ok" crc="9FC43DD8" sha1="A4A44A54BA682223503BEABE55D5031B5C62B2A7">
|
||||||
|
<board mapper="34">
|
||||||
|
<prg size="32k" />
|
||||||
|
<vram size="8k" />
|
||||||
|
<wram size="8k" />
|
||||||
|
<pad h="1" v="0" />
|
||||||
|
</board>
|
||||||
|
</cartridge>
|
||||||
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="Famicom" dump="ok" crc="9FD35802" sha1="C38AF729C2BE2940FCA620F86415FAE304F1D8C9">
|
<cartridge system="Famicom" dump="ok" crc="9FD35802" sha1="C38AF729C2BE2940FCA620F86415FAE304F1D8C9">
|
||||||
<board type="HVC-CNROM" mapper="3">
|
<board type="HVC-CNROM" mapper="3">
|
||||||
@@ -17110,6 +17401,16 @@
|
|||||||
</board>
|
</board>
|
||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
|
<game>
|
||||||
|
<cartridge system="NES-NTSC" dump="ok" crc="A512BDF6" sha1="F794FDA12D34E611D58E652319ED583AE61B81E0">
|
||||||
|
<board type="NES-HKROM" mapper="4">
|
||||||
|
<prg size="32k" />
|
||||||
|
<chr size="8k" />
|
||||||
|
<pad h="0" v="1" />
|
||||||
|
<chip type="MMC6B" battery="0" />
|
||||||
|
</board>
|
||||||
|
</cartridge>
|
||||||
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="Famicom" dump="unknown" crc="A531E1AB" sha1="0A0772721642DE00FE575CA109891E274251D815">
|
<cartridge system="Famicom" dump="unknown" crc="A531E1AB" sha1="0A0772721642DE00FE575CA109891E274251D815">
|
||||||
<board mapper="4">
|
<board mapper="4">
|
||||||
@@ -18875,7 +19176,7 @@
|
|||||||
</game>
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="Famicom" dump="unknown" crc="B4735FAC" sha1="4AC3E9136706AB009EE2F68C7D009422D73EE8E8">
|
<cartridge system="Famicom" dump="unknown" crc="B4735FAC" sha1="4AC3E9136706AB009EE2F68C7D009422D73EE8E8">
|
||||||
<board mapper="5">
|
<board type="HVC-ELROM" mapper="5">
|
||||||
<prg size="512k" />
|
<prg size="512k" />
|
||||||
<chr size="512k" />
|
<chr size="512k" />
|
||||||
</board>
|
</board>
|
||||||
@@ -19569,7 +19870,7 @@
|
|||||||
</game>
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="Famicom" dump="unknown" crc="BB7F829A" sha1="BC7393653D04F3E3D35E3D0623ACA4A2C27E0AA1">
|
<cartridge system="Famicom" dump="unknown" crc="BB7F829A" sha1="BC7393653D04F3E3D35E3D0623ACA4A2C27E0AA1">
|
||||||
<board mapper="5">
|
<board type= "HVC-ELROM" mapper="5">
|
||||||
<prg size="128k" />
|
<prg size="128k" />
|
||||||
<chr size="128k" />
|
<chr size="128k" />
|
||||||
</board>
|
</board>
|
||||||
@@ -19696,7 +19997,7 @@
|
|||||||
</game>
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="Famicom" dump="unknown" crc="BC80FB52" sha1="74DBA27392CA4451875AD0267E5466F92D835A62">
|
<cartridge system="Famicom" dump="unknown" crc="BC80FB52" sha1="74DBA27392CA4451875AD0267E5466F92D835A62">
|
||||||
<board mapper="5">
|
<board type="HVC-EKROM" mapper="5">
|
||||||
<prg size="256k" />
|
<prg size="256k" />
|
||||||
<chr size="256k" />
|
<chr size="256k" />
|
||||||
<wram size="8k" battery="1" />
|
<wram size="8k" battery="1" />
|
||||||
@@ -19828,6 +20129,15 @@
|
|||||||
</board>
|
</board>
|
||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
|
<game>
|
||||||
|
<cartridge system="NES-NTSC" dump="unknown" crc="BD75ED79" sha1="2E2B42DA4E0F41F411C778B5A9B66AA9FBD1167E">
|
||||||
|
<board type="CAMERICA-BF9093" mapper="71">
|
||||||
|
<prg size="64k" />
|
||||||
|
<vram size="8k" />
|
||||||
|
<pad h="1" v="0" />
|
||||||
|
</board>
|
||||||
|
</cartridge>
|
||||||
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="Famicom" dump="ok" crc="BD9D0E85" sha1="F82654BE44E4B01B3F9627D81232A086B1CF7599">
|
<cartridge system="Famicom" dump="ok" crc="BD9D0E85" sha1="F82654BE44E4B01B3F9627D81232A086B1CF7599">
|
||||||
<board type="HVC-UNROM" mapper="2">
|
<board type="HVC-UNROM" mapper="2">
|
||||||
@@ -20371,6 +20681,9 @@
|
|||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
<game>
|
<game>
|
||||||
|
<peripherals>
|
||||||
|
<device type="pachinko" />
|
||||||
|
</peripherals>
|
||||||
<cartridge system="Famicom" dump="unknown" crc="C22C23AB" sha1="4CEA0ECDF0A22E678B827C9BFD8D80B5DEBB4094">
|
<cartridge system="Famicom" dump="unknown" crc="C22C23AB" sha1="4CEA0ECDF0A22E678B827C9BFD8D80B5DEBB4094">
|
||||||
<board mapper="1">
|
<board mapper="1">
|
||||||
<prg size="128k" />
|
<prg size="128k" />
|
||||||
@@ -20947,6 +21260,18 @@
|
|||||||
</board>
|
</board>
|
||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
|
<game>
|
||||||
|
<peripherals>
|
||||||
|
<device type="pokkunmoguraa" />
|
||||||
|
</peripherals>
|
||||||
|
<cartridge system="Famicom" dump="unknown" crc="C7BCC981" sha1="8A0DEADD84A0967B1D2DB0634262C7BDBBB732B7">
|
||||||
|
<board mapper="3">
|
||||||
|
<prg size="32k" />
|
||||||
|
<chr size="32k" />
|
||||||
|
<pad h="0" v="1" />
|
||||||
|
</board>
|
||||||
|
</cartridge>
|
||||||
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="Dendy" dump="unknown" crc="C7EDBC2E" sha1="E4414C160C7E91136C62D99154336035E5636EEB">
|
<cartridge system="Dendy" dump="unknown" crc="C7EDBC2E" sha1="E4414C160C7E91136C62D99154336035E5636EEB">
|
||||||
<board mapper="13">
|
<board mapper="13">
|
||||||
@@ -22458,7 +22783,7 @@
|
|||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="NES-PAL-B" dump="ok" crc="D67FD6A6" sha1="85DE67A28E01EF680F2FF6AAE80E4315491CEEE9">
|
<cartridge system="NES-NTSC" dump="ok" crc="D67FD6A6" sha1="85DE67A28E01EF680F2FF6AAE80E4315491CEEE9">
|
||||||
<board type="NES-SNROM" mapper="1">
|
<board type="NES-SNROM" mapper="1">
|
||||||
<prg size="128k" />
|
<prg size="128k" />
|
||||||
<vram size="8k" />
|
<vram size="8k" />
|
||||||
@@ -23635,6 +23960,9 @@
|
|||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
<game>
|
<game>
|
||||||
|
<peripherals>
|
||||||
|
<device type="pachinko" />
|
||||||
|
</peripherals>
|
||||||
<cartridge system="Famicom" dump="unknown" crc="E08C8A60" sha1="BAF3A4E0423A86E53234E806843149EF7D0974A9">
|
<cartridge system="Famicom" dump="unknown" crc="E08C8A60" sha1="BAF3A4E0423A86E53234E806843149EF7D0974A9">
|
||||||
<board mapper="4">
|
<board mapper="4">
|
||||||
<prg size="512k" />
|
<prg size="512k" />
|
||||||
@@ -25207,7 +25535,7 @@
|
|||||||
</game>
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="Famicom" dump="unknown" crc="EEE9A682" sha1="46C443D0EB27AF7A566E744F096F981034A06E59">
|
<cartridge system="Famicom" dump="unknown" crc="EEE9A682" sha1="46C443D0EB27AF7A566E744F096F981034A06E59">
|
||||||
<board mapper="5">
|
<board type="HVC-ETROM" mapper="5">
|
||||||
<prg size="256k" />
|
<prg size="256k" />
|
||||||
<chr size="128k" />
|
<chr size="128k" />
|
||||||
<wram size="8k" />
|
<wram size="8k" />
|
||||||
@@ -25492,6 +25820,14 @@
|
|||||||
</board>
|
</board>
|
||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
|
<game>
|
||||||
|
<cartridge system="NES-NTSC" dump="ok" crc="F312D1DE" sha1="35C157A921156E47FD3F6573D150F54108D0EDFC">
|
||||||
|
<board type="NES-TXROM" mapper="4">
|
||||||
|
<prg size="16k" />
|
||||||
|
<chip type="MMC3A" />
|
||||||
|
</board>
|
||||||
|
</cartridge>
|
||||||
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="NES-NTSC" dump="ok" crc="F31D36A3" sha1="00147962462C44354735861D0258D72314635458">
|
<cartridge system="NES-NTSC" dump="ok" crc="F31D36A3" sha1="00147962462C44354735861D0258D72314635458">
|
||||||
<board type="NES-TSROM" mapper="4">
|
<board type="NES-TSROM" mapper="4">
|
||||||
@@ -25606,17 +25942,6 @@
|
|||||||
</board>
|
</board>
|
||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
<game>
|
|
||||||
<peripherals>
|
|
||||||
<device type="fourplayer" />
|
|
||||||
</peripherals>
|
|
||||||
<cartridge system="NES-PAL" dump="ok" crc="73298C87" sha1="16151504C01A3E89C7893A87567B0F31DC651D96">
|
|
||||||
<board type="PAL-ZZ" mapper="37">
|
|
||||||
<prg size="256k" />
|
|
||||||
<chr size="256k" />
|
|
||||||
</board>
|
|
||||||
</cartridge>
|
|
||||||
</game>
|
|
||||||
<game>
|
<game>
|
||||||
<peripherals>
|
<peripherals>
|
||||||
<device type="fourplayer" />
|
<device type="fourplayer" />
|
||||||
@@ -25698,7 +26023,7 @@
|
|||||||
</game>
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="Famicom" dump="unknown" crc="F540677B" sha1="44A5BC2B8156D50518EEBEEFA522A7642E0476DC">
|
<cartridge system="Famicom" dump="unknown" crc="F540677B" sha1="44A5BC2B8156D50518EEBEEFA522A7642E0476DC">
|
||||||
<board mapper="5">
|
<board type="HVC-EWROM" mapper="5">
|
||||||
<prg size="512k" />
|
<prg size="512k" />
|
||||||
<chr size="256k" />
|
<chr size="256k" />
|
||||||
<wram size="32k" battery="1" />
|
<wram size="32k" battery="1" />
|
||||||
@@ -26686,6 +27011,16 @@
|
|||||||
</board>
|
</board>
|
||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
|
<game>
|
||||||
|
<cartridge system="NES-NTSC" dump="ok" crc="FDC7C50B" sha1="5E8F67BEFB2B1BCBD0384E3144ACB2766FC3E443">
|
||||||
|
<board type="NES-ETROM" mapper="5">
|
||||||
|
<prg size="128k" />
|
||||||
|
<chr size="128k" />
|
||||||
|
<wram size="8k" />
|
||||||
|
<wram size="8k" battery="1" />
|
||||||
|
</board>
|
||||||
|
</cartridge>
|
||||||
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="Famicom" dump="unknown" crc="FDD89C45" sha1="D25327E0F0D8539AE761DF861254BDE3A60FDD96">
|
<cartridge system="Famicom" dump="unknown" crc="FDD89C45" sha1="D25327E0F0D8539AE761DF861254BDE3A60FDD96">
|
||||||
<board mapper="0">
|
<board mapper="0">
|
||||||
@@ -26767,7 +27102,7 @@
|
|||||||
</game>
|
</game>
|
||||||
<game>
|
<game>
|
||||||
<cartridge system="Famicom" dump="unknown" crc="FE3488D1" sha1="800AEFE756E85A0A78CCB4DAE68EBBA5DF24BF41">
|
<cartridge system="Famicom" dump="unknown" crc="FE3488D1" sha1="800AEFE756E85A0A78CCB4DAE68EBBA5DF24BF41">
|
||||||
<board mapper="5">
|
<board type="HVC-ETROM" mapper="5">
|
||||||
<prg size="512k" />
|
<prg size="512k" />
|
||||||
<chr size="128k" />
|
<chr size="128k" />
|
||||||
<wram size="8k" />
|
<wram size="8k" />
|
||||||
@@ -26916,4 +27251,15 @@
|
|||||||
</board>
|
</board>
|
||||||
</cartridge>
|
</cartridge>
|
||||||
</game>
|
</game>
|
||||||
|
<!--game>
|
||||||
|
<peripherals>
|
||||||
|
<device type="zapper" />
|
||||||
|
</peripherals>
|
||||||
|
<cartridge system="Famicom" dump="ok" crc="A0FBF02E" sha1="38236FBD5B70F651674D52EE519AB4DBB11F7955">
|
||||||
|
<board type="UNL-TF1201" mapper="298">
|
||||||
|
<prg size="128k" />
|
||||||
|
<chr size="128k" />
|
||||||
|
</board>
|
||||||
|
</cartridge>
|
||||||
|
</game>-->
|
||||||
</database>
|
</database>
|
||||||
|
|||||||
+12
-12
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"generated_at": "2026-03-27T22:52:09Z",
|
"generated_at": "2026-03-28T08:10:17Z",
|
||||||
"total_files": 6756,
|
"total_files": 6756,
|
||||||
"total_size": 5589782999,
|
"total_size": 5589795834,
|
||||||
"files": {
|
"files": {
|
||||||
"520d3d1b5897800af47f92efd2444a26b7a7dead": {
|
"520d3d1b5897800af47f92efd2444a26b7a7dead": {
|
||||||
"path": "bios/3DO Company/3DO/3do_arcade_saot.bin",
|
"path": "bios/3DO Company/3DO/3do_arcade_saot.bin",
|
||||||
@@ -33213,15 +33213,15 @@
|
|||||||
"crc32": "5205222b",
|
"crc32": "5205222b",
|
||||||
"adler32": "34389d20"
|
"adler32": "34389d20"
|
||||||
},
|
},
|
||||||
"26322f182540211e9b5e3647675b7c593706ae2b": {
|
"f92312bae56e29c5bf00a5103105fce78472bf5c": {
|
||||||
"path": "bios/Nintendo/NES/NstDatabase.xml",
|
"path": "bios/Nintendo/NES/NstDatabase.xml",
|
||||||
"name": "NstDatabase.xml",
|
"name": "NstDatabase.xml",
|
||||||
"size": 1009534,
|
"size": 1022369,
|
||||||
"sha1": "26322f182540211e9b5e3647675b7c593706ae2b",
|
"sha1": "f92312bae56e29c5bf00a5103105fce78472bf5c",
|
||||||
"md5": "7bfe8c0540ed4bd6a0f1e2a0f0118ced",
|
"md5": "0ee6cbdc6f5c96ce9c8aa5edb59066f4",
|
||||||
"sha256": "914584ee6964e8b4cc4ff092874052e7baf13708cfb3f35940342421fcf1bedc",
|
"sha256": "ef5bfd08928b4c9186ba03628ae9f2b7a3b0e30c9204592ac72cd67fa8a31f3a",
|
||||||
"crc32": "ebb2196c",
|
"crc32": "0e4d552b",
|
||||||
"adler32": "88d01ea2"
|
"adler32": "4f5c1356"
|
||||||
},
|
},
|
||||||
"f430a0d752a9fa0c7032db8131f9090d18f71779": {
|
"f430a0d752a9fa0c7032db8131f9090d18f71779": {
|
||||||
"path": "bios/Nintendo/NES/gamegenie.nes",
|
"path": "bios/Nintendo/NES/gamegenie.nes",
|
||||||
@@ -70887,7 +70887,7 @@
|
|||||||
"b953eb1a8fc9922b3f7051c1cdc451f1": "ae7233cae8f94749796e0b740d6021e3b00a8926",
|
"b953eb1a8fc9922b3f7051c1cdc451f1": "ae7233cae8f94749796e0b740d6021e3b00a8926",
|
||||||
"413154dd0e2c824c9b18b807fd03ec4e": "691e46213d8428befdf568157e670b971ab94e1d",
|
"413154dd0e2c824c9b18b807fd03ec4e": "691e46213d8428befdf568157e670b971ab94e1d",
|
||||||
"c03f6bbaf644eb9b3ee261dbe199eb42": "2faaf92bcaffe675f54f7249d30f3791507e22ab",
|
"c03f6bbaf644eb9b3ee261dbe199eb42": "2faaf92bcaffe675f54f7249d30f3791507e22ab",
|
||||||
"7bfe8c0540ed4bd6a0f1e2a0f0118ced": "26322f182540211e9b5e3647675b7c593706ae2b",
|
"0ee6cbdc6f5c96ce9c8aa5edb59066f4": "f92312bae56e29c5bf00a5103105fce78472bf5c",
|
||||||
"7f98d77d7a094ad7d069b74bd553ec98": "f430a0d752a9fa0c7032db8131f9090d18f71779",
|
"7f98d77d7a094ad7d069b74bd553ec98": "f430a0d752a9fa0c7032db8131f9090d18f71779",
|
||||||
"aaf3666e4ed478e2964b46d6a7aa27ad": "37027d92e1015b82a7dc5c43e9f1649a961577ab",
|
"aaf3666e4ed478e2964b46d6a7aa27ad": "37027d92e1015b82a7dc5c43e9f1649a961577ab",
|
||||||
"8d3d9f294b6e174bc7b1d2fd1c727530": "bf861922dcb78c316360e3e742f4f70ff63c9bc3",
|
"8d3d9f294b6e174bc7b1d2fd1c727530": "bf861922dcb78c316360e3e742f4f70ff63c9bc3",
|
||||||
@@ -83500,7 +83500,7 @@
|
|||||||
"2faaf92bcaffe675f54f7249d30f3791507e22ab"
|
"2faaf92bcaffe675f54f7249d30f3791507e22ab"
|
||||||
],
|
],
|
||||||
"NstDatabase.xml": [
|
"NstDatabase.xml": [
|
||||||
"26322f182540211e9b5e3647675b7c593706ae2b"
|
"f92312bae56e29c5bf00a5103105fce78472bf5c"
|
||||||
],
|
],
|
||||||
"gamegenie.nes": [
|
"gamegenie.nes": [
|
||||||
"f430a0d752a9fa0c7032db8131f9090d18f71779"
|
"f430a0d752a9fa0c7032db8131f9090d18f71779"
|
||||||
@@ -99007,7 +99007,7 @@
|
|||||||
"54c7d10e": "ae7233cae8f94749796e0b740d6021e3b00a8926",
|
"54c7d10e": "ae7233cae8f94749796e0b740d6021e3b00a8926",
|
||||||
"8bbef508": "691e46213d8428befdf568157e670b971ab94e1d",
|
"8bbef508": "691e46213d8428befdf568157e670b971ab94e1d",
|
||||||
"5205222b": "2faaf92bcaffe675f54f7249d30f3791507e22ab",
|
"5205222b": "2faaf92bcaffe675f54f7249d30f3791507e22ab",
|
||||||
"ebb2196c": "26322f182540211e9b5e3647675b7c593706ae2b",
|
"0e4d552b": "f92312bae56e29c5bf00a5103105fce78472bf5c",
|
||||||
"4c514089": "f430a0d752a9fa0c7032db8131f9090d18f71779",
|
"4c514089": "f430a0d752a9fa0c7032db8131f9090d18f71779",
|
||||||
"76f51d6b": "37027d92e1015b82a7dc5c43e9f1649a961577ab",
|
"76f51d6b": "37027d92e1015b82a7dc5c43e9f1649a961577ab",
|
||||||
"7f933ce2": "bf861922dcb78c316360e3e742f4f70ff63c9bc3",
|
"7f933ce2": "bf861922dcb78c316360e3e742f4f70ff63c9bc3",
|
||||||
|
|||||||
@@ -1325,9 +1325,9 @@ systems:
|
|||||||
- name: NstDatabase.xml
|
- name: NstDatabase.xml
|
||||||
destination: NstDatabase.xml
|
destination: NstDatabase.xml
|
||||||
required: true
|
required: true
|
||||||
sha1: 26322f182540211e9b5e3647675b7c593706ae2b
|
sha1: f92312bae56e29c5bf00a5103105fce78472bf5c
|
||||||
md5: 7bfe8c0540ed4bd6a0f1e2a0f0118ced
|
md5: 0ee6cbdc6f5c96ce9c8aa5edb59066f4
|
||||||
crc32: ebb2196c
|
crc32: 0e4d552b
|
||||||
size: 1009534
|
size: 1009534
|
||||||
core: fceumm
|
core: fceumm
|
||||||
manufacturer: Nintendo
|
manufacturer: Nintendo
|
||||||
|
|||||||
@@ -7310,7 +7310,7 @@ systems:
|
|||||||
- name: NstDatabase.xml
|
- name: NstDatabase.xml
|
||||||
destination: bios/NstDatabase.xml
|
destination: bios/NstDatabase.xml
|
||||||
required: false
|
required: false
|
||||||
md5: 7bfe8c0540ed4bd6a0f1e2a0f0118ced
|
md5: 0ee6cbdc6f5c96ce9c8aa5edb59066f4
|
||||||
nintendo-pokemon-mini:
|
nintendo-pokemon-mini:
|
||||||
files:
|
files:
|
||||||
- name: bios.min
|
- name: bios.min
|
||||||
|
|||||||
+2
-2
@@ -960,8 +960,8 @@ systems:
|
|||||||
- name: NstDatabase.xml
|
- name: NstDatabase.xml
|
||||||
destination: nes/NstDatabase.xml
|
destination: nes/NstDatabase.xml
|
||||||
required: true
|
required: true
|
||||||
sha1: 26322f182540211e9b5e3647675b7c593706ae2b
|
sha1: f92312bae56e29c5bf00a5103105fce78472bf5c
|
||||||
md5: 7bfe8c0540ed4bd6a0f1e2a0f0118ced
|
md5: 0ee6cbdc6f5c96ce9c8aa5edb59066f4
|
||||||
crc32: ebb2196c
|
crc32: ebb2196c
|
||||||
size: 1009534
|
size: 1009534
|
||||||
nintendo-pokemon-mini:
|
nintendo-pokemon-mini:
|
||||||
|
|||||||
+35
-5
@@ -647,6 +647,29 @@ def resolve_platform_cores(
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
MANUFACTURER_PREFIXES = (
|
||||||
|
"microsoft-", "nintendo-", "sony-", "sega-", "snk-", "panasonic-",
|
||||||
|
"nec-", "epoch-", "mattel-", "fairchild-", "hartung-", "tiger-",
|
||||||
|
"magnavox-", "philips-", "bandai-", "casio-", "coleco-",
|
||||||
|
"commodore-", "sharp-", "sinclair-", "atari-",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def derive_manufacturer(system_id: str, system_data: dict) -> str:
|
||||||
|
"""Derive manufacturer name for a system.
|
||||||
|
|
||||||
|
Priority: explicit manufacturer field > system ID prefix > 'Other'.
|
||||||
|
"""
|
||||||
|
mfr = system_data.get("manufacturer", "")
|
||||||
|
if mfr and mfr not in ("Various", "Other"):
|
||||||
|
return mfr.split("|")[0].strip()
|
||||||
|
s = system_id.lower().replace("_", "-")
|
||||||
|
for prefix in MANUFACTURER_PREFIXES:
|
||||||
|
if s.startswith(prefix):
|
||||||
|
return prefix.rstrip("-").title()
|
||||||
|
return "Other"
|
||||||
|
|
||||||
|
|
||||||
def _norm_system_id(sid: str) -> str:
|
def _norm_system_id(sid: str) -> str:
|
||||||
"""Normalize system ID for cross-platform matching.
|
"""Normalize system ID for cross-platform matching.
|
||||||
|
|
||||||
@@ -655,11 +678,7 @@ def _norm_system_id(sid: str) -> str:
|
|||||||
(e.g., "microsoft-xbox", "nintendo-wii-u").
|
(e.g., "microsoft-xbox", "nintendo-wii-u").
|
||||||
"""
|
"""
|
||||||
s = sid.lower().replace("_", "-")
|
s = sid.lower().replace("_", "-")
|
||||||
for prefix in ("microsoft-", "nintendo-", "sony-", "sega-",
|
for prefix in MANUFACTURER_PREFIXES:
|
||||||
"snk-", "panasonic-", "nec-", "epoch-", "mattel-",
|
|
||||||
"fairchild-", "hartung-", "tiger-", "magnavox-",
|
|
||||||
"philips-", "bandai-", "casio-", "coleco-",
|
|
||||||
"commodore-", "sharp-", "sinclair-"):
|
|
||||||
if s.startswith(prefix):
|
if s.startswith(prefix):
|
||||||
s = s[len(prefix):]
|
s = s[len(prefix):]
|
||||||
break
|
break
|
||||||
@@ -1068,3 +1087,14 @@ def list_system_ids(emulators_dir: str) -> None:
|
|||||||
for sys_id in sorted(system_emus):
|
for sys_id in sorted(system_emus):
|
||||||
count = len(system_emus[sys_id])
|
count = len(system_emus[sys_id])
|
||||||
print(f" {sys_id:35s} ({count} emulator{'s' if count > 1 else ''})")
|
print(f" {sys_id:35s} ({count} emulator{'s' if count > 1 else ''})")
|
||||||
|
|
||||||
|
|
||||||
|
def list_platform_system_ids(platform_name: str, platforms_dir: str) -> None:
|
||||||
|
"""Print system IDs from a platform's YAML config."""
|
||||||
|
config = load_platform_config(platform_name, platforms_dir)
|
||||||
|
systems = config.get("systems", {})
|
||||||
|
for sys_id in sorted(systems):
|
||||||
|
file_count = len(systems[sys_id].get("files", []))
|
||||||
|
mfr = systems[sys_id].get("manufacturer", "")
|
||||||
|
mfr_display = f" [{mfr.split('|')[0]}]" if mfr else ""
|
||||||
|
print(f" {sys_id:35s} ({file_count} file{'s' if file_count != 1 else ''}){mfr_display}")
|
||||||
|
|||||||
+519
-32
@@ -16,6 +16,7 @@ import argparse
|
|||||||
import hashlib
|
import hashlib
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import urllib.request
|
import urllib.request
|
||||||
@@ -25,9 +26,11 @@ from pathlib import Path
|
|||||||
|
|
||||||
sys.path.insert(0, os.path.dirname(__file__))
|
sys.path.insert(0, os.path.dirname(__file__))
|
||||||
from common import (
|
from common import (
|
||||||
|
MANUFACTURER_PREFIXES,
|
||||||
_build_validation_index, build_zip_contents_index, check_file_validation,
|
_build_validation_index, build_zip_contents_index, check_file_validation,
|
||||||
check_inside_zip, compute_hashes, fetch_large_file, filter_files_by_mode,
|
check_inside_zip, compute_hashes, fetch_large_file, filter_files_by_mode,
|
||||||
group_identical_platforms, list_emulator_profiles, list_registered_platforms,
|
group_identical_platforms, list_emulator_profiles, list_platform_system_ids,
|
||||||
|
list_registered_platforms,
|
||||||
filter_systems_by_target, list_system_ids, load_database,
|
filter_systems_by_target, list_system_ids, load_database,
|
||||||
load_data_dir_registry, load_emulator_profiles, load_platform_config,
|
load_data_dir_registry, load_emulator_profiles, load_platform_config,
|
||||||
md5_composite, resolve_local_file,
|
md5_composite, resolve_local_file,
|
||||||
@@ -46,6 +49,106 @@ DEFAULT_OUTPUT_DIR = "dist"
|
|||||||
DEFAULT_BIOS_DIR = "bios"
|
DEFAULT_BIOS_DIR = "bios"
|
||||||
MAX_ENTRY_SIZE = 512 * 1024 * 1024 # 512MB
|
MAX_ENTRY_SIZE = 512 * 1024 * 1024 # 512MB
|
||||||
|
|
||||||
|
_HEX_RE = re.compile(r"\b([0-9a-fA-F]{8,40})\b")
|
||||||
|
|
||||||
|
|
||||||
|
def _detect_hash_type(h: str) -> str:
|
||||||
|
n = len(h)
|
||||||
|
if n == 40:
|
||||||
|
return "sha1"
|
||||||
|
if n == 32:
|
||||||
|
return "md5"
|
||||||
|
if n == 8:
|
||||||
|
return "crc32"
|
||||||
|
return "md5"
|
||||||
|
|
||||||
|
|
||||||
|
def parse_hash_input(raw: str) -> list[tuple[str, str]]:
|
||||||
|
"""Parse comma-separated hash string into (type, hash) tuples."""
|
||||||
|
results: list[tuple[str, str]] = []
|
||||||
|
for part in raw.split(","):
|
||||||
|
part = part.strip().lower()
|
||||||
|
if not part:
|
||||||
|
continue
|
||||||
|
m = _HEX_RE.search(part)
|
||||||
|
if m:
|
||||||
|
h = m.group(1)
|
||||||
|
results.append((_detect_hash_type(h), h))
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def parse_hash_file(path: str) -> list[tuple[str, str]]:
|
||||||
|
"""Parse hash file (one per line, comments with #, mixed formats)."""
|
||||||
|
results: list[tuple[str, str]] = []
|
||||||
|
with open(path) as f:
|
||||||
|
for line in f:
|
||||||
|
line = line.strip()
|
||||||
|
if not line or line.startswith("#"):
|
||||||
|
continue
|
||||||
|
m = _HEX_RE.search(line.lower())
|
||||||
|
if m:
|
||||||
|
h = m.group(1)
|
||||||
|
results.append((_detect_hash_type(h), h))
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def lookup_hashes(
|
||||||
|
hashes: list[tuple[str, str]],
|
||||||
|
db: dict,
|
||||||
|
bios_dir: str,
|
||||||
|
emulators_dir: str,
|
||||||
|
platforms_dir: str,
|
||||||
|
) -> None:
|
||||||
|
"""Print diagnostic info for each hash."""
|
||||||
|
files_db = db.get("files", {})
|
||||||
|
by_md5 = db.get("indexes", {}).get("by_md5", {})
|
||||||
|
by_crc32 = db.get("indexes", {}).get("by_crc32", {})
|
||||||
|
|
||||||
|
for hash_type, hash_val in hashes:
|
||||||
|
sha1 = None
|
||||||
|
if hash_type == "sha1" and hash_val in files_db:
|
||||||
|
sha1 = hash_val
|
||||||
|
elif hash_type == "md5":
|
||||||
|
sha1 = by_md5.get(hash_val)
|
||||||
|
elif hash_type == "crc32":
|
||||||
|
sha1 = by_crc32.get(hash_val)
|
||||||
|
|
||||||
|
if not sha1 or sha1 not in files_db:
|
||||||
|
print(f"\n{hash_type.upper()}: {hash_val}")
|
||||||
|
print(" NOT FOUND in database")
|
||||||
|
continue
|
||||||
|
|
||||||
|
entry = files_db[sha1]
|
||||||
|
name = entry.get("name", "?")
|
||||||
|
md5 = entry.get("md5", "?")
|
||||||
|
paths = entry.get("paths") or []
|
||||||
|
aliases = entry.get("aliases") or []
|
||||||
|
|
||||||
|
print(f"\n{hash_type.upper()}: {hash_val}")
|
||||||
|
print(f" SHA1: {sha1}")
|
||||||
|
print(f" MD5: {md5}")
|
||||||
|
print(f" Name: {name}")
|
||||||
|
if paths:
|
||||||
|
print(f" Path: {paths[0]}")
|
||||||
|
if aliases:
|
||||||
|
print(f" Aliases: {aliases}")
|
||||||
|
|
||||||
|
# Check if file exists in repo (by path or by resolve_local_file)
|
||||||
|
in_repo = False
|
||||||
|
if paths:
|
||||||
|
primary = os.path.join(bios_dir, paths[0])
|
||||||
|
if os.path.exists(primary):
|
||||||
|
in_repo = True
|
||||||
|
if not in_repo:
|
||||||
|
try:
|
||||||
|
fe_check = {"name": name, "sha1": sha1, "md5": md5}
|
||||||
|
local, status = resolve_file(fe_check, db, bios_dir, {})
|
||||||
|
if local and status != "not_found":
|
||||||
|
in_repo = True
|
||||||
|
except (KeyError, OSError):
|
||||||
|
pass
|
||||||
|
print(f" In repo: {'YES' if in_repo else 'NO'}")
|
||||||
|
|
||||||
|
|
||||||
def _find_candidate_satisfying_both(
|
def _find_candidate_satisfying_both(
|
||||||
file_entry: dict,
|
file_entry: dict,
|
||||||
@@ -231,6 +334,9 @@ def generate_pack(
|
|||||||
data_registry: dict | None = None,
|
data_registry: dict | None = None,
|
||||||
emu_profiles: dict | None = None,
|
emu_profiles: dict | None = None,
|
||||||
target_cores: set[str] | None = None,
|
target_cores: set[str] | None = None,
|
||||||
|
required_only: bool = False,
|
||||||
|
system_filter: list[str] | None = None,
|
||||||
|
precomputed_extras: list[dict] | None = None,
|
||||||
) -> str | None:
|
) -> str | None:
|
||||||
"""Generate a ZIP pack for a platform.
|
"""Generate a ZIP pack for a platform.
|
||||||
|
|
||||||
@@ -246,7 +352,22 @@ def generate_pack(
|
|||||||
|
|
||||||
version = config.get("version", config.get("dat_version", ""))
|
version = config.get("version", config.get("dat_version", ""))
|
||||||
version_tag = f"_{version.replace(' ', '')}" if version else ""
|
version_tag = f"_{version.replace(' ', '')}" if version else ""
|
||||||
zip_name = f"{platform_display.replace(' ', '_')}{version_tag}_BIOS_Pack.zip"
|
req_tag = "_Required" if required_only else ""
|
||||||
|
|
||||||
|
sys_tag = ""
|
||||||
|
if system_filter:
|
||||||
|
display_parts = []
|
||||||
|
for sid in system_filter:
|
||||||
|
s = sid.lower().replace("_", "-")
|
||||||
|
for prefix in MANUFACTURER_PREFIXES:
|
||||||
|
if s.startswith(prefix):
|
||||||
|
s = s[len(prefix):]
|
||||||
|
break
|
||||||
|
parts = s.split("-")
|
||||||
|
display_parts.append("_".join(p.title() for p in parts if p))
|
||||||
|
sys_tag = "_" + "_".join(display_parts)
|
||||||
|
|
||||||
|
zip_name = f"{platform_display.replace(' ', '_')}{version_tag}{req_tag}_BIOS_Pack{sys_tag}.zip"
|
||||||
zip_path = os.path.join(output_dir, zip_name)
|
zip_path = os.path.join(output_dir, zip_name)
|
||||||
os.makedirs(output_dir, exist_ok=True)
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
|
|
||||||
@@ -279,9 +400,23 @@ def generate_pack(
|
|||||||
platform_cores=plat_cores,
|
platform_cores=plat_cores,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if system_filter:
|
||||||
|
from common import _norm_system_id
|
||||||
|
norm_filter = {_norm_system_id(s) for s in system_filter}
|
||||||
|
filtered = {sid: sys_data for sid, sys_data in pack_systems.items()
|
||||||
|
if sid in system_filter or _norm_system_id(sid) in norm_filter}
|
||||||
|
if not filtered:
|
||||||
|
available = sorted(pack_systems.keys())[:10]
|
||||||
|
print(f" WARNING: no systems matched filter {system_filter} "
|
||||||
|
f"(available: {', '.join(available)})")
|
||||||
|
return None
|
||||||
|
pack_systems = filtered
|
||||||
|
|
||||||
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
|
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
|
||||||
for sys_id, system in sorted(pack_systems.items()):
|
for sys_id, system in sorted(pack_systems.items()):
|
||||||
for file_entry in system.get("files", []):
|
for file_entry in system.get("files", []):
|
||||||
|
if required_only and file_entry.get("required") is False:
|
||||||
|
continue
|
||||||
dest = _sanitize_path(file_entry.get("destination", file_entry["name"]))
|
dest = _sanitize_path(file_entry.get("destination", file_entry["name"]))
|
||||||
if not dest:
|
if not dest:
|
||||||
# EmuDeck-style entries (system:md5 whitelist, no filename).
|
# EmuDeck-style entries (system:md5 whitelist, no filename).
|
||||||
@@ -423,12 +558,19 @@ def generate_pack(
|
|||||||
# Core requirements: files platform's cores need but YAML doesn't declare
|
# Core requirements: files platform's cores need but YAML doesn't declare
|
||||||
if emu_profiles is None:
|
if emu_profiles is None:
|
||||||
emu_profiles = load_emulator_profiles(emulators_dir)
|
emu_profiles = load_emulator_profiles(emulators_dir)
|
||||||
core_files = _collect_emulator_extras(
|
if precomputed_extras is not None:
|
||||||
config, emulators_dir, db,
|
core_files = precomputed_extras
|
||||||
seen_destinations, base_dest, emu_profiles, target_cores=target_cores,
|
elif system_filter:
|
||||||
)
|
core_files = []
|
||||||
|
else:
|
||||||
|
core_files = _collect_emulator_extras(
|
||||||
|
config, emulators_dir, db,
|
||||||
|
seen_destinations, base_dest, emu_profiles, target_cores=target_cores,
|
||||||
|
)
|
||||||
core_count = 0
|
core_count = 0
|
||||||
for fe in core_files:
|
for fe in core_files:
|
||||||
|
if required_only and fe.get("required") is False:
|
||||||
|
continue
|
||||||
dest = _sanitize_path(fe.get("destination", fe["name"]))
|
dest = _sanitize_path(fe.get("destination", fe["name"]))
|
||||||
if not dest:
|
if not dest:
|
||||||
continue
|
continue
|
||||||
@@ -591,6 +733,7 @@ def generate_emulator_pack(
|
|||||||
output_dir: str,
|
output_dir: str,
|
||||||
standalone: bool = False,
|
standalone: bool = False,
|
||||||
zip_contents: dict | None = None,
|
zip_contents: dict | None = None,
|
||||||
|
required_only: bool = False,
|
||||||
) -> str | None:
|
) -> str | None:
|
||||||
"""Generate a ZIP pack for specific emulator profiles."""
|
"""Generate a ZIP pack for specific emulator profiles."""
|
||||||
all_profiles = load_emulator_profiles(emulators_dir, skip_aliases=False)
|
all_profiles = load_emulator_profiles(emulators_dir, skip_aliases=False)
|
||||||
@@ -710,6 +853,8 @@ def generate_emulator_pack(
|
|||||||
|
|
||||||
# Pack individual files (skip archived ones)
|
# Pack individual files (skip archived ones)
|
||||||
for fe in files:
|
for fe in files:
|
||||||
|
if required_only and fe.get("required") is False:
|
||||||
|
continue
|
||||||
if fe.get("archive"):
|
if fe.get("archive"):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -801,6 +946,7 @@ def generate_system_pack(
|
|||||||
output_dir: str,
|
output_dir: str,
|
||||||
standalone: bool = False,
|
standalone: bool = False,
|
||||||
zip_contents: dict | None = None,
|
zip_contents: dict | None = None,
|
||||||
|
required_only: bool = False,
|
||||||
) -> str | None:
|
) -> str | None:
|
||||||
"""Generate a ZIP pack for all emulators supporting given system IDs."""
|
"""Generate a ZIP pack for all emulators supporting given system IDs."""
|
||||||
profiles = load_emulator_profiles(emulators_dir)
|
profiles = load_emulator_profiles(emulators_dir)
|
||||||
@@ -835,7 +981,7 @@ def generate_system_pack(
|
|||||||
)
|
)
|
||||||
result = generate_emulator_pack(
|
result = generate_emulator_pack(
|
||||||
matching, emulators_dir, db, bios_dir, output_dir,
|
matching, emulators_dir, db, bios_dir, output_dir,
|
||||||
standalone, zip_contents,
|
standalone, zip_contents, required_only=required_only,
|
||||||
)
|
)
|
||||||
if result:
|
if result:
|
||||||
# Rename to system-based name
|
# Rename to system-based name
|
||||||
@@ -852,6 +998,253 @@ def list_platforms(platforms_dir: str) -> list[str]:
|
|||||||
return list_registered_platforms(platforms_dir, include_archived=True)
|
return list_registered_platforms(platforms_dir, include_archived=True)
|
||||||
|
|
||||||
|
|
||||||
|
def _system_display_name(system_id: str) -> str:
|
||||||
|
"""Convert system ID to display name for ZIP naming."""
|
||||||
|
s = system_id.lower().replace("_", "-")
|
||||||
|
for prefix in MANUFACTURER_PREFIXES:
|
||||||
|
if s.startswith(prefix):
|
||||||
|
s = s[len(prefix):]
|
||||||
|
break
|
||||||
|
parts = s.split("-")
|
||||||
|
return "_".join(p.title() for p in parts if p)
|
||||||
|
|
||||||
|
|
||||||
|
def _group_systems_by_manufacturer(
|
||||||
|
systems: dict[str, dict],
|
||||||
|
db: dict,
|
||||||
|
bios_dir: str,
|
||||||
|
) -> dict[str, list[str]]:
|
||||||
|
"""Group system IDs by manufacturer for --split --group-by manufacturer."""
|
||||||
|
from common import derive_manufacturer
|
||||||
|
groups: dict[str, list[str]] = {}
|
||||||
|
for sid, sys_data in systems.items():
|
||||||
|
mfr = derive_manufacturer(sid, sys_data)
|
||||||
|
groups.setdefault(mfr, []).append(sid)
|
||||||
|
return groups
|
||||||
|
|
||||||
|
|
||||||
|
def generate_split_packs(
|
||||||
|
platform_name: str,
|
||||||
|
platforms_dir: str,
|
||||||
|
db: dict,
|
||||||
|
bios_dir: str,
|
||||||
|
output_dir: str,
|
||||||
|
group_by: str = "system",
|
||||||
|
emulators_dir: str = "emulators",
|
||||||
|
zip_contents: dict | None = None,
|
||||||
|
data_registry: dict | None = None,
|
||||||
|
emu_profiles: dict | None = None,
|
||||||
|
target_cores: set[str] | None = None,
|
||||||
|
required_only: bool = False,
|
||||||
|
) -> list[str]:
|
||||||
|
"""Generate split packs (one ZIP per system or manufacturer)."""
|
||||||
|
config = load_platform_config(platform_name, platforms_dir)
|
||||||
|
platform_display = config.get("platform", platform_name)
|
||||||
|
split_dir = os.path.join(output_dir, f"{platform_display.replace(' ', '_')}_Split")
|
||||||
|
os.makedirs(split_dir, exist_ok=True)
|
||||||
|
|
||||||
|
systems = config.get("systems", {})
|
||||||
|
|
||||||
|
if group_by == "manufacturer":
|
||||||
|
groups = _group_systems_by_manufacturer(systems, db, bios_dir)
|
||||||
|
else:
|
||||||
|
groups = {_system_display_name(sid): [sid] for sid in systems}
|
||||||
|
|
||||||
|
# Pre-compute core extras once (expensive: scans 260+ emulator profiles)
|
||||||
|
# then distribute per group based on emulator system overlap
|
||||||
|
if emu_profiles is None:
|
||||||
|
emu_profiles = load_emulator_profiles(emulators_dir)
|
||||||
|
base_dest = config.get("base_destination", "")
|
||||||
|
if emu_profiles:
|
||||||
|
all_extras = _collect_emulator_extras(
|
||||||
|
config, emulators_dir, db, set(), base_dest, emu_profiles,
|
||||||
|
target_cores=target_cores,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
all_extras = []
|
||||||
|
# Map each extra to matching systems via source_emulator.
|
||||||
|
# Index by both profile key AND display name (source_emulator uses display).
|
||||||
|
from common import _norm_system_id
|
||||||
|
emu_system_map: dict[str, set[str]] = {}
|
||||||
|
for name, p in emu_profiles.items():
|
||||||
|
raw = set(p.get("systems", []))
|
||||||
|
norm = {_norm_system_id(s) for s in raw}
|
||||||
|
combined = raw | norm
|
||||||
|
emu_system_map[name] = combined
|
||||||
|
display = p.get("emulator", "")
|
||||||
|
if display and display != name:
|
||||||
|
emu_system_map[display] = combined
|
||||||
|
|
||||||
|
plat_norm = {_norm_system_id(s): s for s in systems}
|
||||||
|
|
||||||
|
results = []
|
||||||
|
for group_name, group_system_ids in sorted(groups.items()):
|
||||||
|
group_sys_set = set(group_system_ids)
|
||||||
|
group_norm = {_norm_system_id(s) for s in group_system_ids}
|
||||||
|
group_match = group_sys_set | group_norm
|
||||||
|
group_extras = [
|
||||||
|
fe for fe in all_extras
|
||||||
|
if emu_system_map.get(fe.get("source_emulator", ""), set()) & group_match
|
||||||
|
]
|
||||||
|
zip_path = generate_pack(
|
||||||
|
platform_name, platforms_dir, db, bios_dir, split_dir,
|
||||||
|
emulators_dir=emulators_dir, zip_contents=zip_contents,
|
||||||
|
data_registry=data_registry, emu_profiles=emu_profiles,
|
||||||
|
target_cores=target_cores, required_only=required_only,
|
||||||
|
system_filter=group_system_ids, precomputed_extras=group_extras,
|
||||||
|
)
|
||||||
|
if zip_path:
|
||||||
|
version = config.get("version", config.get("dat_version", ""))
|
||||||
|
ver_tag = f"_{version.replace(' ', '')}" if version else ""
|
||||||
|
req_tag = "_Required" if required_only else ""
|
||||||
|
safe_group = group_name.replace(" ", "_")
|
||||||
|
new_name = f"{platform_display.replace(' ', '_')}{ver_tag}{req_tag}_{safe_group}_BIOS_Pack.zip"
|
||||||
|
new_path = os.path.join(split_dir, new_name)
|
||||||
|
if new_path != zip_path:
|
||||||
|
os.rename(zip_path, new_path)
|
||||||
|
zip_path = new_path
|
||||||
|
results.append(zip_path)
|
||||||
|
|
||||||
|
# Warn about extras that couldn't be distributed (emulators without systems: field)
|
||||||
|
all_groups_match = set()
|
||||||
|
for group_system_ids in groups.values():
|
||||||
|
group_norm = {_norm_system_id(s) for s in group_system_ids}
|
||||||
|
all_groups_match |= set(group_system_ids) | group_norm
|
||||||
|
undistributed = [
|
||||||
|
fe for fe in all_extras
|
||||||
|
if not emu_system_map.get(fe.get("source_emulator", ""), set()) & all_groups_match
|
||||||
|
]
|
||||||
|
if undistributed:
|
||||||
|
emus = sorted({fe.get("source_emulator", "?") for fe in undistributed})
|
||||||
|
print(f" NOTE: {len(undistributed)} core extras from {len(emus)} emulators "
|
||||||
|
f"not in split packs (missing systems: field in profiles: "
|
||||||
|
f"{', '.join(emus[:5])}{'...' if len(emus) > 5 else ''})")
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def generate_md5_pack(
|
||||||
|
hashes: list[tuple[str, str]],
|
||||||
|
db: dict,
|
||||||
|
bios_dir: str,
|
||||||
|
output_dir: str,
|
||||||
|
zip_contents: dict | None = None,
|
||||||
|
platform_name: str | None = None,
|
||||||
|
platforms_dir: str | None = None,
|
||||||
|
emulator_name: str | None = None,
|
||||||
|
emulators_dir: str | None = None,
|
||||||
|
standalone: bool = False,
|
||||||
|
) -> str | None:
|
||||||
|
"""Build a pack from an explicit list of hashes with layout context."""
|
||||||
|
files_db = db.get("files", {})
|
||||||
|
by_md5 = db.get("indexes", {}).get("by_md5", {})
|
||||||
|
by_crc32 = db.get("indexes", {}).get("by_crc32", {})
|
||||||
|
if zip_contents is None:
|
||||||
|
zip_contents = {}
|
||||||
|
|
||||||
|
plat_file_index: dict[str, dict] = {}
|
||||||
|
base_dest = ""
|
||||||
|
plat_display = "Custom"
|
||||||
|
if platform_name and platforms_dir:
|
||||||
|
config = load_platform_config(platform_name, platforms_dir)
|
||||||
|
base_dest = config.get("base_destination", "")
|
||||||
|
plat_display = config.get("platform", platform_name)
|
||||||
|
for _sys_id, system in config.get("systems", {}).items():
|
||||||
|
for fe in system.get("files", []):
|
||||||
|
plat_file_index[fe.get("name", "").lower()] = fe
|
||||||
|
|
||||||
|
emu_pack_structure = None
|
||||||
|
emu_display = ""
|
||||||
|
if emulator_name and emulators_dir:
|
||||||
|
profiles = load_emulator_profiles(emulators_dir, skip_aliases=False)
|
||||||
|
if emulator_name in profiles:
|
||||||
|
profile = profiles[emulator_name]
|
||||||
|
emu_display = profile.get("emulator", emulator_name)
|
||||||
|
emu_pack_structure = profile.get("pack_structure")
|
||||||
|
for fe in profile.get("files", []):
|
||||||
|
plat_file_index[fe.get("name", "").lower()] = fe
|
||||||
|
for alias in fe.get("aliases", []):
|
||||||
|
plat_file_index[alias.lower()] = fe
|
||||||
|
|
||||||
|
context_name = plat_display if platform_name else (emu_display or "Custom")
|
||||||
|
zip_name = f"{context_name.replace(' ', '_')}_Custom_BIOS_Pack.zip"
|
||||||
|
zip_path = os.path.join(output_dir, zip_name)
|
||||||
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
|
|
||||||
|
packed: list[tuple[str, str]] = []
|
||||||
|
not_in_repo: list[tuple[str, str]] = []
|
||||||
|
not_in_db: list[str] = []
|
||||||
|
seen: set[str] = set()
|
||||||
|
|
||||||
|
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
|
||||||
|
for hash_type, hash_val in hashes:
|
||||||
|
sha1 = None
|
||||||
|
if hash_type == "sha1" and hash_val in files_db:
|
||||||
|
sha1 = hash_val
|
||||||
|
elif hash_type == "md5":
|
||||||
|
sha1 = by_md5.get(hash_val)
|
||||||
|
elif hash_type == "crc32":
|
||||||
|
sha1 = by_crc32.get(hash_val)
|
||||||
|
|
||||||
|
if not sha1 or sha1 not in files_db:
|
||||||
|
not_in_db.append(hash_val)
|
||||||
|
continue
|
||||||
|
|
||||||
|
entry = files_db[sha1]
|
||||||
|
name = entry.get("name", "")
|
||||||
|
aliases = entry.get("aliases") or []
|
||||||
|
paths = entry.get("paths") or []
|
||||||
|
|
||||||
|
dest = name
|
||||||
|
matched_fe = None
|
||||||
|
for lookup_name in [name] + aliases:
|
||||||
|
if lookup_name.lower() in plat_file_index:
|
||||||
|
matched_fe = plat_file_index[lookup_name.lower()]
|
||||||
|
break
|
||||||
|
|
||||||
|
if matched_fe:
|
||||||
|
if emulator_name and emu_pack_structure is not None:
|
||||||
|
dest = _resolve_destination(matched_fe, emu_pack_structure, standalone)
|
||||||
|
else:
|
||||||
|
dest = matched_fe.get("destination", matched_fe.get("name", name))
|
||||||
|
elif paths:
|
||||||
|
dest = paths[0]
|
||||||
|
|
||||||
|
if base_dest and not dest.startswith(base_dest):
|
||||||
|
full_dest = f"{base_dest}/{dest}"
|
||||||
|
else:
|
||||||
|
full_dest = dest
|
||||||
|
|
||||||
|
if full_dest in seen:
|
||||||
|
continue
|
||||||
|
seen.add(full_dest)
|
||||||
|
|
||||||
|
fe_for_resolve = {"name": name, "sha1": sha1, "md5": entry.get("md5", "")}
|
||||||
|
local_path, status = resolve_file(fe_for_resolve, db, bios_dir, zip_contents)
|
||||||
|
|
||||||
|
if status == "not_found" or not local_path:
|
||||||
|
not_in_repo.append((name, hash_val))
|
||||||
|
continue
|
||||||
|
|
||||||
|
zf.write(local_path, full_dest)
|
||||||
|
packed.append((name, hash_val))
|
||||||
|
|
||||||
|
total = len(hashes)
|
||||||
|
print(f"\nPacked {len(packed)}/{total} requested files")
|
||||||
|
for name, h in packed:
|
||||||
|
print(f" PACKED: {name} ({h[:16]}...)")
|
||||||
|
for name, h in not_in_repo:
|
||||||
|
print(f" NOT IN REPO: {name} ({h[:16]}...)")
|
||||||
|
for h in not_in_db:
|
||||||
|
print(f" NOT IN DB: {h}")
|
||||||
|
|
||||||
|
if not packed:
|
||||||
|
if os.path.exists(zip_path):
|
||||||
|
os.unlink(zip_path)
|
||||||
|
return None
|
||||||
|
return zip_path
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(description="Generate platform BIOS ZIP packs")
|
parser = argparse.ArgumentParser(description="Generate platform BIOS ZIP packs")
|
||||||
parser.add_argument("--platform", "-p", help="Platform name (e.g., retroarch)")
|
parser.add_argument("--platform", "-p", help="Platform name (e.g., retroarch)")
|
||||||
@@ -874,8 +1267,19 @@ def main():
|
|||||||
parser.add_argument("--refresh-data", action="store_true",
|
parser.add_argument("--refresh-data", action="store_true",
|
||||||
help="Force re-download all data directories")
|
help="Force re-download all data directories")
|
||||||
parser.add_argument("--list", action="store_true", help="List available platforms")
|
parser.add_argument("--list", action="store_true", help="List available platforms")
|
||||||
|
parser.add_argument("--required-only", action="store_true",
|
||||||
|
help="Only include required files, skip optional")
|
||||||
|
parser.add_argument("--split", action="store_true",
|
||||||
|
help="Generate one ZIP per system/manufacturer")
|
||||||
|
parser.add_argument("--group-by", choices=["system", "manufacturer"],
|
||||||
|
default="system",
|
||||||
|
help="Grouping for --split (default: system)")
|
||||||
parser.add_argument("--target", "-t", help="Hardware target (e.g., switch, rpi4)")
|
parser.add_argument("--target", "-t", help="Hardware target (e.g., switch, rpi4)")
|
||||||
parser.add_argument("--list-targets", action="store_true", help="List available targets for the platform")
|
parser.add_argument("--list-targets", action="store_true", help="List available targets for the platform")
|
||||||
|
parser.add_argument("--from-md5",
|
||||||
|
help="Hash(es) to look up or pack (comma-separated)")
|
||||||
|
parser.add_argument("--from-md5-file",
|
||||||
|
help="File with hashes (one per line)")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if args.list:
|
if args.list:
|
||||||
@@ -887,7 +1291,10 @@ def main():
|
|||||||
list_emulator_profiles(args.emulators_dir)
|
list_emulator_profiles(args.emulators_dir)
|
||||||
return
|
return
|
||||||
if args.list_systems:
|
if args.list_systems:
|
||||||
list_system_ids(args.emulators_dir)
|
if args.platform:
|
||||||
|
list_platform_system_ids(args.platform, args.platforms_dir)
|
||||||
|
else:
|
||||||
|
list_system_ids(args.emulators_dir)
|
||||||
return
|
return
|
||||||
if args.list_targets:
|
if args.list_targets:
|
||||||
if not args.platform:
|
if not args.platform:
|
||||||
@@ -902,18 +1309,68 @@ def main():
|
|||||||
print(f" {t['name']:30s} {t['architecture']:10s} {t['core_count']:>4d} cores{aliases}")
|
print(f" {t['name']:30s} {t['architecture']:10s} {t['core_count']:>4d} cores{aliases}")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Mutual exclusion
|
# Mode validation
|
||||||
modes = sum(1 for x in (args.platform, args.all, args.emulator, args.system) if x)
|
has_platform = bool(args.platform)
|
||||||
if modes == 0:
|
has_all = args.all
|
||||||
parser.error("Specify --platform, --all, --emulator, or --system")
|
has_emulator = bool(args.emulator)
|
||||||
if modes > 1:
|
has_system = bool(args.system)
|
||||||
parser.error("--platform, --all, --emulator, and --system are mutually exclusive")
|
has_from_md5 = bool(args.from_md5 or getattr(args, 'from_md5_file', None))
|
||||||
if args.standalone and not (args.emulator or args.system):
|
|
||||||
parser.error("--standalone requires --emulator or --system")
|
if args.from_md5 and getattr(args, 'from_md5_file', None):
|
||||||
if args.target and not (args.platform or args.all):
|
parser.error("--from-md5 and --from-md5-file are mutually exclusive")
|
||||||
|
if has_from_md5 and has_all:
|
||||||
|
parser.error("--from-md5 requires --platform or --emulator, not --all")
|
||||||
|
if has_from_md5 and has_system:
|
||||||
|
parser.error("--from-md5 and --system are mutually exclusive")
|
||||||
|
if has_from_md5 and args.split:
|
||||||
|
parser.error("--split and --from-md5 are mutually exclusive")
|
||||||
|
|
||||||
|
# --platform/--all and --system can combine (system filters within platform)
|
||||||
|
# --emulator is exclusive with everything else
|
||||||
|
if has_emulator and (has_platform or has_all or has_system):
|
||||||
|
parser.error("--emulator is mutually exclusive with --platform, --all, and --system")
|
||||||
|
if has_platform and has_all:
|
||||||
|
parser.error("--platform and --all are mutually exclusive")
|
||||||
|
if not (has_platform or has_all or has_emulator or has_system or has_from_md5):
|
||||||
|
parser.error("Specify --platform, --all, --emulator, --system, or --from-md5")
|
||||||
|
if args.standalone and not (has_emulator or (has_system and not has_platform and not has_all)):
|
||||||
|
parser.error("--standalone requires --emulator or --system (without --platform)")
|
||||||
|
if args.split and not (has_platform or has_all):
|
||||||
|
parser.error("--split requires --platform or --all")
|
||||||
|
if args.split and has_emulator:
|
||||||
|
parser.error("--split is incompatible with --emulator")
|
||||||
|
if args.group_by != "system" and not args.split:
|
||||||
|
parser.error("--group-by requires --split")
|
||||||
|
if args.target and not (has_platform or has_all):
|
||||||
parser.error("--target requires --platform or --all")
|
parser.error("--target requires --platform or --all")
|
||||||
if args.target and (args.emulator or args.system):
|
if args.target and has_emulator:
|
||||||
parser.error("--target is incompatible with --emulator and --system")
|
parser.error("--target is incompatible with --emulator")
|
||||||
|
|
||||||
|
# Hash lookup / pack mode
|
||||||
|
if has_from_md5:
|
||||||
|
if args.from_md5:
|
||||||
|
hashes = parse_hash_input(args.from_md5)
|
||||||
|
else:
|
||||||
|
hashes = parse_hash_file(args.from_md5_file)
|
||||||
|
if not hashes:
|
||||||
|
print("No valid hashes found in input", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
db = load_database(args.db)
|
||||||
|
if not has_platform and not has_emulator:
|
||||||
|
lookup_hashes(hashes, db, args.bios_dir, args.emulators_dir,
|
||||||
|
args.platforms_dir)
|
||||||
|
return
|
||||||
|
zip_contents = build_zip_contents_index(db)
|
||||||
|
result = generate_md5_pack(
|
||||||
|
hashes=hashes, db=db, bios_dir=args.bios_dir,
|
||||||
|
output_dir=args.output_dir, zip_contents=zip_contents,
|
||||||
|
platform_name=args.platform, platforms_dir=args.platforms_dir,
|
||||||
|
emulator_name=args.emulator, emulators_dir=args.emulators_dir,
|
||||||
|
standalone=getattr(args, "standalone", False),
|
||||||
|
)
|
||||||
|
if not result:
|
||||||
|
sys.exit(1)
|
||||||
|
return
|
||||||
|
|
||||||
db = load_database(args.db)
|
db = load_database(args.db)
|
||||||
zip_contents = build_zip_contents_index(db)
|
zip_contents = build_zip_contents_index(db)
|
||||||
@@ -923,23 +1380,27 @@ def main():
|
|||||||
names = [n.strip() for n in args.emulator.split(",") if n.strip()]
|
names = [n.strip() for n in args.emulator.split(",") if n.strip()]
|
||||||
result = generate_emulator_pack(
|
result = generate_emulator_pack(
|
||||||
names, args.emulators_dir, db, args.bios_dir, args.output_dir,
|
names, args.emulators_dir, db, args.bios_dir, args.output_dir,
|
||||||
args.standalone, zip_contents,
|
args.standalone, zip_contents, required_only=args.required_only,
|
||||||
)
|
)
|
||||||
if not result:
|
if not result:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
return
|
return
|
||||||
|
|
||||||
# System mode
|
# System mode (standalone, without platform context)
|
||||||
if args.system:
|
if has_system and not has_platform and not has_all:
|
||||||
system_ids = [s.strip() for s in args.system.split(",") if s.strip()]
|
system_ids = [s.strip() for s in args.system.split(",") if s.strip()]
|
||||||
result = generate_system_pack(
|
result = generate_system_pack(
|
||||||
system_ids, args.emulators_dir, db, args.bios_dir, args.output_dir,
|
system_ids, args.emulators_dir, db, args.bios_dir, args.output_dir,
|
||||||
args.standalone, zip_contents,
|
args.standalone, zip_contents, required_only=args.required_only,
|
||||||
)
|
)
|
||||||
if not result:
|
if not result:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
system_filter = None
|
||||||
|
if args.system:
|
||||||
|
system_filter = [s.strip() for s in args.system.split(",") if s.strip()]
|
||||||
|
|
||||||
# Platform mode (existing)
|
# Platform mode (existing)
|
||||||
if args.all:
|
if args.all:
|
||||||
platforms = list_registered_platforms(
|
platforms = list_registered_platforms(
|
||||||
@@ -998,13 +1459,25 @@ def main():
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
tc = target_cores_cache.get(representative) if args.target else None
|
tc = target_cores_cache.get(representative) if args.target else None
|
||||||
zip_path = generate_pack(
|
if args.split:
|
||||||
representative, args.platforms_dir, db, args.bios_dir, args.output_dir,
|
zip_paths = generate_split_packs(
|
||||||
include_extras=args.include_extras, emulators_dir=args.emulators_dir,
|
representative, args.platforms_dir, db, args.bios_dir,
|
||||||
zip_contents=zip_contents, data_registry=data_registry,
|
args.output_dir, group_by=args.group_by,
|
||||||
emu_profiles=emu_profiles, target_cores=tc,
|
emulators_dir=args.emulators_dir, zip_contents=zip_contents,
|
||||||
)
|
data_registry=data_registry, emu_profiles=emu_profiles,
|
||||||
if zip_path and variants:
|
target_cores=tc, required_only=args.required_only,
|
||||||
|
)
|
||||||
|
print(f" Split into {len(zip_paths)} packs")
|
||||||
|
else:
|
||||||
|
zip_path = generate_pack(
|
||||||
|
representative, args.platforms_dir, db, args.bios_dir, args.output_dir,
|
||||||
|
include_extras=args.include_extras, emulators_dir=args.emulators_dir,
|
||||||
|
zip_contents=zip_contents, data_registry=data_registry,
|
||||||
|
emu_profiles=emu_profiles, target_cores=tc,
|
||||||
|
required_only=args.required_only,
|
||||||
|
system_filter=system_filter,
|
||||||
|
)
|
||||||
|
if not args.split and zip_path and variants:
|
||||||
rep_cfg = load_platform_config(representative, args.platforms_dir)
|
rep_cfg = load_platform_config(representative, args.platforms_dir)
|
||||||
ver = rep_cfg.get("version", rep_cfg.get("dat_version", ""))
|
ver = rep_cfg.get("version", rep_cfg.get("dat_version", ""))
|
||||||
ver_tag = f"_{ver.replace(' ', '')}" if ver else ""
|
ver_tag = f"_{ver.replace(' ', '')}" if ver else ""
|
||||||
@@ -1020,7 +1493,17 @@ def main():
|
|||||||
# Post-generation: verify all packs + inject manifests + SHA256SUMS
|
# Post-generation: verify all packs + inject manifests + SHA256SUMS
|
||||||
if not args.list_emulators and not args.list_systems:
|
if not args.list_emulators and not args.list_systems:
|
||||||
print("\nVerifying packs and generating manifests...")
|
print("\nVerifying packs and generating manifests...")
|
||||||
all_ok = verify_and_finalize_packs(args.output_dir, db)
|
# Skip platform conformance for filtered/split/custom packs
|
||||||
|
skip_conf = bool(system_filter or args.split)
|
||||||
|
all_ok = verify_and_finalize_packs(args.output_dir, db,
|
||||||
|
skip_conformance=skip_conf)
|
||||||
|
# Also verify split subdirectories
|
||||||
|
if args.split:
|
||||||
|
for entry in os.listdir(args.output_dir):
|
||||||
|
sub = os.path.join(args.output_dir, entry)
|
||||||
|
if os.path.isdir(sub) and entry.endswith("_Split"):
|
||||||
|
ok = verify_and_finalize_packs(sub, db, skip_conformance=True)
|
||||||
|
all_ok = all_ok and ok
|
||||||
if not all_ok:
|
if not all_ok:
|
||||||
print("WARNING: some packs have verification errors")
|
print("WARNING: some packs have verification errors")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@@ -1267,7 +1750,8 @@ def verify_pack_against_platform(
|
|||||||
|
|
||||||
|
|
||||||
def verify_and_finalize_packs(output_dir: str, db: dict,
|
def verify_and_finalize_packs(output_dir: str, db: dict,
|
||||||
platforms_dir: str = "platforms") -> bool:
|
platforms_dir: str = "platforms",
|
||||||
|
skip_conformance: bool = False) -> bool:
|
||||||
"""Verify all packs, inject manifests, generate SHA256SUMS.
|
"""Verify all packs, inject manifests, generate SHA256SUMS.
|
||||||
|
|
||||||
Two-stage verification:
|
Two-stage verification:
|
||||||
@@ -1307,6 +1791,9 @@ def verify_and_finalize_packs(output_dir: str, db: dict,
|
|||||||
inject_manifest(zip_path, manifest)
|
inject_manifest(zip_path, manifest)
|
||||||
|
|
||||||
# Stage 2: platform conformance (extract + verify)
|
# Stage 2: platform conformance (extract + verify)
|
||||||
|
# Skipped for filtered/split/custom packs (intentionally partial)
|
||||||
|
if skip_conformance:
|
||||||
|
continue
|
||||||
platforms = pack_to_platform.get(name, [])
|
platforms = pack_to_platform.get(name, [])
|
||||||
for pname in platforms:
|
for pname in platforms:
|
||||||
(p_ok, total, matched, p_errors,
|
(p_ok, total, matched, p_errors,
|
||||||
|
|||||||
@@ -1604,10 +1604,22 @@ def generate_wiki_tools() -> str:
|
|||||||
"Build platform-specific BIOS ZIP packs.",
|
"Build platform-specific BIOS ZIP packs.",
|
||||||
"",
|
"",
|
||||||
"```bash",
|
"```bash",
|
||||||
|
"# Full platform packs",
|
||||||
"python scripts/generate_pack.py --all --output-dir dist/",
|
"python scripts/generate_pack.py --all --output-dir dist/",
|
||||||
"python scripts/generate_pack.py --platform batocera",
|
"python scripts/generate_pack.py --platform batocera",
|
||||||
"python scripts/generate_pack.py --emulator dolphin",
|
"python scripts/generate_pack.py --emulator dolphin",
|
||||||
"python scripts/generate_pack.py --system atari-lynx",
|
"python scripts/generate_pack.py --system atari-lynx",
|
||||||
|
"",
|
||||||
|
"# Granular options",
|
||||||
|
"python scripts/generate_pack.py --platform retroarch --system sony-playstation",
|
||||||
|
"python scripts/generate_pack.py --platform batocera --required-only",
|
||||||
|
"python scripts/generate_pack.py --platform retroarch --split",
|
||||||
|
"python scripts/generate_pack.py --platform retroarch --split --group-by manufacturer",
|
||||||
|
"",
|
||||||
|
"# Hash-based lookup and custom packs",
|
||||||
|
"python scripts/generate_pack.py --from-md5 d8f1206299c48946e6ec5ef96d014eaa",
|
||||||
|
"python scripts/generate_pack.py --platform batocera --from-md5-file missing.txt",
|
||||||
|
"python scripts/generate_pack.py --platform retroarch --list-systems",
|
||||||
"```",
|
"```",
|
||||||
"",
|
"",
|
||||||
"Packs include platform baseline files plus files required by the platform's cores.",
|
"Packs include platform baseline files plus files required by the platform's cores.",
|
||||||
@@ -1615,6 +1627,15 @@ def generate_wiki_tools() -> str:
|
|||||||
"the tool searches for a variant that satisfies both.",
|
"the tool searches for a variant that satisfies both.",
|
||||||
"If none exists, the platform version is kept and the discrepancy is reported.",
|
"If none exists, the platform version is kept and the discrepancy is reported.",
|
||||||
"",
|
"",
|
||||||
|
"**Granular options:**",
|
||||||
|
"",
|
||||||
|
"- `--system` with `--platform`: filter to specific systems within a platform pack",
|
||||||
|
"- `--required-only`: exclude optional files, keep only required",
|
||||||
|
"- `--split`: generate one ZIP per system instead of one big pack",
|
||||||
|
"- `--split --group-by manufacturer`: group split packs by manufacturer (Sony, Nintendo, Sega...)",
|
||||||
|
"- `--from-md5`: look up a hash in the database, or build a custom pack with `--platform`/`--emulator`",
|
||||||
|
"- `--from-md5-file`: same, reading hashes from a file (one per line, comments with #)",
|
||||||
|
"",
|
||||||
"### cross_reference.py",
|
"### cross_reference.py",
|
||||||
"",
|
"",
|
||||||
"Compare emulator profiles against platform configs.",
|
"Compare emulator profiles against platform configs.",
|
||||||
|
|||||||
@@ -1539,6 +1539,522 @@ class TestE2E(unittest.TestCase):
|
|||||||
self.assertEqual(gt["total"], result["total_files"])
|
self.assertEqual(gt["total"], result["total_files"])
|
||||||
self.assertGreaterEqual(gt["with_validation"], 1)
|
self.assertGreaterEqual(gt["with_validation"], 1)
|
||||||
|
|
||||||
|
def test_130_required_only_excludes_optional(self):
|
||||||
|
"""--required-only excludes files with required: false from pack."""
|
||||||
|
from generate_pack import generate_pack
|
||||||
|
output_dir = os.path.join(self.root, "pack_reqonly")
|
||||||
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
|
# Create a platform with one required and one optional file
|
||||||
|
config = {
|
||||||
|
"platform": "ReqOnlyTest",
|
||||||
|
"verification_mode": "existence",
|
||||||
|
"base_destination": "system",
|
||||||
|
"systems": {
|
||||||
|
"test-sys": {
|
||||||
|
"files": [
|
||||||
|
{"name": "present_req.bin", "destination": "present_req.bin", "required": True},
|
||||||
|
{"name": "present_opt.bin", "destination": "present_opt.bin", "required": False},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
with open(os.path.join(self.platforms_dir, "test_reqonly.yml"), "w") as fh:
|
||||||
|
yaml.dump(config, fh)
|
||||||
|
zip_path = generate_pack(
|
||||||
|
"test_reqonly", self.platforms_dir, self.db, self.bios_dir, output_dir,
|
||||||
|
required_only=True,
|
||||||
|
)
|
||||||
|
self.assertIsNotNone(zip_path)
|
||||||
|
with zipfile.ZipFile(zip_path) as zf:
|
||||||
|
names = zf.namelist()
|
||||||
|
self.assertTrue(any("present_req.bin" in n for n in names))
|
||||||
|
self.assertFalse(any("present_opt.bin" in n for n in names))
|
||||||
|
# Verify _Required tag in filename
|
||||||
|
self.assertIn("_Required_", os.path.basename(zip_path))
|
||||||
|
|
||||||
|
def test_131_required_only_keeps_default_required(self):
|
||||||
|
"""--required-only keeps files with no required field (default = required)."""
|
||||||
|
from generate_pack import generate_pack
|
||||||
|
output_dir = os.path.join(self.root, "pack_reqdef")
|
||||||
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
|
# File with no required field
|
||||||
|
config = {
|
||||||
|
"platform": "ReqDefTest",
|
||||||
|
"verification_mode": "existence",
|
||||||
|
"base_destination": "system",
|
||||||
|
"systems": {
|
||||||
|
"test-sys": {
|
||||||
|
"files": [
|
||||||
|
{"name": "present_req.bin", "destination": "present_req.bin"},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
with open(os.path.join(self.platforms_dir, "test_reqdef.yml"), "w") as fh:
|
||||||
|
yaml.dump(config, fh)
|
||||||
|
zip_path = generate_pack(
|
||||||
|
"test_reqdef", self.platforms_dir, self.db, self.bios_dir, output_dir,
|
||||||
|
required_only=True,
|
||||||
|
)
|
||||||
|
self.assertIsNotNone(zip_path)
|
||||||
|
with zipfile.ZipFile(zip_path) as zf:
|
||||||
|
names = zf.namelist()
|
||||||
|
self.assertTrue(any("present_req.bin" in n for n in names))
|
||||||
|
|
||||||
|
|
||||||
|
def test_132_platform_system_filter(self):
|
||||||
|
"""--platform + --system filters systems within a platform pack."""
|
||||||
|
from generate_pack import generate_pack
|
||||||
|
output_dir = os.path.join(self.root, "pack_sysfilter")
|
||||||
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
|
config = {
|
||||||
|
"platform": "SysFilterTest",
|
||||||
|
"verification_mode": "existence",
|
||||||
|
"base_destination": "system",
|
||||||
|
"systems": {
|
||||||
|
"system-a": {
|
||||||
|
"files": [
|
||||||
|
{"name": "present_req.bin", "destination": "present_req.bin"},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"system-b": {
|
||||||
|
"files": [
|
||||||
|
{"name": "present_opt.bin", "destination": "present_opt.bin"},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
with open(os.path.join(self.platforms_dir, "test_sysfilter.yml"), "w") as fh:
|
||||||
|
yaml.dump(config, fh)
|
||||||
|
zip_path = generate_pack(
|
||||||
|
"test_sysfilter", self.platforms_dir, self.db, self.bios_dir, output_dir,
|
||||||
|
system_filter=["system-a"],
|
||||||
|
)
|
||||||
|
self.assertIsNotNone(zip_path)
|
||||||
|
with zipfile.ZipFile(zip_path) as zf:
|
||||||
|
names = zf.namelist()
|
||||||
|
self.assertTrue(any("present_req.bin" in n for n in names))
|
||||||
|
self.assertFalse(any("present_opt.bin" in n for n in names))
|
||||||
|
|
||||||
|
def test_133_platform_system_filter_normalized(self):
|
||||||
|
"""_norm_system_id normalization matches with manufacturer prefix."""
|
||||||
|
from common import _norm_system_id
|
||||||
|
self.assertEqual(
|
||||||
|
_norm_system_id("sony-playstation"),
|
||||||
|
_norm_system_id("playstation"),
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_134_list_systems_platform_context(self):
|
||||||
|
"""list_platform_system_ids lists systems from a platform YAML."""
|
||||||
|
from common import list_platform_system_ids
|
||||||
|
import io
|
||||||
|
config = {
|
||||||
|
"platform": "ListSysTest",
|
||||||
|
"verification_mode": "existence",
|
||||||
|
"systems": {
|
||||||
|
"alpha-sys": {
|
||||||
|
"files": [
|
||||||
|
{"name": "a.bin", "destination": "a.bin"},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"beta-sys": {
|
||||||
|
"files": [
|
||||||
|
{"name": "b1.bin", "destination": "b1.bin"},
|
||||||
|
{"name": "b2.bin", "destination": "b2.bin"},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
with open(os.path.join(self.platforms_dir, "test_listsys.yml"), "w") as fh:
|
||||||
|
yaml.dump(config, fh)
|
||||||
|
captured = io.StringIO()
|
||||||
|
old_stdout = sys.stdout
|
||||||
|
sys.stdout = captured
|
||||||
|
try:
|
||||||
|
list_platform_system_ids("test_listsys", self.platforms_dir)
|
||||||
|
finally:
|
||||||
|
sys.stdout = old_stdout
|
||||||
|
output = captured.getvalue()
|
||||||
|
self.assertIn("alpha-sys", output)
|
||||||
|
self.assertIn("beta-sys", output)
|
||||||
|
self.assertIn("1 file", output)
|
||||||
|
self.assertIn("2 files", output)
|
||||||
|
|
||||||
|
|
||||||
|
def test_135_split_by_system(self):
|
||||||
|
"""--split generates one ZIP per system in a subdirectory."""
|
||||||
|
import tempfile
|
||||||
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
|
plat_dir = os.path.join(tmpdir, "platforms")
|
||||||
|
os.makedirs(plat_dir)
|
||||||
|
bios_dir = os.path.join(tmpdir, "bios", "Test")
|
||||||
|
os.makedirs(os.path.join(bios_dir, "SysA"))
|
||||||
|
os.makedirs(os.path.join(bios_dir, "SysB"))
|
||||||
|
emu_dir = os.path.join(tmpdir, "emulators")
|
||||||
|
os.makedirs(emu_dir)
|
||||||
|
out_dir = os.path.join(tmpdir, "dist")
|
||||||
|
|
||||||
|
file_a = os.path.join(bios_dir, "SysA", "bios_a.bin")
|
||||||
|
file_b = os.path.join(bios_dir, "SysB", "bios_b.bin")
|
||||||
|
with open(file_a, "wb") as f:
|
||||||
|
f.write(b"system_a")
|
||||||
|
with open(file_b, "wb") as f:
|
||||||
|
f.write(b"system_b")
|
||||||
|
|
||||||
|
from common import compute_hashes
|
||||||
|
ha = compute_hashes(file_a)
|
||||||
|
hb = compute_hashes(file_b)
|
||||||
|
|
||||||
|
db = {
|
||||||
|
"files": {
|
||||||
|
ha["sha1"]: {"name": "bios_a.bin", "md5": ha["md5"],
|
||||||
|
"sha1": ha["sha1"], "sha256": ha["sha256"],
|
||||||
|
"path": file_a,
|
||||||
|
"paths": [file_a]},
|
||||||
|
hb["sha1"]: {"name": "bios_b.bin", "md5": hb["md5"],
|
||||||
|
"sha1": hb["sha1"], "sha256": hb["sha256"],
|
||||||
|
"path": file_b,
|
||||||
|
"paths": [file_b]},
|
||||||
|
},
|
||||||
|
"indexes": {
|
||||||
|
"by_md5": {ha["md5"]: ha["sha1"], hb["md5"]: hb["sha1"]},
|
||||||
|
"by_name": {"bios_a.bin": [ha["sha1"]], "bios_b.bin": [hb["sha1"]]},
|
||||||
|
"by_crc32": {}, "by_path_suffix": {},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
registry = {"platforms": {"splitplat": {"status": "active"}}}
|
||||||
|
with open(os.path.join(plat_dir, "_registry.yml"), "w") as f:
|
||||||
|
yaml.dump(registry, f)
|
||||||
|
plat_cfg = {
|
||||||
|
"platform": "SplitTest",
|
||||||
|
"verification_mode": "existence",
|
||||||
|
"systems": {
|
||||||
|
"test-system-a": {"files": [{"name": "bios_a.bin", "sha1": ha["sha1"]}]},
|
||||||
|
"test-system-b": {"files": [{"name": "bios_b.bin", "sha1": hb["sha1"]}]},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
with open(os.path.join(plat_dir, "splitplat.yml"), "w") as f:
|
||||||
|
yaml.dump(plat_cfg, f)
|
||||||
|
|
||||||
|
from generate_pack import generate_split_packs
|
||||||
|
from common import build_zip_contents_index, load_emulator_profiles
|
||||||
|
zip_contents = build_zip_contents_index(db)
|
||||||
|
emu_profiles = load_emulator_profiles(emu_dir)
|
||||||
|
|
||||||
|
zip_paths = generate_split_packs(
|
||||||
|
"splitplat", plat_dir, db, os.path.join(tmpdir, "bios"), out_dir,
|
||||||
|
emulators_dir=emu_dir, zip_contents=zip_contents,
|
||||||
|
emu_profiles=emu_profiles, group_by="system",
|
||||||
|
)
|
||||||
|
self.assertEqual(len(zip_paths), 2)
|
||||||
|
|
||||||
|
# Check subdirectory exists
|
||||||
|
split_dir = os.path.join(out_dir, "SplitTest_Split")
|
||||||
|
self.assertTrue(os.path.isdir(split_dir))
|
||||||
|
|
||||||
|
# Verify each ZIP contains only its system's files
|
||||||
|
for zp in zip_paths:
|
||||||
|
with zipfile.ZipFile(zp) as zf:
|
||||||
|
names = zf.namelist()
|
||||||
|
basename = os.path.basename(zp)
|
||||||
|
if "System_A" in basename:
|
||||||
|
self.assertIn("bios_a.bin", names)
|
||||||
|
self.assertNotIn("bios_b.bin", names)
|
||||||
|
elif "System_B" in basename:
|
||||||
|
self.assertIn("bios_b.bin", names)
|
||||||
|
self.assertNotIn("bios_a.bin", names)
|
||||||
|
|
||||||
|
|
||||||
|
def test_136_derive_manufacturer(self):
|
||||||
|
"""derive_manufacturer extracts manufacturer correctly."""
|
||||||
|
from common import derive_manufacturer
|
||||||
|
# From system ID prefix
|
||||||
|
self.assertEqual(derive_manufacturer("sony-playstation", {}), "Sony")
|
||||||
|
self.assertEqual(derive_manufacturer("nintendo-snes", {}), "Nintendo")
|
||||||
|
self.assertEqual(derive_manufacturer("sega-saturn", {}), "Sega")
|
||||||
|
self.assertEqual(derive_manufacturer("atari-5200", {}), "Atari")
|
||||||
|
# From explicit manufacturer field
|
||||||
|
self.assertEqual(
|
||||||
|
derive_manufacturer("3do", {"manufacturer": "Panasonic|GoldStar"}),
|
||||||
|
"Panasonic",
|
||||||
|
)
|
||||||
|
# Various = skip to prefix check, then Other
|
||||||
|
self.assertEqual(derive_manufacturer("arcade", {"manufacturer": "Various"}), "Other")
|
||||||
|
# Fallback
|
||||||
|
self.assertEqual(derive_manufacturer("dos", {}), "Other")
|
||||||
|
|
||||||
|
def test_137_group_systems_by_manufacturer(self):
|
||||||
|
"""_group_systems_by_manufacturer groups correctly."""
|
||||||
|
from generate_pack import _group_systems_by_manufacturer
|
||||||
|
systems = {
|
||||||
|
"sony-playstation": {"files": [{"name": "a.bin"}]},
|
||||||
|
"sony-psp": {"files": [{"name": "b.bin"}]},
|
||||||
|
"nintendo-snes": {"files": [{"name": "c.bin"}]},
|
||||||
|
"arcade": {"manufacturer": "Various", "files": [{"name": "d.bin"}]},
|
||||||
|
}
|
||||||
|
groups = _group_systems_by_manufacturer(systems, {}, "")
|
||||||
|
self.assertIn("Sony", groups)
|
||||||
|
self.assertEqual(sorted(groups["Sony"]), ["sony-playstation", "sony-psp"])
|
||||||
|
self.assertIn("Nintendo", groups)
|
||||||
|
self.assertEqual(groups["Nintendo"], ["nintendo-snes"])
|
||||||
|
self.assertIn("Other", groups)
|
||||||
|
self.assertEqual(groups["Other"], ["arcade"])
|
||||||
|
|
||||||
|
|
||||||
|
def test_138_parse_hash_input(self):
|
||||||
|
"""parse_hash_input handles various formats."""
|
||||||
|
from generate_pack import parse_hash_input
|
||||||
|
# Plain MD5
|
||||||
|
result = parse_hash_input("d8f1206299c48946e6ec5ef96d014eaa")
|
||||||
|
self.assertEqual(result, [("md5", "d8f1206299c48946e6ec5ef96d014eaa")])
|
||||||
|
# Comma-separated
|
||||||
|
result = parse_hash_input("d8f1206299c48946e6ec5ef96d014eaa,d8f1206299c48946e6ec5ef96d014eab")
|
||||||
|
self.assertEqual(len(result), 2)
|
||||||
|
# SHA1
|
||||||
|
sha1 = "a" * 40
|
||||||
|
result = parse_hash_input(sha1)
|
||||||
|
self.assertEqual(result, [("sha1", sha1)])
|
||||||
|
# CRC32
|
||||||
|
result = parse_hash_input("abcd1234")
|
||||||
|
self.assertEqual(result, [("crc32", "abcd1234")])
|
||||||
|
|
||||||
|
def test_139_parse_hash_file(self):
|
||||||
|
"""parse_hash_file handles comments, empty lines, various formats."""
|
||||||
|
from generate_pack import parse_hash_file
|
||||||
|
with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False) as f:
|
||||||
|
f.write("# PS1 BIOS files\n")
|
||||||
|
f.write("\n")
|
||||||
|
f.write("d8f1206299c48946e6ec5ef96d014eaa\n")
|
||||||
|
f.write("d8f1206299c48946e6ec5ef96d014eab scph5501.bin\n")
|
||||||
|
f.write("scph5502.bin d8f1206299c48946e6ec5ef96d014eac OK\n")
|
||||||
|
tmp_path = f.name
|
||||||
|
try:
|
||||||
|
result = parse_hash_file(tmp_path)
|
||||||
|
self.assertEqual(len(result), 3)
|
||||||
|
self.assertTrue(all(t == "md5" for t, _ in result))
|
||||||
|
finally:
|
||||||
|
os.unlink(tmp_path)
|
||||||
|
|
||||||
|
def test_140_lookup_hashes_found(self):
|
||||||
|
"""lookup_hashes returns file info for known hashes."""
|
||||||
|
import io
|
||||||
|
import contextlib
|
||||||
|
from generate_pack import lookup_hashes
|
||||||
|
db = {
|
||||||
|
"files": {
|
||||||
|
"sha1abc": {
|
||||||
|
"name": "test.bin", "md5": "md5abc",
|
||||||
|
"sha1": "sha1abc", "sha256": "sha256abc",
|
||||||
|
"paths": ["Mfr/Console/test.bin"],
|
||||||
|
"aliases": ["alt.bin"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"indexes": {
|
||||||
|
"by_md5": {"md5abc": "sha1abc"},
|
||||||
|
"by_crc32": {},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
buf = io.StringIO()
|
||||||
|
with contextlib.redirect_stdout(buf):
|
||||||
|
lookup_hashes([("md5", "md5abc")], db, "bios", "emulators", "platforms")
|
||||||
|
output = buf.getvalue()
|
||||||
|
self.assertIn("test.bin", output)
|
||||||
|
self.assertIn("sha1abc", output)
|
||||||
|
self.assertIn("alt.bin", output)
|
||||||
|
|
||||||
|
def test_141_lookup_hashes_not_found(self):
|
||||||
|
"""lookup_hashes reports unknown hashes."""
|
||||||
|
import io
|
||||||
|
import contextlib
|
||||||
|
from generate_pack import lookup_hashes
|
||||||
|
db = {"files": {}, "indexes": {"by_md5": {}, "by_crc32": {}}}
|
||||||
|
buf = io.StringIO()
|
||||||
|
with contextlib.redirect_stdout(buf):
|
||||||
|
lookup_hashes([("md5", "unknown123" + "0" * 22)], db, "bios", "emulators", "platforms")
|
||||||
|
output = buf.getvalue()
|
||||||
|
self.assertIn("NOT FOUND", output)
|
||||||
|
|
||||||
|
|
||||||
|
def test_142_from_md5_platform_pack(self):
|
||||||
|
"""--from-md5 with --platform generates correctly laid out ZIP."""
|
||||||
|
import tempfile
|
||||||
|
import json
|
||||||
|
import zipfile
|
||||||
|
import yaml
|
||||||
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
|
plat_dir = os.path.join(tmpdir, "platforms")
|
||||||
|
os.makedirs(plat_dir)
|
||||||
|
bios_dir = os.path.join(tmpdir, "bios", "Sony", "PS1")
|
||||||
|
os.makedirs(bios_dir)
|
||||||
|
emu_dir = os.path.join(tmpdir, "emulators")
|
||||||
|
os.makedirs(emu_dir)
|
||||||
|
out_dir = os.path.join(tmpdir, "dist")
|
||||||
|
|
||||||
|
bios_file = os.path.join(bios_dir, "scph5501.bin")
|
||||||
|
with open(bios_file, "wb") as f:
|
||||||
|
f.write(b"ps1_bios_content")
|
||||||
|
from common import compute_hashes
|
||||||
|
h = compute_hashes(bios_file)
|
||||||
|
|
||||||
|
db = {
|
||||||
|
"files": {
|
||||||
|
h["sha1"]: {
|
||||||
|
"name": "scph5501.bin", "md5": h["md5"],
|
||||||
|
"sha1": h["sha1"], "sha256": h["sha256"],
|
||||||
|
"path": bios_file,
|
||||||
|
"paths": ["Sony/PS1/scph5501.bin"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"indexes": {
|
||||||
|
"by_md5": {h["md5"]: h["sha1"]},
|
||||||
|
"by_name": {"scph5501.bin": [h["sha1"]]},
|
||||||
|
"by_crc32": {}, "by_path_suffix": {},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
registry = {"platforms": {"testplat": {"status": "active"}}}
|
||||||
|
with open(os.path.join(plat_dir, "_registry.yml"), "w") as f:
|
||||||
|
yaml.dump(registry, f)
|
||||||
|
plat_cfg = {
|
||||||
|
"platform": "TestPlat",
|
||||||
|
"verification_mode": "md5",
|
||||||
|
"base_destination": "bios",
|
||||||
|
"systems": {
|
||||||
|
"sony-playstation": {
|
||||||
|
"files": [
|
||||||
|
{"name": "scph5501.bin", "md5": h["md5"],
|
||||||
|
"destination": "scph5501.bin"},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
with open(os.path.join(plat_dir, "testplat.yml"), "w") as f:
|
||||||
|
yaml.dump(plat_cfg, f)
|
||||||
|
|
||||||
|
from generate_pack import generate_md5_pack
|
||||||
|
from common import build_zip_contents_index
|
||||||
|
zip_contents = build_zip_contents_index(db)
|
||||||
|
|
||||||
|
zip_path = generate_md5_pack(
|
||||||
|
hashes=[("md5", h["md5"])],
|
||||||
|
db=db, bios_dir=bios_dir, output_dir=out_dir,
|
||||||
|
zip_contents=zip_contents,
|
||||||
|
platform_name="testplat", platforms_dir=plat_dir,
|
||||||
|
)
|
||||||
|
self.assertIsNotNone(zip_path)
|
||||||
|
with zipfile.ZipFile(zip_path) as zf:
|
||||||
|
names = zf.namelist()
|
||||||
|
self.assertIn("bios/scph5501.bin", names)
|
||||||
|
self.assertIn("Custom", os.path.basename(zip_path))
|
||||||
|
|
||||||
|
def test_143_from_md5_not_in_repo(self):
|
||||||
|
"""--from-md5 reports files in DB but missing from repo."""
|
||||||
|
import tempfile
|
||||||
|
import io
|
||||||
|
import contextlib
|
||||||
|
from generate_pack import generate_md5_pack
|
||||||
|
|
||||||
|
db = {
|
||||||
|
"files": {
|
||||||
|
"sha1known": {
|
||||||
|
"name": "missing.bin", "md5": "md5known" + "0" * 25,
|
||||||
|
"sha1": "sha1known", "sha256": "sha256known",
|
||||||
|
"path": "/nonexistent/missing.bin",
|
||||||
|
"paths": ["Test/missing.bin"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"indexes": {
|
||||||
|
"by_md5": {"md5known" + "0" * 25: "sha1known"},
|
||||||
|
"by_crc32": {},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
|
out_dir = os.path.join(tmpdir, "dist")
|
||||||
|
bios_dir = os.path.join(tmpdir, "bios")
|
||||||
|
os.makedirs(bios_dir)
|
||||||
|
buf = io.StringIO()
|
||||||
|
with contextlib.redirect_stdout(buf):
|
||||||
|
result = generate_md5_pack(
|
||||||
|
hashes=[("md5", "md5known" + "0" * 25)],
|
||||||
|
db=db, bios_dir=bios_dir, output_dir=out_dir,
|
||||||
|
zip_contents={},
|
||||||
|
)
|
||||||
|
output = buf.getvalue()
|
||||||
|
self.assertIn("NOT IN REPO", output)
|
||||||
|
self.assertIsNone(result)
|
||||||
|
|
||||||
|
|
||||||
|
def test_144_invalid_split_emulator(self):
|
||||||
|
"""--split + --emulator is rejected."""
|
||||||
|
import subprocess
|
||||||
|
result = subprocess.run(
|
||||||
|
["python", "scripts/generate_pack.py", "--emulator", "test", "--split"],
|
||||||
|
capture_output=True, text=True,
|
||||||
|
)
|
||||||
|
self.assertNotEqual(result.returncode, 0)
|
||||||
|
self.assertIn("error", result.stderr.lower())
|
||||||
|
|
||||||
|
def test_145_invalid_from_md5_all(self):
|
||||||
|
"""--from-md5 + --all is rejected."""
|
||||||
|
import subprocess
|
||||||
|
result = subprocess.run(
|
||||||
|
["python", "scripts/generate_pack.py", "--all", "--from-md5", "abc123" + "0" * 26],
|
||||||
|
capture_output=True, text=True,
|
||||||
|
)
|
||||||
|
self.assertNotEqual(result.returncode, 0)
|
||||||
|
|
||||||
|
def test_146_invalid_from_md5_system(self):
|
||||||
|
"""--from-md5 + --system is rejected."""
|
||||||
|
import subprocess
|
||||||
|
result = subprocess.run(
|
||||||
|
["python", "scripts/generate_pack.py", "--system", "psx", "--from-md5", "abc123" + "0" * 26],
|
||||||
|
capture_output=True, text=True,
|
||||||
|
)
|
||||||
|
self.assertNotEqual(result.returncode, 0)
|
||||||
|
|
||||||
|
def test_147_invalid_group_by_without_split(self):
|
||||||
|
"""--group-by without --split is rejected."""
|
||||||
|
import subprocess
|
||||||
|
result = subprocess.run(
|
||||||
|
["python", "scripts/generate_pack.py", "--platform", "retroarch", "--group-by", "manufacturer"],
|
||||||
|
capture_output=True, text=True,
|
||||||
|
)
|
||||||
|
self.assertNotEqual(result.returncode, 0)
|
||||||
|
|
||||||
|
def test_148_valid_platform_system(self):
|
||||||
|
"""--platform + --system is accepted (not rejected at validation stage)."""
|
||||||
|
import argparse
|
||||||
|
sys.path.insert(0, "scripts")
|
||||||
|
# Build the same parser as generate_pack.main()
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("--platform", "-p")
|
||||||
|
parser.add_argument("--all", action="store_true")
|
||||||
|
parser.add_argument("--emulator", "-e")
|
||||||
|
parser.add_argument("--system", "-s")
|
||||||
|
parser.add_argument("--standalone", action="store_true")
|
||||||
|
parser.add_argument("--split", action="store_true")
|
||||||
|
parser.add_argument("--group-by", choices=["system", "manufacturer"], default="system")
|
||||||
|
parser.add_argument("--target", "-t")
|
||||||
|
parser.add_argument("--from-md5")
|
||||||
|
parser.add_argument("--from-md5-file")
|
||||||
|
parser.add_argument("--required-only", action="store_true")
|
||||||
|
args = parser.parse_args(["--platform", "retroarch", "--system", "psx"])
|
||||||
|
|
||||||
|
# Replicate validation logic from main()
|
||||||
|
has_platform = bool(args.platform)
|
||||||
|
has_all = args.all
|
||||||
|
has_emulator = bool(args.emulator)
|
||||||
|
has_system = bool(args.system)
|
||||||
|
has_from_md5 = bool(args.from_md5 or args.from_md5_file)
|
||||||
|
|
||||||
|
# These should NOT raise
|
||||||
|
self.assertFalse(has_emulator and (has_platform or has_all or has_system))
|
||||||
|
self.assertFalse(has_platform and has_all)
|
||||||
|
self.assertTrue(has_platform or has_all or has_emulator or has_system or has_from_md5)
|
||||||
|
# --platform + --system is a valid combination
|
||||||
|
self.assertTrue(has_platform and has_system)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
@@ -51,10 +51,22 @@ Verification modes per platform:
|
|||||||
Build platform-specific BIOS ZIP packs.
|
Build platform-specific BIOS ZIP packs.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# Full platform packs
|
||||||
python scripts/generate_pack.py --all --output-dir dist/
|
python scripts/generate_pack.py --all --output-dir dist/
|
||||||
python scripts/generate_pack.py --platform batocera
|
python scripts/generate_pack.py --platform batocera
|
||||||
python scripts/generate_pack.py --emulator dolphin
|
python scripts/generate_pack.py --emulator dolphin
|
||||||
python scripts/generate_pack.py --system atari-lynx
|
python scripts/generate_pack.py --system atari-lynx
|
||||||
|
|
||||||
|
# Granular options
|
||||||
|
python scripts/generate_pack.py --platform retroarch --system sony-playstation
|
||||||
|
python scripts/generate_pack.py --platform batocera --required-only
|
||||||
|
python scripts/generate_pack.py --platform retroarch --split
|
||||||
|
python scripts/generate_pack.py --platform retroarch --split --group-by manufacturer
|
||||||
|
|
||||||
|
# Hash-based lookup and custom packs
|
||||||
|
python scripts/generate_pack.py --from-md5 d8f1206299c48946e6ec5ef96d014eaa
|
||||||
|
python scripts/generate_pack.py --platform batocera --from-md5-file missing.txt
|
||||||
|
python scripts/generate_pack.py --platform retroarch --list-systems
|
||||||
```
|
```
|
||||||
|
|
||||||
Packs include platform baseline files plus files required by the platform's cores.
|
Packs include platform baseline files plus files required by the platform's cores.
|
||||||
@@ -62,6 +74,15 @@ When a file passes platform verification but fails emulator validation,
|
|||||||
the tool searches for a variant that satisfies both.
|
the tool searches for a variant that satisfies both.
|
||||||
If none exists, the platform version is kept and the discrepancy is reported.
|
If none exists, the platform version is kept and the discrepancy is reported.
|
||||||
|
|
||||||
|
**Granular options:**
|
||||||
|
|
||||||
|
- `--system` with `--platform`: filter to specific systems within a platform pack
|
||||||
|
- `--required-only`: exclude optional files, keep only required
|
||||||
|
- `--split`: generate one ZIP per system instead of one big pack
|
||||||
|
- `--split --group-by manufacturer`: group split packs by manufacturer (Sony, Nintendo, Sega...)
|
||||||
|
- `--from-md5`: look up a hash in the database, or build a custom pack with `--platform`/`--emulator`
|
||||||
|
- `--from-md5-file`: same, reading hashes from a file (one per line, comments with #)
|
||||||
|
|
||||||
### cross_reference.py
|
### cross_reference.py
|
||||||
|
|
||||||
Compare emulator profiles against platform configs.
|
Compare emulator profiles against platform configs.
|
||||||
|
|||||||
Reference in New Issue
Block a user