use crate::card_info::CardInfo;
use crate::card_info::CardInfoRead;
use crate::crypto::AESCtr;
use crate::error::Error;
use crate::ncch::NCCH;
use crate::ncsd::NCSD;
use crate::ncsd::NCSDRead;
use std::cmp;
use std::io;
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom;
use ctr::cipher::StreamCipher;
use ctr::cipher::StreamCipherSeek;
pub struct Cart {
pub ncsd: NCSD,
pub card_info: CardInfo,
exefs_ctr: Option<AESCtr>,
}
impl Cart {
pub fn from_io<T: Read + Seek>(io: &mut T) -> Result<Self, Error> {
let ncsd = io.read_ncsd()?;
let card_info = io.read_card_info()?;
let exefs_ctr = ncsd.find_exefs_ncch().map(NCCH::exefs_ctr);
Ok(Self {
ncsd,
card_info,
exefs_ctr,
})
}
}
const fn is_overlap(region1: (u32, u32), region2: (u32, u32)) -> bool {
(region1.0 <= region2.1) && (region1.1 >= region2.0)
}
pub struct CartIo<'cart, T: Read + Seek> {
pub cart: &'cart mut Cart,
io: &'cart mut T,
}
impl<'cart, T: Read + Seek> CartIo<'cart, T> {
pub const fn new(cart: &'cart mut Cart, io: &'cart mut T) -> Self {
CartIo { cart, io }
}
#[must_use]
pub const fn release(self) -> (&'cart mut Cart, &'cart mut T) {
(self.cart, self.io)
}
}
impl<T: Read + Seek> Read for CartIo<'_, T> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let cur = self.io.stream_position()?;
let cur = u32::try_from(cur).map_err(|_| io::Error::from(io::ErrorKind::Other))?;
let end = cur
+ u32::try_from(buf.len()).map_err(|_| io::Error::from(io::ErrorKind::InvalidInput))?;
let result = self.io.read(buf)?;
let exefs_ncch = self.cart.ncsd.find_exefs_ncch();
if let Some(exefs_ncch) = exefs_ncch {
let exefs_offset =
(exefs_ncch.partition_entry.offset + exefs_ncch.exefs_offset) * 0x200;
let exefs_region = (exefs_offset, exefs_offset + exefs_ncch.exefs_size * 0x200);
if is_overlap(exefs_region, (cur, end)) {
let start = cmp::max(exefs_region.0, cur);
let end = cmp::min(exefs_region.1, end);
self.cart
.exefs_ctr
.as_mut()
.unwrap()
.seek(start - exefs_region.0);
let start = usize::try_from(start).unwrap();
let end = usize::try_from(end).unwrap();
let cur = usize::try_from(cur).unwrap();
let start = start - cur;
let end = end - cur;
self.cart
.exefs_ctr
.as_mut()
.unwrap()
.apply_keystream(&mut buf[start..end]);
}
}
Ok(result)
}
}
impl<T: Read + Seek> Seek for CartIo<'_, T> {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
self.io.seek(pos)
}
}