1use log::error;
2
3use crate::backup::Backup;
4
5#[derive(Default, Clone)]
6pub struct Rom {
7 pub data: Vec<u8>,
8 pub title: Vec<u8>,
9 pub game_code: [u8; 4],
10 pub maker_code: [u8; 2],
11 pub main_unit_code: u8,
12 pub device_type: u8,
13 pub rom_version: u8,
14}
15
16#[derive(thiserror::Error, Debug)]
17pub enum RomError {
18 #[error("Invalid magic number: 0x{0:02X}, expected: 0x96")]
19 InvalidMagic(u8),
20}
21
22impl Rom {
23 pub fn from_bytes(data: &[u8]) -> Result<Self, RomError> {
24 let header = &data[0xA0..0xC0];
25
26 let title = header[..0xC].to_vec();
27 let game_code = header[0xC..0x10].try_into().unwrap();
28 let maker_code = header[0x10..0x12].try_into().unwrap();
29
30 let magic = header[0x12];
31 if magic != 0x96 {
32 Err(RomError::InvalidMagic(magic))?
33 }
34
35 let main_unit_code = header[0x13];
36 let device_type = header[0x14];
37
38 let rom_version = header[0x1C];
39 let complement_check = header[0x1D];
40
41 for i in (0x15..=0x1B).chain(0x1E..=0x1F) {
42 if header[i] != 0 {
43 error!("Non-zero value in reserved area: {:02X}", header[i]);
44 }
45 }
46
47 let sum = header[..=0x1C]
48 .iter()
49 .fold(0_u8, |a, b| a.wrapping_add(*b))
50 .wrapping_add(0x19);
51
52 if sum.wrapping_add(complement_check) != 0 {
53 error!("Invalid complement check: {sum:02X} + {complement_check:02X} != 0");
54 }
55
56 let ret = Rom {
57 data: data.to_vec(),
58 title,
59 game_code,
60 maker_code,
61 main_unit_code,
62 device_type,
63 rom_version,
64 };
65
66 Ok(ret)
67 }
68
69 pub fn backup_type(&self) -> &'static str {
70 Backup::detect_backup(&self.data, None).backup_type()
71 }
72}