bhd: complete parsing

This commit is contained in:
Dece 2020-04-13 22:31:26 +02:00
parent 817c8491ef
commit de2513e22a
2 changed files with 120 additions and 20 deletions

View file

@ -1,9 +1,14 @@
use std::env::current_exe;
use std::fs::File; use std::fs::File;
use std::io::{Error, Read}; use std::io::{Error, Read};
use std::path::{Path, PathBuf};
extern crate clap; //extern crate clap;
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand}; use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
//extern crate nom;
use nom::Err::{Error as NomError, Failure as NomFailure};
mod parsers { mod parsers {
pub mod bhd; pub mod bhd;
} }
@ -21,7 +26,12 @@ fn main() {
.short("o") .short("o")
.long("output") .long("output")
.takes_value(true) .takes_value(true)
.required(true))) .required(true))
.arg(Arg::with_name("namefile")
.short("n")
.long("names")
.takes_value(true)
.required(false)))
.get_matches(); .get_matches();
match matches.subcommand() { match matches.subcommand() {
@ -33,13 +43,31 @@ fn main() {
fn cmd_bhd(args: &ArgMatches) -> Result::<(), Error> { fn cmd_bhd(args: &ArgMatches) -> Result::<(), Error> {
let filepath: &str = args.value_of("file").unwrap(); let filepath: &str = args.value_of("file").unwrap();
let outputpath: &str = args.value_of("output").unwrap(); let outputpath: &str = args.value_of("output").unwrap();
println!("File: {:?}", filepath); let namefilepath: &str = args.value_of("namefile").unwrap_or(&get_default_namefilepath());
println!("Output: {:?}", outputpath);
let mut bhd_file: File = File::open(filepath)?; let mut bhd_file: File = File::open(filepath)?;
let mut bhd_data = vec![0u8; bhd_file.metadata()?.len() as usize]; let file_len = bhd_file.metadata()?.len() as usize;
bhd_file.read_to_end(&mut bhd_data)?; let mut bhd_data = vec![0u8; file_len];
bhd_file.read_exact(&mut bhd_data)?;
let bhd = match bhd::parse(&bhd_data) {
Ok((_, bhd)) => { println!("BHD: {:?}", bhd); bhd }
Err(NomError(e)) | Err(NomFailure(e)) => {
let (_, kind) = e;
let reason = format!("{:?} {:?}", kind, kind.description());
eprintln!("BHD parsing failed: {}", reason); return Ok(())
}
e => {
eprintln!("Unknown error: {:?}", e); return Ok(())
}
};
bhd::parse(&bhd_data);
Ok(()) Ok(())
} }
fn get_default_namefilepath() -> String {
let programpath: PathBuf = current_exe().unwrap();
let programdir: &Path = programpath.parent().unwrap();
let mut namefilepath: PathBuf = PathBuf::from(programdir);
namefilepath.push("res/namefile.json");
String::from(namefilepath.to_str().unwrap())
}

View file

@ -1,23 +1,95 @@
extern crate nom; extern crate nom;
use nom::{IResult, named, verify}; use nom::{IResult};
use nom::combinator::verify;
use nom::multi::count;
use nom::number::complete::*; use nom::number::complete::*;
use nom::sequence::tuple;
#[derive(Debug)] #[derive(Debug)]
pub struct BhdHeader { pub struct BhdHeader {
pub magic: u32, pub magic: u32,
// pub unk04: i8, pub unk04: i8, // PC=0, PS3=-1
// pub unk05: i8, pub unk05: i8,
// pub unk06: i8, pub unk06: i8,
// pub unk07: i8, pub unk07: i8,
// pub unk08: u32, pub unk08: u32,
// pub num_buckets: u32, pub file_len: u32,
// pub ofs_buckets: u32, pub num_buckets: u32,
pub ofs_buckets: u32,
} }
const MAGIC: u32 = 0x35444842; const MAGIC: u32 = 0x35444842;
named!(check_magic<&[u8], u32>, verify!(le_u32, |m| *m == MAGIC));
pub fn parse(i: &[u8]) -> IResult<&[u8], BhdHeader> { fn parse_header(i: &[u8]) -> IResult<&[u8], BhdHeader> {
let (i, magic) = check_magic(i)?; let (i, (magic, flags, unk08, file_len, num_buckets, ofs_buckets)) =
Ok((i, BhdHeader { magic })) tuple((
verify(le_u32, |m| *m == MAGIC),
count(le_i8, 4),
le_u32,
le_u32,
le_u32,
le_u32,
))(i)?;
Ok((i, BhdHeader {
magic,
unk04: flags[0], unk05: flags[1], unk06: flags[2], unk07: flags[3],
unk08,
file_len,
num_buckets,
ofs_buckets,
}))
} }
#[derive(Debug)]
pub struct BhdBucketInfo {
pub count: u32,
pub offset: u32,
}
fn parse_bucket_info(i: &[u8]) -> IResult<&[u8], BhdBucketInfo> {
let (i, (count, offset)) = tuple((le_u32, le_u32))(i)?;
Ok((i, BhdBucketInfo { count, offset }))
}
#[derive(Debug)]
pub struct BhdFile {
pub hash: u32,
pub size: u32,
pub offset: u64,
}
pub fn parse_file(i: &[u8]) -> IResult<&[u8], BhdFile> {
let (i, (hash, size, offset)) = tuple((le_u32, le_u32, le_u64))(i)?;
Ok((i, BhdFile { hash, size, offset }))
}
#[derive(Debug)]
pub struct Bhd {
pub header: BhdHeader,
pub bucket_infos: Vec<BhdBucketInfo>,
pub buckets: Vec<Vec<BhdFile>>,
}
/// Parse a BHD file into a usable Bhd struct.
pub fn parse(i: &[u8]) -> IResult<&[u8], Bhd> {
let full_file = i;
let (i, header) = parse_header(i)?;
let (i, bucket_infos) = count(parse_bucket_info, header.num_buckets as usize)(i)?;
let mut buckets: Vec<Vec<BhdFile>> = vec!();
for b in 0..header.num_buckets {
println!("Bucket {}", b);
let bucket_info = &bucket_infos[b as usize];
let bucket_data = &full_file[bucket_info.offset as usize..];
let (_, bucket) = count(parse_file, bucket_info.count as usize)(bucket_data)?;
buckets.push(bucket);
}
Ok((i, Bhd { header, bucket_infos, buckets }))
}
/// Extract files from a BHD/BDT pair.
pub fn extract(bhd: Bhd, bdt_path: &str) {
}