//! CLI to print details about the 2 Byte zlib header, as defined in //! [RFC 1950](https://datatracker.ietf.org/doc/html/rfc1950). //! This was initially written to reverse-engineer more details about Blizzard's proprietary MPQ //! archive format, which uses zlib streams to store data. //! ``` //! Usage: zlib-header-cli [OPTIONS] [HEX4CHAR] //! //! Arguments: //! [HEX4CHAR] A four-character hex string representing the ZlibHeader (Big Endian) //! //! Options: //! -e, --explain Explain the ZlibHeader struct and abbreviations //! -h, --help Print help //! ``` //! ``` //! drfrugal@karazhan:~$ zlib-header-cli 789C //! CM 8 (DEFLATE) //! CINFO 7 (Window: 32768 Bytes) //! FCHECK 28 (valid) //! FDICT false (Preset Dictionary not used) //! FLEVEL 2 (default) //! ``` //! ``` //! drfrugal@karazhan:~$ zlib-header-cli --explain //! 2 Byte zlib header according to https://datatracker.ietf.org/doc/html/rfc1950 //! 1st Byte - CMF (Compression Method and Flags) //! 0000_1111 CM Compression Method 8, _ (DEFLATE, UNDEFINED) //! 1111_0000 CINFO Compression Info window size is 2.pow(CINFO + 8) //! 2nd Byte - FLG (Flags) //! 0001_1111 FCHECK Checksum Adjustment valid if (CMF * 256 + FLG) % 31 == 0 //! 0010_0000 FDICT Preset Dictionary a preset dictionary was used //! 1100_0000 FLEVEL Compression Level 0, 1, 2, 3 (fastest, fast, default, best) //! ``` use clap::Parser; use zlib_header::ZlibHeader; #[derive(Debug, Parser)] pub struct Cli { /// Explain the ZlibHeader struct and abbreviations #[arg(short, long)] explain: bool, /// A four-character hex string representing the ZlibHeader (Big Endian) #[arg(required_unless_present = "explain")] hex4char: Option, } pub fn main() { let args = Cli::parse(); if args.explain { explain(); } let header = match args.hex4char { Some(hex) => match ZlibHeader::from_hex(&hex) { Ok(header) => header, Err(err) => { eprintln!("{}", err); return; } }, None => { return; } }; if args.explain { println!(); } let cm = header.get_cm(); let algorithm = header.get_cm_str(); let cinfo = header.get_cinfo(); let window = header.get_window_size(); let fcheck = header.get_fcheck(); let valid = if header.is_valid() { "valid" } else { "not valid" }; let fdict = header.get_fdict(); let dict = if fdict { "used" } else { "not used" }; let flevel = header.get_flevel(); let level = header.get_flevel_str(); println!("CM {} ({})", cm, algorithm); println!("CINFO {} (Window: {} Bytes)", cinfo, window); println!("FCHECK {} ({})", fcheck, valid); println!("FDICT {} (Preset Dictionary {})", fdict, dict); println!("FLEVEL {} ({})", flevel, level); } /// Prints a few lines in order to explain the ZlibHeader struct. pub fn explain() { println!("2 Byte zlib header as defined in https://datatracker.ietf.org/doc/html/rfc1950"); println!("1st Byte - CMF (Compression Method and Flags)"); println!(" 0000_1111 CM Compression Method 8, _ (DEFLATE, UNDEFINED)"); println!(" 1111_0000 CINFO Compression Info window size is 2.pow(CINFO + 8)"); println!("2nd Byte - FLG (Flags)"); println!(" 0001_1111 FCHECK Checksum Adjustment valid if (CMF * 256 + FLG) % 31 == 0"); println!(" 0010_0000 FDICT Preset Dictionary a preset dictionary was used"); println!(" 1100_0000 FLEVEL Compression Level 0, 1, 2, 3 (fastest, fast, default, best)"); }