Playing around with reading gameboy roms, and maybe emulation
at main 4.8 kB view raw
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}