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::{
|
||||||
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)?;
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
30
src/fst.rs
30
src/fst.rs
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)?))
|
||||||
|
|
|
@ -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,10 +193,9 @@ 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
|
||||||
|
|
19
src/lib.rs
19
src/lib.rs
|
@ -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)
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue