Implement our own gba header

This commit is contained in:
Gwilym Inzani 2023-07-26 10:18:28 +01:00
parent eb31a98f29
commit 9eee5a03f2
4 changed files with 66 additions and 33 deletions

17
agb-gbafix/Cargo.lock generated
View file

@ -7,10 +7,8 @@ name = "agb-gbafix"
version = "0.16.0" version = "0.16.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytemuck",
"clap", "clap",
"elf", "elf",
"gbafix",
] ]
[[package]] [[package]]
@ -74,12 +72,6 @@ version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42"
[[package]]
name = "bytemuck"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.0.79" version = "1.0.79"
@ -146,15 +138,6 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "gbafix"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7e47af9d5377b8b0def53d9916f6c28521f2e5b97c5fec137797417c26ab4b7"
dependencies = [
"bytemuck",
]
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.3.1" version = "0.3.1"

View file

@ -9,8 +9,6 @@ repository = "https://github.com/agbrs/agb"
[dependencies] [dependencies]
elf = "0.7" elf = "0.7"
gbafix = "1"
bytemuck = "1"
anyhow = "1" anyhow = "1"
clap = "4" clap = "4"

View file

@ -1,11 +1,66 @@
use anyhow::{anyhow, bail, ensure, Result}; use anyhow::{anyhow, bail, ensure, Result};
use std::io::Write; use std::io::Write;
pub fn write_gba_file<W: Write>( const GBA_HEADER_SIZE: usize = 192;
input: &[u8],
mut header: gbafix::GBAHeader, const NINTENDO_LOGO: &[u8] = &[
output: &mut W, 0x24, 0xFF, 0xAE, 0x51, 0x69, 0x9A, 0xA2, 0x21, 0x3D, 0x84, 0x82, 0x0A, 0x84, 0xE4, 0x09, 0xAD,
) -> Result<()> { 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<u8> {
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<W: Write>(input: &[u8], mut header: GbaHeader, output: &mut W) -> Result<()> {
let elf_file = elf::ElfBytes::<elf::endian::AnyEndian>::minimal_parse(input)?; let elf_file = elf::ElfBytes::<elf::endian::AnyEndian>::minimal_parse(input)?;
let section_headers = elf_file let section_headers = elf_file
@ -38,18 +93,15 @@ pub fn write_gba_file<W: Write>(
} }
if bytes_written == 0 { if bytes_written == 0 {
const GBA_HEADER_SIZE: usize = 192;
ensure!( ensure!(
data.len() > GBA_HEADER_SIZE, data.len() > GBA_HEADER_SIZE,
"first section must be at least as big as the gba header" "first section must be at least as big as the gba header"
); );
header.start_code = data[0..4].try_into().unwrap(); header.start_code = data[0..4].try_into().unwrap();
header.update_checksum();
let header_bytes = bytemuck::bytes_of(&header); let header_bytes = header.produce_header();
output.write_all(header_bytes)?; output.write_all(&header_bytes)?;
data = &data[GBA_HEADER_SIZE..]; data = &data[GBA_HEADER_SIZE..];
bytes_written += GBA_HEADER_SIZE as u64; bytes_written += GBA_HEADER_SIZE as u64;

View file

@ -7,7 +7,7 @@ use std::{
path::PathBuf, path::PathBuf,
}; };
use agb_gbafix::write_gba_file; use agb_gbafix::{write_gba_file, GbaHeader};
fn main() -> Result<()> { fn main() -> Result<()> {
let matches = clap::Command::new("agb-gbafix") let matches = clap::Command::new("agb-gbafix")
@ -27,7 +27,7 @@ fn main() -> Result<()> {
None => input.with_extension("gba"), 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::<String>("title") { let title = if let Some(title) = matches.get_one::<String>("title") {
@ -41,7 +41,7 @@ fn main() -> Result<()> {
}; };
for (i, &c) in title.as_bytes().iter().enumerate().take(12) { 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::<u8>("gameversion") { if let Some(game_version) = matches.get_one::<u8>("gameversion") {
header.version = *game_version; header.software_version = *game_version;
} }
if let Some(game_code) = matches.get_one::<String>("gamecode") { if let Some(game_code) = matches.get_one::<String>("gamecode") {