diff --git a/src/main.rs b/src/main.rs index 340c957..4220f16 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,14 @@ +use std::env::current_exe; use std::fs::File; use std::io::{Error, Read}; +use std::path::{Path, PathBuf}; -extern crate clap; +//extern crate clap; use clap::{App, AppSettings, Arg, ArgMatches, SubCommand}; +//extern crate nom; +use nom::Err::{Error as NomError, Failure as NomFailure}; + mod parsers { pub mod bhd; } @@ -21,7 +26,12 @@ fn main() { .short("o") .long("output") .takes_value(true) - .required(true))) + .required(true)) + .arg(Arg::with_name("namefile") + .short("n") + .long("names") + .takes_value(true) + .required(false))) .get_matches(); match matches.subcommand() { @@ -33,13 +43,31 @@ fn main() { fn cmd_bhd(args: &ArgMatches) -> Result::<(), Error> { let filepath: &str = args.value_of("file").unwrap(); let outputpath: &str = args.value_of("output").unwrap(); - println!("File: {:?}", filepath); - println!("Output: {:?}", outputpath); - + let namefilepath: &str = args.value_of("namefile").unwrap_or(&get_default_namefilepath()); let mut bhd_file: File = File::open(filepath)?; - let mut bhd_data = vec![0u8; bhd_file.metadata()?.len() as usize]; - bhd_file.read_to_end(&mut bhd_data)?; + let file_len = bhd_file.metadata()?.len() as usize; + 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(()) } + +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()) +} diff --git a/src/parsers/bhd.rs b/src/parsers/bhd.rs index ff5569b..b3a94a0 100644 --- a/src/parsers/bhd.rs +++ b/src/parsers/bhd.rs @@ -1,23 +1,95 @@ 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::sequence::tuple; #[derive(Debug)] pub struct BhdHeader { pub magic: u32, -// pub unk04: i8, -// pub unk05: i8, -// pub unk06: i8, -// pub unk07: i8, -// pub unk08: u32, -// pub num_buckets: u32, -// pub ofs_buckets: u32, + pub unk04: i8, // PC=0, PS3=-1 + pub unk05: i8, + pub unk06: i8, + pub unk07: i8, + pub unk08: u32, + pub file_len: u32, + pub num_buckets: u32, + pub ofs_buckets: u32, } const MAGIC: u32 = 0x35444842; -named!(check_magic<&[u8], u32>, verify!(le_u32, |m| *m == MAGIC)); -pub fn parse(i: &[u8]) -> IResult<&[u8], BhdHeader> { - let (i, magic) = check_magic(i)?; - Ok((i, BhdHeader { magic })) +fn parse_header(i: &[u8]) -> IResult<&[u8], BhdHeader> { + let (i, (magic, flags, unk08, file_len, num_buckets, ofs_buckets)) = + 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, + pub buckets: Vec>, +} + +/// 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!(); + 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) { + +} +