aboutsummaryrefslogtreecommitdiff
path: root/cli/src/main.rs
blob: 9392372bc3e942c3047d51885887526faf98e6ef (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
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)");
}