mirror of https://github.com/encounter/nod-rs.git
Reformat with rustfmt
This commit is contained in:
parent
9df1aa4149
commit
aaf9d37306
|
@ -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
|
36
src/bin.rs
36
src/bin.rs
|
@ -1,14 +1,17 @@
|
|||
use std::{env, fs, io};
|
||||
use std::io::BufWriter;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{
|
||||
env, fs, io,
|
||||
io::BufWriter,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use clap::{AppSettings, clap_app};
|
||||
use clap::{clap_app, AppSettings};
|
||||
use file_size;
|
||||
|
||||
use nod::disc::{new_disc_base, PartReadStream};
|
||||
use nod::fst::NodeType;
|
||||
use nod::io::{has_extension, new_disc_io};
|
||||
use nod::Result;
|
||||
use nod::{
|
||||
disc::{new_disc_base, PartReadStream},
|
||||
fst::NodeType,
|
||||
io::{has_extension, new_disc_io},
|
||||
Result,
|
||||
};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let matches = clap_app!(nodtool =>
|
||||
|
@ -36,7 +39,8 @@ Phillip Stephens (Antidote)")
|
|||
(@arg DIR: "Output directory (optional)")
|
||||
(@arg quiet: -q "Quiet output")
|
||||
)
|
||||
).get_matches();
|
||||
)
|
||||
.get_matches();
|
||||
if let Some(matches) = matches.subcommand_matches("extract") {
|
||||
let file: PathBuf = PathBuf::from(matches.value_of("FILE").unwrap());
|
||||
let output_dir: PathBuf;
|
||||
|
@ -61,12 +65,20 @@ Phillip Stephens (Antidote)")
|
|||
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 {
|
||||
NodeType::File(v) => {
|
||||
let mut file_path = base_path.to_owned();
|
||||
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 mut buf_writer = BufWriter::with_capacity(partition.ideal_buffer_size(), file);
|
||||
io::copy(&mut partition.begin_file_stream(v)?, &mut buf_writer)?;
|
||||
|
|
|
@ -1,29 +1,31 @@
|
|||
use std::io;
|
||||
use std::io::{Read, Seek, SeekFrom};
|
||||
use std::{
|
||||
io,
|
||||
io::{Read, Seek, SeekFrom},
|
||||
};
|
||||
|
||||
use binread::prelude::*;
|
||||
|
||||
use crate::{div_rem, Result};
|
||||
use crate::disc::{BI2Header, BUFFER_SIZE, DiscBase, DiscIO, Header, PartHeader, PartReadStream};
|
||||
use crate::fst::{find_node, Node, node_parser, NodeKind, NodeType};
|
||||
use crate::streams::{ReadStream, SharedWindowedReadStream};
|
||||
use crate::{
|
||||
disc::{BI2Header, DiscBase, DiscIO, Header, PartHeader, PartReadStream, BUFFER_SIZE},
|
||||
div_rem,
|
||||
fst::{find_node, node_parser, Node, NodeKind, NodeType},
|
||||
streams::{ReadStream, SharedWindowedReadStream},
|
||||
Result,
|
||||
};
|
||||
|
||||
pub(crate) struct DiscGCN {
|
||||
pub(crate) header: Header,
|
||||
}
|
||||
|
||||
pub(crate) fn new_disc_gcn(header: Header) -> Result<DiscGCN> {
|
||||
Result::Ok(DiscGCN {
|
||||
header
|
||||
})
|
||||
}
|
||||
pub(crate) fn new_disc_gcn(header: Header) -> Result<DiscGCN> { Result::Ok(DiscGCN { header }) }
|
||||
|
||||
impl DiscBase for DiscGCN {
|
||||
fn get_header(&self) -> &Header {
|
||||
&self.header
|
||||
}
|
||||
fn get_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 {
|
||||
stream: disc_io.begin_read_stream(0)?,
|
||||
offset: 0,
|
||||
|
@ -40,7 +42,6 @@ struct GCPartReadStream<'a> {
|
|||
buf: [u8; BUFFER_SIZE],
|
||||
}
|
||||
|
||||
|
||||
impl<'a> Read for GCPartReadStream<'a> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
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)
|
||||
}
|
||||
|
||||
fn stream_position(&mut self) -> io::Result<u64> {
|
||||
io::Result::Ok(self.offset)
|
||||
}
|
||||
fn stream_position(&mut self) -> io::Result<u64> { io::Result::Ok(self.offset) }
|
||||
}
|
||||
|
||||
impl<'a> ReadStream for GCPartReadStream<'a> {
|
||||
fn stable_stream_len(&mut self) -> io::Result<u64> {
|
||||
self.stream.stable_stream_len()
|
||||
}
|
||||
fn stable_stream_len(&mut self) -> io::Result<u64> { self.stream.stable_stream_len() }
|
||||
}
|
||||
|
||||
impl<'a> PartReadStream for GCPartReadStream<'a> {
|
||||
|
@ -108,9 +105,7 @@ impl<'a> PartReadStream for GCPartReadStream<'a> {
|
|||
Result::Ok(Box::from(self.read_be::<GCPartition>()?))
|
||||
}
|
||||
|
||||
fn ideal_buffer_size(&self) -> usize {
|
||||
BUFFER_SIZE
|
||||
}
|
||||
fn ideal_buffer_size(&self) -> usize { BUFFER_SIZE }
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, BinRead)]
|
||||
|
@ -123,11 +118,7 @@ pub(crate) struct GCPartition {
|
|||
}
|
||||
|
||||
impl PartHeader for GCPartition {
|
||||
fn root_node(&self) -> &NodeType {
|
||||
&self.root_node
|
||||
}
|
||||
fn root_node(&self) -> &NodeType { &self.root_node }
|
||||
|
||||
fn find_node(&self, path: &str) -> Option<&NodeType> {
|
||||
find_node(&self.root_node, path)
|
||||
}
|
||||
fn find_node(&self, path: &str) -> Option<&NodeType> { find_node(&self.root_node, path) }
|
||||
}
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
//! Disc type related logic (GameCube, Wii)
|
||||
|
||||
use std::fmt::Debug;
|
||||
use std::io;
|
||||
use std::{fmt::Debug, io};
|
||||
|
||||
use binread::{BinReaderExt, NullString, prelude::*};
|
||||
use binread::{prelude::*, BinReaderExt, NullString};
|
||||
|
||||
use crate::{Error, Result};
|
||||
use crate::disc::{gcn::new_disc_gcn, wii::new_disc_wii};
|
||||
use crate::fst::{Node, NodeType};
|
||||
use crate::io::DiscIO;
|
||||
use crate::streams::{ReadStream, SharedWindowedReadStream};
|
||||
use crate::{
|
||||
disc::{gcn::new_disc_gcn, wii::new_disc_wii},
|
||||
fst::{Node, NodeType},
|
||||
io::DiscIO,
|
||||
streams::{ReadStream, SharedWindowedReadStream},
|
||||
Error, Result,
|
||||
};
|
||||
|
||||
pub(crate) mod gcn;
|
||||
pub(crate) mod wii;
|
||||
|
@ -89,7 +90,10 @@ pub trait DiscBase {
|
|||
/// let disc_base = new_disc_base(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.
|
||||
|
|
|
@ -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 block_modes::{block_padding::NoPadding, BlockMode, Cbc};
|
||||
use sha1::{digest, Digest, Sha1};
|
||||
|
||||
use crate::disc::{BI2Header, BUFFER_SIZE, DiscBase, DiscIO, Header, PartHeader, PartReadStream};
|
||||
use crate::{Error, div_rem, Result, array_ref};
|
||||
use crate::fst::{find_node, Node, NodeKind, NodeType, node_parser};
|
||||
use crate::streams::{OwningWindowedReadStream, ReadStream, SharedWindowedReadStream, wrap_windowed};
|
||||
use crate::{
|
||||
array_ref,
|
||||
disc::{BI2Header, DiscBase, DiscIO, Header, PartHeader, PartReadStream, BUFFER_SIZE},
|
||||
div_rem,
|
||||
fst::{find_node, node_parser, Node, NodeKind, NodeType},
|
||||
streams::{wrap_windowed, OwningWindowedReadStream, ReadStream, SharedWindowedReadStream},
|
||||
Error, Result,
|
||||
};
|
||||
|
||||
type Aes128Cbc = Cbc<Aes128, NoPadding>;
|
||||
|
||||
const BLOCK_SIZE: usize = 0x7c00;
|
||||
const BUFFER_OFFSET: usize = BUFFER_SIZE - BLOCK_SIZE;
|
||||
#[rustfmt::skip]
|
||||
const COMMON_KEYS: [[u8; 16]; 2] = [
|
||||
/* Normal */
|
||||
[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> {
|
||||
let mut disc = DiscWii {
|
||||
header,
|
||||
part_info: stream.read_be()?,
|
||||
};
|
||||
let mut disc = DiscWii { header, part_info: stream.read_be()? };
|
||||
disc.decrypt_partition_keys()?;
|
||||
Result::Ok(disc)
|
||||
}
|
||||
|
@ -202,29 +207,38 @@ impl DiscWii {
|
|||
Aes128Cbc::new(
|
||||
Aes128::new(&COMMON_KEYS[ticket.common_key_idx as usize].into()),
|
||||
&iv.into(),
|
||||
).decrypt(&mut ticket.enc_key)?;
|
||||
)
|
||||
.decrypt(&mut ticket.enc_key)?;
|
||||
}
|
||||
Result::Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl DiscBase for DiscWii {
|
||||
fn get_header(&self) -> &Header {
|
||||
&self.header
|
||||
}
|
||||
fn get_header(&self) -> &Header { &self.header }
|
||||
|
||||
fn get_data_partition<'a>(&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)
|
||||
fn get_data_partition<'a>(
|
||||
&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()))?;
|
||||
let data_off = part.part_header.data_off;
|
||||
let result = Box::new(WiiPartReadStream {
|
||||
stream: wrap_windowed(
|
||||
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() {
|
||||
Aes128::new(&part.part_header.ticket.enc_key.into()).into()
|
||||
} else { Option::None },
|
||||
} else {
|
||||
Option::None
|
||||
},
|
||||
offset: 0,
|
||||
cur_block: u64::MAX,
|
||||
buf: [0; 0x8000],
|
||||
|
@ -254,9 +268,7 @@ impl<'a> PartReadStream for WiiPartReadStream<'a> {
|
|||
Result::Ok(Box::from(self.read_be::<WiiPartition>()?))
|
||||
}
|
||||
|
||||
fn ideal_buffer_size(&self) -> usize {
|
||||
BLOCK_SIZE
|
||||
}
|
||||
fn ideal_buffer_size(&self) -> usize { BLOCK_SIZE }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -277,7 +289,9 @@ fn decrypt_block(part: &mut WiiPartReadStream, cluster: usize) -> io::Result<()>
|
|||
.decrypt(&mut part.buf[BUFFER_OFFSET..])
|
||||
.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);
|
||||
group %= 8;
|
||||
// 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 output = hash.finalize();
|
||||
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
|
||||
|
@ -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 output = hash.finalize();
|
||||
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
|
||||
|
@ -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 output = hash.finalize();
|
||||
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;
|
||||
}
|
||||
|
||||
buf[read..read + cache_size]
|
||||
.copy_from_slice(&self.buf[BUFFER_OFFSET + block_offset..
|
||||
BUFFER_OFFSET + block_offset + cache_size]);
|
||||
buf[read..read + cache_size].copy_from_slice(
|
||||
&self.buf[BUFFER_OFFSET + block_offset..BUFFER_OFFSET + block_offset + cache_size],
|
||||
);
|
||||
read += cache_size;
|
||||
rem -= cache_size;
|
||||
block_offset = 0;
|
||||
|
@ -365,9 +394,7 @@ impl<'a> Seek for WiiPartReadStream<'a> {
|
|||
io::Result::Ok(self.offset)
|
||||
}
|
||||
|
||||
fn stream_position(&mut self) -> io::Result<u64> {
|
||||
io::Result::Ok(self.offset)
|
||||
}
|
||||
fn stream_position(&mut self) -> io::Result<u64> { io::Result::Ok(self.offset) }
|
||||
}
|
||||
|
||||
impl<'a> ReadStream for WiiPartReadStream<'a> {
|
||||
|
@ -386,11 +413,7 @@ pub(crate) struct WiiPartition {
|
|||
}
|
||||
|
||||
impl PartHeader for WiiPartition {
|
||||
fn root_node(&self) -> &NodeType {
|
||||
&self.root_node
|
||||
}
|
||||
fn root_node(&self) -> &NodeType { &self.root_node }
|
||||
|
||||
fn find_node(&self, path: &str) -> Option<&NodeType> {
|
||||
find_node(&self.root_node, path)
|
||||
}
|
||||
fn find_node(&self, path: &str) -> Option<&NodeType> { find_node(&self.root_node, path) }
|
||||
}
|
||||
|
|
30
src/fst.rs
30
src/fst.rs
|
@ -2,7 +2,7 @@
|
|||
|
||||
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;
|
||||
|
||||
/// 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<()> {
|
||||
if !root {
|
||||
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(())
|
||||
};
|
||||
match node {
|
||||
NodeType::File(v) => { decode_name(v)?; }
|
||||
NodeType::File(v) => {
|
||||
decode_name(v)?;
|
||||
}
|
||||
NodeType::Directory(v, c) => {
|
||||
decode_name(v)?;
|
||||
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(())
|
||||
}
|
||||
|
||||
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 base = reader.stream_position()?;
|
||||
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 {
|
||||
match node {
|
||||
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()) {
|
||||
match node {
|
||||
NodeType::File(_) => {
|
||||
return if split.next().is_none() {
|
||||
Option::Some(node)
|
||||
} else {
|
||||
Option::None
|
||||
};
|
||||
return if split.next().is_none() { Option::Some(node) } else { Option::None };
|
||||
}
|
||||
NodeType::Directory(v, c) => {
|
||||
// Find child
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use std::fs::File;
|
||||
use std::io::{Seek, SeekFrom};
|
||||
use std::io;
|
||||
use std::path::{PathBuf, Path};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::io::DiscIO;
|
||||
use crate::streams::ReadStream;
|
||||
use crate::Result;
|
||||
use crate::streams::ReadStream;
|
||||
|
||||
pub(crate) struct DiscIOISO {
|
||||
pub(crate) filename: PathBuf,
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
//! Disc file format related logic (ISO, NFS, etc)
|
||||
|
||||
use std::{fs, io};
|
||||
use std::path::Path;
|
||||
use std::{fs, io, path::Path};
|
||||
|
||||
use crate::{Error, Result};
|
||||
use crate::io::{iso::new_disc_io_iso, nfs::new_disc_io_nfs};
|
||||
use crate::streams::ReadStream;
|
||||
use crate::{
|
||||
io::{iso::new_disc_io_iso, nfs::new_disc_io_nfs},
|
||||
streams::ReadStream,
|
||||
Error, Result,
|
||||
};
|
||||
|
||||
pub(crate) mod iso;
|
||||
pub(crate) mod nfs;
|
||||
|
@ -27,7 +28,9 @@ pub fn has_extension(filename: &Path, extension: &str) -> bool {
|
|||
// TODO use with Rust 1.53+
|
||||
// ext.eq_ignore_ascii_case(extension)
|
||||
ext.to_str().unwrap_or("").eq_ignore_ascii_case(extension)
|
||||
} else { false }
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// 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() {
|
||||
return Result::Err(Error::DiscFormat(
|
||||
format!("Input is not a file: {}", filename.to_string_lossy())
|
||||
));
|
||||
return Result::Err(Error::DiscFormat(format!(
|
||||
"Input is not a file: {}",
|
||||
filename.to_string_lossy()
|
||||
)));
|
||||
}
|
||||
if has_extension(path, "iso") {
|
||||
Result::Ok(Box::from(new_disc_io_iso(path)?))
|
||||
|
|
|
@ -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 binread::{derive_binread, prelude::*};
|
||||
use block_modes::{block_padding::NoPadding, BlockMode, Cbc};
|
||||
|
||||
use crate::disc::{BUFFER_SIZE};
|
||||
use crate::io::DiscIO;
|
||||
use crate::{Error,Result};
|
||||
use crate::streams::ReadStream;
|
||||
use crate::{disc::BUFFER_SIZE, io::DiscIO, streams::ReadStream, Error, Result};
|
||||
|
||||
type Aes128Cbc = Cbc<Aes128, NoPadding>;
|
||||
|
||||
|
@ -39,18 +41,18 @@ pub(crate) struct FBO {
|
|||
pub(crate) offset: u32,
|
||||
}
|
||||
|
||||
pub(crate) fn fbo_max() -> FBO {
|
||||
FBO {
|
||||
file: u32::MAX,
|
||||
block: u32::MAX,
|
||||
l_block: u32::MAX,
|
||||
offset: u32::MAX,
|
||||
impl Default for FBO {
|
||||
fn default() -> Self {
|
||||
FBO { file: u32::MAX, block: u32::MAX, l_block: u32::MAX, offset: u32::MAX }
|
||||
}
|
||||
}
|
||||
|
||||
impl NFSHeader {
|
||||
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);
|
||||
(((total_block_count as u64) * 0x8000u64 + (0x200u64 + 0xF9FFFFFu64)) / 0xFA00000u64) as u32
|
||||
}
|
||||
|
@ -68,14 +70,9 @@ impl NFSHeader {
|
|||
physical_block += range.num_blocks;
|
||||
}
|
||||
if block == u32::MAX {
|
||||
fbo_max()
|
||||
FBO::default()
|
||||
} else {
|
||||
FBO {
|
||||
file: block / 8000,
|
||||
block: block % 8000,
|
||||
l_block: block_div,
|
||||
offset: block_off,
|
||||
}
|
||||
FBO { 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> {
|
||||
let mut disc_io = DiscIONFS {
|
||||
directory: directory.to_owned(),
|
||||
key: [0; 16],
|
||||
header: Option::None,
|
||||
};
|
||||
let mut disc_io =
|
||||
DiscIONFS { directory: directory.to_owned(), key: [0; 16], header: Option::None };
|
||||
disc_io.validate_files()?;
|
||||
Result::Ok(disc_io)
|
||||
}
|
||||
|
@ -124,9 +118,10 @@ impl<'a> NFSReadStream<'a> {
|
|||
|
||||
fn set_cur_block(&mut self, cur_block: u32) -> io::Result<()> {
|
||||
self.cur_block = cur_block;
|
||||
self.file.as_ref().unwrap().seek(
|
||||
SeekFrom::Start(self.cur_block as u64 * BUFFER_SIZE as u64 + 0x200u64)
|
||||
)?;
|
||||
self.file
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.seek(SeekFrom::Start(self.cur_block as u64 * BUFFER_SIZE as u64 + 0x200u64))?;
|
||||
io::Result::Ok(())
|
||||
}
|
||||
|
||||
|
@ -164,6 +159,7 @@ impl<'a> NFSReadStream<'a> {
|
|||
}
|
||||
|
||||
// Decrypt
|
||||
#[rustfmt::skip]
|
||||
let iv: [u8; 16] = [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
(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 >> 24) & 0xFF) as u8,
|
||||
];
|
||||
Aes128Cbc::new(self.crypto.clone(), &iv.into())
|
||||
.decrypt(&mut self.buf)?;
|
||||
Aes128Cbc::new(self.crypto.clone(), &iv.into()).decrypt(&mut self.buf)?;
|
||||
|
||||
Result::Ok(())
|
||||
}
|
||||
|
@ -188,7 +183,8 @@ impl<'a> Read for NFSReadStream<'a> {
|
|||
let mut read: usize = 0;
|
||||
while rem > 0 {
|
||||
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 {
|
||||
read_size = BUFFER_SIZE - block_offset
|
||||
}
|
||||
|
@ -197,10 +193,9 @@ impl<'a> Read for NFSReadStream<'a> {
|
|||
read += read_size;
|
||||
rem -= read_size;
|
||||
self.offset += read_size as u64;
|
||||
self.set_logical_addr(self.offset)
|
||||
.map_err(|v| match v {
|
||||
self.set_logical_addr(self.offset).map_err(|v| match v {
|
||||
Error::Io(_, v) => v,
|
||||
_ => io::Error::from(io::ErrorKind::Other)
|
||||
_ => io::Error::from(io::ErrorKind::Other),
|
||||
})?;
|
||||
}
|
||||
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::Current(v) => (self.offset as i64 + v) as u64,
|
||||
};
|
||||
self.set_logical_addr(self.offset)
|
||||
.map_err(|v| match v {
|
||||
self.set_logical_addr(self.offset).map_err(|v| match v {
|
||||
Error::Io(_, v) => v,
|
||||
_ => io::Error::from(io::ErrorKind::Other)
|
||||
_ => io::Error::from(io::ErrorKind::Other),
|
||||
})?;
|
||||
io::Result::Ok(self.offset)
|
||||
}
|
||||
|
||||
fn stream_position(&mut self) -> io::Result<u64> {
|
||||
io::Result::Ok(self.offset)
|
||||
}
|
||||
fn stream_position(&mut self) -> io::Result<u64> { io::Result::Ok(self.offset) }
|
||||
}
|
||||
|
||||
impl<'a> ReadStream for NFSReadStream<'a> {
|
||||
fn stable_stream_len(&mut self) -> io::Result<u64> {
|
||||
todo!()
|
||||
}
|
||||
fn stable_stream_len(&mut self) -> io::Result<u64> { todo!() }
|
||||
}
|
||||
|
||||
impl DiscIO for DiscIONFS {
|
||||
|
@ -239,7 +229,7 @@ impl DiscIO for DiscIONFS {
|
|||
disc_io: self,
|
||||
file: Option::None,
|
||||
crypto: Aes128::new(&self.key.into()),
|
||||
phys_addr: fbo_max(),
|
||||
phys_addr: FBO::default(),
|
||||
offset,
|
||||
cur_file: u32::MAX,
|
||||
cur_block: u32::MAX,
|
||||
|
@ -282,9 +272,13 @@ impl DiscIONFS {
|
|||
)));
|
||||
}
|
||||
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)
|
||||
.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
|
||||
|
|
19
src/lib.rs
19
src/lib.rs
|
@ -26,8 +26,8 @@
|
|||
//! println!(s);
|
||||
//! }
|
||||
//! ```
|
||||
pub mod fst;
|
||||
pub mod disc;
|
||||
pub mod fst;
|
||||
pub mod io;
|
||||
pub mod streams;
|
||||
|
||||
|
@ -42,25 +42,22 @@ pub enum Error {
|
|||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
impl From<std::io::Error> for Error {
|
||||
fn from(v: std::io::Error) -> Self {
|
||||
Error::Io("I/O error".to_string(), v)
|
||||
}
|
||||
fn from(v: std::io::Error) -> Self { Error::Io("I/O error".to_string(), v) }
|
||||
}
|
||||
|
||||
impl From<binread::Error> for Error {
|
||||
fn from(v: binread::Error) -> Self {
|
||||
Error::BinaryFormat(v)
|
||||
}
|
||||
fn from(v: binread::Error) -> Self { Error::BinaryFormat(v) }
|
||||
}
|
||||
|
||||
impl From<block_modes::BlockModeError> for Error {
|
||||
fn from(v: block_modes::BlockModeError) -> Self {
|
||||
Error::Encryption(v)
|
||||
}
|
||||
fn from(v: block_modes::BlockModeError) -> Self { Error::Encryption(v) }
|
||||
}
|
||||
|
||||
#[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 rem = x % y;
|
||||
(quot, rem)
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
//! Common stream types
|
||||
|
||||
use std::{fs::File, io, io::{Read, Seek, SeekFrom}};
|
||||
use std::ops::DerefMut;
|
||||
use std::{
|
||||
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_rules! array_ref {
|
||||
($slice:expr, $offset:expr, $size:expr) => {{
|
||||
|
@ -12,7 +16,19 @@ macro_rules! array_ref {
|
|||
unsafe { &*(slice.as_ptr() as *const [_; $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 {
|
||||
|
@ -24,13 +40,10 @@ pub trait ReadStream: Read + Seek {
|
|||
/// Creates a windowed read sub-stream with offset and size.
|
||||
///
|
||||
/// 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))?;
|
||||
io::Result::Ok(SharedWindowedReadStream {
|
||||
base: self,
|
||||
begin: offset,
|
||||
end: offset + size,
|
||||
})
|
||||
io::Result::Ok(SharedWindowedReadStream { 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.
|
||||
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))?;
|
||||
io::Result::Ok(OwningWindowedReadStream {
|
||||
base,
|
||||
begin: offset,
|
||||
end: offset + size,
|
||||
})
|
||||
io::Result::Ok(OwningWindowedReadStream { base, begin: offset, end: offset + size })
|
||||
}
|
||||
|
||||
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> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
windowed_read(self, buf)
|
||||
}
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { windowed_read(self, buf) }
|
||||
}
|
||||
|
||||
impl<'a> Seek for OwningWindowedReadStream<'a> {
|
||||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||
windowed_seek(self, pos)
|
||||
}
|
||||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { windowed_seek(self, pos) }
|
||||
|
||||
fn stream_position(&mut self) -> io::Result<u64> {
|
||||
Result::Ok(self.base.stream_position()? - self.begin)
|
||||
|
@ -114,31 +123,21 @@ impl<'a> Seek for OwningWindowedReadStream<'a> {
|
|||
}
|
||||
|
||||
impl<'a> ReadStream for OwningWindowedReadStream<'a> {
|
||||
fn stable_stream_len(&mut self) -> io::Result<u64> {
|
||||
Result::Ok(self.end - self.begin)
|
||||
}
|
||||
fn stable_stream_len(&mut self) -> io::Result<u64> { Result::Ok(self.end - self.begin) }
|
||||
}
|
||||
|
||||
impl<'a> WindowedReadStream for OwningWindowedReadStream<'a> {
|
||||
fn base_stream(&mut self) -> &mut dyn ReadStream {
|
||||
self.base.deref_mut()
|
||||
}
|
||||
fn base_stream(&mut self) -> &mut dyn ReadStream { self.base.deref_mut() }
|
||||
|
||||
fn window(&self) -> (u64, u64) {
|
||||
(self.begin, self.end)
|
||||
}
|
||||
fn window(&self) -> (u64, u64) { (self.begin, self.end) }
|
||||
}
|
||||
|
||||
impl<'a> Read for SharedWindowedReadStream<'a> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
windowed_read(self, buf)
|
||||
}
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { windowed_read(self, buf) }
|
||||
}
|
||||
|
||||
impl<'a> Seek for SharedWindowedReadStream<'a> {
|
||||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||
windowed_seek(self, pos)
|
||||
}
|
||||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> { windowed_seek(self, pos) }
|
||||
|
||||
fn stream_position(&mut self) -> io::Result<u64> {
|
||||
Result::Ok(self.base.stream_position()? - self.begin)
|
||||
|
@ -146,17 +145,11 @@ impl<'a> Seek for SharedWindowedReadStream<'a> {
|
|||
}
|
||||
|
||||
impl<'a> ReadStream for SharedWindowedReadStream<'a> {
|
||||
fn stable_stream_len(&mut self) -> io::Result<u64> {
|
||||
Result::Ok(self.end - self.begin)
|
||||
}
|
||||
fn stable_stream_len(&mut self) -> io::Result<u64> { Result::Ok(self.end - self.begin) }
|
||||
}
|
||||
|
||||
impl<'a> WindowedReadStream for SharedWindowedReadStream<'a> {
|
||||
fn base_stream(&mut self) -> &mut dyn ReadStream {
|
||||
self.base
|
||||
}
|
||||
fn base_stream(&mut self) -> &mut dyn ReadStream { self.base }
|
||||
|
||||
fn window(&self) -> (u64, u64) {
|
||||
(self.begin, self.end)
|
||||
}
|
||||
fn window(&self) -> (u64, u64) { (self.begin, self.end) }
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue