Skip to content

Commit 52b2696

Browse files
committed
FEAT: Added ZIP encoder
1 parent 0406fd3 commit 52b2696

File tree

2 files changed

+187
-1
lines changed

2 files changed

+187
-1
lines changed

src/mezz/codec-zip.reb

+152-1
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,158 @@ register-codec [
146146
result
147147
]
148148

149-
;encode: function [data [binary!]][ ]
149+
encode: wrap [
150+
bin: dir: data: date: file: add-data: root: none
151+
compressed-data: method:
152+
compressed-size: size: crc: entries: filename-length: offset: 0
153+
154+
add-file: func[file [file!] /local dir spec][
155+
try/except [
156+
spec: query/mode file [type: date:]
157+
either spec [
158+
file-name: find/tail file root
159+
either spec/type = 'dir [
160+
dir: file
161+
add-data file-name spec
162+
foreach file read dir [
163+
add-file dir/:file
164+
]
165+
][
166+
add-data file-name reduce [spec/date read file]
167+
]
168+
][
169+
; wildcard?
170+
dir: first split-path file
171+
foreach file read file [
172+
add-file dir/:file
173+
]
174+
]
175+
][
176+
sys/log/error 'ZIP ["Failed to add file:" as-green file]
177+
]
178+
]
179+
180+
add-data: func[file spec][
181+
sys/log/info 'ZIP ["Adding:" as-green file]
182+
183+
any [file? file cause-error 'user 'message reduce [reform ["found" type? file "where file! expected"]]]
184+
data: date: none
185+
compressed-size: size: crc: filename-length: 0
186+
any [
187+
all [
188+
block? spec
189+
parse spec [any [
190+
spec:
191+
date! (date: spec/1)
192+
| string! (data: to binary! spec/1)
193+
| binary! (data: spec/1)
194+
| 1 skip
195+
]]
196+
]
197+
all [binary? spec data: spec]
198+
all [string? spec data: to binary! spec]
199+
none? spec ; just a directory
200+
spec = 'none
201+
;else..
202+
all [
203+
sys/log/error 'ZIP ["Invalid zip file's data specification:" as-red mold/part spec 30]
204+
continue
205+
]
206+
]
207+
method: either any [
208+
none? data
209+
lesser-or-equal? size: length? data length? compressed-data: compress data
210+
][
211+
compressed-data: data
212+
0 ;store
213+
][
214+
compressed-data: copy/part skip compressed-data 2 skip tail compressed-data -8 ;@@ FIXME once compress/zlib will be fixed!
215+
8 ;deflate
216+
]
217+
218+
either compressed-data [
219+
crc: checksum data 'CRC32
220+
compressed-size: length? compressed-data
221+
][ compressed-data: #{}
222+
compressed-size: 0
223+
]
224+
if any [
225+
none? date
226+
"?date?" = form date ; temp fix for invalid date!
227+
][ date: now ]
228+
229+
filename-length: length? file
230+
offset: -1 + index? bin/buffer-write
231+
232+
binary/write bin [
233+
#{504B0304 1400 0000} ;signature / version / flags
234+
UI16LE :method
235+
MSDOS-DATETIME :date
236+
UI32LE :crc
237+
UI32LE :compressed-size
238+
UI32LE :size
239+
UI16LE :filename-length
240+
UI16LE 0 ; extra
241+
BYTES :file
242+
BYTES :compressed-data
243+
]
244+
binary/write dir [
245+
#{504B0102 1400 1400 0000} ; signature / version made / version needed / flags
246+
UI16LE :method
247+
MSDOS-DATETIME :date
248+
UI32LE :crc
249+
UI32LE :compressed-size
250+
UI32LE :size
251+
UI16LE :filename-length
252+
UI16LE 0 ; Extra field length
253+
UI16LE 0 ; File comment length
254+
UI16LE 0 ; Disk number where file starts
255+
UI16LE 0 ; Internal file attributes
256+
UI32LE 0 ; External file attributes
257+
UI32LE :offset ; Relative offset of local file header
258+
BYTES :file
259+
;#{} ; Extra field
260+
;#{} ; File comment
261+
]
262+
++ entries
263+
]
264+
265+
;- ENCODE:
266+
func [
267+
"Compress given block of files."
268+
files [block! file!] "[file! binary! ..] or [file! [date! crc binary!] or [dir! none!] ..]"
269+
][
270+
bin: binary 10000
271+
dir: binary 1000
272+
entries: 0
273+
274+
either file? files [
275+
root: first split-path files
276+
add-file files
277+
][
278+
foreach [file spec] files [
279+
add-data file spec
280+
]
281+
]
282+
283+
dir-size: length? dir/buffer
284+
bin-size: length? bin/buffer
285+
286+
binary/write bin [
287+
BYTES :dir/buffer
288+
#{504B0506} ; End of central directory signature
289+
UI16LE 0 ; Number of this disk
290+
UI16LE 0 ; Disk where central directory starts
291+
UI16LE :entries ; Number of central directory records on this disk
292+
UI16LE :entries ; Total number of central directory records
293+
UI32LE :dir-size ; Size of central directory
294+
UI32LE :bin-size ; Offset of start of central directory
295+
UI16LE 0 ; Comment length
296+
;#{} ; Comment
297+
]
298+
bin/buffer
299+
]
300+
]
150301

151302
identify: function [data [binary!]][
152303
all [

src/tests/units/codecs-test.r3

+35
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,41 @@ if find codecs 'zip [
239239
data: codecs/zip/decompress-file at bin info/2/2 reduce [info/2/5 info/2/3 info/2/4]
240240
--assert info/2/6 = checksum data 'crc32
241241

242+
--test-- "Encode ZIP using encode"
243+
--assert binary? try [bin: encode 'ZIP [
244+
%empty-folder/ none
245+
%file-1 "Hello!"
246+
%file-2 "Hello, Hello, Hello, Hello, Hello!"
247+
%file-3 #{000102030400010203040001020304}
248+
%folder/file-4 [1-Jan-2021 "This file is with date!"]
249+
]]
250+
data: decode 'ZIP bin
251+
--assert all [
252+
data/1 = %empty-folder/
253+
data/3 = %file-1
254+
data/4/3 = #{48656C6C6F21}
255+
data/5 = %file-2
256+
data/9 = %folder/file-4
257+
data/10/1 = 1-Jan-2021/0:00
258+
]
259+
260+
--test-- "Encode ZIP using directory"
261+
--assert not error? try [save %ico.zip %units/files/ico/]
262+
data: load %ico.zip
263+
--assert 30 = length? data ; 14 files and 1 directory
264+
--assert %ico/ = data/1
265+
--assert %ico/icon_128.png = data/3
266+
delete %ico.zip
267+
268+
--test-- "Encode ZIP using wildcard"
269+
--assert not error? try [save %temp.zip %units/files/issue-2186*.txt]
270+
data: load %temp.zip
271+
--assert 8 = length? data ; 4 files
272+
--assert %issue-2186-UTF16-BE.txt = data/1
273+
--assert %issue-2186-UTF16-LE.txt = data/3
274+
delete %temp.zip
275+
276+
242277
===end-group===
243278
system/options/log/zip: v
244279
]

0 commit comments

Comments
 (0)