mirror of https://github.com/encounter/nod-rs.git
Add Disc::new_stream/new_stream_with_options
Allows opening a disc image from a custom stream, rather than a filesystem path.
This commit is contained in:
parent
5ad514d59c
commit
370d03fa9a
|
@ -61,7 +61,7 @@ impl Node {
|
||||||
/// For directories, this is the parent node index in the FST.
|
/// For directories, this is the parent node index in the FST.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn offset(&self, is_wii: bool) -> u64 {
|
pub fn offset(&self, is_wii: bool) -> u64 {
|
||||||
if is_wii && self.kind == 0 {
|
if is_wii && self.is_file() {
|
||||||
self.offset.get() as u64 * 4
|
self.offset.get() as u64 * 4
|
||||||
} else {
|
} else {
|
||||||
self.offset.get() as u64
|
self.offset.get() as u64
|
||||||
|
@ -108,7 +108,7 @@ impl<'a> Fst<'a> {
|
||||||
|
|
||||||
/// Get the name of a node.
|
/// Get the name of a node.
|
||||||
#[allow(clippy::missing_inline_in_public_items)]
|
#[allow(clippy::missing_inline_in_public_items)]
|
||||||
pub fn get_name(&self, node: &Node) -> Result<Cow<str>, String> {
|
pub fn get_name(&self, node: &Node) -> Result<Cow<'a, str>, String> {
|
||||||
let name_buf = self.string_table.get(node.name_offset() as usize..).ok_or_else(|| {
|
let name_buf = self.string_table.get(node.name_offset() as usize..).ok_or_else(|| {
|
||||||
format!(
|
format!(
|
||||||
"FST: name offset {} out of bounds (string table size: {})",
|
"FST: name offset {} out of bounds (string table size: {})",
|
||||||
|
@ -128,7 +128,7 @@ impl<'a> Fst<'a> {
|
||||||
|
|
||||||
/// Finds a particular file or directory by path.
|
/// Finds a particular file or directory by path.
|
||||||
#[allow(clippy::missing_inline_in_public_items)]
|
#[allow(clippy::missing_inline_in_public_items)]
|
||||||
pub fn find(&self, path: &str) -> Option<(usize, &Node)> {
|
pub fn find(&self, path: &str) -> Option<(usize, &'a Node)> {
|
||||||
let mut split = path.trim_matches('/').split('/');
|
let mut split = path.trim_matches('/').split('/');
|
||||||
let mut current = split.next()?;
|
let mut current = split.next()?;
|
||||||
let mut idx = 1;
|
let mut idx = 1;
|
||||||
|
|
|
@ -11,6 +11,7 @@ use super::{
|
||||||
PartitionMeta, BI2_SIZE, BOOT_SIZE, SECTOR_SIZE,
|
PartitionMeta, BI2_SIZE, BOOT_SIZE, SECTOR_SIZE,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
|
disc::streams::OwnedFileStream,
|
||||||
io::block::{Block, BlockIO},
|
io::block::{Block, BlockIO},
|
||||||
util::read::{read_box, read_box_slice, read_vec},
|
util::read::{read_box, read_box_slice, read_vec},
|
||||||
Result, ResultContext,
|
Result, ResultContext,
|
||||||
|
@ -132,6 +133,16 @@ impl PartitionBase for PartitionGC {
|
||||||
}
|
}
|
||||||
FileStream::new(self, node.offset(false), node.length())
|
FileStream::new(self, node.offset(false), node.length())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn into_open_file(self: Box<Self>, node: &Node) -> io::Result<OwnedFileStream> {
|
||||||
|
if !node.is_file() {
|
||||||
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidInput,
|
||||||
|
"Node is not a file".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
OwnedFileStream::new(self, node.offset(false), node.length())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn read_part_meta(
|
pub(crate) fn read_part_meta(
|
||||||
|
|
|
@ -23,7 +23,7 @@ pub(crate) mod streams;
|
||||||
pub(crate) mod wii;
|
pub(crate) mod wii;
|
||||||
|
|
||||||
pub use fst::{Fst, Node, NodeKind};
|
pub use fst::{Fst, Node, NodeKind};
|
||||||
pub use streams::FileStream;
|
pub use streams::{FileStream, OwnedFileStream, WindowedStream};
|
||||||
pub use wii::{SignedHeader, Ticket, TicketLimit, TmdHeader};
|
pub use wii::{SignedHeader, Ticket, TicketLimit, TmdHeader};
|
||||||
|
|
||||||
/// Size in bytes of a disc sector.
|
/// Size in bytes of a disc sector.
|
||||||
|
@ -308,6 +308,35 @@ pub trait PartitionBase: DynClone + BufRead + Seek + Send + Sync {
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
fn open_file(&mut self, node: &Node) -> io::Result<FileStream>;
|
fn open_file(&mut self, node: &Node) -> io::Result<FileStream>;
|
||||||
|
|
||||||
|
/// Consumes the partition instance and returns a windowed stream.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// use std::io::Read;
|
||||||
|
///
|
||||||
|
/// use nod::{Disc, PartitionKind, OwnedFileStream};
|
||||||
|
///
|
||||||
|
/// fn main() -> nod::Result<()> {
|
||||||
|
/// let disc = Disc::new("path/to/file.iso")?;
|
||||||
|
/// let mut partition = disc.open_partition_kind(PartitionKind::Data)?;
|
||||||
|
/// let meta = partition.meta()?;
|
||||||
|
/// let fst = meta.fst()?;
|
||||||
|
/// if let Some((_, node)) = fst.find("/disc.tgc") {
|
||||||
|
/// let file: OwnedFileStream = partition
|
||||||
|
/// .clone() // Clone the Box<dyn PartitionBase>
|
||||||
|
/// .into_open_file(node) // Get an OwnedFileStream
|
||||||
|
/// .expect("Failed to open file stream");
|
||||||
|
/// // Open the inner disc image using the owned stream
|
||||||
|
/// let inner_disc = Disc::new_stream(Box::new(file))
|
||||||
|
/// .expect("Failed to open inner disc");
|
||||||
|
/// // ...
|
||||||
|
/// }
|
||||||
|
/// Ok(())
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
fn into_open_file(self: Box<Self>, node: &Node) -> io::Result<OwnedFileStream>;
|
||||||
}
|
}
|
||||||
|
|
||||||
dyn_clone::clone_trait_object!(PartitionBase);
|
dyn_clone::clone_trait_object!(PartitionBase);
|
||||||
|
|
|
@ -7,30 +7,39 @@ use std::{
|
||||||
|
|
||||||
use super::PartitionBase;
|
use super::PartitionBase;
|
||||||
|
|
||||||
/// A file read stream for a [`PartitionBase`].
|
/// A file read stream borrowing a [`PartitionBase`].
|
||||||
pub struct FileStream<'a> {
|
pub type FileStream<'a> = WindowedStream<&'a mut dyn PartitionBase>;
|
||||||
base: &'a mut dyn PartitionBase,
|
|
||||||
|
/// A file read stream owning a [`PartitionBase`].
|
||||||
|
pub type OwnedFileStream = WindowedStream<Box<dyn PartitionBase>>;
|
||||||
|
|
||||||
|
/// A read stream with a fixed window.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct WindowedStream<T>
|
||||||
|
where T: BufRead + Seek
|
||||||
|
{
|
||||||
|
base: T,
|
||||||
pos: u64,
|
pos: u64,
|
||||||
begin: u64,
|
begin: u64,
|
||||||
end: u64,
|
end: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileStream<'_> {
|
impl<T> WindowedStream<T>
|
||||||
/// Creates a new file stream with offset and size.
|
where T: BufRead + Seek
|
||||||
|
{
|
||||||
|
/// Creates a new windowed stream with offset and size.
|
||||||
///
|
///
|
||||||
/// Seeks underlying stream immediately.
|
/// Seeks underlying stream immediately.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn new(
|
pub fn new(mut base: T, offset: u64, size: u64) -> io::Result<Self> {
|
||||||
base: &mut dyn PartitionBase,
|
|
||||||
offset: u64,
|
|
||||||
size: u64,
|
|
||||||
) -> io::Result<FileStream> {
|
|
||||||
base.seek(SeekFrom::Start(offset))?;
|
base.seek(SeekFrom::Start(offset))?;
|
||||||
Ok(FileStream { base, pos: offset, begin: offset, end: offset + size })
|
Ok(Self { base, pos: offset, begin: offset, end: offset + size })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Read for FileStream<'a> {
|
impl<T> Read for WindowedStream<T>
|
||||||
|
where T: BufRead + Seek
|
||||||
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn read(&mut self, out: &mut [u8]) -> io::Result<usize> {
|
fn read(&mut self, out: &mut [u8]) -> io::Result<usize> {
|
||||||
let buf = self.fill_buf()?;
|
let buf = self.fill_buf()?;
|
||||||
|
@ -41,7 +50,9 @@ impl<'a> Read for FileStream<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> BufRead for FileStream<'a> {
|
impl<T> BufRead for WindowedStream<T>
|
||||||
|
where T: BufRead + Seek
|
||||||
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
fn fill_buf(&mut self) -> io::Result<&[u8]> {
|
||||||
let limit = self.end.saturating_sub(self.pos);
|
let limit = self.end.saturating_sub(self.pos);
|
||||||
|
@ -60,7 +71,9 @@ impl<'a> BufRead for FileStream<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Seek for FileStream<'a> {
|
impl<T> Seek for WindowedStream<T>
|
||||||
|
where T: BufRead + Seek
|
||||||
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||||
let mut pos = match pos {
|
let mut pos = match pos {
|
||||||
|
|
|
@ -14,6 +14,7 @@ use super::{
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
array_ref,
|
array_ref,
|
||||||
|
disc::streams::OwnedFileStream,
|
||||||
io::{
|
io::{
|
||||||
aes_decrypt,
|
aes_decrypt,
|
||||||
block::{Block, BlockIO, PartitionInfo},
|
block::{Block, BlockIO, PartitionInfo},
|
||||||
|
@ -497,4 +498,14 @@ impl PartitionBase for PartitionWii {
|
||||||
}
|
}
|
||||||
FileStream::new(self, node.offset(true), node.length())
|
FileStream::new(self, node.offset(true), node.length())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn into_open_file(self: Box<Self>, node: &Node) -> io::Result<OwnedFileStream> {
|
||||||
|
if !node.is_file() {
|
||||||
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidInput,
|
||||||
|
"Node is not a file".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
OwnedFileStream::new(self, node.offset(true), node.length())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
use std::{cmp::min, fs, fs::File, io, path::Path};
|
use std::{
|
||||||
|
cmp::min,
|
||||||
|
fs, io,
|
||||||
|
io::{Read, Seek},
|
||||||
|
path::Path,
|
||||||
|
};
|
||||||
|
|
||||||
use dyn_clone::DynClone;
|
use dyn_clone::DynClone;
|
||||||
use zerocopy::transmute_ref;
|
use zerocopy::transmute_ref;
|
||||||
|
@ -10,11 +15,18 @@ use crate::{
|
||||||
wii::{WiiPartitionHeader, HASHES_SIZE, SECTOR_DATA_SIZE},
|
wii::{WiiPartitionHeader, HASHES_SIZE, SECTOR_DATA_SIZE},
|
||||||
SECTOR_SIZE,
|
SECTOR_SIZE,
|
||||||
},
|
},
|
||||||
io::{aes_decrypt, aes_encrypt, KeyBytes, MagicBytes},
|
io::{aes_decrypt, aes_encrypt, split::SplitFileReader, KeyBytes, MagicBytes},
|
||||||
util::{lfg::LaggedFibonacci, read::read_from},
|
util::{lfg::LaggedFibonacci, read::read_from},
|
||||||
DiscHeader, DiscMeta, Error, PartitionHeader, PartitionKind, Result, ResultContext,
|
DiscHeader, DiscMeta, Error, PartitionHeader, PartitionKind, Result, ResultContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Required trait bounds for reading disc images.
|
||||||
|
pub trait DiscStream: Read + Seek + DynClone + Send + Sync {}
|
||||||
|
|
||||||
|
impl<T> DiscStream for T where T: Read + Seek + DynClone + Send + Sync + ?Sized {}
|
||||||
|
|
||||||
|
dyn_clone::clone_trait_object!(DiscStream);
|
||||||
|
|
||||||
/// Block I/O trait for reading disc images.
|
/// Block I/O trait for reading disc images.
|
||||||
pub trait BlockIO: DynClone + Send + Sync {
|
pub trait BlockIO: DynClone + Send + Sync {
|
||||||
/// Reads a block from the disc image.
|
/// Reads a block from the disc image.
|
||||||
|
@ -78,7 +90,27 @@ pub trait BlockIO: DynClone + Send + Sync {
|
||||||
|
|
||||||
dyn_clone::clone_trait_object!(BlockIO);
|
dyn_clone::clone_trait_object!(BlockIO);
|
||||||
|
|
||||||
/// Creates a new [`BlockIO`] instance.
|
/// Creates a new [`BlockIO`] instance from a stream.
|
||||||
|
pub fn new(mut stream: Box<dyn DiscStream>) -> Result<Box<dyn BlockIO>> {
|
||||||
|
let magic: MagicBytes = read_from(stream.as_mut()).context("Reading magic bytes")?;
|
||||||
|
stream.seek(io::SeekFrom::Start(0)).context("Seeking to start")?;
|
||||||
|
let io: Box<dyn BlockIO> = match magic {
|
||||||
|
crate::io::ciso::CISO_MAGIC => crate::io::ciso::DiscIOCISO::new(stream)?,
|
||||||
|
#[cfg(feature = "compress-zlib")]
|
||||||
|
crate::io::gcz::GCZ_MAGIC => crate::io::gcz::DiscIOGCZ::new(stream)?,
|
||||||
|
crate::io::nfs::NFS_MAGIC => todo!(),
|
||||||
|
crate::io::wbfs::WBFS_MAGIC => crate::io::wbfs::DiscIOWBFS::new(stream)?,
|
||||||
|
crate::io::wia::WIA_MAGIC | crate::io::wia::RVZ_MAGIC => {
|
||||||
|
crate::io::wia::DiscIOWIA::new(stream)?
|
||||||
|
}
|
||||||
|
crate::io::tgc::TGC_MAGIC => crate::io::tgc::DiscIOTGC::new(stream)?,
|
||||||
|
_ => crate::io::iso::DiscIOISO::new(stream)?,
|
||||||
|
};
|
||||||
|
check_block_size(io.as_ref())?;
|
||||||
|
Ok(io)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new [`BlockIO`] instance from a filesystem path.
|
||||||
pub fn open(filename: &Path) -> Result<Box<dyn BlockIO>> {
|
pub fn open(filename: &Path) -> Result<Box<dyn BlockIO>> {
|
||||||
let path_result = fs::canonicalize(filename);
|
let path_result = fs::canonicalize(filename);
|
||||||
if let Err(err) = path_result {
|
if let Err(err) = path_result {
|
||||||
|
@ -92,16 +124,16 @@ pub fn open(filename: &Path) -> Result<Box<dyn BlockIO>> {
|
||||||
if !meta.unwrap().is_file() {
|
if !meta.unwrap().is_file() {
|
||||||
return Err(Error::DiscFormat(format!("Input is not a file: {}", filename.display())));
|
return Err(Error::DiscFormat(format!("Input is not a file: {}", filename.display())));
|
||||||
}
|
}
|
||||||
let magic: MagicBytes = {
|
let mut stream = Box::new(SplitFileReader::new(filename)?);
|
||||||
let mut file =
|
let magic: MagicBytes = read_from(stream.as_mut())
|
||||||
File::open(path).with_context(|| format!("Opening file {}", filename.display()))?;
|
.with_context(|| format!("Reading magic bytes from {}", filename.display()))?;
|
||||||
read_from(&mut file)
|
stream
|
||||||
.with_context(|| format!("Reading magic bytes from {}", filename.display()))?
|
.seek(io::SeekFrom::Start(0))
|
||||||
};
|
.with_context(|| format!("Seeking to start of {}", filename.display()))?;
|
||||||
let io: Box<dyn BlockIO> = match magic {
|
let io: Box<dyn BlockIO> = match magic {
|
||||||
crate::io::ciso::CISO_MAGIC => crate::io::ciso::DiscIOCISO::new(path)?,
|
crate::io::ciso::CISO_MAGIC => crate::io::ciso::DiscIOCISO::new(stream)?,
|
||||||
#[cfg(feature = "compress-zlib")]
|
#[cfg(feature = "compress-zlib")]
|
||||||
crate::io::gcz::GCZ_MAGIC => crate::io::gcz::DiscIOGCZ::new(path)?,
|
crate::io::gcz::GCZ_MAGIC => crate::io::gcz::DiscIOGCZ::new(stream)?,
|
||||||
crate::io::nfs::NFS_MAGIC => match path.parent() {
|
crate::io::nfs::NFS_MAGIC => match path.parent() {
|
||||||
Some(parent) if parent.is_dir() => {
|
Some(parent) if parent.is_dir() => {
|
||||||
crate::io::nfs::DiscIONFS::new(path.parent().unwrap())?
|
crate::io::nfs::DiscIONFS::new(path.parent().unwrap())?
|
||||||
|
@ -110,13 +142,18 @@ pub fn open(filename: &Path) -> Result<Box<dyn BlockIO>> {
|
||||||
return Err(Error::DiscFormat("Failed to locate NFS parent directory".to_string()));
|
return Err(Error::DiscFormat("Failed to locate NFS parent directory".to_string()));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
crate::io::wbfs::WBFS_MAGIC => crate::io::wbfs::DiscIOWBFS::new(path)?,
|
crate::io::wbfs::WBFS_MAGIC => crate::io::wbfs::DiscIOWBFS::new(stream)?,
|
||||||
crate::io::wia::WIA_MAGIC | crate::io::wia::RVZ_MAGIC => {
|
crate::io::wia::WIA_MAGIC | crate::io::wia::RVZ_MAGIC => {
|
||||||
crate::io::wia::DiscIOWIA::new(path)?
|
crate::io::wia::DiscIOWIA::new(stream)?
|
||||||
}
|
}
|
||||||
crate::io::tgc::TGC_MAGIC => crate::io::tgc::DiscIOTGC::new(path)?,
|
crate::io::tgc::TGC_MAGIC => crate::io::tgc::DiscIOTGC::new(stream)?,
|
||||||
_ => crate::io::iso::DiscIOISO::new(path)?,
|
_ => crate::io::iso::DiscIOISO::new(stream)?,
|
||||||
};
|
};
|
||||||
|
check_block_size(io.as_ref())?;
|
||||||
|
Ok(io)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_block_size(io: &dyn BlockIO) -> Result<()> {
|
||||||
if io.block_size_internal() < SECTOR_SIZE as u32
|
if io.block_size_internal() < SECTOR_SIZE as u32
|
||||||
&& SECTOR_SIZE as u32 % io.block_size_internal() != 0
|
&& SECTOR_SIZE as u32 % io.block_size_internal() != 0
|
||||||
{
|
{
|
||||||
|
@ -133,7 +170,7 @@ pub fn open(filename: &Path) -> Result<Box<dyn BlockIO>> {
|
||||||
SECTOR_SIZE
|
SECTOR_SIZE
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
Ok(io)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wii partition information.
|
/// Wii partition information.
|
||||||
|
|
|
@ -2,7 +2,6 @@ use std::{
|
||||||
io,
|
io,
|
||||||
io::{Read, Seek, SeekFrom},
|
io::{Read, Seek, SeekFrom},
|
||||||
mem::size_of,
|
mem::size_of,
|
||||||
path::Path,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use zerocopy::{little_endian::*, AsBytes, FromBytes, FromZeroes};
|
use zerocopy::{little_endian::*, AsBytes, FromBytes, FromZeroes};
|
||||||
|
@ -10,9 +9,8 @@ use zerocopy::{little_endian::*, AsBytes, FromBytes, FromZeroes};
|
||||||
use crate::{
|
use crate::{
|
||||||
disc::SECTOR_SIZE,
|
disc::SECTOR_SIZE,
|
||||||
io::{
|
io::{
|
||||||
block::{Block, BlockIO, PartitionInfo},
|
block::{Block, BlockIO, DiscStream, PartitionInfo},
|
||||||
nkit::NKitHeader,
|
nkit::NKitHeader,
|
||||||
split::SplitFileReader,
|
|
||||||
Format, MagicBytes,
|
Format, MagicBytes,
|
||||||
},
|
},
|
||||||
static_assert,
|
static_assert,
|
||||||
|
@ -36,18 +34,16 @@ static_assert!(size_of::<CISOHeader>() == SECTOR_SIZE);
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct DiscIOCISO {
|
pub struct DiscIOCISO {
|
||||||
inner: SplitFileReader,
|
inner: Box<dyn DiscStream>,
|
||||||
header: CISOHeader,
|
header: CISOHeader,
|
||||||
block_map: [u16; CISO_MAP_SIZE],
|
block_map: [u16; CISO_MAP_SIZE],
|
||||||
nkit_header: Option<NKitHeader>,
|
nkit_header: Option<NKitHeader>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DiscIOCISO {
|
impl DiscIOCISO {
|
||||||
pub fn new(filename: &Path) -> Result<Box<Self>> {
|
pub fn new(mut inner: Box<dyn DiscStream>) -> Result<Box<Self>> {
|
||||||
let mut inner = SplitFileReader::new(filename)?;
|
|
||||||
|
|
||||||
// Read header
|
// Read header
|
||||||
let header: CISOHeader = read_from(&mut inner).context("Reading CISO header")?;
|
let header: CISOHeader = read_from(inner.as_mut()).context("Reading CISO header")?;
|
||||||
if header.magic != CISO_MAGIC {
|
if header.magic != CISO_MAGIC {
|
||||||
return Err(Error::DiscFormat("Invalid CISO magic".to_string()));
|
return Err(Error::DiscFormat("Invalid CISO magic".to_string()));
|
||||||
}
|
}
|
||||||
|
@ -64,18 +60,18 @@ impl DiscIOCISO {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let file_size = SECTOR_SIZE as u64 + block as u64 * header.block_size.get() as u64;
|
let file_size = SECTOR_SIZE as u64 + block as u64 * header.block_size.get() as u64;
|
||||||
if file_size > inner.len() {
|
let len = inner.seek(SeekFrom::End(0)).context("Determining stream length")?;
|
||||||
|
if file_size > len {
|
||||||
return Err(Error::DiscFormat(format!(
|
return Err(Error::DiscFormat(format!(
|
||||||
"CISO file size mismatch: expected at least {} bytes, got {}",
|
"CISO file size mismatch: expected at least {} bytes, got {}",
|
||||||
file_size,
|
file_size, len
|
||||||
inner.len()
|
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read NKit header if present (after CISO data)
|
// Read NKit header if present (after CISO data)
|
||||||
let nkit_header = if inner.len() > file_size + 4 {
|
let nkit_header = if len > file_size + 4 {
|
||||||
inner.seek(SeekFrom::Start(file_size)).context("Seeking to NKit header")?;
|
inner.seek(SeekFrom::Start(file_size)).context("Seeking to NKit header")?;
|
||||||
NKitHeader::try_read_from(&mut inner, header.block_size.get(), true)
|
NKitHeader::try_read_from(inner.as_mut(), header.block_size.get(), true)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,7 +2,6 @@ use std::{
|
||||||
io,
|
io,
|
||||||
io::{Read, Seek, SeekFrom},
|
io::{Read, Seek, SeekFrom},
|
||||||
mem::size_of,
|
mem::size_of,
|
||||||
path::Path,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use adler::adler32_slice;
|
use adler::adler32_slice;
|
||||||
|
@ -12,8 +11,7 @@ use zstd::zstd_safe::WriteBuf;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
io::{
|
io::{
|
||||||
block::{Block, BlockIO},
|
block::{Block, BlockIO, DiscStream},
|
||||||
split::SplitFileReader,
|
|
||||||
MagicBytes,
|
MagicBytes,
|
||||||
},
|
},
|
||||||
static_assert,
|
static_assert,
|
||||||
|
@ -38,7 +36,7 @@ struct GCZHeader {
|
||||||
static_assert!(size_of::<GCZHeader>() == 32);
|
static_assert!(size_of::<GCZHeader>() == 32);
|
||||||
|
|
||||||
pub struct DiscIOGCZ {
|
pub struct DiscIOGCZ {
|
||||||
inner: SplitFileReader,
|
inner: Box<dyn DiscStream>,
|
||||||
header: GCZHeader,
|
header: GCZHeader,
|
||||||
block_map: Box<[U64]>,
|
block_map: Box<[U64]>,
|
||||||
block_hashes: Box<[U32]>,
|
block_hashes: Box<[U32]>,
|
||||||
|
@ -60,21 +58,19 @@ impl Clone for DiscIOGCZ {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DiscIOGCZ {
|
impl DiscIOGCZ {
|
||||||
pub fn new(filename: &Path) -> Result<Box<Self>> {
|
pub fn new(mut inner: Box<dyn DiscStream>) -> Result<Box<Self>> {
|
||||||
let mut inner = SplitFileReader::new(filename)?;
|
|
||||||
|
|
||||||
// Read header
|
// Read header
|
||||||
let header: GCZHeader = read_from(&mut inner).context("Reading GCZ header")?;
|
let header: GCZHeader = read_from(inner.as_mut()).context("Reading GCZ header")?;
|
||||||
if header.magic != GCZ_MAGIC {
|
if header.magic != GCZ_MAGIC {
|
||||||
return Err(Error::DiscFormat("Invalid GCZ magic".to_string()));
|
return Err(Error::DiscFormat("Invalid GCZ magic".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read block map and hashes
|
// Read block map and hashes
|
||||||
let block_count = header.block_count.get();
|
let block_count = header.block_count.get();
|
||||||
let block_map =
|
let block_map = read_box_slice(inner.as_mut(), block_count as usize)
|
||||||
read_box_slice(&mut inner, block_count as usize).context("Reading GCZ block map")?;
|
.context("Reading GCZ block map")?;
|
||||||
let block_hashes =
|
let block_hashes = read_box_slice(inner.as_mut(), block_count as usize)
|
||||||
read_box_slice(&mut inner, block_count as usize).context("Reading GCZ block hashes")?;
|
.context("Reading GCZ block hashes")?;
|
||||||
|
|
||||||
// header + block_count * (u64 + u32)
|
// header + block_count * (u64 + u32)
|
||||||
let data_offset = size_of::<GCZHeader>() as u64 + block_count as u64 * 12;
|
let data_offset = size_of::<GCZHeader>() as u64 + block_count as u64 * 12;
|
||||||
|
|
|
@ -1,28 +1,28 @@
|
||||||
use std::{
|
use std::{
|
||||||
io,
|
io,
|
||||||
io::{Read, Seek, SeekFrom},
|
io::{Read, Seek, SeekFrom},
|
||||||
path::Path,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
disc::SECTOR_SIZE,
|
disc::SECTOR_SIZE,
|
||||||
io::{
|
io::{
|
||||||
block::{Block, BlockIO, PartitionInfo},
|
block::{Block, BlockIO, DiscStream, PartitionInfo},
|
||||||
split::SplitFileReader,
|
|
||||||
Format,
|
Format,
|
||||||
},
|
},
|
||||||
DiscMeta, Result,
|
DiscMeta, Result, ResultContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct DiscIOISO {
|
pub struct DiscIOISO {
|
||||||
inner: SplitFileReader,
|
inner: Box<dyn DiscStream>,
|
||||||
|
stream_len: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DiscIOISO {
|
impl DiscIOISO {
|
||||||
pub fn new(filename: &Path) -> Result<Box<Self>> {
|
pub fn new(mut inner: Box<dyn DiscStream>) -> Result<Box<Self>> {
|
||||||
let inner = SplitFileReader::new(filename)?;
|
let stream_len = inner.seek(SeekFrom::End(0)).context("Determining stream length")?;
|
||||||
Ok(Box::new(Self { inner }))
|
inner.seek(SeekFrom::Start(0)).context("Seeking to start")?;
|
||||||
|
Ok(Box::new(Self { inner, stream_len }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,16 +34,15 @@ impl BlockIO for DiscIOISO {
|
||||||
_partition: Option<&PartitionInfo>,
|
_partition: Option<&PartitionInfo>,
|
||||||
) -> io::Result<Block> {
|
) -> io::Result<Block> {
|
||||||
let offset = block as u64 * SECTOR_SIZE as u64;
|
let offset = block as u64 * SECTOR_SIZE as u64;
|
||||||
let total_size = self.inner.len();
|
if offset >= self.stream_len {
|
||||||
if offset >= total_size {
|
|
||||||
// End of file
|
// End of file
|
||||||
return Ok(Block::Zero);
|
return Ok(Block::Zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.inner.seek(SeekFrom::Start(offset))?;
|
self.inner.seek(SeekFrom::Start(offset))?;
|
||||||
if offset + SECTOR_SIZE as u64 > total_size {
|
if offset + SECTOR_SIZE as u64 > self.stream_len {
|
||||||
// If the last block is not a full sector, fill the rest with zeroes
|
// If the last block is not a full sector, fill the rest with zeroes
|
||||||
let read = (total_size - offset) as usize;
|
let read = (self.stream_len - offset) as usize;
|
||||||
self.inner.read_exact(&mut out[..read])?;
|
self.inner.read_exact(&mut out[..read])?;
|
||||||
out[read..].fill(0);
|
out[read..].fill(0);
|
||||||
} else {
|
} else {
|
||||||
|
@ -58,7 +57,7 @@ impl BlockIO for DiscIOISO {
|
||||||
DiscMeta {
|
DiscMeta {
|
||||||
format: Format::Iso,
|
format: Format::Iso,
|
||||||
lossless: true,
|
lossless: true,
|
||||||
disc_size: Some(self.inner.len()),
|
disc_size: Some(self.stream_len),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ use std::{
|
||||||
io,
|
io,
|
||||||
io::{Read, Seek, SeekFrom},
|
io::{Read, Seek, SeekFrom},
|
||||||
mem::size_of,
|
mem::size_of,
|
||||||
path::Path,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use zerocopy::{big_endian::U32, AsBytes, FromBytes, FromZeroes};
|
use zerocopy::{big_endian::U32, AsBytes, FromBytes, FromZeroes};
|
||||||
|
@ -10,8 +9,7 @@ use zerocopy::{big_endian::U32, AsBytes, FromBytes, FromZeroes};
|
||||||
use crate::{
|
use crate::{
|
||||||
disc::SECTOR_SIZE,
|
disc::SECTOR_SIZE,
|
||||||
io::{
|
io::{
|
||||||
block::{Block, BlockIO, PartitionInfo},
|
block::{Block, BlockIO, DiscStream, PartitionInfo},
|
||||||
split::SplitFileReader,
|
|
||||||
Format, MagicBytes,
|
Format, MagicBytes,
|
||||||
},
|
},
|
||||||
util::read::{read_box_slice, read_from},
|
util::read::{read_box_slice, read_from},
|
||||||
|
@ -56,17 +54,19 @@ struct TGCHeader {
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct DiscIOTGC {
|
pub struct DiscIOTGC {
|
||||||
inner: SplitFileReader,
|
inner: Box<dyn DiscStream>,
|
||||||
|
stream_len: u64,
|
||||||
header: TGCHeader,
|
header: TGCHeader,
|
||||||
fst: Box<[u8]>,
|
fst: Box<[u8]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DiscIOTGC {
|
impl DiscIOTGC {
|
||||||
pub fn new(filename: &Path) -> Result<Box<Self>> {
|
pub fn new(mut inner: Box<dyn DiscStream>) -> Result<Box<Self>> {
|
||||||
let mut inner = SplitFileReader::new(filename)?;
|
let stream_len = inner.seek(SeekFrom::End(0)).context("Determining stream length")?;
|
||||||
|
inner.seek(SeekFrom::Start(0)).context("Seeking to start")?;
|
||||||
|
|
||||||
// Read header
|
// Read header
|
||||||
let header: TGCHeader = read_from(&mut inner).context("Reading TGC header")?;
|
let header: TGCHeader = read_from(inner.as_mut()).context("Reading TGC header")?;
|
||||||
if header.magic != TGC_MAGIC {
|
if header.magic != TGC_MAGIC {
|
||||||
return Err(Error::DiscFormat("Invalid TGC magic".to_string()));
|
return Err(Error::DiscFormat("Invalid TGC magic".to_string()));
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ impl DiscIOTGC {
|
||||||
inner
|
inner
|
||||||
.seek(SeekFrom::Start(header.fst_offset.get() as u64))
|
.seek(SeekFrom::Start(header.fst_offset.get() as u64))
|
||||||
.context("Seeking to TGC FST")?;
|
.context("Seeking to TGC FST")?;
|
||||||
let mut fst = read_box_slice(&mut inner, header.fst_size.get() as usize)
|
let mut fst = read_box_slice(inner.as_mut(), header.fst_size.get() as usize)
|
||||||
.context("Reading TGC FST")?;
|
.context("Reading TGC FST")?;
|
||||||
let root_node = Node::ref_from_prefix(&fst)
|
let root_node = Node::ref_from_prefix(&fst)
|
||||||
.ok_or_else(|| Error::DiscFormat("Invalid TGC FST".to_string()))?;
|
.ok_or_else(|| Error::DiscFormat("Invalid TGC FST".to_string()))?;
|
||||||
|
@ -89,7 +89,7 @@ impl DiscIOTGC {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Box::new(Self { inner, header, fst }))
|
Ok(Box::new(Self { inner, stream_len, header, fst }))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,16 +101,15 @@ impl BlockIO for DiscIOTGC {
|
||||||
_partition: Option<&PartitionInfo>,
|
_partition: Option<&PartitionInfo>,
|
||||||
) -> io::Result<Block> {
|
) -> io::Result<Block> {
|
||||||
let offset = self.header.header_offset.get() as u64 + block as u64 * SECTOR_SIZE as u64;
|
let offset = self.header.header_offset.get() as u64 + block as u64 * SECTOR_SIZE as u64;
|
||||||
let total_size = self.inner.len();
|
if offset >= self.stream_len {
|
||||||
if offset >= total_size {
|
|
||||||
// End of file
|
// End of file
|
||||||
return Ok(Block::Zero);
|
return Ok(Block::Zero);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.inner.seek(SeekFrom::Start(offset))?;
|
self.inner.seek(SeekFrom::Start(offset))?;
|
||||||
if offset + SECTOR_SIZE as u64 > total_size {
|
if offset + SECTOR_SIZE as u64 > self.stream_len {
|
||||||
// If the last block is not a full sector, fill the rest with zeroes
|
// If the last block is not a full sector, fill the rest with zeroes
|
||||||
let read = (total_size - offset) as usize;
|
let read = (self.stream_len - offset) as usize;
|
||||||
self.inner.read_exact(&mut out[..read])?;
|
self.inner.read_exact(&mut out[..read])?;
|
||||||
out[read..].fill(0);
|
out[read..].fill(0);
|
||||||
} else {
|
} else {
|
||||||
|
@ -149,7 +148,7 @@ impl BlockIO for DiscIOTGC {
|
||||||
DiscMeta {
|
DiscMeta {
|
||||||
format: Format::Tgc,
|
format: Format::Tgc,
|
||||||
lossless: true,
|
lossless: true,
|
||||||
disc_size: Some(self.inner.len() - self.header.header_offset.get() as u64),
|
disc_size: Some(self.stream_len - self.header.header_offset.get() as u64),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,16 +2,14 @@ use std::{
|
||||||
io,
|
io,
|
||||||
io::{Read, Seek, SeekFrom},
|
io::{Read, Seek, SeekFrom},
|
||||||
mem::size_of,
|
mem::size_of,
|
||||||
path::Path,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use zerocopy::{big_endian::*, AsBytes, FromBytes, FromZeroes};
|
use zerocopy::{big_endian::*, AsBytes, FromBytes, FromZeroes};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
io::{
|
io::{
|
||||||
block::{Block, BlockIO, PartitionInfo},
|
block::{Block, BlockIO, DiscStream, PartitionInfo},
|
||||||
nkit::NKitHeader,
|
nkit::NKitHeader,
|
||||||
split::SplitFileReader,
|
|
||||||
DiscMeta, Format, MagicBytes,
|
DiscMeta, Format, MagicBytes,
|
||||||
},
|
},
|
||||||
util::read::{read_box_slice, read_from},
|
util::read::{read_box_slice, read_from},
|
||||||
|
@ -35,18 +33,6 @@ impl WBFSHeader {
|
||||||
|
|
||||||
fn block_size(&self) -> u32 { 1 << self.block_size_shift }
|
fn block_size(&self) -> u32 { 1 << self.block_size_shift }
|
||||||
|
|
||||||
// fn align_lba(&self, x: u32) -> u32 { (x + self.sector_size() - 1) & !(self.sector_size() - 1) }
|
|
||||||
//
|
|
||||||
// fn num_wii_sectors(&self) -> u32 {
|
|
||||||
// (self.num_sectors.get() / SECTOR_SIZE as u32) * self.sector_size()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fn max_wii_sectors(&self) -> u32 { NUM_WII_SECTORS }
|
|
||||||
//
|
|
||||||
// fn num_wbfs_sectors(&self) -> u32 {
|
|
||||||
// self.num_wii_sectors() >> (self.wbfs_sector_size_shift - 15)
|
|
||||||
// }
|
|
||||||
|
|
||||||
fn max_blocks(&self) -> u32 { NUM_WII_SECTORS >> (self.block_size_shift - 15) }
|
fn max_blocks(&self) -> u32 { NUM_WII_SECTORS >> (self.block_size_shift - 15) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +41,7 @@ const NUM_WII_SECTORS: u32 = 143432 * 2; // Double layer discs
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct DiscIOWBFS {
|
pub struct DiscIOWBFS {
|
||||||
inner: SplitFileReader,
|
inner: Box<dyn DiscStream>,
|
||||||
/// WBFS header
|
/// WBFS header
|
||||||
header: WBFSHeader,
|
header: WBFSHeader,
|
||||||
/// Map of Wii LBAs to WBFS LBAs
|
/// Map of Wii LBAs to WBFS LBAs
|
||||||
|
@ -65,14 +51,12 @@ pub struct DiscIOWBFS {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DiscIOWBFS {
|
impl DiscIOWBFS {
|
||||||
pub fn new(filename: &Path) -> Result<Box<Self>> {
|
pub fn new(mut inner: Box<dyn DiscStream>) -> Result<Box<Self>> {
|
||||||
let mut inner = SplitFileReader::new(filename)?;
|
let header: WBFSHeader = read_from(inner.as_mut()).context("Reading WBFS header")?;
|
||||||
|
|
||||||
let header: WBFSHeader = read_from(&mut inner).context("Reading WBFS header")?;
|
|
||||||
if header.magic != WBFS_MAGIC {
|
if header.magic != WBFS_MAGIC {
|
||||||
return Err(Error::DiscFormat("Invalid WBFS magic".to_string()));
|
return Err(Error::DiscFormat("Invalid WBFS magic".to_string()));
|
||||||
}
|
}
|
||||||
let file_len = inner.len();
|
let file_len = inner.seek(SeekFrom::End(0)).context("Determining stream length")?;
|
||||||
let expected_file_len = header.num_sectors.get() as u64 * header.sector_size() as u64;
|
let expected_file_len = header.num_sectors.get() as u64 * header.sector_size() as u64;
|
||||||
if file_len != expected_file_len {
|
if file_len != expected_file_len {
|
||||||
return Err(Error::DiscFormat(format!(
|
return Err(Error::DiscFormat(format!(
|
||||||
|
@ -81,8 +65,11 @@ impl DiscIOWBFS {
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inner
|
||||||
|
.seek(SeekFrom::Start(size_of::<WBFSHeader>() as u64))
|
||||||
|
.context("Seeking to WBFS disc table")?;
|
||||||
let disc_table: Box<[u8]> =
|
let disc_table: Box<[u8]> =
|
||||||
read_box_slice(&mut inner, header.sector_size() as usize - size_of::<WBFSHeader>())
|
read_box_slice(inner.as_mut(), header.sector_size() as usize - size_of::<WBFSHeader>())
|
||||||
.context("Reading WBFS disc table")?;
|
.context("Reading WBFS disc table")?;
|
||||||
if disc_table[0] != 1 {
|
if disc_table[0] != 1 {
|
||||||
return Err(Error::DiscFormat("WBFS doesn't contain a disc".to_string()));
|
return Err(Error::DiscFormat("WBFS doesn't contain a disc".to_string()));
|
||||||
|
@ -95,12 +82,12 @@ impl DiscIOWBFS {
|
||||||
inner
|
inner
|
||||||
.seek(SeekFrom::Start(header.sector_size() as u64 + DISC_HEADER_SIZE as u64))
|
.seek(SeekFrom::Start(header.sector_size() as u64 + DISC_HEADER_SIZE as u64))
|
||||||
.context("Seeking to WBFS LBA table")?; // Skip header
|
.context("Seeking to WBFS LBA table")?; // Skip header
|
||||||
let block_map: Box<[U16]> = read_box_slice(&mut inner, header.max_blocks() as usize)
|
let block_map: Box<[U16]> = read_box_slice(inner.as_mut(), header.max_blocks() as usize)
|
||||||
.context("Reading WBFS LBA table")?;
|
.context("Reading WBFS LBA table")?;
|
||||||
|
|
||||||
// Read NKit header if present (always at 0x10000)
|
// Read NKit header if present (always at 0x10000)
|
||||||
inner.seek(SeekFrom::Start(0x10000)).context("Seeking to NKit header")?;
|
inner.seek(SeekFrom::Start(0x10000)).context("Seeking to NKit header")?;
|
||||||
let nkit_header = NKitHeader::try_read_from(&mut inner, header.block_size(), true);
|
let nkit_header = NKitHeader::try_read_from(inner.as_mut(), header.block_size(), true);
|
||||||
|
|
||||||
Ok(Box::new(Self { inner, header, block_map, nkit_header }))
|
Ok(Box::new(Self { inner, header, block_map, nkit_header }))
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ use std::{
|
||||||
io,
|
io,
|
||||||
io::{Read, Seek, SeekFrom},
|
io::{Read, Seek, SeekFrom},
|
||||||
mem::size_of,
|
mem::size_of,
|
||||||
path::Path,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use zerocopy::{big_endian::*, AsBytes, FromBytes, FromZeroes};
|
use zerocopy::{big_endian::*, AsBytes, FromBytes, FromZeroes};
|
||||||
|
@ -14,9 +13,8 @@ use crate::{
|
||||||
SECTOR_SIZE,
|
SECTOR_SIZE,
|
||||||
},
|
},
|
||||||
io::{
|
io::{
|
||||||
block::{Block, BlockIO, PartitionInfo},
|
block::{Block, BlockIO, DiscStream, PartitionInfo},
|
||||||
nkit::NKitHeader,
|
nkit::NKitHeader,
|
||||||
split::SplitFileReader,
|
|
||||||
Compression, Format, HashBytes, KeyBytes, MagicBytes,
|
Compression, Format, HashBytes, KeyBytes, MagicBytes,
|
||||||
},
|
},
|
||||||
static_assert,
|
static_assert,
|
||||||
|
@ -502,7 +500,7 @@ impl Decompressor {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DiscIOWIA {
|
pub struct DiscIOWIA {
|
||||||
inner: SplitFileReader,
|
inner: Box<dyn DiscStream>,
|
||||||
header: WIAFileHeader,
|
header: WIAFileHeader,
|
||||||
disc: WIADisc,
|
disc: WIADisc,
|
||||||
partitions: Box<[WIAPartition]>,
|
partitions: Box<[WIAPartition]>,
|
||||||
|
@ -549,17 +547,16 @@ fn verify_hash(buf: &[u8], expected: &HashBytes) -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DiscIOWIA {
|
impl DiscIOWIA {
|
||||||
pub fn new(filename: &Path) -> Result<Box<Self>> {
|
pub fn new(mut inner: Box<dyn DiscStream>) -> Result<Box<Self>> {
|
||||||
let mut inner = SplitFileReader::new(filename)?;
|
|
||||||
|
|
||||||
// Load & verify file header
|
// Load & verify file header
|
||||||
let header: WIAFileHeader = read_from(&mut inner).context("Reading WIA/RVZ file header")?;
|
let header: WIAFileHeader =
|
||||||
|
read_from(inner.as_mut()).context("Reading WIA/RVZ file header")?;
|
||||||
header.validate()?;
|
header.validate()?;
|
||||||
let is_rvz = header.is_rvz();
|
let is_rvz = header.is_rvz();
|
||||||
// log::debug!("Header: {:?}", header);
|
// log::debug!("Header: {:?}", header);
|
||||||
|
|
||||||
// Load & verify disc header
|
// Load & verify disc header
|
||||||
let mut disc_buf: Vec<u8> = read_vec(&mut inner, header.disc_size.get() as usize)
|
let mut disc_buf: Vec<u8> = read_vec(inner.as_mut(), header.disc_size.get() as usize)
|
||||||
.context("Reading WIA/RVZ disc header")?;
|
.context("Reading WIA/RVZ disc header")?;
|
||||||
verify_hash(&disc_buf, &header.disc_hash)?;
|
verify_hash(&disc_buf, &header.disc_hash)?;
|
||||||
disc_buf.resize(size_of::<WIADisc>(), 0);
|
disc_buf.resize(size_of::<WIADisc>(), 0);
|
||||||
|
@ -576,14 +573,14 @@ impl DiscIOWIA {
|
||||||
// log::debug!("Disc: {:?}", disc);
|
// log::debug!("Disc: {:?}", disc);
|
||||||
|
|
||||||
// Read NKit header if present (after disc header)
|
// Read NKit header if present (after disc header)
|
||||||
let nkit_header = NKitHeader::try_read_from(&mut inner, disc.chunk_size.get(), false);
|
let nkit_header = NKitHeader::try_read_from(inner.as_mut(), disc.chunk_size.get(), false);
|
||||||
|
|
||||||
// Load & verify partition headers
|
// Load & verify partition headers
|
||||||
inner
|
inner
|
||||||
.seek(SeekFrom::Start(disc.partition_offset.get()))
|
.seek(SeekFrom::Start(disc.partition_offset.get()))
|
||||||
.context("Seeking to WIA/RVZ partition headers")?;
|
.context("Seeking to WIA/RVZ partition headers")?;
|
||||||
let partitions: Box<[WIAPartition]> =
|
let partitions: Box<[WIAPartition]> =
|
||||||
read_box_slice(&mut inner, disc.num_partitions.get() as usize)
|
read_box_slice(inner.as_mut(), disc.num_partitions.get() as usize)
|
||||||
.context("Reading WIA/RVZ partition headers")?;
|
.context("Reading WIA/RVZ partition headers")?;
|
||||||
verify_hash(partitions.as_ref().as_bytes(), &disc.partition_hash)?;
|
verify_hash(partitions.as_ref().as_bytes(), &disc.partition_hash)?;
|
||||||
// log::debug!("Partitions: {:?}", partitions);
|
// log::debug!("Partitions: {:?}", partitions);
|
||||||
|
@ -597,7 +594,7 @@ impl DiscIOWIA {
|
||||||
.seek(SeekFrom::Start(disc.raw_data_offset.get()))
|
.seek(SeekFrom::Start(disc.raw_data_offset.get()))
|
||||||
.context("Seeking to WIA/RVZ raw data headers")?;
|
.context("Seeking to WIA/RVZ raw data headers")?;
|
||||||
let mut reader = decompressor
|
let mut reader = decompressor
|
||||||
.wrap((&mut inner).take(disc.raw_data_size.get() as u64))
|
.wrap(inner.as_mut().take(disc.raw_data_size.get() as u64))
|
||||||
.context("Creating WIA/RVZ decompressor")?;
|
.context("Creating WIA/RVZ decompressor")?;
|
||||||
read_box_slice(&mut reader, disc.num_raw_data.get() as usize)
|
read_box_slice(&mut reader, disc.num_raw_data.get() as usize)
|
||||||
.context("Reading WIA/RVZ raw data headers")?
|
.context("Reading WIA/RVZ raw data headers")?
|
||||||
|
@ -621,7 +618,7 @@ impl DiscIOWIA {
|
||||||
.seek(SeekFrom::Start(disc.group_offset.get()))
|
.seek(SeekFrom::Start(disc.group_offset.get()))
|
||||||
.context("Seeking to WIA/RVZ group headers")?;
|
.context("Seeking to WIA/RVZ group headers")?;
|
||||||
let mut reader = decompressor
|
let mut reader = decompressor
|
||||||
.wrap((&mut inner).take(disc.group_size.get() as u64))
|
.wrap(inner.as_mut().take(disc.group_size.get() as u64))
|
||||||
.context("Creating WIA/RVZ decompressor")?;
|
.context("Creating WIA/RVZ decompressor")?;
|
||||||
if is_rvz {
|
if is_rvz {
|
||||||
read_box_slice(&mut reader, disc.num_groups.get() as usize)
|
read_box_slice(&mut reader, disc.num_groups.get() as usize)
|
||||||
|
|
|
@ -64,11 +64,14 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use disc::{
|
pub use disc::{
|
||||||
ApploaderHeader, DiscHeader, DolHeader, FileStream, Fst, Node, NodeKind, PartitionBase,
|
ApploaderHeader, DiscHeader, DolHeader, FileStream, Fst, Node, NodeKind, OwnedFileStream,
|
||||||
PartitionHeader, PartitionKind, PartitionMeta, SignedHeader, Ticket, TicketLimit, TmdHeader,
|
PartitionBase, PartitionHeader, PartitionKind, PartitionMeta, SignedHeader, Ticket,
|
||||||
BI2_SIZE, BOOT_SIZE, SECTOR_SIZE,
|
TicketLimit, TmdHeader, WindowedStream, BI2_SIZE, BOOT_SIZE, SECTOR_SIZE,
|
||||||
|
};
|
||||||
|
pub use io::{
|
||||||
|
block::{DiscStream, PartitionInfo},
|
||||||
|
Compression, DiscMeta, Format, KeyBytes,
|
||||||
};
|
};
|
||||||
pub use io::{block::PartitionInfo, Compression, DiscMeta, Format, KeyBytes};
|
|
||||||
|
|
||||||
mod disc;
|
mod disc;
|
||||||
mod io;
|
mod io;
|
||||||
|
@ -170,6 +173,23 @@ impl Disc {
|
||||||
Ok(Disc { reader, options: options.clone() })
|
Ok(Disc { reader, options: options.clone() })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Opens a disc image from a read stream.
|
||||||
|
#[inline]
|
||||||
|
pub fn new_stream(stream: Box<dyn DiscStream>) -> Result<Disc> {
|
||||||
|
Disc::new_stream_with_options(stream, &OpenOptions::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Opens a disc image from a read stream with custom options.
|
||||||
|
#[inline]
|
||||||
|
pub fn new_stream_with_options(
|
||||||
|
stream: Box<dyn DiscStream>,
|
||||||
|
options: &OpenOptions,
|
||||||
|
) -> Result<Disc> {
|
||||||
|
let io = io::block::new(stream)?;
|
||||||
|
let reader = disc::reader::DiscReader::new(io, options)?;
|
||||||
|
Ok(Disc { reader, options: options.clone() })
|
||||||
|
}
|
||||||
|
|
||||||
/// The disc's primary header.
|
/// The disc's primary header.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn header(&self) -> &DiscHeader { self.reader.header() }
|
pub fn header(&self) -> &DiscHeader { self.reader.header() }
|
||||||
|
|
Loading…
Reference in New Issue