1use std::collections::HashMap;
2use std::fmt::{Display, Formatter};
3use std::str::FromStr;
4use crc::{Crc, CRC_32_ISO_HDLC};
5use log::debug;
6
7pub const CRC: Crc<u32> = Crc::<u32>::new(&CRC_32_ISO_HDLC);
8
9pub mod sixtyfourdrive;
10
11lazy_static! {
12 pub static ref ROMDB: HashMap<String, SaveType> = {
13 #[derive(Clone, Debug, PartialEq, Default)]
14 pub struct DbEntry {
15 pub md5: String,
16 pub savetype: SaveType
17 }
18
19 let mut entries = HashMap::new();
20
21 let mut lines = include_str!("romdb.ini").lines();
22 let mut entry = None;
23 while let Some(line) = lines.next() {
24 if line.starts_with("[") {
25 entry = Some(DbEntry::default());
26
27 let mut tmp = entry.unwrap();
28 tmp.md5 = line.trim_matches('[').trim_matches(']').to_string();
29
30 entry = Some(tmp);
31 } else if line.starts_with("SaveType=") {
32 let mut tmp = entry.unwrap();
33 tmp.savetype = match line.split_once('=').unwrap().1 {
34 "None" => SaveType::Nothing,
35 "SRAM" => SaveType::Sram256Kbit,
36 "Eeprom 4KB" => SaveType::Eeprom4Kbit,
37 "Eeprom 16KB" => SaveType::Eeprom16Kbit,
38 "Flash RAM" => SaveType::FlashRam1Mbit,
39 _ => SaveType::Unknown
40 };
41
42 entry = Some(tmp);
43 } else if line.starts_with("GoodName=") {
44 let mut tmp = entry.unwrap();
45 let goodname = line.split_once('=').unwrap().1;
46 if goodname.contains("Dezaemon 3D") {
47 tmp.savetype = SaveType::Sram768Kbit;
48 } else if goodname.contains("Pokemon Stadium 2") {
49 tmp.savetype = SaveType::FlashRam1MbitStadium;
50 }
51
52 entry = Some(tmp);
53 } else if line.is_empty() && entry.is_some() {
54 let tmp = entry.unwrap();
55 entries.insert(tmp.md5, tmp.savetype);
56
57 entry = None;
58 }
59 }
60
61 entries
62 };
63}
64
65
66
67#[derive(Copy, Clone, Debug, PartialEq)]
68pub enum Cic {
69 Auto,
70 Var6101,
71 Var6102,
72 Var7101,
73 Var7102,
74 VarX103,
75 VarX105,
76 VarX106,
77 Var5101,
78 Unknown,
79}
80impl Display for Cic {
81 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
82 use Cic::*;
83
84 write!(f, "{}", match self {
85 Auto => "auto",
86 Var6101 => "6101",
87 Var6102 => "6102",
88 Var7101 => "7101",
89 Var7102 => "7102",
90 VarX103 => "x103",
91 VarX105 => "x105",
92 VarX106 => "x106",
93 Var5101 => "5101",
94 Unknown => "unknown",
95 })
96 }
97}
98impl FromStr for Cic {
99 type Err = String;
100
101 fn from_str(s: &str) -> Result<Self, Self::Err> {
102 use Cic::*;
103
104 Ok(match s.to_lowercase().as_str() {
105 "auto" => Auto,
106 "6101" => Var6101,
107 "6102" => Var6102,
108 "7101" => Var7101,
109 "7102" => Var7102,
110 "x103" => VarX103,
111 "x105" => VarX105,
112 "x106" => VarX106,
113 "5101" => Var5101,
114
115 _ => return Err("Accepted values: auto, 6101, 6102, 7101, 7102, x103, x105, x106, or 5101".into())
116 })
117 }
118}
119
120impl Cic {
121 pub fn from_rom(data: &[u8]) -> Cic {
126 if data.len() < 0x1000 { return Cic::Unknown }
127
128 Self::from_ipl3(&data[0x40..0x1000])
129 }
130
131 pub fn from_ipl3(data: &[u8]) -> Cic {
135 use Cic::*;
136
137 let sum = CRC.checksum(data);
138 debug!("Calculated IPL3 CRC: {:#010X}", sum);
139 match sum {
140 0x6170A4A1 => Var6101,
141 0x90BB6CB5 => Var6102,
142 0x009E9EA3 => Var7102,
143 0x0B050EE0 => VarX103,
144 0x98BC2C86 => VarX105,
145 0xACC8580A => VarX106,
146 _ => Unknown
147 }
148 }
149}
150
151
152#[derive(Copy, Clone, Debug, PartialEq)]
153pub enum SaveType {
154 Auto,
155 Nothing,
156 Eeprom4Kbit,
157 Eeprom16Kbit,
158 Sram256Kbit,
159 FlashRam1Mbit,
160 Sram768Kbit,
161 FlashRam1MbitStadium,
162 Unknown,
163}
164impl Default for SaveType {
165 fn default() -> Self {
166 SaveType::Nothing
167 }
168}
169impl FromStr for SaveType {
170 type Err = String;
171
172 fn from_str(s: &str) -> Result<Self, Self::Err> {
173 use SaveType::*;
174
175 Ok(match s.to_lowercase().as_str() {
176 "auto" => Auto,
177 "eeprom4kbit" => Eeprom4Kbit,
178 "eeprom16kbit" => Eeprom16Kbit,
179 "sram256kbit" => Sram256Kbit,
180 "flashram1mbit" => FlashRam1Mbit,
181 "sram768kbit" => Sram768Kbit,
182 "pokestadium2" => FlashRam1MbitStadium,
183 "none" | "nothing" => Nothing,
184
185 _ => return Err("Accepted values: auto, eeprom4kbit, eeprom16kbit, sram256kbit, flashram1mbit, sram768kbit, pokestadium2, or none".into())
186 })
187 }
188}
189impl SaveType {
190 pub fn from_rom(data: &[u8]) -> SaveType {
191 let hash = md5::compute(data).0;
192 let mut hash_str = String::new();
193 for byte in hash {
194 hash_str.push_str(&format!("{:02X}", byte));
195 }
196 debug!("Calculated ROM Hash: {}", hash_str);
197
198 match ROMDB.get(&hash_str) {
199 Some(savetype) => *savetype,
200 None => SaveType::Unknown,
201 }
202 }
203}