use chmlib::{ChmFile, Filter, UnitInfo};
use std::{
env,
error::Error,
fs::{self, File},
io::Write,
path::{Path, PathBuf},
};
fn main() {
let args: Vec<_> = env::args().skip(1).collect();
if args.len() != 2 || args.iter().any(|arg| arg.contains("-h")) {
println!("Usage: extract <chm-file> <out-dir>");
return;
}
let mut file = ChmFile::open(&args[0]).expect("Unable to open the file");
let out_dir = PathBuf::from(&args[1]);
file.for_each(Filter::all(), |file, item| extract(&out_dir, file, &item))
.unwrap();
}
fn extract(
root_dir: &Path,
file: &mut ChmFile,
item: &UnitInfo,
) -> Result<(), Box<dyn Error>> {
if !item.is_file() || !item.is_normal() {
return Ok(());
}
let path = match item.path() {
Some(p) => p,
None => return Ok(()),
};
let mut dest = root_dir.to_path_buf();
dest.extend(path.components().skip(1));
if let Some(parent) = dest.parent() {
fs::create_dir_all(parent)?;
}
let mut f = File::create(dest)?;
let mut start_offset = 0;
let mut buffer = vec![0; 1 << 16];
loop {
let bytes_read = file.read(item, start_offset, &mut buffer)?;
if bytes_read == 0 {
break;
} else {
start_offset += bytes_read as u64;
f.write_all(&buffer)?;
}
}
Ok(())
}