tw_unpack 0.1.11

A Total War series pack file unpacker
extern crate getopts;
extern crate glob;
extern crate tw_pack_lib;

use std::env;
use std::fs::File;
use std::fs::OpenOptions;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;

use getopts::Options;
use glob::glob;

static VERSION: &str = env!("CARGO_PKG_VERSION");

struct Config {
    verbose: bool
}

fn unpack_pack(path: &PathBuf, output_directory: &PathBuf, config: &Config) {
    match File::open(&path) {
        Ok(pack_filename) => {
            let pack = tw_pack_lib::parse_pack(pack_filename).unwrap();
            println!("unpacking {}: {}", &path.display(), &pack);

            for item in pack.into_iter() {
                if config.verbose {
                    println!("{}", &item);
                }
                let target_directory = output_directory.join(&Path::new(&item.path).parent().unwrap());
                let target_path = output_directory.join(&item.path);
                std::fs::create_dir_all(target_directory).unwrap();
                let mut file = OpenOptions::new().write(true).create(true).open(&target_path).unwrap();
                file.write(&item.get_data().unwrap()).unwrap();
            }
        },
        Err(e) => panic!("Could not open file {} ({})", &path.display(), e)
    }
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("tw_unpack version {}\nUsage: {} FILE", VERSION, program);
    println!("{}", opts.usage(&brief));
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();
    let mut opts = Options::new();
    opts.optopt("o", "", "the output directory for the extracted files. If no output directory is specified, tw_unpack will save the files in the current directory", "OUTPUT");
    opts.optflag("v", "", "enable verbose logging");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => {
            println!("failed to parse arguments ({})", f);
            return;
        }
    };

    let output_directory_param = matches.opt_str("o");
    let pack_filename_param = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        print_usage(&program, opts);
        return;
    };

    let verbose = if matches.opt_present("v") {
        true
    } else {
        false
    };

    let output_directory = match output_directory_param {
        Some(p) => {
            let path = PathBuf::from(&p);
            if path.exists() {
                path
            } else {
                println!("output directory does not exist");
                return;
            }
        },
        None => {
            match env::current_dir() {
                Ok(curr_dir) => curr_dir,
                Err(e) => {
                    println!("invalid current directory ({})", e);
                    return;
                }
            }
        }
    };

    let config = Config {
        verbose: verbose
    };

    match glob(&pack_filename_param) {
        Ok(glob) => {
            for entry in glob {
                match entry {
                    Ok(path) => {
                        unpack_pack(&path, &output_directory, &config)
                    }
                    Err(e) => println!("failed to handle glob entry ({})", e),
                }
            }
        },
        Err(e) => {
            println!("invalid glob pattern ({})", e);
            return;
        }
    }
}