From 0d054955d48711279e152e123a5aa75cbb49b60c Mon Sep 17 00:00:00 2001 From: drfrugal Date: Fri, 7 Mar 2025 21:15:18 +0100 Subject: init --- cli/Cargo.toml | 8 ++++ cli/src/main.rs | 112 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 cli/Cargo.toml create mode 100644 cli/src/main.rs (limited to 'cli') diff --git a/cli/Cargo.toml b/cli/Cargo.toml new file mode 100644 index 0000000..e84f99e --- /dev/null +++ b/cli/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "zlib-header-cli" +version = "0.1.0" +edition = "2024" + +[dependencies] +clap = { version = "4.5.30", features = ["derive"] } +zlib-header = { path = "../lib" } diff --git a/cli/src/main.rs b/cli/src/main.rs new file mode 100644 index 0000000..9392372 --- /dev/null +++ b/cli/src/main.rs @@ -0,0 +1,112 @@ +//! 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); + + let cm = 8; + let cinfo = 7; + let fdict = false; + let flevel = 2; + let header = ZlibHeader::new(cm, cinfo, fdict, flevel); + match header { + Ok(header) => { + println!("header is valid (strict): {}", header.is_valid_strict()); // + }, + Err(err) => eprintln!("Unable to initialize zlib header: {:?}", err) + } +} + +/// 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)"); +} -- cgit v1.2.3