-
Notifications
You must be signed in to change notification settings - Fork 0
/
unzip2.pq
86 lines (83 loc) · 3.56 KB
/
unzip2.pq
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
let
fx=(ZIPFile) =>
let
//shorthand
UInt32 = BinaryFormat.ByteOrder(BinaryFormat.UnsignedInteger32,
ByteOrder.LittleEndian),
UInt16 = BinaryFormat.ByteOrder(BinaryFormat.UnsignedInteger16,
ByteOrder.LittleEndian),
//Local File Header
Header = BinaryFormat.Record([
Version = UInt16,
Flags = UInt16,
Compression = UInt16,
ModTime = UInt16,
ModDate = UInt16,
CRC32 = UInt32,
CompressedSize = UInt32,
UncompressedSize = UInt32,
FileNameLen = UInt16,
ExtraFieldLen = UInt16]),
FileData = (h)=> BinaryFormat.Record([
Header = h,
FileName = BinaryFormat.Text(h[FileNameLen]),
ExtraField = BinaryFormat.Text(h[ExtraFieldLen]),
UncompressedData = BinaryFormat.Transform(
BinaryFormat.Binary(h[CompressedSize]),
(x) => try
Binary.Buffer(Binary.Decompress(x, Compression.Deflate))
otherwise null)]),
//Central Directory
CentralDirHeader = BinaryFormat.Record([
Version = UInt16,
VersNeeded = UInt16,
Flags = UInt16,
Compression = UInt16,
ModTime = UInt16,
ModDate = UInt16,
CRC32 = UInt32,
CompressedSize = UInt32,
UncompressedSize = UInt32,
FileNameLen = UInt16,
ExtraFieldLen = UInt16,
FileCommLen = UInt16,
DiskStart = UInt16,
InternalAttr = UInt16,
ExternalAttr = UInt32,
OffsetOfLocalHeader = UInt32]),
CentralDir = (cdh)=> BinaryFormat.Record([
CentralDirHeader = cdh,
FileName = BinaryFormat.Text(cdh[FileNameLen]),
ExtraField = BinaryFormat.Text(cdh[ExtraFieldLen]),
FileComment = BinaryFormat.Text(cdh[FileCommLen])]),
// End block of Central Directory
EndDirHeader = BinaryFormat.Record([
DiskNumber = UInt16,
DiskWCD = UInt16,
DiskEntries = UInt16,
TotalEntries = UInt16,
CentDirSize = UInt32,
OffsetCDwrtStartDisk = UInt32,
CommentLen = UInt16]),
EndDir = (edh)=> BinaryFormat.Record([
EndDirHeader = edh,
ZipFileComment = BinaryFormat.Text(edh[CommentLen])]),
//Choosing which Zip block type to parse depending on the signature
SignatureChoice = (x) =>
let
SignatureOptions ={ [sig = 0x04034B50, head = Header, body = FileData],
[sig = 0x02014B50, head = CentralDirHeader, body = CentralDir],
[sig = 0x06054B50, head = EndDirHeader, body = EndDir]}
in List.First(List.Select(SignatureOptions, each _[sig]=x)),
ZipPart = BinaryFormat.Choice(UInt32, (_) =>
let
zipPartChoice = SignatureChoice(_)
in BinaryFormat.Choice(zipPartChoice[head],(z)=>zipPartChoice[body](z),
type binary),
type binary),
ZipFormat = BinaryFormat.List(ZipPart),
Entries = ZipFormat(File.Contents(ZIPFile))
in
Entries
in
fx