diff --git a/bindings/rustboyadvance-jni/src/rom_helper.rs b/bindings/rustboyadvance-jni/src/rom_helper.rs index 317737f..cf7671f 100644 --- a/bindings/rustboyadvance-jni/src/rom_helper.rs +++ b/bindings/rustboyadvance-jni/src/rom_helper.rs @@ -6,7 +6,7 @@ use rustboyadvance_core::cartridge; fn parse_rom_header(env: &JNIEnv, barr: jbyteArray) -> cartridge::header::CartridgeHeader { let rom_data = env.convert_byte_array(barr).unwrap(); - cartridge::header::parse(&rom_data) + cartridge::header::parse(&rom_data).unwrap() } #[no_mangle] diff --git a/core/src/cartridge/builder.rs b/core/src/cartridge/builder.rs index 9cb67c3..addaa24 100644 --- a/core/src/cartridge/builder.rs +++ b/core/src/cartridge/builder.rs @@ -120,7 +120,7 @@ impl GamepakBuilder { )) }?; - let header = header::parse(&bytes); + let header = header::parse(&bytes)?; info!("Loaded ROM: {:?}", header); if !self.create_backup_file { diff --git a/core/src/cartridge/header.rs b/core/src/cartridge/header.rs index 42ea696..3b781ee 100644 --- a/core/src/cartridge/header.rs +++ b/core/src/cartridge/header.rs @@ -1,6 +1,8 @@ use serde::{Deserialize, Serialize}; use std::str::from_utf8; +use super::super::{GBAError, GBAResult}; + /// From GBATEK /// /// The first 192 bytes at 8000000h-80000BFh in ROM are used as cartridge header. The same header is also used for Multiboot images at 2000000h-20000BFh (plus some additional multiboot entries at 20000C0h and up). @@ -38,22 +40,49 @@ pub struct CartridgeHeader { // joybus_entry_point: Addr, } -pub fn parse(bytes: &[u8]) -> CartridgeHeader { +fn calculate_checksum(bytes: &[u8]) -> u8 { + bytes + .iter() + .cloned() + .fold(0u8, u8::wrapping_sub) + .wrapping_sub(0x19) +} + +pub fn parse(bytes: &[u8]) -> GBAResult { + if bytes.len() < 0xc0 { + return Err(GBAError::CartridgeLoadError( + "incomplete cartridge header".to_string(), + )); + } + + let checksum = bytes[0xbd]; + if calculate_checksum(&bytes[0xa0..=0xbc]) != checksum { + return Err(GBAError::CartridgeLoadError( + "invalid header checksum".to_string(), + )); + } + + let game_title = from_utf8(&bytes[0xa0..0xac]) + .map_err(|_| GBAError::CartridgeLoadError("invalid game title".to_string()))?; + + let game_code = from_utf8(&bytes[0xac..0xb0]) + .map_err(|_| GBAError::CartridgeLoadError("invalid game code".to_string()))?; + + let maker_code = from_utf8(&bytes[0xb0..0xb2]) + .map_err(|_| GBAError::CartridgeLoadError("invalid marker code".to_string()))?; + // let (_, rom_entry_point) = le_u32(bytes).unwrap(); - let game_title = from_utf8(&bytes[0xa0..0xac]).unwrap(); - let game_code = from_utf8(&bytes[0xac..0xb0]).unwrap(); - let maker_code = from_utf8(&bytes[0xb0..0xb2]).unwrap(); // let (_, ram_entry_point) = le_u32(&bytes[0xc0..]).unwrap(); // let (_, joybus_entry_point) = le_u32(&bytes[0xc0..]).unwrap(); - CartridgeHeader { + Ok(CartridgeHeader { // rom_entry_point: rom_entry_point, game_title: String::from(game_title), game_code: String::from(game_code), maker_code: String::from(maker_code), software_version: bytes[0xbc], - checksum: bytes[0xbd], + checksum: checksum, // ram_entry_point: ram_entry_point, // joybus_entry_point: joybus_entry_point, - } + }) } diff --git a/platform/rustboyadvance-wasm/src/lib.rs b/platform/rustboyadvance-wasm/src/lib.rs index 5f4d616..b0cd769 100644 --- a/platform/rustboyadvance-wasm/src/lib.rs +++ b/platform/rustboyadvance-wasm/src/lib.rs @@ -55,5 +55,5 @@ impl From for RomInfo { #[wasm_bindgen] pub fn parse_rom_header(rom_bin: &[u8]) -> RomInfo { - cartridge::header::parse(rom_bin).into() + cartridge::header::parse(rom_bin).unwrap().into() }