1//! Detection for games that are known to use EEPROM chips, which require knowing how the EEPROM
2//! chip is mapped into the cartridge's address space
3//!
4//! List of games and metadata from this thread:
5//! <https://gendev.spritesmind.net/forum/viewtopic.php?f=25&t=206>
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum EepromType {
9    X24C01,
10    X24C02,
11    X24C08,
12    X24C16,
13    X24C64,
14}
15
16#[derive(Debug, Clone)]
17pub struct EepromMetadata {
18    pub eeprom_type: EepromType,
19    pub sda_in_addr: u32,
20    pub sda_in_bit: u8,
21    pub sda_out_addr: u32,
22    pub sda_out_bit: u8,
23    pub scl_addr: u32,
24    pub scl_bit: u8,
25}
26
27// Values from https://gendev.spritesmind.net/forum/viewtopic.php?f=25&t=206
28
29const NBA_JAM_METADATA: EepromMetadata = EepromMetadata {
30    eeprom_type: EepromType::X24C02,
31    sda_in_addr: 0x200000,
32    sda_in_bit: 0,
33    sda_out_addr: 0x200000,
34    sda_out_bit: 1,
35    scl_addr: 0x200000,
36    scl_bit: 1,
37};
38
39const ACCLAIM_24C02_METADATA: EepromMetadata = EepromMetadata {
40    eeprom_type: EepromType::X24C02,
41    sda_in_addr: 0x200001,
42    sda_in_bit: 0,
43    sda_out_addr: 0x200001,
44    sda_out_bit: 0,
45    scl_addr: 0x200000,
46    scl_bit: 0,
47};
48
49const ACCLAIM_24C16_METADATA: EepromMetadata = EepromMetadata {
50    eeprom_type: EepromType::X24C16,
51    sda_in_addr: 0x200001,
52    sda_in_bit: 0,
53    sda_out_addr: 0x200001,
54    sda_out_bit: 0,
55    scl_addr: 0x200000,
56    scl_bit: 0,
57};
58
59const ACCLAIM_24C64_METADATA: EepromMetadata = EepromMetadata {
60    eeprom_type: EepromType::X24C64,
61    sda_in_addr: 0x200001,
62    sda_in_bit: 0,
63    sda_out_addr: 0x200001,
64    sda_out_bit: 0,
65    scl_addr: 0x200000,
66    scl_bit: 0,
67};
68
69const SEGA_CAPCOM_METADATA: EepromMetadata = EepromMetadata {
70    eeprom_type: EepromType::X24C01,
71    sda_in_addr: 0x200001,
72    sda_in_bit: 0,
73    sda_out_addr: 0x200001,
74    sda_out_bit: 0,
75    scl_addr: 0x200001,
76    scl_bit: 1,
77};
78
79const EA_METADATA: EepromMetadata = EepromMetadata {
80    eeprom_type: EepromType::X24C01,
81    sda_in_addr: 0x200000,
82    sda_in_bit: 7,
83    sda_out_addr: 0x200000,
84    sda_out_bit: 7,
85    scl_addr: 0x200000,
86    scl_bit: 6,
87};
88
89const CODEMASTERS_24C08_METADATA: EepromMetadata = EepromMetadata {
90    eeprom_type: EepromType::X24C08,
91    sda_in_addr: 0x300000,
92    sda_in_bit: 0,
93    sda_out_addr: 0x380001,
94    sda_out_bit: 7,
95    scl_addr: 0x300000,
96    scl_bit: 1,
97};
98
99const CODEMASTERS_24C16_METADATA: EepromMetadata = EepromMetadata {
100    eeprom_type: EepromType::X24C16,
101    sda_in_addr: 0x300000,
102    sda_in_bit: 0,
103    sda_out_addr: 0x380001,
104    sda_out_bit: 7,
105    scl_addr: 0x300000,
106    scl_bit: 1,
107};
108
109// Mostly from https://gendev.spritesmind.net/forum/viewtopic.php?p=2485#p2485
110#[rustfmt::skip]
111const SERIAL_NUMBER_TO_METADATA: &[(&[u8], EepromMetadata)] = &[
112    (b"G-4060  ", SEGA_CAPCOM_METADATA),   // Wonder Boy in Monster World (U/E)
113    (b"PR-1993 ", SEGA_CAPCOM_METADATA),   // Wonder Boy V: Monster World III (J)
114    (b"T-12046 ", SEGA_CAPCOM_METADATA),   // Mega Man: The Wily Wars (E)
115    (b"T-12053 ", SEGA_CAPCOM_METADATA),   // Rockman Mega World (J)
116    (b"MK-1215 ", SEGA_CAPCOM_METADATA),   // Evander Holyfield's "Real Deal" Boxing (World)
117    (b"MK-1228 ", SEGA_CAPCOM_METADATA),   // Greatest Heavyweights (U/E)
118    (b"G-5538  ", SEGA_CAPCOM_METADATA),   // Greatest Heavyweights (J)
119    (b"T-081326", NBA_JAM_METADATA),       // NBA Jam (U/E)
120    (b"T-81033 ", NBA_JAM_METADATA),       // NBA Jam (J)
121    (b"T-81406 ", ACCLAIM_24C02_METADATA), // NBA Jam Tournament Edition (World)
122    (b"T-8104B ", ACCLAIM_24C02_METADATA), // NBA Jam Tournament Edition (32X) (World)
123    (b"T-081276", ACCLAIM_24C02_METADATA), // NFL Quarterback Club (World)
124    (b"T-8102B ", ACCLAIM_24C02_METADATA), // NFL Quarterback Club (32X) (World)
125    (b"T-081586", ACCLAIM_24C16_METADATA), // NFL Quarterback Club 96 (U/E)
126    (b"T-81576 ", ACCLAIM_24C64_METADATA), // College Slam (U)
127    (b"T-81476 ", ACCLAIM_24C64_METADATA), // Frank Thomas Big Hurt Baseball (U/E)
128    (b"T-50176 ", EA_METADATA),            // Rings of Power (U/E)
129    (b"T-50396 ", EA_METADATA),            // NHLPA Hockey 93 (U/E)
130];
131
132pub fn eeprom(rom: &[u8], checksum: u32) -> Option<EepromMetadata> {
133    let rom_serial_number = &rom[0x183..0x18B];
134    for (serial_number, metadata) in SERIAL_NUMBER_TO_METADATA {
135        if rom_serial_number == *serial_number {
136            return Some(metadata.clone());
137        }
138    }
139
140    // Micro Machines 2: Turbo Tournament (E)
141    if is_micro_machines_2(rom) {
142        return Some(CODEMASTERS_24C08_METADATA);
143    }
144
145    match checksum {
146        // Micro Machines: Military (E)
147        0xB3ABB15E => Some(CODEMASTERS_24C08_METADATA),
148        // Micro Machines: Turbo Tournament 96 (E)
149        0x23319D0D => Some(CODEMASTERS_24C16_METADATA),
150        // Honoo no Toukyuuji - Dodge Danpei (J)
151        0x630F07C6 => Some(SEGA_CAPCOM_METADATA),
152        _ => None,
153    }
154}
155
156pub fn is_micro_machines_2(rom: &[u8]) -> bool {
157    let extended_serial = &rom[0x183..0x18E];
158    extended_serial == b"T-120096-50"
159}