use crate::error::Error;
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom;
use std::str;
use byteorder::BigEndian;
use byteorder::ReadBytesExt;
use ipl3checksum;
#[derive(Debug)]
pub struct Metadata {
pub pi_dom1_configuration_flags: u32,
pub clock_rate: u32,
pub boot_address: u32,
pub libultra_version: String,
pub check_code: u64,
pub reserved_maybe: u64,
pub game_title: [u8; 0x14],
pub reserved2: [u8; 7],
pub game_code: String,
pub rom_version: u8,
pub ipl3: [u8; 0xFC0],
}
impl Metadata {
pub fn from_io<T: Read + Seek>(io: &mut T) -> Result<Self, Error> {
io.read_n64_metadata()
}
}
pub trait MetadataRead {
fn read_n64_metadata(&mut self) -> Result<Metadata, Error>;
}
impl<T: Read + Seek> MetadataRead for T {
fn read_n64_metadata(&mut self) -> Result<Metadata, Error> {
self.seek(SeekFrom::Start(0))?;
let pi_dom1_configuration_flags = self.read_u32::<BigEndian>()?;
let clock_rate = self.read_u32::<BigEndian>()?;
let boot_address = self.read_u32::<BigEndian>()?;
let libultra_version = self.read_u32::<BigEndian>()?;
let major_minor = ((libultra_version >> 8) & 0xFF) as u8;
let major = major_minor / 10;
let minor = major_minor % 10;
let revision = str::from_utf8(&[(libultra_version & 0xFF) as u8])?.to_string();
let libultra_version = format!("{major}.{minor}{revision}");
let check_code = self.read_u64::<BigEndian>()?;
let reserved_maybe = self.read_u64::<BigEndian>()?;
let mut game_title = [0u8; 0x14];
self.read_exact(&mut game_title)?;
let mut reserved2 = [0u8; 7];
self.read_exact(&mut reserved2)?;
let mut game_code = [0u8; 4];
self.read_exact(&mut game_code)?;
if !game_code.is_ascii() {
return Err(Error::Parse("game code is not valid ASCII".into()));
}
let game_code = str::from_utf8(&game_code)?.to_string();
let rom_version = self.read_u8()?;
let mut ipl3 = [0u8; 0xFC0];
self.read_exact(&mut ipl3)?;
Ok(Metadata {
pi_dom1_configuration_flags,
clock_rate,
boot_address,
libultra_version,
check_code,
reserved_maybe,
game_title,
reserved2,
game_code,
rom_version,
ipl3,
})
}
}
impl Metadata {
pub fn compute_checksum<T: Read + Seek>(&mut self, io: &mut T) -> Result<u64, Error> {
let mut data = vec![0u8; 0x10_1000];
io.seek(SeekFrom::Start(0))?;
io.read_exact(&mut data)?;
match ipl3checksum::calculate_checksum_autodetect(&data) {
Ok((a1, a2)) => Ok(u64::from(a1) << 32 | u64::from(a2)),
Err(_) => Err(Error::Parse("unable to compute ROM checksum".into())),
}
}
}