use crate::error::Error;
use crate::ncch::NCCHRead;
use crate::ncch::NCCH;
use crate::ncsd::NCCHPartitionEntry;
use std::fmt;
use std::fmt::Display;
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom;
use byteorder::LittleEndian;
use byteorder::ReadBytesExt;
#[derive(Debug)]
pub struct InitialData {
pub seed: [u8; 0x10],
pub title_key: [u8; 0x10],
pub aes_ccm_mac: [u8; 0x10],
pub aes_ccm_nonce: [u8; 0xC],
pub ncch: NCCH,
}
#[derive(Debug)]
pub struct Version {
major: u8,
minor: u8,
patch: u8,
}
impl From<u16> for Version {
fn from(data: u16) -> Self {
Self {
major: (data >> 10) as u8,
minor: ((data >> 4) & 0x3F) as u8,
patch: (data & 0xF) as u8,
}
}
}
impl Display for Version {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}.{}.{}", self.major, self.minor, self.patch)
}
}
#[derive(Debug)]
pub struct CardInfo {
pub writeable_address: u32,
pub flags: u32,
pub filled_size: u32,
pub title_version: Version,
pub card_revision: u16,
pub cver_title_id: u64,
pub cver_version: Version,
pub initial_data: InitialData,
}
pub trait CardInfoRead {
fn read_card_info(&mut self) -> Result<CardInfo, Error>;
}
impl<T: Read + Seek> CardInfoRead for T {
fn read_card_info(&mut self) -> Result<CardInfo, Error> {
self.seek(SeekFrom::Start(0x200))?;
let writeable_address = self.read_u32::<LittleEndian>()?;
let flags = self.read_u32::<LittleEndian>()?;
self.seek(SeekFrom::Start(0x300))?;
let filled_size = self.read_u32::<LittleEndian>()?;
self.seek(SeekFrom::Start(0x310))?;
let title_version = self.read_u16::<LittleEndian>()?.into();
let card_revision = self.read_u16::<LittleEndian>()?;
self.seek(SeekFrom::Start(0x320))?;
let cver_title_id = self.read_u64::<LittleEndian>()?;
let cver_version = self.read_u16::<LittleEndian>()?.into();
self.seek(SeekFrom::Start(0x1000))?;
let mut seed = [0u8; 0x10];
self.read_exact(&mut seed)?;
let mut title_key = [0u8; 0x10];
self.read_exact(&mut title_key)?;
let mut aes_ccm_mac = [0u8; 0x10];
self.read_exact(&mut aes_ccm_mac)?;
let mut aes_ccm_nonce = [0u8; 0xC];
self.read_exact(&mut aes_ccm_nonce)?;
let partition = NCCHPartitionEntry {
offset: (0x1100 - 0x100) / 0x200,
size: 0x100, };
let ncch = self.read_ncch(partition)?;
if ncch.is_none() {
return Err(Error::Parse(
"failed to parse NCCH in card info header".into(),
));
}
let mut ncch = ncch.unwrap();
ncch.signature.fill(0);
Ok(CardInfo {
writeable_address,
flags,
filled_size,
title_version,
card_revision,
cver_title_id,
cver_version,
initial_data: InitialData {
seed,
title_key,
aes_ccm_mac,
aes_ccm_nonce,
ncch,
},
})
}
}