use std::path::Path;
use clap::{App, Arg, ArgMatches, SubCommand};
use crate::CliResult;
pub fn clap<'a, 'b>() -> App<'a, 'b> {
#[allow(clippy::needless_pass_by_value)]
fn validate_input(value: String) -> Result<(), String> {
let path = Path::new(&value);
match path.metadata() {
Ok(ref md) if md.is_file() => Ok(()),
Ok(_) => Err(String::from("Not a valid file")),
Err(_) => Err(String::from("Not a valid file")),
}
}
SubCommand::with_name("print")
.about("Print information of a hpk archive")
.display_order(30)
.arg(Arg::from_usage("<file> 'hpk archive'").validator(validate_input))
.arg(Arg::from_usage(
"[header] --header-only 'Print only the header informations'",
))
}
pub fn execute(matches: &ArgMatches<'_>) -> CliResult {
let input = value_t!(matches, "file", String)?;
let mut walk = hpk::walk(input)?;
println!("reading file: {}", walk.path().display());
if walk.is_compressed() {
println!("file is compressed");
}
println!("header:");
println!(" data_offset: 0x{:X}", walk.header().data_offset);
println!(
" fragments_residual_offset: 0x{:X}",
walk.header().fragments_residual_offset
);
println!(
" fragments_residual_count: {}",
walk.header().fragments_residual_count
);
println!(" fragments_per_file: {}", walk.header().fragments_per_file);
println!(
" fragments_filesystem_offset: 0x{:X}",
walk.header().fragmented_filesystem_offset
);
println!(
" fragments_filesystem_length: {}",
walk.header().fragmented_filesystem_length
);
println!("filesystem entries: {}", walk.header().filesystem_entries());
if matches.is_present("header") {
return Ok(());
}
println!("filesystem fragments:");
for chunk in &walk.fragments {
let mut start = if walk.header().fragments_per_file == 1 {
None
} else {
Some(true)
};
for fragment in chunk {
print!("{}", if start.take().is_some() { "- " } else { " " });
println!("0x{:<6X} len: {}", fragment.offset, fragment.length);
}
}
if !walk.residuals.is_empty() {
println!("residual fragments:");
for f in &walk.residuals {
println!(" 0x{:<6X} len: {}", f.offset, f.length);
}
}
while let Some(Ok(dent)) = walk.next() {
println!(
"{} index={} depth={} {:?}",
if dent.is_dir() { "dir: " } else { "file:" },
dent.index() + 1,
dent.depth(),
dent.path().display(),
);
let fragment = &walk.fragments[dent.index()][0];
println!(
" fragment: 0x{:X} len: {}",
fragment.offset, fragment.length
);
walk.read_file(&dent, |mut r| {
if r.is_empty() {
println!(" empty file");
} else if hpk::get_compression(&mut r)?.is_compressed() {
let hdr = hpk::CompressionHeader::read_from(r.len(), &mut r)?;
println!(
" compressed: {} inflated_length={} chunk_size={} chunks={}",
hdr.compressor,
hdr.inflated_length,
hdr.chunk_size,
hdr.chunks.len()
);
let mut first = Some(true);
for chunk in &hdr.chunks {
if first.take().is_some() {
println!(" chunks: 0x{:<6X} len: {}", chunk.offset, chunk.length);
} else {
println!(" 0x{:<6X} len: {}", chunk.offset, chunk.length);
}
}
} else {
println!(" compressed: no");
}
Ok(())
})?;
}
Ok(())
}