ironring: allow an output dir to unpack DCX
This commit is contained in:
parent
b252fa1781
commit
a81c28c479
|
@ -5,7 +5,7 @@ use std::process;
|
||||||
|
|
||||||
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
|
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
|
||||||
|
|
||||||
use rir::{name_hashes, unpackers};
|
use rir::{name_hashes, unpackers, utils};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let default_namefilepath: &str = &get_default_namefilepath();
|
let default_namefilepath: &str = &get_default_namefilepath();
|
||||||
|
@ -44,21 +44,28 @@ fn main() {
|
||||||
.required(false)
|
.required(false)
|
||||||
.default_value(default_namefilepath)))
|
.default_value(default_namefilepath)))
|
||||||
.subcommand(SubCommand::with_name("hash")
|
.subcommand(SubCommand::with_name("hash")
|
||||||
.about("Calculate hash for a string")
|
.about("Calculates hash for a string")
|
||||||
.arg(Arg::with_name("value")
|
.arg(Arg::with_name("value")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.required(true)))
|
.required(true)))
|
||||||
.subcommand(SubCommand::with_name("dcx")
|
.subcommand(SubCommand::with_name("dcx")
|
||||||
.about("?TODO?")
|
.about("Extracts and decompress DCX data")
|
||||||
.arg(Arg::with_name("file")
|
.arg(Arg::with_name("file")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.required(true))
|
.required(true))
|
||||||
.arg(Arg::with_name("output")
|
.arg(Arg::with_name("output")
|
||||||
|
.short("o")
|
||||||
|
.long("output")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.required(false)))
|
.required(false)))
|
||||||
.subcommand(SubCommand::with_name("bnd")
|
.subcommand(SubCommand::with_name("bnd")
|
||||||
.about("?TODO?")
|
.about("Extracts BND contents")
|
||||||
.arg(Arg::with_name("file")
|
.arg(Arg::with_name("file")
|
||||||
|
.takes_value(true)
|
||||||
|
.required(true))
|
||||||
|
.arg(Arg::with_name("output")
|
||||||
|
.short("o")
|
||||||
|
.long("output")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.required(true)))
|
.required(true)))
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
@ -68,6 +75,7 @@ fn main() {
|
||||||
("bhds", Some(s)) => { cmd_bhds(s) }
|
("bhds", Some(s)) => { cmd_bhds(s) }
|
||||||
("hash", Some(s)) => { cmd_hash(s) }
|
("hash", Some(s)) => { cmd_hash(s) }
|
||||||
("dcx", Some(s)) => { cmd_dcx(s) }
|
("dcx", Some(s)) => { cmd_dcx(s) }
|
||||||
|
("bnd", Some(s)) => { cmd_bnd(s) }
|
||||||
_ => { 0 }
|
_ => { 0 }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -133,7 +141,7 @@ fn cmd_bhds(args: &ArgMatches) -> i32 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cmd_hash(args: &ArgMatches) -> i32 {
|
fn cmd_hash(args: &ArgMatches) -> i32 {
|
||||||
|
@ -144,20 +152,44 @@ fn cmd_hash(args: &ArgMatches) -> i32 {
|
||||||
|
|
||||||
fn cmd_dcx(args: &ArgMatches) -> i32 {
|
fn cmd_dcx(args: &ArgMatches) -> i32 {
|
||||||
let file_path: &str = args.value_of("file").unwrap();
|
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") {
|
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()) }
|
_ => { String::with_capacity(file_path.len()) }
|
||||||
};
|
};
|
||||||
if output_path.is_empty() {
|
// If no output path is provided, try to strip the file extension.
|
||||||
let mut pb = path::PathBuf::from(&file_path);
|
if !output_path_valid {
|
||||||
pb.set_extension("");
|
if let Some(pb) = utils::fs::strip_extension(&path::PathBuf::from(&file_path)) {
|
||||||
if let Some(s) = pb.to_str() {
|
if let Some(s) = pb.to_str() {
|
||||||
output_path.push_str(s);
|
output_path.push_str(s);
|
||||||
} else {
|
output_path_valid = true;
|
||||||
eprintln!("Could not create an uncompressed path for {}. \
|
}
|
||||||
Provide one as \"output\" argument.", file_path);
|
}
|
||||||
|
}
|
||||||
|
if !output_path_valid {
|
||||||
|
eprintln!("Could not determine a valid output path.");
|
||||||
return 1
|
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) {
|
match unpackers::dcx::extract_dcx(file_path, &output_path) {
|
||||||
|
@ -165,3 +197,9 @@ fn cmd_dcx(args: &ArgMatches) -> i32 {
|
||||||
_ => { 0 }
|
_ => { 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
|
||||||
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ use crate::parsers::bhd;
|
||||||
use crate::unpackers::errors::{self as unpackers_errors, UnpackError};
|
use crate::unpackers::errors::{self as unpackers_errors, UnpackError};
|
||||||
use crate::utils::fs as fs_utils;
|
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,
|
/// As names are often a path rather than a simple file name,
|
||||||
/// output path is used as the BHD root and required subdirs
|
/// output path is used as the BHD root and required subdirs
|
||||||
|
|
0
src/unpackers/bnd.rs
Normal file
0
src/unpackers/bnd.rs
Normal file
|
@ -7,7 +7,7 @@ use nom::Err::{Error as NomError, Failure as NomFailure};
|
||||||
use crate::parsers::dcx;
|
use crate::parsers::dcx;
|
||||||
use crate::unpackers::errors::{self as unpackers_errors, UnpackError};
|
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> {
|
pub fn extract_dcx(dcx_path: &str, output_path: &str) -> Result<(), UnpackError> {
|
||||||
let (_dcx, decomp_data) = load_dcx(dcx_path)?;
|
let (_dcx, decomp_data) = load_dcx(dcx_path)?;
|
||||||
let mut output_file = fs::File::create(output_path)?;
|
let mut output_file = fs::File::create(output_path)?;
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::path::Path;
|
use std::path;
|
||||||
|
|
||||||
/// Ensure a directory exists, creating it with parents if necessary.
|
/// 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.is_dir() {
|
||||||
if path.exists() {
|
if path.exists() {
|
||||||
return Err(io::Error::new(io::ErrorKind::AlreadyExists, "Not a directory."));
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Strip the extension from a file path.
|
||||||
|
pub fn strip_extension(path: &path::PathBuf) -> Option<path::PathBuf> {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Reference in a new issue