Reformat with rustfmt

This commit is contained in:
Luke Street 2021-08-25 11:22:17 -04:00
parent 9df1aa4149
commit aaf9d37306
11 changed files with 252 additions and 216 deletions

8
rustfmt.toml Normal file
View File

@ -0,0 +1,8 @@
fn_single_line = true
group_imports = "StdExternalCrate"
imports_granularity = "Crate"
overflow_delimited_expr = true
reorder_impl_items = true
use_field_init_shorthand = true
use_small_heuristics = "Max"
where_single_line = true

View File

@ -1,14 +1,17 @@
use std::{env, fs, io}; use std::{
use std::io::BufWriter; env, fs, io,
use std::path::{Path, PathBuf}; io::BufWriter,
path::{Path, PathBuf},
};
use clap::{AppSettings, clap_app}; use clap::{clap_app, AppSettings};
use file_size; use file_size;
use nod::{
use nod::disc::{new_disc_base, PartReadStream}; disc::{new_disc_base, PartReadStream},
use nod::fst::NodeType; fst::NodeType,
use nod::io::{has_extension, new_disc_io}; io::{has_extension, new_disc_io},
use nod::Result; Result,
};
fn main() -> Result<()> { fn main() -> Result<()> {
let matches = clap_app!(nodtool => let matches = clap_app!(nodtool =>
@ -36,7 +39,8 @@ Phillip Stephens (Antidote)")
(@arg DIR: "Output directory (optional)") (@arg DIR: "Output directory (optional)")
(@arg quiet: -q "Quiet output") (@arg quiet: -q "Quiet output")
) )
).get_matches(); )
.get_matches();
if let Some(matches) = matches.subcommand_matches("extract") { if let Some(matches) = matches.subcommand_matches("extract") {
let file: PathBuf = PathBuf::from(matches.value_of("FILE").unwrap()); let file: PathBuf = PathBuf::from(matches.value_of("FILE").unwrap());
let output_dir: PathBuf; let output_dir: PathBuf;
@ -61,12 +65,20 @@ Phillip Stephens (Antidote)")
Result::Ok(()) Result::Ok(())
} }
fn extract_node(node: &NodeType, partition: &mut dyn PartReadStream, base_path: &Path) -> io::Result<()> { fn extract_node(
node: &NodeType,
partition: &mut dyn PartReadStream,
base_path: &Path,
) -> io::Result<()> {
match node { match node {
NodeType::File(v) => { NodeType::File(v) => {
let mut file_path = base_path.to_owned(); let mut file_path = base_path.to_owned();
file_path.push(v.name.as_ref()); file_path.push(v.name.as_ref());
println!("Extracting {} (size: {})", file_path.to_string_lossy(), file_size::fit_4(v.length as u64)); println!(
"Extracting {} (size: {})",
file_path.to_string_lossy(),
file_size::fit_4(v.length as u64)
);
let file = fs::File::create(file_path)?; let file = fs::File::create(file_path)?;
let mut buf_writer = BufWriter::with_capacity(partition.ideal_buffer_size(), file); let mut buf_writer = BufWriter::with_capacity(partition.ideal_buffer_size(), file);
io::copy(&mut partition.begin_file_stream(v)?, &mut buf_writer)?; io::copy(&mut partition.begin_file_stream(v)?, &mut buf_writer)?;

View File

@ -1,29 +1,31 @@
use std::io; use std::{
use std::io::{Read, Seek, SeekFrom}; io,
io::{Read, Seek, SeekFrom},
};
use binread::prelude::*; use binread::prelude::*;
use crate::{div_rem, Result}; use crate::{
use crate::disc::{BI2Header, BUFFER_SIZE, DiscBase, DiscIO, Header, PartHeader, PartReadStream}; disc::{BI2Header, DiscBase, DiscIO, Header, PartHeader, PartReadStream, BUFFER_SIZE},
use crate::fst::{find_node, Node, node_parser, NodeKind, NodeType}; div_rem,
use crate::streams::{ReadStream, SharedWindowedReadStream}; fst::{find_node, node_parser, Node, NodeKind, NodeType},
streams::{ReadStream, SharedWindowedReadStream},
Result,
};
pub(crate) struct DiscGCN { pub(crate) struct DiscGCN {
pub(crate) header: Header, pub(crate) header: Header,
} }
pub(crate) fn new_disc_gcn(header: Header) -> Result<DiscGCN> { pub(crate) fn new_disc_gcn(header: Header) -> Result<DiscGCN> { Result::Ok(DiscGCN { header }) }
Result::Ok(DiscGCN {
header
})
}
impl DiscBase for DiscGCN { impl DiscBase for DiscGCN {
fn get_header(&self) -> &Header { fn get_header(&self) -> &Header { &self.header }
&self.header
}
fn get_data_partition<'a>(&self, disc_io: &'a mut dyn DiscIO) -> Result<Box<dyn PartReadStream + 'a>> { fn get_data_partition<'a>(
&self,
disc_io: &'a mut dyn DiscIO,
) -> Result<Box<dyn PartReadStream + 'a>> {
Result::Ok(Box::from(GCPartReadStream { Result::Ok(Box::from(GCPartReadStream {
stream: disc_io.begin_read_stream(0)?, stream: disc_io.begin_read_stream(0)?,
offset: 0, offset: 0,
@ -40,7 +42,6 @@ struct GCPartReadStream<'a> {
buf: [u8; BUFFER_SIZE], buf: [u8; BUFFER_SIZE],
} }
impl<'a> Read for GCPartReadStream<'a> { impl<'a> Read for GCPartReadStream<'a> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let (mut block, mut block_offset) = div_rem(self.offset as usize, BUFFER_SIZE); let (mut block, mut block_offset) = div_rem(self.offset as usize, BUFFER_SIZE);
@ -86,15 +87,11 @@ impl<'a> Seek for GCPartReadStream<'a> {
io::Result::Ok(self.offset) io::Result::Ok(self.offset)
} }
fn stream_position(&mut self) -> io::Result<u64> { fn stream_position(&mut self) -> io::Result<u64> { io::Result::Ok(self.offset) }
io::Result::Ok(self.offset)
}
} }
impl<'a> ReadStream for GCPartReadStream<'a> { impl<'a> ReadStream for GCPartReadStream<'a> {
fn stable_stream_len(&mut self) -> io::Result<u64> { fn stable_stream_len(&mut self) -> io::Result<u64> { self.stream.stable_stream_len() }
self.stream.stable_stream_len()
}
} }
impl<'a> PartReadStream for GCPartReadStream<'a> { impl<'a> PartReadStream for GCPartReadStream<'a> {
@ -108,9 +105,7 @@ impl<'a> PartReadStream for GCPartReadStream<'a> {
Result::Ok(Box::from(self.read_be::<GCPartition>()?)) Result::Ok(Box::from(self.read_be::<GCPartition>()?))
} }
fn ideal_buffer_size(&self) -> usize { fn ideal_buffer_size(&self) -> usize { BUFFER_SIZE }
BUFFER_SIZE
}
} }
#[derive(Clone, Debug, PartialEq, BinRead)] #[derive(Clone, Debug, PartialEq, BinRead)]
@ -123,11 +118,7 @@ pub(crate) struct GCPartition {
} }
impl PartHeader for GCPartition { impl PartHeader for GCPartition {
fn root_node(&self) -> &NodeType { fn root_node(&self) -> &NodeType { &self.root_node }
&self.root_node
}
fn find_node(&self, path: &str) -> Option<&NodeType> { fn find_node(&self, path: &str) -> Option<&NodeType> { find_node(&self.root_node, path) }
find_node(&self.root_node, path)
}
} }

View File

@ -1,15 +1,16 @@
//! Disc type related logic (GameCube, Wii) //! Disc type related logic (GameCube, Wii)
use std::fmt::Debug; use std::{fmt::Debug, io};
use std::io;
use binread::{BinReaderExt, NullString, prelude::*}; use binread::{prelude::*, BinReaderExt, NullString};
use crate::{Error, Result}; use crate::{
use crate::disc::{gcn::new_disc_gcn, wii::new_disc_wii}; disc::{gcn::new_disc_gcn, wii::new_disc_wii},
use crate::fst::{Node, NodeType}; fst::{Node, NodeType},
use crate::io::DiscIO; io::DiscIO,
use crate::streams::{ReadStream, SharedWindowedReadStream}; streams::{ReadStream, SharedWindowedReadStream},
Error, Result,
};
pub(crate) mod gcn; pub(crate) mod gcn;
pub(crate) mod wii; pub(crate) mod wii;
@ -89,7 +90,10 @@ pub trait DiscBase {
/// let disc_base = new_disc_base(disc_io.as_mut())?; /// let disc_base = new_disc_base(disc_io.as_mut())?;
/// let mut partition = disc_base.get_data_partition(disc_io.as_mut())?; /// let mut partition = disc_base.get_data_partition(disc_io.as_mut())?;
/// ``` /// ```
fn get_data_partition<'a>(&self, disc_io: &'a mut dyn DiscIO) -> Result<Box<dyn PartReadStream + 'a>>; fn get_data_partition<'a>(
&self,
disc_io: &'a mut dyn DiscIO,
) -> Result<Box<dyn PartReadStream + 'a>>;
} }
/// Creates a new [`DiscBase`] instance. /// Creates a new [`DiscBase`] instance.

View File

@ -1,19 +1,27 @@
use std::{io, io::{Read, Seek, SeekFrom}}; use std::{
io,
io::{Read, Seek, SeekFrom},
};
use aes::{Aes128, NewBlockCipher, Block}; use aes::{Aes128, Block, NewBlockCipher};
use binread::prelude::*; use binread::prelude::*;
use block_modes::{block_padding::NoPadding, BlockMode, Cbc}; use block_modes::{block_padding::NoPadding, BlockMode, Cbc};
use sha1::{digest, Digest, Sha1}; use sha1::{digest, Digest, Sha1};
use crate::disc::{BI2Header, BUFFER_SIZE, DiscBase, DiscIO, Header, PartHeader, PartReadStream}; use crate::{
use crate::{Error, div_rem, Result, array_ref}; array_ref,
use crate::fst::{find_node, Node, NodeKind, NodeType, node_parser}; disc::{BI2Header, DiscBase, DiscIO, Header, PartHeader, PartReadStream, BUFFER_SIZE},
use crate::streams::{OwningWindowedReadStream, ReadStream, SharedWindowedReadStream, wrap_windowed}; div_rem,
fst::{find_node, node_parser, Node, NodeKind, NodeType},
streams::{wrap_windowed, OwningWindowedReadStream, ReadStream, SharedWindowedReadStream},
Error, Result,
};
type Aes128Cbc = Cbc<Aes128, NoPadding>; type Aes128Cbc = Cbc<Aes128, NoPadding>;
const BLOCK_SIZE: usize = 0x7c00; const BLOCK_SIZE: usize = 0x7c00;
const BUFFER_OFFSET: usize = BUFFER_SIZE - BLOCK_SIZE; const BUFFER_OFFSET: usize = BUFFER_SIZE - BLOCK_SIZE;
#[rustfmt::skip]
const COMMON_KEYS: [[u8; 16]; 2] = [ const COMMON_KEYS: [[u8; 16]; 2] = [
/* Normal */ /* Normal */
[0xeb, 0xe4, 0x2a, 0x22, 0x5e, 0x85, 0x93, 0xe4, 0x48, 0xd9, 0xc5, 0x45, 0x73, 0x81, 0xaa, 0xf7], [0xeb, 0xe4, 0x2a, 0x22, 0x5e, 0x85, 0x93, 0xe4, 0x48, 0xd9, 0xc5, 0x45, 0x73, 0x81, 0xaa, 0xf7],
@ -185,10 +193,7 @@ pub(crate) struct DiscWii {
} }
pub(crate) fn new_disc_wii(mut stream: &mut dyn ReadStream, header: Header) -> Result<DiscWii> { pub(crate) fn new_disc_wii(mut stream: &mut dyn ReadStream, header: Header) -> Result<DiscWii> {
let mut disc = DiscWii { let mut disc = DiscWii { header, part_info: stream.read_be()? };
header,
part_info: stream.read_be()?,
};
disc.decrypt_partition_keys()?; disc.decrypt_partition_keys()?;
Result::Ok(disc) Result::Ok(disc)
} }
@ -202,29 +207,38 @@ impl DiscWii {
Aes128Cbc::new( Aes128Cbc::new(
Aes128::new(&COMMON_KEYS[ticket.common_key_idx as usize].into()), Aes128::new(&COMMON_KEYS[ticket.common_key_idx as usize].into()),
&iv.into(), &iv.into(),
).decrypt(&mut ticket.enc_key)?; )
.decrypt(&mut ticket.enc_key)?;
} }
Result::Ok(()) Result::Ok(())
} }
} }
impl DiscBase for DiscWii { impl DiscBase for DiscWii {
fn get_header(&self) -> &Header { fn get_header(&self) -> &Header { &self.header }
&self.header
}
fn get_data_partition<'a>(&self, disc_io: &'a mut dyn DiscIO) -> Result<Box<dyn PartReadStream + 'a>> { fn get_data_partition<'a>(
let part = self.part_info.parts.iter().find(|v| v.part_type == WiiPartType::Data) &self,
disc_io: &'a mut dyn DiscIO,
) -> Result<Box<dyn PartReadStream + 'a>> {
let part = self
.part_info
.parts
.iter()
.find(|v| v.part_type == WiiPartType::Data)
.ok_or(Error::DiscFormat("Failed to locate data partition".to_string()))?; .ok_or(Error::DiscFormat("Failed to locate data partition".to_string()))?;
let data_off = part.part_header.data_off; let data_off = part.part_header.data_off;
let result = Box::new(WiiPartReadStream { let result = Box::new(WiiPartReadStream {
stream: wrap_windowed( stream: wrap_windowed(
disc_io.begin_read_stream(data_off)?, disc_io.begin_read_stream(data_off)?,
data_off, part.part_header.data_size, data_off,
part.part_header.data_size,
)?, )?,
crypto: if disc_io.has_wii_crypto() { crypto: if disc_io.has_wii_crypto() {
Aes128::new(&part.part_header.ticket.enc_key.into()).into() Aes128::new(&part.part_header.ticket.enc_key.into()).into()
} else { Option::None }, } else {
Option::None
},
offset: 0, offset: 0,
cur_block: u64::MAX, cur_block: u64::MAX,
buf: [0; 0x8000], buf: [0; 0x8000],
@ -254,9 +268,7 @@ impl<'a> PartReadStream for WiiPartReadStream<'a> {
Result::Ok(Box::from(self.read_be::<WiiPartition>()?)) Result::Ok(Box::from(self.read_be::<WiiPartition>()?))
} }
fn ideal_buffer_size(&self) -> usize { fn ideal_buffer_size(&self) -> usize { BLOCK_SIZE }
BLOCK_SIZE
}
} }
#[inline(always)] #[inline(always)]
@ -277,7 +289,9 @@ fn decrypt_block(part: &mut WiiPartReadStream, cluster: usize) -> io::Result<()>
.decrypt(&mut part.buf[BUFFER_OFFSET..]) .decrypt(&mut part.buf[BUFFER_OFFSET..])
.expect("Failed to decrypt block"); .expect("Failed to decrypt block");
} }
if part.validate_hashes && part.crypto.is_some() /* FIXME NFS validation? */ { if part.validate_hashes && part.crypto.is_some()
/* FIXME NFS validation? */
{
let (mut group, sub_group) = div_rem(cluster, 8); let (mut group, sub_group) = div_rem(cluster, 8);
group %= 8; group %= 8;
// H0 hashes // H0 hashes
@ -287,7 +301,12 @@ fn decrypt_block(part: &mut WiiPartReadStream, cluster: usize) -> io::Result<()>
let expected = as_digest(array_ref![part.buf, i * 20, 20]); let expected = as_digest(array_ref![part.buf, i * 20, 20]);
let output = hash.finalize(); let output = hash.finalize();
if output != expected { if output != expected {
panic!("Invalid hash! (block {:?}) {:?}\n\texpected {:?}", i, output.as_slice(), expected); panic!(
"Invalid hash! (block {:?}) {:?}\n\texpected {:?}",
i,
output.as_slice(),
expected
);
} }
} }
// H1 hash // H1 hash
@ -297,7 +316,12 @@ fn decrypt_block(part: &mut WiiPartReadStream, cluster: usize) -> io::Result<()>
let expected = as_digest(array_ref![part.buf, 0x280 + sub_group * 20, 20]); let expected = as_digest(array_ref![part.buf, 0x280 + sub_group * 20, 20]);
let output = hash.finalize(); let output = hash.finalize();
if output != expected { if output != expected {
panic!("Invalid hash! (subgroup {:?}) {:?}\n\texpected {:?}", sub_group, output.as_slice(), expected); panic!(
"Invalid hash! (subgroup {:?}) {:?}\n\texpected {:?}",
sub_group,
output.as_slice(),
expected
);
} }
} }
// H2 hash // H2 hash
@ -307,7 +331,12 @@ fn decrypt_block(part: &mut WiiPartReadStream, cluster: usize) -> io::Result<()>
let expected = as_digest(array_ref![part.buf, 0x340 + group * 20, 20]); let expected = as_digest(array_ref![part.buf, 0x340 + group * 20, 20]);
let output = hash.finalize(); let output = hash.finalize();
if output != expected { if output != expected {
panic!("Invalid hash! (group {:?}) {:?}\n\texpected {:?}", group, output.as_slice(), expected); panic!(
"Invalid hash! (group {:?}) {:?}\n\texpected {:?}",
group,
output.as_slice(),
expected
);
} }
} }
} }
@ -331,9 +360,9 @@ impl<'a> Read for WiiPartReadStream<'a> {
cache_size = BLOCK_SIZE - block_offset; cache_size = BLOCK_SIZE - block_offset;
} }
buf[read..read + cache_size] buf[read..read + cache_size].copy_from_slice(
.copy_from_slice(&self.buf[BUFFER_OFFSET + block_offset.. &self.buf[BUFFER_OFFSET + block_offset..BUFFER_OFFSET + block_offset + cache_size],
BUFFER_OFFSET + block_offset + cache_size]); );
read += cache_size; read += cache_size;
rem -= cache_size; rem -= cache_size;
block_offset = 0; block_offset = 0;
@ -365,9 +394,7 @@ impl<'a> Seek for WiiPartReadStream<'a> {
io::Result::Ok(self.offset) io::Result::Ok(self.offset)
} }
fn stream_position(&mut self) -> io::Result<u64> { fn stream_position(&mut self) -> io::Result<u64> { io::Result::Ok(self.offset) }
io::Result::Ok(self.offset)
}
} }
impl<'a> ReadStream for WiiPartReadStream<'a> { impl<'a> ReadStream for WiiPartReadStream<'a> {
@ -386,11 +413,7 @@ pub(crate) struct WiiPartition {
} }
impl PartHeader for WiiPartition { impl PartHeader for WiiPartition {
fn root_node(&self) -> &NodeType { fn root_node(&self) -> &NodeType { &self.root_node }
&self.root_node
}
fn find_node(&self, path: &str) -> Option<&NodeType> { fn find_node(&self, path: &str) -> Option<&NodeType> { find_node(&self.root_node, path) }
find_node(&self.root_node, path)
}
} }

View File

@ -2,7 +2,7 @@
use std::io::{Read, Seek, SeekFrom}; use std::io::{Read, Seek, SeekFrom};
use binread::{derive_binread, NullString, prelude::*, ReadOptions}; use binread::{derive_binread, prelude::*, NullString, ReadOptions};
use encoding_rs::SHIFT_JIS; use encoding_rs::SHIFT_JIS;
/// File system node kind. /// File system node kind.
@ -64,7 +64,13 @@ fn read_node<R: Read + Seek>(reader: &mut R, ro: &ReadOptions, i: &mut u32) -> B
}) })
} }
fn read_node_name<R: Read + Seek>(reader: &mut R, ro: &ReadOptions, base: u64, node: &mut NodeType, root: bool) -> BinResult<()> { fn read_node_name<R: Read + Seek>(
reader: &mut R,
ro: &ReadOptions,
base: u64,
node: &mut NodeType,
root: bool,
) -> BinResult<()> {
let mut decode_name = |v: &mut Node| -> BinResult<()> { let mut decode_name = |v: &mut Node| -> BinResult<()> {
if !root { if !root {
let offset = base + v.name_offset as u64; let offset = base + v.name_offset as u64;
@ -82,7 +88,9 @@ fn read_node_name<R: Read + Seek>(reader: &mut R, ro: &ReadOptions, base: u64, n
BinResult::Ok(()) BinResult::Ok(())
}; };
match node { match node {
NodeType::File(v) => { decode_name(v)?; } NodeType::File(v) => {
decode_name(v)?;
}
NodeType::Directory(v, c) => { NodeType::Directory(v, c) => {
decode_name(v)?; decode_name(v)?;
for x in c { for x in c {
@ -93,7 +101,11 @@ fn read_node_name<R: Read + Seek>(reader: &mut R, ro: &ReadOptions, base: u64, n
BinResult::Ok(()) BinResult::Ok(())
} }
pub(crate) fn node_parser<R: Read + Seek>(reader: &mut R, ro: &ReadOptions, _: ()) -> BinResult<NodeType> { pub(crate) fn node_parser<R: Read + Seek>(
reader: &mut R,
ro: &ReadOptions,
_: (),
) -> BinResult<NodeType> {
let mut node = read_node(reader, ro, &mut 0)?; let mut node = read_node(reader, ro, &mut 0)?;
let base = reader.stream_position()?; let base = reader.stream_position()?;
read_node_name(reader, ro, base, &mut node, true)?; read_node_name(reader, ro, base, &mut node, true)?;
@ -103,7 +115,9 @@ pub(crate) fn node_parser<R: Read + Seek>(reader: &mut R, ro: &ReadOptions, _: (
fn matches_name(node: &NodeType, name: &str) -> bool { fn matches_name(node: &NodeType, name: &str) -> bool {
match node { match node {
NodeType::File(v) => v.name.as_ref().eq_ignore_ascii_case(name), NodeType::File(v) => v.name.as_ref().eq_ignore_ascii_case(name),
NodeType::Directory(v, _) => v.name.is_empty() /* root */ || v.name.as_ref().eq_ignore_ascii_case(name), NodeType::Directory(v, _) => {
v.name.is_empty() /* root */ || v.name.as_ref().eq_ignore_ascii_case(name)
}
} }
} }
@ -114,11 +128,7 @@ pub(crate) fn find_node<'a>(mut node: &'a NodeType, path: &str) -> Option<&'a No
if matches_name(node, current.unwrap()) { if matches_name(node, current.unwrap()) {
match node { match node {
NodeType::File(_) => { NodeType::File(_) => {
return if split.next().is_none() { return if split.next().is_none() { Option::Some(node) } else { Option::None };
Option::Some(node)
} else {
Option::None
};
} }
NodeType::Directory(v, c) => { NodeType::Directory(v, c) => {
// Find child // Find child

View File

@ -1,11 +1,11 @@
use std::fs::File; use std::fs::File;
use std::io::{Seek, SeekFrom}; use std::io::{Seek, SeekFrom};
use std::io; use std::io;
use std::path::{PathBuf, Path}; use std::path::{Path, PathBuf};
use crate::io::DiscIO; use crate::io::DiscIO;
use crate::streams::ReadStream;
use crate::Result; use crate::Result;
use crate::streams::ReadStream;
pub(crate) struct DiscIOISO { pub(crate) struct DiscIOISO {
pub(crate) filename: PathBuf, pub(crate) filename: PathBuf,

View File

@ -1,11 +1,12 @@
//! Disc file format related logic (ISO, NFS, etc) //! Disc file format related logic (ISO, NFS, etc)
use std::{fs, io}; use std::{fs, io, path::Path};
use std::path::Path;
use crate::{Error, Result}; use crate::{
use crate::io::{iso::new_disc_io_iso, nfs::new_disc_io_nfs}; io::{iso::new_disc_io_iso, nfs::new_disc_io_nfs},
use crate::streams::ReadStream; streams::ReadStream,
Error, Result,
};
pub(crate) mod iso; pub(crate) mod iso;
pub(crate) mod nfs; pub(crate) mod nfs;
@ -27,7 +28,9 @@ pub fn has_extension(filename: &Path, extension: &str) -> bool {
// TODO use with Rust 1.53+ // TODO use with Rust 1.53+
// ext.eq_ignore_ascii_case(extension) // ext.eq_ignore_ascii_case(extension)
ext.to_str().unwrap_or("").eq_ignore_ascii_case(extension) ext.to_str().unwrap_or("").eq_ignore_ascii_case(extension)
} else { false } } else {
false
}
} }
/// Creates a new [`DiscIO`] instance. /// Creates a new [`DiscIO`] instance.
@ -57,9 +60,10 @@ pub fn new_disc_io(filename: &Path) -> Result<Box<dyn DiscIO>> {
)); ));
} }
if !meta.unwrap().is_file() { if !meta.unwrap().is_file() {
return Result::Err(Error::DiscFormat( return Result::Err(Error::DiscFormat(format!(
format!("Input is not a file: {}", filename.to_string_lossy()) "Input is not a file: {}",
)); filename.to_string_lossy()
)));
} }
if has_extension(path, "iso") { if has_extension(path, "iso") {
Result::Ok(Box::from(new_disc_io_iso(path)?)) Result::Ok(Box::from(new_disc_io_iso(path)?))

View File

@ -1,13 +1,15 @@
use std::{fs::File, io, io::{Read, Seek, SeekFrom}, path::{Path, PathBuf}}; use std::{
fs::File,
io,
io::{Read, Seek, SeekFrom},
path::{Path, PathBuf},
};
use aes::{Aes128, NewBlockCipher}; use aes::{Aes128, NewBlockCipher};
use binread::{derive_binread, prelude::*}; use binread::{derive_binread, prelude::*};
use block_modes::{block_padding::NoPadding, BlockMode, Cbc}; use block_modes::{block_padding::NoPadding, BlockMode, Cbc};
use crate::disc::{BUFFER_SIZE}; use crate::{disc::BUFFER_SIZE, io::DiscIO, streams::ReadStream, Error, Result};
use crate::io::DiscIO;
use crate::{Error,Result};
use crate::streams::ReadStream;
type Aes128Cbc = Cbc<Aes128, NoPadding>; type Aes128Cbc = Cbc<Aes128, NoPadding>;
@ -39,18 +41,18 @@ pub(crate) struct FBO {
pub(crate) offset: u32, pub(crate) offset: u32,
} }
pub(crate) fn fbo_max() -> FBO { impl Default for FBO {
FBO { fn default() -> Self {
file: u32::MAX, FBO { file: u32::MAX, block: u32::MAX, l_block: u32::MAX, offset: u32::MAX }
block: u32::MAX,
l_block: u32::MAX,
offset: u32::MAX,
} }
} }
impl NFSHeader { impl NFSHeader {
pub(crate) fn calculate_num_files(&self) -> u32 { pub(crate) fn calculate_num_files(&self) -> u32 {
let total_block_count = self.lba_ranges.iter().take(self.lba_range_count as usize) let total_block_count = self
.lba_ranges
.iter()
.take(self.lba_range_count as usize)
.fold(0u32, |acc, range| acc + range.num_blocks); .fold(0u32, |acc, range| acc + range.num_blocks);
(((total_block_count as u64) * 0x8000u64 + (0x200u64 + 0xF9FFFFFu64)) / 0xFA00000u64) as u32 (((total_block_count as u64) * 0x8000u64 + (0x200u64 + 0xF9FFFFFu64)) / 0xFA00000u64) as u32
} }
@ -68,14 +70,9 @@ impl NFSHeader {
physical_block += range.num_blocks; physical_block += range.num_blocks;
} }
if block == u32::MAX { if block == u32::MAX {
fbo_max() FBO::default()
} else { } else {
FBO { FBO { file: block / 8000, block: block % 8000, l_block: block_div, offset: block_off }
file: block / 8000,
block: block % 8000,
l_block: block_div,
offset: block_off,
}
} }
} }
} }
@ -87,11 +84,8 @@ pub(crate) struct DiscIONFS {
} }
pub(crate) fn new_disc_io_nfs(directory: &Path) -> Result<DiscIONFS> { pub(crate) fn new_disc_io_nfs(directory: &Path) -> Result<DiscIONFS> {
let mut disc_io = DiscIONFS { let mut disc_io =
directory: directory.to_owned(), DiscIONFS { directory: directory.to_owned(), key: [0; 16], header: Option::None };
key: [0; 16],
header: Option::None,
};
disc_io.validate_files()?; disc_io.validate_files()?;
Result::Ok(disc_io) Result::Ok(disc_io)
} }
@ -124,9 +118,10 @@ impl<'a> NFSReadStream<'a> {
fn set_cur_block(&mut self, cur_block: u32) -> io::Result<()> { fn set_cur_block(&mut self, cur_block: u32) -> io::Result<()> {
self.cur_block = cur_block; self.cur_block = cur_block;
self.file.as_ref().unwrap().seek( self.file
SeekFrom::Start(self.cur_block as u64 * BUFFER_SIZE as u64 + 0x200u64) .as_ref()
)?; .unwrap()
.seek(SeekFrom::Start(self.cur_block as u64 * BUFFER_SIZE as u64 + 0x200u64))?;
io::Result::Ok(()) io::Result::Ok(())
} }
@ -164,6 +159,7 @@ impl<'a> NFSReadStream<'a> {
} }
// Decrypt // Decrypt
#[rustfmt::skip]
let iv: [u8; 16] = [ let iv: [u8; 16] = [
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
(phys_addr.l_block & 0xFF) as u8, (phys_addr.l_block & 0xFF) as u8,
@ -171,8 +167,7 @@ impl<'a> NFSReadStream<'a> {
((phys_addr.l_block >> 16) & 0xFF) as u8, ((phys_addr.l_block >> 16) & 0xFF) as u8,
((phys_addr.l_block >> 24) & 0xFF) as u8, ((phys_addr.l_block >> 24) & 0xFF) as u8,
]; ];
Aes128Cbc::new(self.crypto.clone(), &iv.into()) Aes128Cbc::new(self.crypto.clone(), &iv.into()).decrypt(&mut self.buf)?;
.decrypt(&mut self.buf)?;
Result::Ok(()) Result::Ok(())
} }
@ -188,7 +183,8 @@ impl<'a> Read for NFSReadStream<'a> {
let mut read: usize = 0; let mut read: usize = 0;
while rem > 0 { while rem > 0 {
let mut read_size = rem; let mut read_size = rem;
let block_offset: usize = if self.phys_addr.offset == u32::MAX { 0 } else { self.phys_addr.offset as usize }; let block_offset: usize =
if self.phys_addr.offset == u32::MAX { 0 } else { self.phys_addr.offset as usize };
if read_size + block_offset > BUFFER_SIZE { if read_size + block_offset > BUFFER_SIZE {
read_size = BUFFER_SIZE - block_offset read_size = BUFFER_SIZE - block_offset
} }
@ -197,11 +193,10 @@ impl<'a> Read for NFSReadStream<'a> {
read += read_size; read += read_size;
rem -= read_size; rem -= read_size;
self.offset += read_size as u64; self.offset += read_size as u64;
self.set_logical_addr(self.offset) self.set_logical_addr(self.offset).map_err(|v| match v {
.map_err(|v| match v { Error::Io(_, v) => v,
Error::Io(_, v) => v, _ => io::Error::from(io::ErrorKind::Other),
_ => io::Error::from(io::ErrorKind::Other) })?;
})?;
} }
io::Result::Ok(read) io::Result::Ok(read)
} }
@ -214,23 +209,18 @@ impl<'a> Seek for NFSReadStream<'a> {
SeekFrom::End(v) => (self.stable_stream_len()? as i64 + v) as u64, SeekFrom::End(v) => (self.stable_stream_len()? as i64 + v) as u64,
SeekFrom::Current(v) => (self.offset as i64 + v) as u64, SeekFrom::Current(v) => (self.offset as i64 + v) as u64,
}; };
self.set_logical_addr(self.offset) self.set_logical_addr(self.offset).map_err(|v| match v {
.map_err(|v| match v { Error::Io(_, v) => v,
Error::Io(_, v) => v, _ => io::Error::from(io::ErrorKind::Other),
_ => io::Error::from(io::ErrorKind::Other) })?;
})?;
io::Result::Ok(self.offset) io::Result::Ok(self.offset)
} }
fn stream_position(&mut self) -> io::Result<u64> { fn stream_position(&mut self) -> io::Result<u64> { io::Result::Ok(self.offset) }
io::Result::Ok(self.offset)
}
} }
impl<'a> ReadStream for NFSReadStream<'a> { impl<'a> ReadStream for NFSReadStream<'a> {
fn stable_stream_len(&mut self) -> io::Result<u64> { fn stable_stream_len(&mut self) -> io::Result<u64> { todo!() }
todo!()
}
} }
impl DiscIO for DiscIONFS { impl DiscIO for DiscIONFS {
@ -239,7 +229,7 @@ impl DiscIO for DiscIONFS {
disc_io: self, disc_io: self,
file: Option::None, file: Option::None,
crypto: Aes128::new(&self.key.into()), crypto: Aes128::new(&self.key.into()),
phys_addr: fbo_max(), phys_addr: FBO::default(),
offset, offset,
cur_file: u32::MAX, cur_file: u32::MAX,
cur_block: u32::MAX, cur_block: u32::MAX,
@ -282,9 +272,13 @@ impl DiscIONFS {
))); )));
} }
File::open(key_path.as_path()) File::open(key_path.as_path())
.map_err(|v| Error::Io(format!("Failed to open {}", key_path.to_string_lossy()), v))? .map_err(|v| {
Error::Io(format!("Failed to open {}", key_path.to_string_lossy()), v)
})?
.read(&mut self.key) .read(&mut self.key)
.map_err(|v| Error::Io(format!("Failed to read {}", key_path.to_string_lossy()), v))?; .map_err(|v| {
Error::Io(format!("Failed to read {}", key_path.to_string_lossy()), v)
})?;
} }
{ {
// Load header from first file // Load header from first file

View File

@ -26,8 +26,8 @@
//! println!(s); //! println!(s);
//! } //! }
//! ``` //! ```
pub mod fst;
pub mod disc; pub mod disc;
pub mod fst;
pub mod io; pub mod io;
pub mod streams; pub mod streams;
@ -42,25 +42,22 @@ pub enum Error {
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
impl From<std::io::Error> for Error { impl From<std::io::Error> for Error {
fn from(v: std::io::Error) -> Self { fn from(v: std::io::Error) -> Self { Error::Io("I/O error".to_string(), v) }
Error::Io("I/O error".to_string(), v)
}
} }
impl From<binread::Error> for Error { impl From<binread::Error> for Error {
fn from(v: binread::Error) -> Self { fn from(v: binread::Error) -> Self { Error::BinaryFormat(v) }
Error::BinaryFormat(v)
}
} }
impl From<block_modes::BlockModeError> for Error { impl From<block_modes::BlockModeError> for Error {
fn from(v: block_modes::BlockModeError) -> Self { fn from(v: block_modes::BlockModeError) -> Self { Error::Encryption(v) }
Error::Encryption(v)
}
} }
#[inline(always)] #[inline(always)]
pub(crate) fn div_rem<T: std::ops::Div<Output=T> + std::ops::Rem<Output=T> + Copy>(x: T, y: T) -> (T, T) { pub(crate) fn div_rem<T: std::ops::Div<Output = T> + std::ops::Rem<Output = T> + Copy>(
x: T,
y: T,
) -> (T, T) {
let quot = x / y; let quot = x / y;
let rem = x % y; let rem = x % y;
(quot, rem) (quot, rem)

View File

@ -1,9 +1,13 @@
//! Common stream types //! Common stream types
use std::{fs::File, io, io::{Read, Seek, SeekFrom}}; use std::{
use std::ops::DerefMut; fs::File,
io,
io::{Read, Seek, SeekFrom},
ops::DerefMut,
};
/// Creates a fixed-size array from a slice. /// Creates a fixed-size array reference from a slice.
#[macro_export] #[macro_export]
macro_rules! array_ref { macro_rules! array_ref {
($slice:expr, $offset:expr, $size:expr) => {{ ($slice:expr, $offset:expr, $size:expr) => {{
@ -12,7 +16,19 @@ macro_rules! array_ref {
unsafe { &*(slice.as_ptr() as *const [_; $size]) } unsafe { &*(slice.as_ptr() as *const [_; $size]) }
} }
to_array(&$slice[$offset..$offset + $size]) to_array(&$slice[$offset..$offset + $size])
}} }};
}
/// Creates a mutable fixed-size array reference from a slice.
#[macro_export]
macro_rules! array_ref_mut {
($slice:expr, $offset:expr, $size:expr) => {{
#[inline]
fn to_array<T>(slice: &mut [T]) -> &mut [T; $size] {
unsafe { &mut *(slice.as_ptr() as *mut [_; $size]) }
}
to_array(&mut $slice[$offset..$offset + $size])
}};
} }
pub trait ReadStream: Read + Seek { pub trait ReadStream: Read + Seek {
@ -24,13 +40,10 @@ pub trait ReadStream: Read + Seek {
/// Creates a windowed read sub-stream with offset and size. /// Creates a windowed read sub-stream with offset and size.
/// ///
/// Seeks underlying stream immediately. /// Seeks underlying stream immediately.
fn new_window(&mut self, offset: u64, size: u64) -> io::Result<SharedWindowedReadStream> where Self: Sized { fn new_window(&mut self, offset: u64, size: u64) -> io::Result<SharedWindowedReadStream>
where Self: Sized {
self.seek(SeekFrom::Start(offset))?; self.seek(SeekFrom::Start(offset))?;
io::Result::Ok(SharedWindowedReadStream { io::Result::Ok(SharedWindowedReadStream { base: self, begin: offset, end: offset + size })
base: self,
begin: offset,
end: offset + size,
})
} }
} }
@ -56,13 +69,13 @@ pub struct OwningWindowedReadStream<'a> {
} }
/// Takes ownership of & wraps a read stream into a windowed read stream. /// Takes ownership of & wraps a read stream into a windowed read stream.
pub fn wrap_windowed<'a>(mut base: Box<dyn ReadStream + 'a>, offset: u64, size: u64) -> io::Result<OwningWindowedReadStream<'a>> { pub fn wrap_windowed<'a>(
mut base: Box<dyn ReadStream + 'a>,
offset: u64,
size: u64,
) -> io::Result<OwningWindowedReadStream<'a>> {
base.seek(SeekFrom::Start(offset))?; base.seek(SeekFrom::Start(offset))?;
io::Result::Ok(OwningWindowedReadStream { io::Result::Ok(OwningWindowedReadStream { base, begin: offset, end: offset + size })
base,
begin: offset,
end: offset + size,
})
} }
pub struct SharedWindowedReadStream<'a> { pub struct SharedWindowedReadStream<'a> {
@ -98,15 +111,11 @@ fn windowed_seek(stream: &mut dyn WindowedReadStream, pos: SeekFrom) -> io::Resu
} }
impl<'a> Read for OwningWindowedReadStream<'a> { impl<'a> Read for OwningWindowedReadStream<'a> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { windowed_read(self, buf) }
windowed_read(self, buf)
}
} }
impl<'a> Seek for OwningWindowedReadStream<'a> { impl<'a> Seek for OwningWindowedReadStream<'a> {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { windowed_seek(self, pos) }
windowed_seek(self, pos)
}
fn stream_position(&mut self) -> io::Result<u64> { fn stream_position(&mut self) -> io::Result<u64> {
Result::Ok(self.base.stream_position()? - self.begin) Result::Ok(self.base.stream_position()? - self.begin)
@ -114,31 +123,21 @@ impl<'a> Seek for OwningWindowedReadStream<'a> {
} }
impl<'a> ReadStream for OwningWindowedReadStream<'a> { impl<'a> ReadStream for OwningWindowedReadStream<'a> {
fn stable_stream_len(&mut self) -> io::Result<u64> { fn stable_stream_len(&mut self) -> io::Result<u64> { Result::Ok(self.end - self.begin) }
Result::Ok(self.end - self.begin)
}
} }
impl<'a> WindowedReadStream for OwningWindowedReadStream<'a> { impl<'a> WindowedReadStream for OwningWindowedReadStream<'a> {
fn base_stream(&mut self) -> &mut dyn ReadStream { fn base_stream(&mut self) -> &mut dyn ReadStream { self.base.deref_mut() }
self.base.deref_mut()
}
fn window(&self) -> (u64, u64) { fn window(&self) -> (u64, u64) { (self.begin, self.end) }
(self.begin, self.end)
}
} }
impl<'a> Read for SharedWindowedReadStream<'a> { impl<'a> Read for SharedWindowedReadStream<'a> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { windowed_read(self, buf) }
windowed_read(self, buf)
}
} }
impl<'a> Seek for SharedWindowedReadStream<'a> { impl<'a> Seek for SharedWindowedReadStream<'a> {
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { windowed_seek(self, pos) }
windowed_seek(self, pos)
}
fn stream_position(&mut self) -> io::Result<u64> { fn stream_position(&mut self) -> io::Result<u64> {
Result::Ok(self.base.stream_position()? - self.begin) Result::Ok(self.base.stream_position()? - self.begin)
@ -146,17 +145,11 @@ impl<'a> Seek for SharedWindowedReadStream<'a> {
} }
impl<'a> ReadStream for SharedWindowedReadStream<'a> { impl<'a> ReadStream for SharedWindowedReadStream<'a> {
fn stable_stream_len(&mut self) -> io::Result<u64> { fn stable_stream_len(&mut self) -> io::Result<u64> { Result::Ok(self.end - self.begin) }
Result::Ok(self.end - self.begin)
}
} }
impl<'a> WindowedReadStream for SharedWindowedReadStream<'a> { impl<'a> WindowedReadStream for SharedWindowedReadStream<'a> {
fn base_stream(&mut self) -> &mut dyn ReadStream { fn base_stream(&mut self) -> &mut dyn ReadStream { self.base }
self.base
}
fn window(&self) -> (u64, u64) { fn window(&self) -> (u64, u64) { (self.begin, self.end) }
(self.begin, self.end)
}
} }