bnd: extract all entries in a dir
Currently it does not preserve internal path dir structure ("N:\\...").
This commit is contained in:
parent
d3c8dba0a4
commit
75ff6ddc7f
|
@ -9,7 +9,7 @@ use rir::{name_hashes, unpackers, utils};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let default_namefilepath: &str = &get_default_namefilepath();
|
let default_namefilepath: &str = &get_default_namefilepath();
|
||||||
let matches = App::new("Rusted Iron Ring")
|
let matches = App::new("Iron Ring")
|
||||||
.setting(AppSettings::ArgRequiredElseHelp)
|
.setting(AppSettings::ArgRequiredElseHelp)
|
||||||
.subcommand(SubCommand::with_name("bhd")
|
.subcommand(SubCommand::with_name("bhd")
|
||||||
.about("Extracts BHD/BDT contents")
|
.about("Extracts BHD/BDT contents")
|
||||||
|
@ -200,8 +200,8 @@ fn cmd_dcx(args: &ArgMatches) -> i32 {
|
||||||
|
|
||||||
fn cmd_bnd(args: &ArgMatches) -> i32 {
|
fn cmd_bnd(args: &ArgMatches) -> i32 {
|
||||||
let file_path: &str = args.value_of("file").unwrap();
|
let file_path: &str = args.value_of("file").unwrap();
|
||||||
let _output_path: &str = args.value_of("output").unwrap();
|
let output_path: &str = args.value_of("output").unwrap();
|
||||||
match unpackers::bnd::load_bnd_file(file_path) {
|
match unpackers::bnd::extract_bnd_file(file_path, output_path) {
|
||||||
Err(e) => { eprintln!("Failed to extract BND: {:?}", e); return 1 }
|
Err(e) => { eprintln!("Failed to extract BND: {:?}", e); return 1 }
|
||||||
_ => { 0 }
|
_ => { 0 }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use std::str;
|
|
||||||
|
|
||||||
use nom::IResult;
|
use nom::IResult;
|
||||||
use nom::bytes::complete::{tag, take};
|
use nom::bytes::complete::{tag, take};
|
||||||
use nom::multi::count;
|
use nom::multi::count;
|
||||||
|
@ -134,10 +132,14 @@ pub struct Bnd {
|
||||||
pub file_infos: Vec<BndFileInfo>,
|
pub file_infos: Vec<BndFileInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse a BND file to a BND struct.
|
||||||
|
///
|
||||||
|
/// On success, returns the full BND data along with the Bnd struct
|
||||||
|
/// instead of the remaining data.
|
||||||
pub fn parse(i: &[u8]) -> IResult<&[u8], Bnd> {
|
pub fn parse(i: &[u8]) -> IResult<&[u8], Bnd> {
|
||||||
let full_file = i;
|
let full_file = i;
|
||||||
let (i, header) = parse_header(i)?;
|
let (i, header) = parse_header(i)?;
|
||||||
let (i, mut file_infos) = count(
|
let (_, mut file_infos) = count(
|
||||||
|i| parse_file_info(i, &header),
|
|i| parse_file_info(i, &header),
|
||||||
header.num_files as usize
|
header.num_files as usize
|
||||||
)(i)?;
|
)(i)?;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io::Read;
|
use std::io::{Read, Write};
|
||||||
use std::path;
|
use std::path;
|
||||||
|
|
||||||
use nom::Err::{Error as NomError, Failure as NomFailure};
|
use nom::Err::{Error as NomError, Failure as NomFailure};
|
||||||
|
@ -11,9 +11,9 @@ use crate::utils::fs as fs_utils;
|
||||||
/// Extract BND file contents to disk.
|
/// Extract BND file contents to disk.
|
||||||
///
|
///
|
||||||
/// Wraps around `extract_bnd` to load the BND from disk.
|
/// Wraps around `extract_bnd` to load the BND from disk.
|
||||||
pub fn extract_bnd_file(bnd_path: &str, output_path: &str) -> Result<(), UnpackError> {
|
pub fn extract_bnd_file(bnd_path: &str, output_dir: &str) -> Result<(), UnpackError> {
|
||||||
let (bnd, data) = load_bnd_file(bnd_path)?;
|
let (bnd, data) = load_bnd_file(bnd_path)?;
|
||||||
extract_bnd(bnd, data, output_path)?;
|
extract_bnd(&bnd, &data, output_dir)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,13 +22,43 @@ pub fn extract_bnd_file(bnd_path: &str, output_path: &str) -> Result<(), UnpackE
|
||||||
/// Files in the BND are written in the output_path directory, creating it if needed, without
|
/// Files in the BND are written in the output_path directory, creating it if needed, without
|
||||||
/// preserving directory structure. If the BND do not contain paths, it will be named after its ID.
|
/// preserving directory structure. If the BND do not contain paths, it will be named after its ID.
|
||||||
/// If it does not have IDs, consecutive integers will be used.
|
/// If it does not have IDs, consecutive integers will be used.
|
||||||
pub fn extract_bnd(bnd: bnd::Bnd, data: Vec<u8>, output_path: &str) -> Result<(), UnpackError> {
|
pub fn extract_bnd(bnd: &bnd::Bnd, bnd_data: &Vec<u8>, output_dir: &str) -> Result<(), UnpackError> {
|
||||||
let output_path = path::Path::new(output_path);
|
let output_dir = path::Path::new(output_dir);
|
||||||
fs_utils::ensure_dir_exists(output_path)?;
|
fs_utils::ensure_dir_exists(output_dir)?;
|
||||||
for info in &bnd.file_infos {
|
for file_info in &bnd.file_infos {
|
||||||
// TODO
|
extract_bnd_entry(file_info, bnd_data, output_dir)?;
|
||||||
}
|
}
|
||||||
//output_file.write_all(&decomp_data)?;
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extract a file contained in a BND using its BndFileInfo.
|
||||||
|
///
|
||||||
|
/// The info struct must have a valid internal path.
|
||||||
|
fn extract_bnd_entry(
|
||||||
|
file_info: &bnd::BndFileInfo,
|
||||||
|
bnd_data: &Vec<u8>,
|
||||||
|
output_dir: &path::Path
|
||||||
|
) -> Result<(), UnpackError> {
|
||||||
|
if file_info.path.is_none() {
|
||||||
|
return Err(UnpackError::Naming("No path for BND entry.".to_owned()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let ofs_start = file_info.ofs_data as usize;
|
||||||
|
let ofs_end = (file_info.ofs_data + file_info.size) as usize;
|
||||||
|
let data = &bnd_data[ofs_start..ofs_end];
|
||||||
|
|
||||||
|
let internal_path = file_info.path.to_owned().unwrap();
|
||||||
|
// For now, do not keep internal dir structure and use only file name.
|
||||||
|
let file_name = if let Some(last_sep_index) = internal_path.rfind('\\') {
|
||||||
|
&internal_path[last_sep_index as usize + 1usize..]
|
||||||
|
} else {
|
||||||
|
&internal_path
|
||||||
|
};
|
||||||
|
let mut file_path = output_dir.to_path_buf();
|
||||||
|
file_path.push(file_name);
|
||||||
|
|
||||||
|
let mut output_file = fs::File::create(file_path)?;
|
||||||
|
output_file.write_all(&data)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +66,7 @@ pub fn extract_bnd(bnd: bnd::Bnd, data: Vec<u8>, output_path: &str) -> Result<()
|
||||||
///
|
///
|
||||||
/// Wraps around `load_bnd` to load the BND from disk. It returns the
|
/// Wraps around `load_bnd` to load the BND from disk. It returns the
|
||||||
/// parsed BND metadata and the whole file as a byte vector.
|
/// parsed BND metadata and the whole file as a byte vector.
|
||||||
|
/// Wraps around `load_bnd` to load the BND from disk.
|
||||||
pub fn load_bnd_file(bnd_path: &str) -> Result<(bnd::Bnd, Vec<u8>), UnpackError> {
|
pub fn load_bnd_file(bnd_path: &str) -> Result<(bnd::Bnd, Vec<u8>), UnpackError> {
|
||||||
let mut bnd_file = fs::File::open(bnd_path)?;
|
let mut bnd_file = fs::File::open(bnd_path)?;
|
||||||
let file_len = bnd_file.metadata()?.len() as usize;
|
let file_len = bnd_file.metadata()?.len() as usize;
|
||||||
|
@ -47,7 +78,7 @@ pub fn load_bnd_file(bnd_path: &str) -> Result<(bnd::Bnd, Vec<u8>), UnpackError>
|
||||||
/// Load a BND file from a bytes slice.
|
/// Load a BND file from a bytes slice.
|
||||||
pub fn load_bnd(bnd_data: &[u8]) -> Result<bnd::Bnd, UnpackError> {
|
pub fn load_bnd(bnd_data: &[u8]) -> Result<bnd::Bnd, UnpackError> {
|
||||||
let (_, bnd) = match bnd::parse(bnd_data) {
|
let (_, bnd) = match bnd::parse(bnd_data) {
|
||||||
Ok(result) => { result }
|
Ok(result) => result,
|
||||||
Err(NomError(e)) | Err(NomFailure(e)) => {
|
Err(NomError(e)) | Err(NomFailure(e)) => {
|
||||||
let reason = unpackers_errors::get_nom_error_reason(e.1);
|
let reason = unpackers_errors::get_nom_error_reason(e.1);
|
||||||
return Err(UnpackError::Parsing("BND parsing failed: ".to_owned() + &reason))
|
return Err(UnpackError::Parsing("BND parsing failed: ".to_owned() + &reason))
|
||||||
|
|
|
@ -5,6 +5,7 @@ pub enum UnpackError {
|
||||||
Io(io::Error),
|
Io(io::Error),
|
||||||
Parsing(String),
|
Parsing(String),
|
||||||
Compression(String),
|
Compression(String),
|
||||||
|
Naming(String),
|
||||||
Unknown(String),
|
Unknown(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Reference in a new issue