-
-
Notifications
You must be signed in to change notification settings - Fork 187
/
zip.hexpat
220 lines (197 loc) · 8.35 KB
/
zip.hexpat
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
#pragma description ZIP compression archive
#pragma MIME application/zip
import std.mem;
import std.math;
import type.time;
struct EndOfCentralDirectory {
u32 headerSignature [[comment("EoCD magic"), name("EoCD PK\\5\\6")]];
u16 diskNum [[comment("Number of this disk "), name("Disk Number")]];
u16 diskStart [[comment("Disk where central directory starts "), name("Central Directory Disk Number")]];
u16 CDRCount [[comment("Number of central directory records on this disk"), name("Central Directory Entries")]];
u16 CentralDirectoryRecordCount [[comment("Total number of entries in the central directory"), name("Total Central Directory Entries")]];
u32 CDSize [[comment("Size of central directory (bytes)"), name("Central Directory Size")]];
u32 CDOffset [[comment("Offset of start of central directory, relative to start of archive"), name("Central Directory Offset")]];
u16 commentLength [[color("00000000")]];
char coment[commentLength] [[name("Comment")]];
};
namespace extra {
bitfield Flags {
bool modification_time_set : 1;
bool access_time_set : 1;
bool creation_time_set : 1;
reserved: 5; //reserved for additional timestamps; not set
};
struct X5455_ExtendedTimestamp {
Flags Flags;
if (Flags.modification_time_set){
u32 ModTime;
}
if (Flags.access_time_set){
u32 AcTime;
}
if (Flags.creation_time_set){
u32 CrTime;
}
};
struct X000A_NTFS {
u32 reserved;
u16 tag;
u16 TSize;
//Timestamps are in FILETIME format. Converted to Unix for easier handling.
type::FILETIME ModTime;
type::FILETIME AcTime;
type::FILETIME CrTime;
};
struct X7875_NewUnix {
u16 tag;
u16 TSize;
u8 version;
u8 UIDSize;
u8 UID[UIDSize];
u8 GIDSize;
u8 GID[GIDSize];
};
struct X5855_InfoZipUnix {
u32 AcTime;
u32 ModTime;
if (parent.TSize > 8){
u16 UID;
}
if (parent.TSize > 10){
u16 GID;
}
};
struct ExtraField {
u16 tag;
u16 TSize;
if (tag == 0x5455){
extra::X5455_ExtendedTimestamp x5455_ExtendedTimestamp;
}else if (tag == 0x000a){
extra::X000A_NTFS x000A_NTFS;
}else if (tag == 0x7875){
extra::X7875_NewUnix x7875_NewUnix;
}else if (tag == 0x5855){
extra::X5855_InfoZipUnix x5855_InfoZipUnix;
}else{
std::print("Unsupported tag 0x{:02X}", tag);
padding[TSize];
}
};
}
fn find_eocd() {
// If there is no zip comment, which is the common case,
// the end-of-central-directory record will be 22 bytes long
// at the end of the file; check if size-22 has the signature.
if (std::mem::read_unsigned(std::mem::size()-22, 4, std::mem::Endian::Little) == 0x06054B50) {
return std::mem::size()-22;
} else {
// If it's not there, then there's probably a zip comment;
// search the last 64KB of the file for the signature.
u128 offset_search_from = std::math::max(0, std::mem::size()-65536-22);
u128 prev_address;
while(1){
u128 current_address = std::mem::find_sequence_in_range(0, offset_search_from, std::mem::size(), 0x50,0x4B,0x05,0x06);
//Reached EOF and did not find valid eocd.
if (current_address == 340282366920938463463374607431768211455){
std::error("Could not find EOCD.");
}
//Potential eocd found. Create a eocd struct
EndOfCentralDirectory EOCD @ current_address;
//If central directory file header is valid, then we know the eocd offset is valid.
if (std::mem::read_unsigned(EOCD.CDOffset, 4, std::mem::Endian::Little) == 0x2014B50){
return current_address;
}
offset_search_from = current_address + 1;
prev_address = current_address;
}
}
};
EndOfCentralDirectory fileInfo @ find_eocd() [[name("End of Central Directory Record")]];
enum CompressionMethod : u16 {
None = 0, // The file is stored (no compression)
Shrunk = 1, // The file is Shrunk
Factor1 = 2, // The file is Reduced with compression factor 1
Factor2 = 3, // The file is Reduced with compression factor 2
Factor3 = 4, // The file is Reduced with compression factor 3
Factor4 = 5, // The file is Reduced with compression factor 4
Implode = 6, // The file is Imploded
// ? = 7, // Reserved for Tokenizing compression algorithm
Deflate = 8, // The file is Deflated
Deflate64 = 9, // Enhanced Deflating using Deflate64(tm)
PKWARE = 10, // PKWARE Data Compression Library Imploding (old IBM TERSE)
// ? =11, // Reserved by PKWARE
BZIP2 = 12, // File is compressed using BZIP2 algorithm
// ? = 13, // Reserved by PKWARE
LZMA = 14, // LZMA
// ? = 15, // Reserved by PKWARE
CMPSC = 16, // IBM z/OS CMPSC Compression
// ? = 17, // Reserved by PKWARE
IBMTERSE = 18, // File is compressed using IBM TERSE (new)
LZ77 = 19, // IBM LZ77 z Architecture
_ZSTD = 20, // deprecated (use method 93 for zstd)
ZSTD = 93, // Zstandard (zstd) Compression
MP3 = 94, // MP3 Compression
XZ = 95, // XZ Compression
JPEG = 96, // JPEG variant
WavPack = 97, // WavPack compressed data
PPMd = 98, // PPMd version I, Rev 1
AE_x = 99, // AE-x encryption marker (see APPENDIX E)
};
bitfield GeneralPurposeBitFlags {
encrypted : 1;
compressionOptions : 2;
crcAndSizesInCDAndDataDescriptor : 1;
enhancedDeflating : 1;
patchedData : 1;
strongEncryption : 1;
unused : 4;
filenameAndCommentAreUtf8 : 1;
reservedPKWARE_0 : 1;
centralDirectoryEncrypted : 1;
reservedPKWARE_1 : 2;
};
struct LocalFileHeader {
u32 headerSignature [[name("LCF PK\\3\\4")]];
u16 version [[ comment("The minimum supported ZIP specification version needed to extract the file") ]];
GeneralPurposeBitFlags generalPurposeBitFlags [[ comment("General purpose bit flag") ]];
CompressionMethod compressionMethod [[ comment("Compression method") ]];
u16 lastModifyTime [[ comment("File last modification time") ]];
u16 lastModifyDate [[ comment("File last modification date") ]];
u32 crc32 [[ comment("CRC-32") ]];
u32 compressedSize [[ comment("Compressed size") ]];
u32 uncompressedSize [[ comment("Uncompressed size") ]];
u16 fileNameLength [[ comment("File name length (n)") ]];
u16 extraFieldLength [[ comment("Extra field length (m)") ]];
char fileName[fileNameLength] [[ comment("File Name") ]];
u64 extraEnd = $ + extraFieldLength;
extra::ExtraField extraFields[while ($ < extraEnd)] [[comment("Extra Fields")]];
u8 data[compressedSize] [[name("File Data")]];
};
union File {
u32 fileOffset [[comment("Offset of local file header, relative to the start of the first disk on which the file occurs.")]];
LocalFileHeader *fileHeader : u32;
};
struct CentralDirectoryFileHeader {
u32 headerSignature [[name("CDFH PK\\1\\2")]];
u16 versionMade [[comment("Version file made by")]];
u16 versionExtract [[comment("Minimum version needed to extract")]];
GeneralPurposeBitFlags generalPurposeBitFlags [[comment("General purpose bit flag")]];
CompressionMethod compressionMethod;
u16 fileLastModifyTime [[comment("File last modification time")]];
u16 fileLastModifyDate [[comment("File last modification date")]];
u32 crc32 [[comment("CRC-32 of uncompressed data")]];
u32 compressedSize;
u32 uncompressedSize;
u16 fileNameLength;
u16 extraFieldLength;
u16 fileCommentLength;
u16 diskNumber [[comment("Disk number where file starts")]];
u16 internalFileAttributes;
u32 externalFileAttributes;
File file;
char fileName[fileNameLength];
u64 extraEnd = $ + extraFieldLength;
extra::ExtraField extraFields[while ($ < extraEnd)] [[comment("Extra Fields")]];
char comment[fileCommentLength];
};
CentralDirectoryFileHeader centralDirHeaders[fileInfo.CDRCount] @ (fileInfo.CDOffset) [[name("Files")]];