Playing around with reading gameboy roms, and maybe emulation
1use crate::NINTENDO_LOGO;
2use crate::enums::{
3 CGBFlag, CartridgeHeaderAddress, CartridgeType, DestinationCode, Error, RamSize, RomSize,
4};
5
6pub struct CartridgeHeader {
7 //Should be 80 bytes (0x014F(335) - 0x0100(256)) + 1 to include the last address
8 pub _buffer: [u8; 80],
9 pub title: [char; 16],
10 pub manufacturer_code: [char; 4],
11 pub cgb_flag: CGBFlag,
12 // https://gbdev.io/pandocs/The_Cartridge_Header.html#01440145--new-licensee-code
13 pub license_code: [char; 2],
14 pub support_gb: bool,
15 pub cart_type: CartridgeType,
16 pub rom_size: RomSize,
17 pub ram_size: RamSize,
18 // If None uses new license code which is just license_code
19 // https://gbdev.io/pandocs/The_Cartridge_Header.html#014b--old-licensee-code
20 pub old_licensee_code: Option<u8>,
21 pub destination_code: DestinationCode,
22 pub version: u8,
23 pub header_checksum: u8,
24 pub global_checksum: u16,
25}
26
27impl CartridgeHeader {
28 fn bytes_to_chars<const N: usize>(bytes: &[u8], break_on_null: bool) -> [char; N] {
29 let mut chars = [0x000 as char; N];
30 for (i, byte) in bytes.iter().enumerate() {
31 if break_on_null && *byte == 0x00 {
32 break;
33 }
34 chars[i] = *byte as char;
35 }
36 chars
37 }
38
39 pub fn parse(rom_bytes: &[u8]) -> Result<Self, Error> {
40 let start = CartridgeHeaderAddress::CartridgeHeaderStart as usize;
41 let end = start + 80;
42 let header_buffer: &[u8] = rom_bytes[start..end]
43 .try_into()
44 .map_err(|_| Error::CartridgeReadError)?;
45
46 //Checks if the Nintendo logo matches the device's version. Early anti piracy feature. Neat to take note of
47 let nintendo_logo_from_rom = &rom_bytes[CartridgeHeaderAddress::NintendoLogoStart as usize
48 ..CartridgeHeaderAddress::NintendoLogoEnd as usize + 1];
49 for (i, true_logo_byte) in NINTENDO_LOGO.iter().enumerate() {
50 let rom_byte = nintendo_logo_from_rom[i];
51 if rom_byte != *true_logo_byte {
52 return Err(Error::NotAValidRom);
53 }
54 }
55
56 let title = &rom_bytes[CartridgeHeaderAddress::TitleStart as usize
57 ..CartridgeHeaderAddress::TitleEnd as usize + 1];
58 let title_chars = Self::bytes_to_chars::<16>(title, true);
59
60 let manufacturer_code = &rom_bytes[CartridgeHeaderAddress::ManufacturerCodeStart as usize
61 ..CartridgeHeaderAddress::ManufacturerCodeEnd as usize + 1];
62 let man_code_chars = Self::bytes_to_chars::<4>(manufacturer_code, false);
63
64 let cgb_flag_byte = rom_bytes[0x0143];
65 let cgb_flag = match cgb_flag_byte {
66 0x80 => CGBFlag::SupportsColorBackwardCompatiable,
67 0xC0 => CGBFlag::ColorOnly,
68 _ => CGBFlag::NotSet,
69 };
70
71 let license_code = &rom_bytes[CartridgeHeaderAddress::NewLicenseStart as usize
72 ..CartridgeHeaderAddress::NewLicenseEnd as usize + 1];
73 let license_code_chars = Self::bytes_to_chars::<2>(license_code, false);
74
75 // https://gbdev.io/pandocs/The_Cartridge_Header.html#0146--sgb-flag
76 let sgb_flag = rom_bytes[CartridgeHeaderAddress::SgbFlag as usize] == 0x03;
77
78 let cart_type =
79 CartridgeType::from_byte(rom_bytes[CartridgeHeaderAddress::CartType as usize]);
80
81 let rom_size = RomSize::from_byte(rom_bytes[CartridgeHeaderAddress::RomSize as usize]);
82 let ram_size = RamSize::from_byte(rom_bytes[CartridgeHeaderAddress::RamSize as usize]);
83
84 let mut old_licensee_code = None;
85 if !sgb_flag {
86 old_licensee_code = Some(rom_bytes[CartridgeHeaderAddress::OldLicenseeCode as usize])
87 }
88
89 let destination_code =
90 DestinationCode::from_byte(rom_bytes[CartridgeHeaderAddress::DestinationCode as usize]);
91
92 let version = rom_bytes[CartridgeHeaderAddress::MaskRomVersion as usize];
93 let header_checksum = rom_bytes[CartridgeHeaderAddress::HeaderChecksum as usize];
94 let global_checksum = u16::from_le_bytes([
95 rom_bytes[CartridgeHeaderAddress::GlobalChecksumStart as usize],
96 rom_bytes[CartridgeHeaderAddress::CartridgeHeaderEnd as usize],
97 ]);
98
99 Ok(Self {
100 _buffer: header_buffer
101 .try_into()
102 .map_err(|_| Error::CartridgeReadError)?,
103 title: title_chars,
104 manufacturer_code: man_code_chars,
105 cgb_flag,
106 license_code: license_code_chars,
107 support_gb: sgb_flag,
108 cart_type,
109 rom_size,
110 ram_size,
111 old_licensee_code,
112 destination_code,
113 version,
114 header_checksum,
115 global_checksum,
116 })
117 }
118}