mirror of https://github.com/encounter/nod-rs.git
Add -h (validate Wii disc hashes); complete documentation
This commit is contained in:
parent
3e78aad790
commit
97c726c209
|
@ -9,6 +9,8 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
toolchain: [ stable, 1.51.0, nightly ]
|
toolchain: [ stable, 1.51.0, nightly ]
|
||||||
|
env:
|
||||||
|
RUSTFLAGS: -D warnings
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions-rs/toolchain@v1
|
- uses: actions-rs/toolchain@v1
|
||||||
|
|
28
src/bin.rs
28
src/bin.rs
|
@ -37,6 +37,7 @@ Phillip Stephens (Antidote)")
|
||||||
(@arg FILE: +required "Path to disc image (ISO or NFS)")
|
(@arg FILE: +required "Path to disc image (ISO or NFS)")
|
||||||
(@arg DIR: "Output directory (optional)")
|
(@arg DIR: "Output directory (optional)")
|
||||||
(@arg quiet: -q "Quiet output")
|
(@arg quiet: -q "Quiet output")
|
||||||
|
(@arg validate: -h "Validate disc hashes (Wii only)")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
@ -57,9 +58,15 @@ Phillip Stephens (Antidote)")
|
||||||
}
|
}
|
||||||
let mut disc_io = new_disc_io(file.as_path())?;
|
let mut disc_io = new_disc_io(file.as_path())?;
|
||||||
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(), matches.is_present("validate"))?;
|
||||||
let header = partition.read_header()?;
|
let header = partition.read_header()?;
|
||||||
extract_node(header.root_node(), partition.as_mut(), output_dir.as_path())?;
|
extract_node(
|
||||||
|
header.root_node(),
|
||||||
|
partition.as_mut(),
|
||||||
|
output_dir.as_path(),
|
||||||
|
matches.is_present("quiet"),
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
Result::Ok(())
|
Result::Ok(())
|
||||||
}
|
}
|
||||||
|
@ -68,16 +75,19 @@ fn extract_node(
|
||||||
node: &NodeType,
|
node: &NodeType,
|
||||||
partition: &mut dyn PartReadStream,
|
partition: &mut dyn PartReadStream,
|
||||||
base_path: &Path,
|
base_path: &Path,
|
||||||
|
quiet: bool,
|
||||||
) -> io::Result<()> {
|
) -> 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!(
|
if !quiet {
|
||||||
"Extracting {} (size: {})",
|
println!(
|
||||||
file_path.to_string_lossy(),
|
"Extracting {} (size: {})",
|
||||||
file_size::fit_4(v.length as u64)
|
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)?;
|
||||||
|
@ -86,14 +96,14 @@ fn extract_node(
|
||||||
if v.name.is_empty() {
|
if v.name.is_empty() {
|
||||||
fs::create_dir_all(base_path)?;
|
fs::create_dir_all(base_path)?;
|
||||||
for x in c {
|
for x in c {
|
||||||
extract_node(x, partition, base_path)?;
|
extract_node(x, partition, base_path, quiet)?;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let mut new_base = base_path.to_owned();
|
let mut new_base = base_path.to_owned();
|
||||||
new_base.push(v.name.as_ref());
|
new_base.push(v.name.as_ref());
|
||||||
fs::create_dir_all(&new_base)?;
|
fs::create_dir_all(&new_base)?;
|
||||||
for x in c {
|
for x in c {
|
||||||
extract_node(x, partition, new_base.as_path())?;
|
extract_node(x, partition, new_base.as_path(), quiet)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ impl DiscBase for DiscGCN {
|
||||||
fn get_data_partition<'a>(
|
fn get_data_partition<'a>(
|
||||||
&self,
|
&self,
|
||||||
disc_io: &'a mut dyn DiscIO,
|
disc_io: &'a mut dyn DiscIO,
|
||||||
|
_validate_hashes: bool,
|
||||||
) -> Result<Box<dyn PartReadStream + 'a>> {
|
) -> 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)?,
|
||||||
|
|
|
@ -18,25 +18,32 @@ pub(crate) mod wii;
|
||||||
/// Shared GameCube & Wii disc header
|
/// Shared GameCube & Wii disc header
|
||||||
#[derive(Clone, Debug, PartialEq, BinRead)]
|
#[derive(Clone, Debug, PartialEq, BinRead)]
|
||||||
pub struct Header {
|
pub struct Header {
|
||||||
|
/// Game ID (e.g. GM8E01 for Metroid Prime)
|
||||||
pub game_id: [u8; 6],
|
pub game_id: [u8; 6],
|
||||||
/// Used in multi-disc games
|
/// Used in multi-disc games
|
||||||
pub disc_num: u8,
|
pub disc_num: u8,
|
||||||
|
/// Disc version
|
||||||
pub disc_version: u8,
|
pub disc_version: u8,
|
||||||
|
/// Audio streaming enabled (bool)
|
||||||
pub audio_streaming: u8,
|
pub audio_streaming: u8,
|
||||||
|
/// Audio streaming buffer size
|
||||||
pub audio_stream_buf_size: u8,
|
pub audio_stream_buf_size: u8,
|
||||||
#[br(pad_before(14))]
|
#[br(pad_before(14))]
|
||||||
/// If this is a Wii disc, this will be 0x5D1C9EA3
|
/// If this is a Wii disc, this will be 0x5D1C9EA3
|
||||||
pub wii_magic: u32,
|
pub wii_magic: u32,
|
||||||
/// If this is a GameCube disc, this will be 0xC2339F3D
|
/// If this is a GameCube disc, this will be 0xC2339F3D
|
||||||
pub gcn_magic: u32,
|
pub gcn_magic: u32,
|
||||||
|
/// Game title
|
||||||
#[br(pad_size_to(64), map = NullString::into_string)]
|
#[br(pad_size_to(64), map = NullString::into_string)]
|
||||||
pub game_title: String,
|
pub game_title: String,
|
||||||
/// Disable hash verification
|
/// Disable hash verification
|
||||||
pub disable_hash_verification: u8,
|
pub disable_hash_verification: u8,
|
||||||
/// Disable disc encryption and H3 hash table loading and verification
|
/// Disable disc encryption and H3 hash table loading and verification
|
||||||
pub disable_disc_enc: u8,
|
pub disable_disc_enc: u8,
|
||||||
|
/// Debug monitor offset
|
||||||
#[br(pad_before(0x39e))]
|
#[br(pad_before(0x39e))]
|
||||||
pub debug_mon_off: u32,
|
pub debug_mon_off: u32,
|
||||||
|
/// Debug monitor load address
|
||||||
pub debug_load_addr: u32,
|
pub debug_load_addr: u32,
|
||||||
#[br(pad_before(0x18))]
|
#[br(pad_before(0x18))]
|
||||||
/// Offset to main DOL (Wii: >> 2)
|
/// Offset to main DOL (Wii: >> 2)
|
||||||
|
@ -47,8 +54,11 @@ pub struct Header {
|
||||||
pub fst_sz: u32,
|
pub fst_sz: u32,
|
||||||
/// File system max size
|
/// File system max size
|
||||||
pub fst_max_sz: u32,
|
pub fst_max_sz: u32,
|
||||||
|
/// File system table load address
|
||||||
pub fst_memory_address: u32,
|
pub fst_memory_address: u32,
|
||||||
|
/// User position
|
||||||
pub user_position: u32,
|
pub user_position: u32,
|
||||||
|
/// User size
|
||||||
#[br(pad_after(4))]
|
#[br(pad_after(4))]
|
||||||
pub user_sz: u32,
|
pub user_sz: u32,
|
||||||
}
|
}
|
||||||
|
@ -79,6 +89,8 @@ pub trait DiscBase: Send + Sync {
|
||||||
|
|
||||||
/// Opens a new partition read stream for the first data partition.
|
/// Opens a new partition read stream for the first data partition.
|
||||||
///
|
///
|
||||||
|
/// `validate_hashes`: Validate Wii disc hashes while reading (slow!)
|
||||||
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// Basic usage:
|
/// Basic usage:
|
||||||
|
@ -88,12 +100,13 @@ pub trait DiscBase: Send + Sync {
|
||||||
///
|
///
|
||||||
/// let mut disc_io = new_disc_io("path/to/file".as_ref())?;
|
/// let mut disc_io = new_disc_io("path/to/file".as_ref())?;
|
||||||
/// 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(), false)?;
|
||||||
/// # Ok::<(), nod::Error>(())
|
/// # Ok::<(), nod::Error>(())
|
||||||
/// ```
|
/// ```
|
||||||
fn get_data_partition<'a>(
|
fn get_data_partition<'a>(
|
||||||
&self,
|
&self,
|
||||||
disc_io: &'a mut dyn DiscIO,
|
disc_io: &'a mut dyn DiscIO,
|
||||||
|
validate_hashes: bool,
|
||||||
) -> Result<Box<dyn PartReadStream + 'a>>;
|
) -> Result<Box<dyn PartReadStream + 'a>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +152,7 @@ pub trait PartReadStream: ReadStream {
|
||||||
///
|
///
|
||||||
/// let mut disc_io = new_disc_io("path/to/file".as_ref())?;
|
/// let mut disc_io = new_disc_io("path/to/file".as_ref())?;
|
||||||
/// 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(), false)?;
|
||||||
/// let header = partition.read_header()?;
|
/// let header = partition.read_header()?;
|
||||||
/// if let Some(NodeType::File(node)) = header.find_node("/MP3/Worlds.txt") {
|
/// if let Some(NodeType::File(node)) = header.find_node("/MP3/Worlds.txt") {
|
||||||
/// let mut s = String::new();
|
/// let mut s = String::new();
|
||||||
|
@ -176,7 +189,7 @@ pub trait PartHeader: Debug + Send + Sync {
|
||||||
///
|
///
|
||||||
/// let mut disc_io = new_disc_io("path/to/file".as_ref())?;
|
/// let mut disc_io = new_disc_io("path/to/file".as_ref())?;
|
||||||
/// 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(), false)?;
|
||||||
/// let header = partition.read_header()?;
|
/// let header = partition.read_header()?;
|
||||||
/// if let Some(NodeType::File(node)) = header.find_node("/MP1/Metroid1.pak") {
|
/// if let Some(NodeType::File(node)) = header.find_node("/MP1/Metroid1.pak") {
|
||||||
/// println!("{}", node.name);
|
/// println!("{}", node.name);
|
||||||
|
|
|
@ -220,6 +220,7 @@ impl DiscBase for DiscWii {
|
||||||
fn get_data_partition<'a>(
|
fn get_data_partition<'a>(
|
||||||
&self,
|
&self,
|
||||||
disc_io: &'a mut dyn DiscIO,
|
disc_io: &'a mut dyn DiscIO,
|
||||||
|
validate_hashes: bool,
|
||||||
) -> Result<Box<dyn PartReadStream + 'a>> {
|
) -> Result<Box<dyn PartReadStream + 'a>> {
|
||||||
let part = self
|
let part = self
|
||||||
.part_info
|
.part_info
|
||||||
|
@ -243,7 +244,7 @@ impl DiscBase for DiscWii {
|
||||||
offset: 0,
|
offset: 0,
|
||||||
cur_block: u64::MAX,
|
cur_block: u64::MAX,
|
||||||
buf: [0; 0x8000],
|
buf: [0; 0x8000],
|
||||||
validate_hashes: false,
|
validate_hashes,
|
||||||
});
|
});
|
||||||
Result::Ok(result)
|
Result::Ok(result)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,9 @@ use encoding_rs::SHIFT_JIS;
|
||||||
/// File system node kind.
|
/// File system node kind.
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum NodeKind {
|
pub enum NodeKind {
|
||||||
|
/// Node is a file.
|
||||||
File,
|
File,
|
||||||
|
/// Node is a directory.
|
||||||
Directory,
|
Directory,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,6 +20,8 @@ pub enum NodeKind {
|
||||||
pub struct Node {
|
pub struct Node {
|
||||||
#[br(temp)]
|
#[br(temp)]
|
||||||
type_and_name_offset: u32,
|
type_and_name_offset: u32,
|
||||||
|
|
||||||
|
/// File system node type.
|
||||||
#[br(calc = if (type_and_name_offset >> 24) != 0 { NodeKind::Directory } else { NodeKind::File })]
|
#[br(calc = if (type_and_name_offset >> 24) != 0 { NodeKind::Directory } else { NodeKind::File })]
|
||||||
pub kind: NodeKind,
|
pub kind: NodeKind,
|
||||||
|
|
||||||
|
|
|
@ -70,10 +70,34 @@ pub fn new_disc_io(filename: &Path) -> Result<Box<dyn DiscIO>> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new [`DiscIO`] instance from a byte slice.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// Basic usage:
|
||||||
|
/// ```no_run
|
||||||
|
/// use nod::io::new_disc_io_from_buf;
|
||||||
|
///
|
||||||
|
/// # #[allow(non_upper_case_globals)] const buf: [u8; 0] = [];
|
||||||
|
/// let mut disc_io = new_disc_io_from_buf(&buf)?;
|
||||||
|
/// # Ok::<(), nod::Error>(())
|
||||||
|
/// ```
|
||||||
pub fn new_disc_io_from_buf(buf: &[u8]) -> Result<Box<dyn DiscIO + '_>> {
|
pub fn new_disc_io_from_buf(buf: &[u8]) -> Result<Box<dyn DiscIO + '_>> {
|
||||||
Ok(Box::from(DiscIOISOStream::new(ByteReadStream { bytes: buf, position: 0 })?))
|
new_disc_io_from_stream(ByteReadStream { bytes: buf, position: 0 })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new [`DiscIO`] instance from an existing [`ReadStream`].
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// Basic usage:
|
||||||
|
/// ```no_run
|
||||||
|
/// use nod::io::new_disc_io_from_buf;
|
||||||
|
///
|
||||||
|
/// # #[allow(non_upper_case_globals)] const buf: [u8; 0] = [];
|
||||||
|
/// let mut disc_io = new_disc_io_from_buf(&buf)?;
|
||||||
|
/// # Ok::<(), nod::Error>(())
|
||||||
|
/// ```
|
||||||
pub fn new_disc_io_from_stream<'a, T: 'a + ReadStream + Sized + Send + Sync>(
|
pub fn new_disc_io_from_stream<'a, T: 'a + ReadStream + Sized + Send + Sync>(
|
||||||
stream: T,
|
stream: T,
|
||||||
) -> Result<Box<dyn DiscIO + 'a>> {
|
) -> Result<Box<dyn DiscIO + 'a>> {
|
||||||
|
|
12
src/lib.rs
12
src/lib.rs
|
@ -1,3 +1,5 @@
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
#![warn(rustdoc::missing_doc_code_examples)]
|
||||||
//! Library for traversing & reading GameCube and Wii disc images.
|
//! Library for traversing & reading GameCube and Wii disc images.
|
||||||
//!
|
//!
|
||||||
//! Based on the C++ library [nod](https://github.com/AxioDL/nod),
|
//! Based on the C++ library [nod](https://github.com/AxioDL/nod),
|
||||||
|
@ -18,7 +20,7 @@
|
||||||
//!
|
//!
|
||||||
//! let mut disc_io = new_disc_io("path/to/file".as_ref())?;
|
//! let mut disc_io = new_disc_io("path/to/file".as_ref())?;
|
||||||
//! 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(), false)?;
|
||||||
//! let header = partition.read_header()?;
|
//! let header = partition.read_header()?;
|
||||||
//! if let Some(NodeType::File(node)) = header.find_node("/MP3/Worlds.txt") {
|
//! if let Some(NodeType::File(node)) = header.find_node("/MP3/Worlds.txt") {
|
||||||
//! let mut s = String::new();
|
//! let mut s = String::new();
|
||||||
|
@ -34,19 +36,25 @@ pub mod fst;
|
||||||
pub mod io;
|
pub mod io;
|
||||||
pub mod streams;
|
pub mod streams;
|
||||||
|
|
||||||
|
/// Error types for nod.
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
/// An error during binary format parsing.
|
||||||
#[error("binary format")]
|
#[error("binary format")]
|
||||||
BinaryFormat(#[from] binrw::Error),
|
BinaryFormat(#[from] binrw::Error),
|
||||||
|
/// An error during Wii disc decryption.
|
||||||
#[error("encryption")]
|
#[error("encryption")]
|
||||||
Encryption(#[from] block_modes::BlockModeError),
|
Encryption(#[from] block_modes::BlockModeError),
|
||||||
|
/// A general I/O error.
|
||||||
#[error("io error: `{0}`")]
|
#[error("io error: `{0}`")]
|
||||||
Io(String, #[source] std::io::Error),
|
Io(String, #[source] std::io::Error),
|
||||||
|
/// An error for disc format related issues.
|
||||||
#[error("disc format error: `{0}`")]
|
#[error("disc format error: `{0}`")]
|
||||||
DiscFormat(String),
|
DiscFormat(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = anyhow::Result<T, Error>;
|
/// Helper result type for [`enum@Error`].
|
||||||
|
pub type Result<T, E = Error> = core::result::Result<T, E>;
|
||||||
|
|
||||||
impl From<std::io::Error> for 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) }
|
||||||
|
|
|
@ -31,6 +31,7 @@ macro_rules! array_ref_mut {
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A helper trait for seekable read streams.
|
||||||
pub trait ReadStream: Read + Seek {
|
pub trait ReadStream: Read + Seek {
|
||||||
/// Replace with [`Read.stream_len`] when stabilized.
|
/// Replace with [`Read.stream_len`] when stabilized.
|
||||||
///
|
///
|
||||||
|
@ -49,6 +50,7 @@ pub trait ReadStream: Read + Seek {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves a type-erased reference to the stream.
|
||||||
fn as_dyn(&mut self) -> &mut dyn ReadStream;
|
fn as_dyn(&mut self) -> &mut dyn ReadStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,9 +74,13 @@ trait WindowedReadStream: ReadStream {
|
||||||
fn window(&self) -> (u64, u64);
|
fn window(&self) -> (u64, u64);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An window into an existing [`ReadStream`], with ownership of the underlying stream.
|
||||||
pub struct OwningWindowedReadStream<'a> {
|
pub struct OwningWindowedReadStream<'a> {
|
||||||
|
/// The base stream.
|
||||||
pub base: Box<dyn ReadStream + 'a>,
|
pub base: Box<dyn ReadStream + 'a>,
|
||||||
|
/// The beginning of the window in bytes.
|
||||||
pub begin: u64,
|
pub begin: u64,
|
||||||
|
/// The end of the window in bytes.
|
||||||
pub end: u64,
|
pub end: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,13 +94,18 @@ pub fn wrap_windowed<'a>(
|
||||||
io::Result::Ok(OwningWindowedReadStream { base, begin: offset, end: offset + size })
|
io::Result::Ok(OwningWindowedReadStream { base, begin: offset, end: offset + size })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A non-owning window into an existing [`ReadStream`].
|
||||||
pub struct SharedWindowedReadStream<'a> {
|
pub struct SharedWindowedReadStream<'a> {
|
||||||
|
/// A reference to the base stream.
|
||||||
pub base: &'a mut dyn ReadStream,
|
pub base: &'a mut dyn ReadStream,
|
||||||
|
/// The beginning of the window in bytes.
|
||||||
pub begin: u64,
|
pub begin: u64,
|
||||||
|
/// The end of the window in bytes.
|
||||||
pub end: u64,
|
pub end: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SharedWindowedReadStream<'a> {
|
impl<'a> SharedWindowedReadStream<'a> {
|
||||||
|
/// Modifies the current window & seeks to the beginning of the window.
|
||||||
pub fn set_window(&mut self, begin: u64, end: u64) -> io::Result<()> {
|
pub fn set_window(&mut self, begin: u64, end: u64) -> io::Result<()> {
|
||||||
self.base.seek(SeekFrom::Start(begin))?;
|
self.base.seek(SeekFrom::Start(begin))?;
|
||||||
self.begin = begin;
|
self.begin = begin;
|
||||||
|
@ -180,8 +191,11 @@ impl<'a> WindowedReadStream for SharedWindowedReadStream<'a> {
|
||||||
fn window(&self) -> (u64, u64) { (self.begin, self.end) }
|
fn window(&self) -> (u64, u64) { (self.begin, self.end) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A non-owning [`ReadStream`] wrapping a byte slice reference.
|
||||||
pub struct ByteReadStream<'a> {
|
pub struct ByteReadStream<'a> {
|
||||||
|
/// A reference to the underlying byte slice.
|
||||||
pub bytes: &'a [u8],
|
pub bytes: &'a [u8],
|
||||||
|
/// The current position in the stream.
|
||||||
pub position: u64,
|
pub position: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue