From 9eee5a03f2c568134b0fb1dc9c8c835db3ef929e Mon Sep 17 00:00:00 2001 From: Gwilym Inzani Date: Wed, 26 Jul 2023 10:18:28 +0100 Subject: [PATCH] Implement our own gba header --- agb-gbafix/Cargo.lock | 17 ---------- agb-gbafix/Cargo.toml | 2 -- agb-gbafix/src/lib.rs | 72 ++++++++++++++++++++++++++++++++++++------ agb-gbafix/src/main.rs | 8 ++--- 4 files changed, 66 insertions(+), 33 deletions(-) diff --git a/agb-gbafix/Cargo.lock b/agb-gbafix/Cargo.lock index 9d46ae05..262ea5d4 100644 --- a/agb-gbafix/Cargo.lock +++ b/agb-gbafix/Cargo.lock @@ -7,10 +7,8 @@ name = "agb-gbafix" version = "0.16.0" dependencies = [ "anyhow", - "bytemuck", "clap", "elf", - "gbafix", ] [[package]] @@ -74,12 +72,6 @@ version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" -[[package]] -name = "bytemuck" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" - [[package]] name = "cc" version = "1.0.79" @@ -146,15 +138,6 @@ dependencies = [ "libc", ] -[[package]] -name = "gbafix" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7e47af9d5377b8b0def53d9916f6c28521f2e5b97c5fec137797417c26ab4b7" -dependencies = [ - "bytemuck", -] - [[package]] name = "hermit-abi" version = "0.3.1" diff --git a/agb-gbafix/Cargo.toml b/agb-gbafix/Cargo.toml index 18b27db9..99eb7c96 100644 --- a/agb-gbafix/Cargo.toml +++ b/agb-gbafix/Cargo.toml @@ -9,8 +9,6 @@ repository = "https://github.com/agbrs/agb" [dependencies] elf = "0.7" -gbafix = "1" -bytemuck = "1" anyhow = "1" clap = "4" diff --git a/agb-gbafix/src/lib.rs b/agb-gbafix/src/lib.rs index 74546d7c..7d65b6d2 100644 --- a/agb-gbafix/src/lib.rs +++ b/agb-gbafix/src/lib.rs @@ -1,11 +1,66 @@ use anyhow::{anyhow, bail, ensure, Result}; use std::io::Write; -pub fn write_gba_file( - input: &[u8], - mut header: gbafix::GBAHeader, - output: &mut W, -) -> Result<()> { +const GBA_HEADER_SIZE: usize = 192; + +const NINTENDO_LOGO: &[u8] = &[ + 0x24, 0xFF, 0xAE, 0x51, 0x69, 0x9A, 0xA2, 0x21, 0x3D, 0x84, 0x82, 0x0A, 0x84, 0xE4, 0x09, 0xAD, + 0x11, 0x24, 0x8B, 0x98, 0xC0, 0x81, 0x7F, 0x21, 0xA3, 0x52, 0xBE, 0x19, 0x93, 0x09, 0xCE, 0x20, + 0x10, 0x46, 0x4A, 0x4A, 0xF8, 0x27, 0x31, 0xEC, 0x58, 0xC7, 0xE8, 0x33, 0x82, 0xE3, 0xCE, 0xBF, + 0x85, 0xF4, 0xDF, 0x94, 0xCE, 0x4B, 0x09, 0xC1, 0x94, 0x56, 0x8A, 0xC0, 0x13, 0x72, 0xA7, 0xFC, + 0x9F, 0x84, 0x4D, 0x73, 0xA3, 0xCA, 0x9A, 0x61, 0x58, 0x97, 0xA3, 0x27, 0xFC, 0x03, 0x98, 0x76, + 0x23, 0x1D, 0xC7, 0x61, 0x03, 0x04, 0xAE, 0x56, 0xBF, 0x38, 0x84, 0x00, 0x40, 0xA7, 0x0E, 0xFD, + 0xFF, 0x52, 0xFE, 0x03, 0x6F, 0x95, 0x30, 0xF1, 0x97, 0xFB, 0xC0, 0x85, 0x60, 0xD6, 0x80, 0x25, + 0xA9, 0x63, 0xBE, 0x03, 0x01, 0x4E, 0x38, 0xE2, 0xF9, 0xA2, 0x34, 0xFF, 0xBB, 0x3E, 0x03, 0x44, + 0x78, 0x00, 0x90, 0xCB, 0x88, 0x11, 0x3A, 0x94, 0x65, 0xC0, 0x7C, 0x63, 0x87, 0xF0, 0x3C, 0xAF, + 0xD6, 0x25, 0xE4, 0x8B, 0x38, 0x0A, 0xAC, 0x72, 0x21, 0xD4, 0xF8, 0x07, +]; + +#[derive(Debug, Default)] +pub struct GbaHeader { + pub start_code: [u8; 4], + pub game_title: [u8; 12], + pub game_code: [u8; 4], + pub maker_code: [u8; 2], + pub software_version: u8, +} + +impl GbaHeader { + fn produce_header(&self) -> Vec { + let mut header_result = vec![]; + + header_result.extend_from_slice(&self.start_code); + header_result.extend_from_slice(NINTENDO_LOGO); + header_result.extend_from_slice(&self.game_title); + header_result.extend_from_slice(&self.game_code); + header_result.extend_from_slice(&self.maker_code); + header_result.push(0x96); // must be 96 + header_result.push(0); // main unit code (0 for current GBA models) + header_result.push(0); // device type, usually 0 + header_result.extend_from_slice(&[0; 7]); // reserved area, should be zero filled + header_result.push(self.software_version); + + let checksum = Self::calculate_checksum(&header_result); + header_result.push(checksum); // checksum + header_result.extend_from_slice(&[0; 2]); // reserved area, should be zero filled + + assert_eq!(header_result.len(), GBA_HEADER_SIZE); + + header_result + } + + fn calculate_checksum(header: &[u8]) -> u8 { + let mut chk = 0u8; + for value in header.iter().take(0xBC).skip(0xA0) { + chk = chk.wrapping_sub(*value); + } + + chk = chk.wrapping_sub(0x19); + chk + } +} + +pub fn write_gba_file(input: &[u8], mut header: GbaHeader, output: &mut W) -> Result<()> { let elf_file = elf::ElfBytes::::minimal_parse(input)?; let section_headers = elf_file @@ -38,18 +93,15 @@ pub fn write_gba_file( } if bytes_written == 0 { - const GBA_HEADER_SIZE: usize = 192; - ensure!( data.len() > GBA_HEADER_SIZE, "first section must be at least as big as the gba header" ); header.start_code = data[0..4].try_into().unwrap(); - header.update_checksum(); - let header_bytes = bytemuck::bytes_of(&header); - output.write_all(header_bytes)?; + let header_bytes = header.produce_header(); + output.write_all(&header_bytes)?; data = &data[GBA_HEADER_SIZE..]; bytes_written += GBA_HEADER_SIZE as u64; diff --git a/agb-gbafix/src/main.rs b/agb-gbafix/src/main.rs index dc0ad093..94a7c697 100644 --- a/agb-gbafix/src/main.rs +++ b/agb-gbafix/src/main.rs @@ -7,7 +7,7 @@ use std::{ path::PathBuf, }; -use agb_gbafix::write_gba_file; +use agb_gbafix::{write_gba_file, GbaHeader}; fn main() -> Result<()> { let matches = clap::Command::new("agb-gbafix") @@ -27,7 +27,7 @@ fn main() -> Result<()> { None => input.with_extension("gba"), }; - let mut header = gbafix::GBAHeader::default(); + let mut header = GbaHeader::default(); { let title = if let Some(title) = matches.get_one::("title") { @@ -41,7 +41,7 @@ fn main() -> Result<()> { }; for (i, &c) in title.as_bytes().iter().enumerate().take(12) { - header.title[i] = c; + header.game_title[i] = c; } } @@ -61,7 +61,7 @@ fn main() -> Result<()> { } if let Some(game_version) = matches.get_one::("gameversion") { - header.version = *game_version; + header.software_version = *game_version; } if let Some(game_code) = matches.get_one::("gamecode") {