Various minor API adjustments

This commit is contained in:
Luke Street 2024-10-03 20:18:44 -06:00
parent 8abe674cb9
commit 5f537f0e7b
11 changed files with 31 additions and 27 deletions

4
Cargo.lock generated
View File

@ -447,7 +447,7 @@ dependencies = [
[[package]] [[package]]
name = "nod" name = "nod"
version = "1.3.0" version = "1.4.0"
dependencies = [ dependencies = [
"adler", "adler",
"aes", "aes",
@ -470,7 +470,7 @@ dependencies = [
[[package]] [[package]]
name = "nodtool" name = "nodtool"
version = "1.3.0" version = "1.4.0"
dependencies = [ dependencies = [
"argp", "argp",
"base16ct", "base16ct",

View File

@ -9,7 +9,7 @@ strip = "debuginfo"
codegen-units = 1 codegen-units = 1
[workspace.package] [workspace.package]
version = "1.3.0" version = "1.4.0"
edition = "2021" edition = "2021"
rust-version = "1.74" rust-version = "1.74"
authors = ["Luke Street <luke@street.dev>"] authors = ["Luke Street <luke@street.dev>"]

View File

@ -19,7 +19,7 @@ pub enum NodeKind {
} }
/// An individual file system node. /// An individual file system node.
#[derive(Clone, Debug, PartialEq, FromBytes, FromZeroes, AsBytes)] #[derive(Copy, Clone, Debug, PartialEq, FromBytes, FromZeroes, AsBytes)]
#[repr(C, align(4))] #[repr(C, align(4))]
pub struct Node { pub struct Node {
kind: u8, kind: u8,
@ -108,7 +108,7 @@ impl<'a> Fst<'a> {
/// Get the name of a node. /// Get the name of a node.
#[allow(clippy::missing_inline_in_public_items)] #[allow(clippy::missing_inline_in_public_items)]
pub fn get_name(&self, node: &Node) -> Result<Cow<'a, str>, String> { pub fn get_name(&self, node: Node) -> Result<Cow<'a, str>, String> {
let name_buf = self.string_table.get(node.name_offset() as usize..).ok_or_else(|| { let name_buf = self.string_table.get(node.name_offset() as usize..).ok_or_else(|| {
format!( format!(
"FST: name offset {} out of bounds (string table size: {})", "FST: name offset {} out of bounds (string table size: {})",
@ -128,12 +128,12 @@ impl<'a> Fst<'a> {
/// Finds a particular file or directory by path. /// Finds a particular file or directory by path.
#[allow(clippy::missing_inline_in_public_items)] #[allow(clippy::missing_inline_in_public_items)]
pub fn find(&self, path: &str) -> Option<(usize, &'a Node)> { pub fn find(&self, path: &str) -> Option<(usize, Node)> {
let mut split = path.trim_matches('/').split('/'); let mut split = path.trim_matches('/').split('/');
let mut current = split.next()?; let mut current = split.next()?;
let mut idx = 1; let mut idx = 1;
let mut stop_at = None; let mut stop_at = None;
while let Some(node) = self.nodes.get(idx) { while let Some(node) = self.nodes.get(idx).copied() {
if self.get_name(node).as_ref().map_or(false, |name| name.eq_ignore_ascii_case(current)) if self.get_name(node).as_ref().map_or(false, |name| name.eq_ignore_ascii_case(current))
{ {
if let Some(next) = split.next() { if let Some(next) = split.next() {
@ -168,11 +168,11 @@ pub struct FstIter<'a> {
} }
impl<'a> Iterator for FstIter<'a> { impl<'a> Iterator for FstIter<'a> {
type Item = (usize, &'a Node, Result<Cow<'a, str>, String>); type Item = (usize, Node, Result<Cow<'a, str>, String>);
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
let idx = self.idx; let idx = self.idx;
let node = self.fst.nodes.get(idx)?; let node = self.fst.nodes.get(idx).copied()?;
let name = self.fst.get_name(node); let name = self.fst.get_name(node);
self.idx += 1; self.idx += 1;
Some((idx, node, name)) Some((idx, node, name))

View File

@ -124,7 +124,7 @@ impl PartitionBase for PartitionGC {
read_part_meta(self, false) read_part_meta(self, false)
} }
fn open_file(&mut self, node: &Node) -> io::Result<FileStream> { fn open_file(&mut self, node: Node) -> io::Result<FileStream> {
if !node.is_file() { if !node.is_file() {
return Err(io::Error::new( return Err(io::Error::new(
io::ErrorKind::InvalidInput, io::ErrorKind::InvalidInput,
@ -134,7 +134,7 @@ impl PartitionBase for PartitionGC {
FileStream::new(self, node.offset(false), node.length()) FileStream::new(self, node.offset(false), node.length())
} }
fn into_open_file(self: Box<Self>, node: &Node) -> io::Result<OwnedFileStream> { fn into_open_file(self: Box<Self>, node: Node) -> io::Result<OwnedFileStream> {
if !node.is_file() { if !node.is_file() {
return Err(io::Error::new( return Err(io::Error::new(
io::ErrorKind::InvalidInput, io::ErrorKind::InvalidInput,

View File

@ -313,7 +313,7 @@ pub trait PartitionBase: DynClone + BufRead + Seek + Send + Sync {
/// Ok(()) /// Ok(())
/// } /// }
/// ``` /// ```
fn open_file(&mut self, node: &Node) -> io::Result<FileStream>; fn open_file(&mut self, node: Node) -> io::Result<FileStream>;
/// Consumes the partition instance and returns a windowed stream. /// Consumes the partition instance and returns a windowed stream.
/// ///
@ -342,7 +342,7 @@ pub trait PartitionBase: DynClone + BufRead + Seek + Send + Sync {
/// Ok(()) /// Ok(())
/// } /// }
/// ``` /// ```
fn into_open_file(self: Box<Self>, node: &Node) -> io::Result<OwnedFileStream>; fn into_open_file(self: Box<Self>, node: Node) -> io::Result<OwnedFileStream>;
} }
dyn_clone::clone_trait_object!(PartitionBase); dyn_clone::clone_trait_object!(PartitionBase);

View File

@ -35,6 +35,11 @@ where T: BufRead + Seek
base.seek(SeekFrom::Start(offset))?; base.seek(SeekFrom::Start(offset))?;
Ok(Self { base, pos: offset, begin: offset, end: offset + size }) Ok(Self { base, pos: offset, begin: offset, end: offset + size })
} }
/// Returns the length of the window.
#[inline]
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> u64 { self.end - self.begin }
} }
impl<T> Read for WindowedStream<T> impl<T> Read for WindowedStream<T>

View File

@ -489,7 +489,7 @@ impl PartitionBase for PartitionWii {
Ok(meta) Ok(meta)
} }
fn open_file(&mut self, node: &Node) -> io::Result<FileStream> { fn open_file(&mut self, node: Node) -> io::Result<FileStream> {
if !node.is_file() { if !node.is_file() {
return Err(io::Error::new( return Err(io::Error::new(
io::ErrorKind::InvalidInput, io::ErrorKind::InvalidInput,
@ -499,7 +499,7 @@ impl PartitionBase for PartitionWii {
FileStream::new(self, node.offset(true), node.length()) FileStream::new(self, node.offset(true), node.length())
} }
fn into_open_file(self: Box<Self>, node: &Node) -> io::Result<OwnedFileStream> { fn into_open_file(self: Box<Self>, node: Node) -> io::Result<OwnedFileStream> {
if !node.is_file() { if !node.is_file() {
return Err(io::Error::new( return Err(io::Error::new(
io::ErrorKind::InvalidInput, io::ErrorKind::InvalidInput,

View File

@ -94,7 +94,7 @@ dyn_clone::clone_trait_object!(BlockIO);
/// Creates a new [`BlockIO`] instance from a stream. /// Creates a new [`BlockIO`] instance from a stream.
pub fn new(mut stream: Box<dyn DiscStream>) -> Result<Box<dyn BlockIO>> { pub fn new(mut stream: Box<dyn DiscStream>) -> Result<Box<dyn BlockIO>> {
let io: Box<dyn BlockIO> = match detect(stream.as_mut())? { let io: Box<dyn BlockIO> = match detect(stream.as_mut()).context("Detecting file type")? {
Some(Format::Iso) => crate::io::iso::DiscIOISO::new(stream)?, Some(Format::Iso) => crate::io::iso::DiscIOISO::new(stream)?,
Some(Format::Ciso) => crate::io::ciso::DiscIOCISO::new(stream)?, Some(Format::Ciso) => crate::io::ciso::DiscIOCISO::new(stream)?,
Some(Format::Gcz) => { Some(Format::Gcz) => {
@ -103,9 +103,7 @@ pub fn new(mut stream: Box<dyn DiscStream>) -> Result<Box<dyn BlockIO>> {
crate::io::gcz::DiscIOGCZ::new(stream)? crate::io::gcz::DiscIOGCZ::new(stream)?
} }
#[cfg(not(feature = "compress-zlib"))] #[cfg(not(feature = "compress-zlib"))]
{ return Err(Error::DiscFormat("GCZ support is disabled".to_string()));
return Err(Error::DiscFormat("GCZ support is disabled".to_string()));
}
} }
Some(Format::Nfs) => { Some(Format::Nfs) => {
return Err(Error::DiscFormat("NFS requires a filesystem path".to_string())) return Err(Error::DiscFormat("NFS requires a filesystem path".to_string()))
@ -134,7 +132,7 @@ pub fn open(filename: &Path) -> Result<Box<dyn BlockIO>> {
return Err(Error::DiscFormat(format!("Input is not a file: {}", filename.display()))); return Err(Error::DiscFormat(format!("Input is not a file: {}", filename.display())));
} }
let mut stream = Box::new(SplitFileReader::new(filename)?); let mut stream = Box::new(SplitFileReader::new(filename)?);
let io: Box<dyn BlockIO> = match detect(stream.as_mut())? { let io: Box<dyn BlockIO> = match detect(stream.as_mut()).context("Detecting file type")? {
Some(Format::Iso) => crate::io::iso::DiscIOISO::new(stream)?, Some(Format::Iso) => crate::io::iso::DiscIOISO::new(stream)?,
Some(Format::Ciso) => crate::io::ciso::DiscIOCISO::new(stream)?, Some(Format::Ciso) => crate::io::ciso::DiscIOCISO::new(stream)?,
Some(Format::Gcz) => { Some(Format::Gcz) => {
@ -143,9 +141,7 @@ pub fn open(filename: &Path) -> Result<Box<dyn BlockIO>> {
crate::io::gcz::DiscIOGCZ::new(stream)? crate::io::gcz::DiscIOGCZ::new(stream)?
} }
#[cfg(not(feature = "compress-zlib"))] #[cfg(not(feature = "compress-zlib"))]
{ return Err(Error::DiscFormat("GCZ support is disabled".to_string()));
return Err(Error::DiscFormat("GCZ support is disabled".to_string()));
}
} }
Some(Format::Nfs) => match path.parent() { Some(Format::Nfs) => match path.parent() {
Some(parent) if parent.is_dir() => { Some(parent) if parent.is_dir() => {
@ -172,11 +168,11 @@ pub const WBFS_MAGIC: MagicBytes = *b"WBFS";
pub const WIA_MAGIC: MagicBytes = *b"WIA\x01"; pub const WIA_MAGIC: MagicBytes = *b"WIA\x01";
pub const RVZ_MAGIC: MagicBytes = *b"RVZ\x01"; pub const RVZ_MAGIC: MagicBytes = *b"RVZ\x01";
pub fn detect<R: Read + ?Sized>(stream: &mut R) -> Result<Option<Format>> { pub fn detect<R: Read + ?Sized>(stream: &mut R) -> io::Result<Option<Format>> {
let data: [u8; 0x20] = match read_from(stream) { let data: [u8; 0x20] = match read_from(stream) {
Ok(magic) => magic, Ok(magic) => magic,
Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => return Ok(None), Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => return Ok(None),
Err(e) => return Err(e).context("Reading magic bytes"), Err(e) => return Err(e),
}; };
let out = match *array_ref!(data, 0, 4) { let out = match *array_ref!(data, 0, 4) {
CISO_MAGIC => Some(Format::Ciso), CISO_MAGIC => Some(Format::Ciso),

View File

@ -192,7 +192,7 @@ impl Disc {
/// Detects the format of a disc image from a read stream. /// Detects the format of a disc image from a read stream.
#[inline] #[inline]
pub fn detect<R>(stream: &mut R) -> Result<Option<Format>> pub fn detect<R>(stream: &mut R) -> std::io::Result<Option<Format>>
where R: Read + ?Sized { where R: Read + ?Sized {
io::block::detect(stream) io::block::detect(stream)
} }

View File

@ -181,7 +181,7 @@ fn extract_file(bytes: &[u8], out_path: &Path, quiet: bool) -> nod::Result<()> {
} }
fn extract_node( fn extract_node(
node: &Node, node: Node,
partition: &mut dyn PartitionBase, partition: &mut dyn PartitionBase,
base_path: &Path, base_path: &Path,
name: &str, name: &str,

View File

@ -3,6 +3,9 @@ use argp::FromArgs;
pub mod cmd; pub mod cmd;
pub(crate) mod util; pub(crate) mod util;
// Re-export nod
pub use nod;
#[derive(FromArgs, Debug)] #[derive(FromArgs, Debug)]
#[argp(subcommand)] #[argp(subcommand)]
pub enum SubCommand { pub enum SubCommand {