-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinflate.monkey
263 lines (198 loc) · 5.82 KB
/
inflate.monkey
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
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
Strict
' A Monkey port of 'Tiny Inflate' by Joergen Ibsen.
' Imports:
Import config
Private
Import util
Import tree
Import impl
Public
Import meta
Import session
Import context
' Functions:
' The destination must support read-write seeking.
' This means reading back serialized data is required.
' For 'FileStream' objects, this requires the "u" or "a" access modes.
Function Inflate:Int(context:InfContext, d:InfSession) ' sourceLen:Int ' uncompress
Local res:Int
' Start a new block:
' Make sure this is our first pass:
If (d.bType = -1) Then
inf_begin_block(d)
Endif
Repeat
' Process the current block:
Select (d.bType)
Case 0
DebugStop()
' "Inflate" uncompressed block.
res = inf_inflate_uncompressed_block(context, d)
Case 1, 2
res = inf_inflate_block_data(context, d, d.lTree, d.dTree)
Default
Return INF_DATA_ERROR
End Select
#Rem
If (d.bFinal) Then
Local src_pos:= d.source.Position
Local dst_pos:= d.destination.Position
If (dst_pos = 45439) Then
DebugStop()
Endif
Endif
#End
If (res = INF_DONE And Not d.bFinal) Then
inf_begin_block(d)
Continue
Elseif (res <> INF_OK) Then
'DebugStop()
Return res
Endif
d.destSize -= 1
Until (d.destSize <= 0) ' Not d.destSize ' Forever ' Until (d.source.Eof())
Return INF_OK
End
#Rem
This inflates using the parameters specified, and outputs
a hash of the type specified by 'data.checksum_type'.
The output stream of 'data' must support full I/O manipulation (Reading/peeking).
Good examples of this are things like 'FileStreams' opened with "u" access-rights,
or 'PublicDataStream' objects, which support both chronological review, or memory-mapping extensions.
#End
Function Inflate_Checksum:Int(context:InfContext, data:InfSession, bigendian_input:Bool=False)
Local start_pos:= data.destination.Position
Local result:= Inflate(context, data)
' Magic number: 0 (Requires error-codes to be negative)
If (result < 0) Then
Return result
Endif
Local end_pos:= data.destination.Position
' Check how many bytes we've written.
Local block_len:= (end_pos - start_pos) ' data.destLen
data.destination.Seek(start_pos)
Select (data.checksum_type)
Case INF_CHECKSUM_ADLER32
data.checksum = inf_adler32(data, start_pos, block_len, data.checksum)
Case INF_CHECKSUM_CRC32
data.checksum = inf_crc32(data, start_pos, block_len, data.checksum)
End Select
data.destination.Seek(end_pos)
' Check if we're done, and if so, read the checksum and check it:
If (result = INF_DONE) Then
Local checksum_raw:= data.source.ReadInt() ' & $FFFFFFFF
Select (data.checksum_type)
Case INF_CHECKSUM_ADLER32
Local checksum:Int ' UInt
If (Not bigendian_input) Then
checksum = NToHL(checksum_raw)
Else
checksum = checksum_raw
Endif
If (data.checksum <> checksum) Then
DebugStop()
Return INF_DATA_ERROR
Endif
Case INF_CHECKSUM_CRC32
Local checksum:Int ' UInt
If (bigendian_input) Then
checksum = HToNL(checksum_raw)
Else
checksum = checksum_raw
Endif
If (~data.checksum <> checksum) Then
Return INF_CHKSUM_ERROR
Endif
' Reserved: Uncompressed size.
Local __size:= data.source.ReadInt()
End Select
Endif
Return result
End
' This parses the 2-byte header of a 'zlib' deflation stream.
Function InfParseZlibHeader:Int(data:InfSession)
Local cmf:Int, flg:Int
' Read the header from the input-stream.
cmf = data.ReadByte()
flg = data.ReadByte()
' Check the format:
' Check aginst the 'checksum':
If (((256*cmf) + flg) Mod 31) Then ' > 0
Return INF_DATA_ERROR
Endif
' Make sure the method is deflate:
If ((cmf & $0F) <> 8) Then
' This was not encoded using deflate.
Return INF_DATA_ERROR
Endif
Local cinfo:= Lsr(cmf, 4)
' Check if the window size is valid:
If (cinfo > 7) Then ' Shr
' This cannot be held within a standard 32K dictionary.
Return INF_DATA_ERROR
Endif
' Check that there's no preset dictionary:
If ((flg & $20)) Then ' > 0
Return INF_DATA_ERROR
Endif
' Initialize checksum behavior:
' Set the checksum type to Adler32.
data.checksum_type = INF_CHECKSUM_ADLER32
' Set the initial value of our checksum.
data.checksum = 1
Return cinfo ' Lsr(cmf, 4) ' Shr ' & $FF
End
' This implementation is currently untested and may be unsafe.
Function InfParseGZipHeader:Int(d:InfSession)
' Constant variable(s):
' Header flag-masks:
Const FTEXT:= 1
Const FHCRC:= 2
Const FEXTRA:= 4
Const FNAME:= 8
Const FCOMMENT:= 16
' -- Check format -- '
' Check the ID bytes:
If (d.ReadByte() <> $1F Or d.ReadByte() <> $8B) Then
Return INF_DATA_ERROR
Endif
' Ensure the method used is deflate.
If (d.ReadByte() <> 8) Then
Return INF_DATA_ERROR
Endif
' Get the flag-byte.
Local flg:= d.ReadByte()
' Check that reserved bits are zero.
If ((flg & $E0)) Then
Return INF_DATA_ERROR
Endif
' -- Find the start of the compressed data stream -- '
' Skip the rest of the base-header (10 bytes total; 6 remaining).
d.SeekForward(6)
' Skip extra data, if present:
If ((flg & FEXTRA)) Then
Local xlen:= d.ReadShort()
d.SeekForward(xlen)
Endif
' Skip the file-name, if present:
If ((flg & FNAME)) Then
' Skip the characters of the name by waiting for a null-character.
While (d.ReadByte() <> 0); Wend
Endif
' Skip the file-comment, if present:
If ((flg & FCOMMENT)) Then
' Skip the characters of the comment as we
' did for the name; wait for a null-character.
While (d.ReadByte() <> 0); Wend
Endif
' Check if the header says a CRC is present.
If ((flg & FHCRC)) Then
' This is currently ignored; a proper
' legitimacy-check may be added at a later date.
Local hcrc:= d.ReadShort()
Endif
' Initialize the CRC32 checksum:
d.checksum_type = INF_CHECKSUM_CRC32
d.checksum = ~0
Return INF_OK
End