use crate::error::Error;
use crate::fst::Fst;
use crate::fst::FstRead;
use crate::utils::from_latin1_or_shift_jis;
use crate::utils::trim;
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom;
use byteorder::BigEndian;
use byteorder::ReadBytesExt;
#[derive(Debug)]
pub struct Header {
pub console_id: String,
pub game_code: String,
pub country_code: String,
pub maker_code: String,
pub disk_id: u8,
pub version: u8,
pub audio_streaming: u8,
pub stream_buffer_size: u8,
pub magic: u32,
pub game_name: String,
pub debug_monitor_offset: u32,
pub debug_monitor_address: u32,
pub executable_offset: u32,
pub fst_offset: u32,
pub fst_size: u32,
pub fst_max_size: u32,
pub user_position: u32,
pub user_length: u32,
}
pub struct Metadata {
pub header: Header,
pub filesystem: Fst,
}
pub trait HeaderRead {
fn read_gcn_header(&mut self) -> Result<Header, Error>;
}
impl Metadata {
pub fn try_from<T: Read + Seek>(io: &mut T) -> Result<Self, Error> {
let header = io.read_gcn_header()?;
let filesystem = io.read_fst()?;
Ok(Self { header, filesystem })
}
}
impl<T: Read + Seek> HeaderRead for T {
fn read_gcn_header(&mut self) -> Result<Header, Error> {
self.seek(SeekFrom::Start(0))?;
let mut game_code = [0u8; 4];
self.read_exact(&mut game_code)?;
let game_code = from_latin1_or_shift_jis(&game_code)?;
let maker_code = self.read_u16::<BigEndian>()?.to_be_bytes();
let maker_code = from_latin1_or_shift_jis(&maker_code)?;
let disk_id = self.read_u8()?;
let version = self.read_u8()?;
let audio_streaming = self.read_u8()?;
let stream_buffer_size = self.read_u8()?;
self.seek(SeekFrom::Current(0x12))?;
let magic = self.read_u32::<BigEndian>()?;
let mut game_name = [0u8; 0x3E0];
self.read_exact(&mut game_name)?;
let game_name = trim(&from_latin1_or_shift_jis(&game_name)?).to_string();
if game_code.len() != 4 {
return Err(Error::Parse("bad game code string, wrong size".into()));
}
if !game_code.is_ascii() {
return Err(Error::Parse("bad game code string, not ASCII".into()));
}
let debug_monitor_offset = self.read_u32::<BigEndian>()?;
let debug_monitor_address = self.read_u32::<BigEndian>()?;
self.seek(SeekFrom::Current(0x18))?;
let executable_offset = self.read_u32::<BigEndian>()?;
let fst_offset = self.read_u32::<BigEndian>()?;
let fst_size = self.read_u32::<BigEndian>()?;
let fst_max_size = self.read_u32::<BigEndian>()?;
let user_position = self.read_u32::<BigEndian>()?;
let user_length = self.read_u32::<BigEndian>()?;
Ok(Header {
console_id: game_code[0..1].to_string(),
game_code: game_code[1..3].to_string(),
country_code: game_code[3..4].to_string(),
maker_code,
disk_id,
version,
audio_streaming,
stream_buffer_size,
magic,
game_name,
debug_monitor_offset,
debug_monitor_address,
executable_offset,
fst_offset,
fst_size,
fst_max_size,
user_position,
user_length,
})
}
}