diff options
Diffstat (limited to 'cli/src/main.rs')
-rw-r--r-- | cli/src/main.rs | 112 |
1 files changed, 112 insertions, 0 deletions
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<String>,
+}
+
+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)");
+}
|