1use chmlib::{ChmFile, Filter, UnitInfo};
2use std::{
3 env,
4 error::Error,
5 fs::{self, File},
6 io::Write,
7 path::{Path, PathBuf},
8};
9
10fn main() {
11 let args: Vec<_> = env::args().skip(1).collect();
12 if args.len() != 2 || args.iter().any(|arg| arg.contains("-h")) {
13 println!("Usage: extract <chm-file> <out-dir>");
14 return;
15 }
16
17 let mut file = ChmFile::open(&args[0]).expect("Unable to open the file");
18
19 let out_dir = PathBuf::from(&args[1]);
20
21 file.for_each(Filter::all(), |file, item| extract(&out_dir, file, &item))
22 .unwrap();
23}
24
25fn extract(
26 root_dir: &Path,
27 file: &mut ChmFile,
28 item: &UnitInfo,
29) -> Result<(), Box<dyn Error>> {
30 if !item.is_file() || !item.is_normal() {
31 return Ok(());
33 }
34 let path = match item.path() {
35 Some(p) => p,
36 None => return Ok(()),
38 };
39
40 let mut dest = root_dir.to_path_buf();
41 dest.extend(path.components().skip(1));
44
45 if let Some(parent) = dest.parent() {
47 fs::create_dir_all(parent)?;
48 }
49
50 let mut f = File::create(dest)?;
51 let mut start_offset = 0;
52 let mut buffer = vec![0; 1 << 16];
56
57 loop {
58 let bytes_read = file.read(item, start_offset, &mut buffer)?;
59 if bytes_read == 0 {
60 break;
62 } else {
63 start_offset += bytes_read as u64;
65 f.write_all(&buffer)?;
66 }
67 }
68
69 Ok(())
70}