From a81c28c4791ca5627eb558f3465fa17de1dfb4cc Mon Sep 17 00:00:00 2001 From: dece Date: Thu, 23 Apr 2020 02:16:03 +0200 Subject: [PATCH] ironring: allow an output dir to unpack DCX --- src/bin/ironring.rs | 68 ++++++++++++++++++++++++++++++++++---------- src/unpackers/bhd.rs | 2 +- src/unpackers/bnd.rs | 0 src/unpackers/dcx.rs | 2 +- src/utils/fs.rs | 28 ++++++++++++++++-- 5 files changed, 81 insertions(+), 19 deletions(-) create mode 100644 src/unpackers/bnd.rs diff --git a/src/bin/ironring.rs b/src/bin/ironring.rs index 6470aef..7ad8dca 100644 --- a/src/bin/ironring.rs +++ b/src/bin/ironring.rs @@ -5,7 +5,7 @@ use std::process; use clap::{App, AppSettings, Arg, ArgMatches, SubCommand}; -use rir::{name_hashes, unpackers}; +use rir::{name_hashes, unpackers, utils}; fn main() { let default_namefilepath: &str = &get_default_namefilepath(); @@ -44,21 +44,28 @@ fn main() { .required(false) .default_value(default_namefilepath))) .subcommand(SubCommand::with_name("hash") - .about("Calculate hash for a string") + .about("Calculates hash for a string") .arg(Arg::with_name("value") .takes_value(true) .required(true))) .subcommand(SubCommand::with_name("dcx") - .about("?TODO?") + .about("Extracts and decompress DCX data") .arg(Arg::with_name("file") .takes_value(true) .required(true)) .arg(Arg::with_name("output") + .short("o") + .long("output") .takes_value(true) .required(false))) .subcommand(SubCommand::with_name("bnd") - .about("?TODO?") + .about("Extracts BND contents") .arg(Arg::with_name("file") + .takes_value(true) + .required(true)) + .arg(Arg::with_name("output") + .short("o") + .long("output") .takes_value(true) .required(true))) .get_matches(); @@ -68,6 +75,7 @@ fn main() { ("bhds", Some(s)) => { cmd_bhds(s) } ("hash", Some(s)) => { cmd_hash(s) } ("dcx", Some(s)) => { cmd_dcx(s) } + ("bnd", Some(s)) => { cmd_bnd(s) } _ => { 0 } }) } @@ -133,7 +141,7 @@ fn cmd_bhds(args: &ArgMatches) -> i32 { } } } - return 0 + 0 } fn cmd_hash(args: &ArgMatches) -> i32 { @@ -144,24 +152,54 @@ fn cmd_hash(args: &ArgMatches) -> i32 { fn cmd_dcx(args: &ArgMatches) -> i32 { let file_path: &str = args.value_of("file").unwrap(); + let mut output_path_valid = false; let mut output_path: String = match args.value_of("output") { - Some(s) => { s.to_string() } + Some(s) => { output_path_valid = true; s.to_string() } _ => { String::with_capacity(file_path.len()) } }; - if output_path.is_empty() { - let mut pb = path::PathBuf::from(&file_path); - pb.set_extension(""); - if let Some(s) = pb.to_str() { - output_path.push_str(s); - } else { - eprintln!("Could not create an uncompressed path for {}. \ - Provide one as \"output\" argument.", file_path); - return 1 + // If no output path is provided, try to strip the file extension. + if !output_path_valid { + if let Some(pb) = utils::fs::strip_extension(&path::PathBuf::from(&file_path)) { + if let Some(s) = pb.to_str() { + output_path.push_str(s); + output_path_valid = true; + } } } + if !output_path_valid { + eprintln!("Could not determine a valid output path."); + return 1 + } + // If the output path is a dir, try to strip extension and place the file there. + if path::Path::new(&output_path).is_dir() { + output_path_valid = false; + let mut out_pb = path::PathBuf::from(&output_path); + if let Some(file_pb) = utils::fs::strip_extension(&path::PathBuf::from(&file_path)) { + if let Some(file_name) = file_pb.file_name() { + if let Some(file_name_str) = file_name.to_str() { + out_pb.push(file_name_str); + if let Some(s) = out_pb.as_path().to_str() { + output_path.clear(); + output_path.push_str(s); + output_path_valid = true; + } + } + } + } + } + if !output_path_valid { + eprintln!("Could not determine a valid output path."); + return 1 + } match unpackers::dcx::extract_dcx(file_path, &output_path) { Err(e) => { eprintln!("Failed to extract DCX: {:?}", e); return 1 } _ => { 0 } } } + +fn cmd_bnd(args: &ArgMatches) -> i32 { + let _file_path: &str = args.value_of("file").unwrap(); + let _output_path: &str = args.value_of("output").unwrap(); + 0 +} diff --git a/src/unpackers/bhd.rs b/src/unpackers/bhd.rs index 38c2a41..58e1ab5 100644 --- a/src/unpackers/bhd.rs +++ b/src/unpackers/bhd.rs @@ -10,7 +10,7 @@ use crate::parsers::bhd; use crate::unpackers::errors::{self as unpackers_errors, UnpackError}; use crate::utils::fs as fs_utils; -/// Parse a BHD file and extract its content. +/// Parse a BHD file and extract its content from sister BDT. /// /// As names are often a path rather than a simple file name, /// output path is used as the BHD root and required subdirs diff --git a/src/unpackers/bnd.rs b/src/unpackers/bnd.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/unpackers/dcx.rs b/src/unpackers/dcx.rs index a742a9c..39af7a8 100644 --- a/src/unpackers/dcx.rs +++ b/src/unpackers/dcx.rs @@ -7,7 +7,7 @@ use nom::Err::{Error as NomError, Failure as NomFailure}; use crate::parsers::dcx; use crate::unpackers::errors::{self as unpackers_errors, UnpackError}; -/// Extract DCX file content. +/// Extract DCX file content to disk. pub fn extract_dcx(dcx_path: &str, output_path: &str) -> Result<(), UnpackError> { let (_dcx, decomp_data) = load_dcx(dcx_path)?; let mut output_file = fs::File::create(output_path)?; diff --git a/src/utils/fs.rs b/src/utils/fs.rs index 971df9a..ba21aba 100644 --- a/src/utils/fs.rs +++ b/src/utils/fs.rs @@ -1,9 +1,9 @@ use std::fs; use std::io; -use std::path::Path; +use std::path; /// Ensure a directory exists, creating it with parents if necessary. -pub fn ensure_dir_exists(path: &Path) -> Result<(), io::Error> { +pub fn ensure_dir_exists(path: &path::Path) -> Result<(), io::Error> { if !path.is_dir() { if path.exists() { return Err(io::Error::new(io::ErrorKind::AlreadyExists, "Not a directory.")); @@ -12,3 +12,27 @@ pub fn ensure_dir_exists(path: &Path) -> Result<(), io::Error> { } Ok(()) } + +/// Strip the extension from a file path. +pub fn strip_extension(path: &path::PathBuf) -> Option { + let mut pb = path::PathBuf::from(&path); + if pb.extension().is_none() { + eprintln!("Path has no extension: {:?}", &path); + return None + } + pb.set_extension(""); + Some(pb) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_strip_ext() { + let pb = path::PathBuf::from("file.ext"); + assert_eq!(strip_ext(&pb).unwrap(), path::PathBuf::from("file")); + let pb = path::PathBuf::from("file"); + assert!(strip_ext(&pb).is_none()); + } +}