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
use crate::{fields, format::*};
use nom::{
    bytes::streaming::tag,
    combinator::opt,
    number::streaming::{le_u16, le_u32, le_u64},
    sequence::preceded,
};

#[derive(Debug)]
/// 4.3.7 Local file header
pub struct LocalFileHeaderRecord {
    /// version needed to extract
    pub reader_version: Version,
    /// general purpose bit flag
    pub flags: u16,
    /// compression method
    pub method: u16,
    /// last mod file datetime
    pub modified: MsdosTimestamp,
    /// crc-32
    pub crc32: u32,
    /// compressed size
    pub compressed_size: u32,
    /// uncompressed size
    pub uncompressed_size: u32,
    // file name
    pub name: ZipString,
    // extra field
    pub extra: ZipBytes,
}

impl LocalFileHeaderRecord {
    pub const SIGNATURE: &'static str = "PK\x03\x04";

    pub fn parse(i: &[u8]) -> parse::Result<'_, Self> {
        preceded(
            tag(Self::SIGNATURE),
            fields!({
                reader_version: Version::parse,
                flags: le_u16,
                method: le_u16,
                modified: MsdosTimestamp::parse,
                crc32: le_u32,
                compressed_size: le_u32,
                uncompressed_size: le_u32,
                name_len: le_u16,
                extra_len: le_u16,
            } chain fields!({
                name: ZipString::parser(name_len),
                extra: ZipBytes::parser(extra_len),
            } map Self {
                reader_version,
                flags,
                method,
                modified,
                crc32,
                compressed_size,
                uncompressed_size,
                name,
                extra,
            })),
        )(i)
    }

    pub fn has_data_descriptor(&self) -> bool {
        // 4.3.9.1 This descriptor MUST exist if bit 3 of the general
        // purpose bit flag is set (see below).
        self.flags & 0b1000 != 0
    }
}

/// 4.3.9  Data descriptor:
#[derive(Debug)]
pub struct DataDescriptorRecord {
    /// CRC32 checksum
    pub crc32: u32,
    /// Compressed size
    pub compressed_size: u64,
    /// Uncompressed size
    pub uncompressed_size: u64,
}

impl DataDescriptorRecord {
    const SIGNATURE: &'static str = "PK\x07\x08";

    pub fn parse(i: &[u8], is_zip64: bool) -> parse::Result<'_, Self> {
        if is_zip64 {
            preceded(
                opt(tag(Self::SIGNATURE)),
                fields!(Self {
                    crc32: le_u32,
                    compressed_size: le_u64,
                    uncompressed_size: le_u64,
                }),
            )(i)
        } else {
            preceded(
                opt(tag(Self::SIGNATURE)),
                fields!({
                    crc32: le_u32,
                    compressed_size: le_u32,
                    uncompressed_size: le_u32,
                } map Self {
                    crc32,
                    compressed_size: compressed_size as u64,
                    uncompressed_size: uncompressed_size as u64,
                }),
            )(i)
        }
    }
}