tgba/
rom.rs

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}