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
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
|
module szip
import os
#flag -I @VEXEROOT/thirdparty/zip
#include "zip.c"
#include "zip.h"
struct C.zip_t {
}
type Zip = C.zip_t
fn C.zip_open(&char, int, char) &Zip
fn C.zip_close(&Zip)
fn C.zip_entry_open(&Zip, &byte) int
fn C.zip_entry_openbyindex(&Zip, int) int
fn C.zip_entry_close(&Zip) int
fn C.zip_entry_name(&Zip) &byte
fn C.zip_entry_index(&Zip) int
fn C.zip_entry_isdir(&Zip) int
fn C.zip_entry_size(&Zip) u64
fn C.zip_entry_crc32(&Zip) u32
fn C.zip_entry_write(&Zip, voidptr, size_t) int
fn C.zip_entry_fwrite(&Zip, &char) int
fn C.zip_entry_read(&Zip, &voidptr, &size_t) int
fn C.zip_entry_noallocread(&Zip, voidptr, size_t) int
fn C.zip_entry_fread(&Zip, &char) int
fn C.zip_total_entries(&Zip) int
fn C.zip_extract_without_callback(&char, &char) int
// CompressionLevel lists compression levels, see in "thirdparty/zip/miniz.h"
pub enum CompressionLevel {
no_compression = 0
best_speed = 1
best_compression = 9
uber_compression = 10
default_level = 6
default_compression = -1
}
// OpenMode lists the opening modes
// .write: opens a file for reading/extracting (the file must exists).
// .read_only: creates an empty file for writing.
// .append: appends to an existing archive.
pub enum OpenMode {
write
read_only
append
}
[inline]
fn (om OpenMode) to_byte() byte {
return match om {
.write {
`w`
}
.read_only {
`r`
}
.append {
`a`
}
}
}
// open opens zip archive with compression level using the given mode.
// name: the name of the zip file to open.
// level: can be any value of the CompressionLevel enum.
// mode: can be any value of the OpenMode enum.
pub fn open(name string, level CompressionLevel, mode OpenMode) ?&Zip {
if name.len == 0 {
return error('szip: name of file empty')
}
p_zip := &Zip(C.zip_open(&char(name.str), int(level), char(mode.to_byte())))
if isnil(p_zip) {
return error('szip: cannot open/create/append new zip archive')
}
return p_zip
}
// close closes the zip archive, releases resources - always finalize.
[inline]
pub fn (mut z Zip) close() {
C.zip_close(z)
}
// open_entry opens an entry by name in the zip archive.
// For zip archive opened in 'w' or 'a' mode the function will append
// a new entry. In readonly mode the function tries to locate the entry
// in global dictionary.
pub fn (mut zentry Zip) open_entry(name string) ? {
res := C.zip_entry_open(zentry, &char(name.str))
if res == -1 {
return error('szip: cannot open archive entry')
}
}
// open_entry_by_index opens an entry by index in the archive.
pub fn (mut z Zip) open_entry_by_index(index int) ? {
res := C.zip_entry_openbyindex(z, index)
if res == -1 {
return error('szip: cannot open archive entry at index $index')
}
}
// close_entry closes a zip entry, flushes buffer and releases resources.
[inline]
pub fn (mut zentry Zip) close_entry() {
C.zip_entry_close(zentry)
}
// name returns a local name of the current zip entry.
// The main difference between user's entry name and local entry name
// is optional relative path.
// Following .ZIP File Format Specification - the path stored MUST not contain
// a drive or device letter, or a leading slash.
// All slashes MUST be forward slashes '/' as opposed to backwards slashes '\'
// for compatibility with Amiga and UNIX file systems etc.
pub fn (mut zentry Zip) name() string {
name := unsafe { &byte(C.zip_entry_name(zentry)) }
if name == 0 {
return ''
}
return unsafe { name.vstring() }
}
// index returns an index of the current zip entry.
pub fn (mut zentry Zip) index() ?int {
index := int(C.zip_entry_index(zentry))
if index == -1 {
return error('szip: cannot get current index of zip entry')
}
return index // must be check for INVALID_VALUE
}
// is_dir determines if the current zip entry is a directory entry.
pub fn (mut zentry Zip) is_dir() ?bool {
isdir := C.zip_entry_isdir(zentry)
if isdir < 0 {
return error('szip: cannot check entry type')
}
return isdir == 1
}
// size returns an uncompressed size of the current zip entry.
[inline]
pub fn (mut zentry Zip) size() u64 {
return C.zip_entry_size(zentry)
}
// crc32 returns CRC-32 checksum of the current zip entry.
[inline]
pub fn (mut zentry Zip) crc32() u32 {
return C.zip_entry_crc32(zentry)
}
// write_entry compresses an input buffer for the current zip entry.
pub fn (mut zentry Zip) write_entry(data []byte) ? {
if (data[0] & 0xff) == -1 {
return error('szip: cannot write entry')
}
res := C.zip_entry_write(zentry, data.data, data.len)
if res != 0 {
return error('szip: failed to write entry')
}
}
// create_entry compresses a file for the current zip entry.
pub fn (mut zentry Zip) create_entry(name string) ? {
res := C.zip_entry_fwrite(zentry, &char(name.str))
if res != 0 {
return error('szip: failed to create entry')
}
}
// read_entry extracts the current zip entry into output buffer.
// The function allocates sufficient memory for an output buffer.
// NOTE: remember to release the memory allocated for an output buffer.
// for large entries, please take a look at zip_entry_extract function.
pub fn (mut zentry Zip) read_entry() ?voidptr {
mut buf := &byte(0)
mut bsize := size_t(0)
res := C.zip_entry_read(zentry, unsafe { &voidptr(&buf) }, &bsize)
if res == -1 {
return error('szip: cannot read properly data from entry')
}
return buf
}
// read_entry_buf extracts the current zip entry into user specified buffer
pub fn (mut zentry Zip) read_entry_buf(buf voidptr, in_bsize int) ?int {
bsize := size_t(in_bsize)
res := C.zip_entry_noallocread(zentry, buf, bsize)
if res == -1 {
return error('szip: cannot read properly data from entry')
}
return res
}
// extract_entry extracts the current zip entry into output file.
pub fn (mut zentry Zip) extract_entry(path string) ? {
if !os.is_file(path) {
return error('szip: cannot open file for extracting, "$path" not exists')
}
res := C.zip_entry_fread(zentry, &char(path.str))
if res != 0 {
return error('szip: failed to extract entry')
}
}
// extract zip file to directory
pub fn extract_zip_to_dir(file string, dir string) ?bool {
if C.access(&char(dir.str), 0) == -1 {
return error('szip: cannot open directory for extracting, directory not exists')
}
res := C.zip_extract_without_callback(&char(file.str), &char(dir.str))
return res == 0
}
// zip files (full path) to zip file
pub fn zip_files(path_to_file []string, path_to_export_zip string) ? {
// open or create new zip
mut zip := open(path_to_export_zip, .no_compression, .write) or { panic(err) }
// add all files from the directory to the archive
for file in path_to_file {
// add file to zip
zip.open_entry(os.base(file)) or { panic(err) }
file_as_byte := os.read_bytes(file) or { panic(err) }
zip.write_entry(file_as_byte) or { panic(err) }
zip.close_entry()
}
// close zip
defer {
zip.close()
}
}
/*
TODO add
// zip all files in directory to zip file
pub fn zip_folder(path_to_dir string, path_to_export_zip string) {
// get list files from directory
files := os.ls(path_to_dir) or { panic(err) }
// open or create new zip
mut zip := szip.open(path_to_export_zip, .no_compression, .write) or { panic(err) }
// add all files from the directory to the archive
for file in files {
eprintln('Zipping $file to ${path_to_export_zip}...')
println(path_to_dir + file)
// add file to zip
zip.open_entry(file) or { panic(err) }
file_as_byte := os.read_bytes(path_to_dir + '/'+ file) or { panic(err) }
zip.write_entry(file_as_byte) or { panic(err) }
zip.close_entry()
}
// close zip
zip.close()
eprintln('Successfully')
}
*/
// total returns the number of all entries (files and directories) in the zip archive.
pub fn (mut zentry Zip) total() ?int {
tentry := int(C.zip_total_entries(zentry))
if tentry == -1 {
return error('szip: cannot count total entries')
}
return tentry
}
|