mirror of https://github.com/encounter/nod-rs.git
104 lines
3.5 KiB
Rust
104 lines
3.5 KiB
Rust
use std::{
|
|
env, fs, io,
|
|
io::BufWriter,
|
|
path::{Path, PathBuf},
|
|
};
|
|
|
|
use clap::{clap_app, AppSettings};
|
|
use file_size;
|
|
use nod::{
|
|
disc::{new_disc_base, PartReadStream},
|
|
fst::NodeType,
|
|
io::{has_extension, new_disc_io},
|
|
Result,
|
|
};
|
|
|
|
fn main() -> Result<()> {
|
|
let matches = clap_app!(nodtool =>
|
|
(settings: &[
|
|
AppSettings::SubcommandRequiredElseHelp,
|
|
AppSettings::GlobalVersion,
|
|
AppSettings::DeriveDisplayOrder,
|
|
AppSettings::VersionlessSubcommands,
|
|
])
|
|
(global_settings: &[
|
|
AppSettings::ColoredHelp,
|
|
AppSettings::UnifiedHelpMessage,
|
|
])
|
|
(version: env!("CARGO_PKG_VERSION"))
|
|
(author: "Luke Street <luke@street.dev>")
|
|
(about: "Tool for reading GameCube and Wii disc images.")
|
|
(long_about: "Tool for reading GameCube and Wii disc images.
|
|
|
|
Based on <https://github.com/AxioDL/nod>, original authors:
|
|
Jack Andersen (jackoalan)
|
|
Phillip Stephens (Antidote)")
|
|
(@subcommand extract =>
|
|
(about: "Extract GameCube & Wii disc images")
|
|
(@arg FILE: +required "Path to disc image (ISO or NFS)")
|
|
(@arg DIR: "Output directory (optional)")
|
|
(@arg quiet: -q "Quiet output")
|
|
)
|
|
)
|
|
.get_matches();
|
|
if let Some(matches) = matches.subcommand_matches("extract") {
|
|
let file: PathBuf = PathBuf::from(matches.value_of("FILE").unwrap());
|
|
let output_dir: PathBuf;
|
|
if let Some(dir) = matches.value_of("DIR") {
|
|
output_dir = PathBuf::from(dir);
|
|
} else if has_extension(file.as_path(), "nfs") {
|
|
// Special logic to extract from content/hif_*.nfs to extracted/..
|
|
if let Some(parent) = file.parent() {
|
|
output_dir = parent.with_file_name("extracted");
|
|
} else {
|
|
output_dir = file.with_extension("");
|
|
}
|
|
} else {
|
|
output_dir = file.with_extension("");
|
|
}
|
|
let mut disc_io = new_disc_io(file.as_path())?;
|
|
let disc_base = new_disc_base(disc_io.as_mut())?;
|
|
let mut partition = disc_base.get_data_partition(disc_io.as_mut())?;
|
|
let header = partition.read_header()?;
|
|
extract_node(header.root_node(), partition.as_mut(), output_dir.as_path())?;
|
|
}
|
|
Result::Ok(())
|
|
}
|
|
|
|
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)
|
|
);
|
|
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)?;
|
|
}
|
|
NodeType::Directory(v, c) => {
|
|
if v.name.is_empty() {
|
|
fs::create_dir_all(base_path)?;
|
|
for x in c {
|
|
extract_node(x, partition, base_path)?;
|
|
}
|
|
} else {
|
|
let mut new_base = base_path.to_owned();
|
|
new_base.push(v.name.as_ref());
|
|
fs::create_dir_all(&new_base)?;
|
|
for x in c {
|
|
extract_node(x, partition, new_base.as_path())?;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
io::Result::Ok(())
|
|
}
|