mirror of https://github.com/encounter/nod-rs.git
Various minor API adjustments
This commit is contained in:
parent
8abe674cb9
commit
5f537f0e7b
|
@ -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",
|
||||||
|
|
|
@ -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>"]
|
||||||
|
|
|
@ -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))
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in New Issue