blob: 263edcafe65b921dc7f1b49822c70908afceab66 [file] [log] [blame]
Antoine Cœur0c2fceb2017-10-05 15:30:37 +08001/* zip.c -- Zip manipulation
Nathan Moinvaziri11679d62017-10-16 20:15:35 -07002 Version 2.0.1, October 16th, 2017
Antoine Cœur0c2fceb2017-10-05 15:30:37 +08003 part of the MiniZip project
4
5 Copyright (C) 2010-2017 Nathan Moinvaziri
6 Modifications for AES, PKWARE disk spanning
7 https://github.com/nmoinvaz/minizip
8 Copyright (C) 2009-2010 Mathias Svensson
9 Modifications for Zip64 support
10 http://result42.com
11 Copyright (C) 1998-2010 Gilles Vollant
12 http://www.winimage.com/zLibDll/minizip.html
13
14 This program is distributed under the terms of the same license as zlib.
15 See the accompanying LICENSE file for the full text of the license.
16*/
17
18#include <stdio.h>
19#include <stdlib.h>
20#include <stdint.h>
21#include <string.h>
22#include <errno.h>
23
24#include "zlib.h"
25
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -070026#include "mz.h"
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080027#include "mz_strm.h"
28#ifdef HAVE_AES
29# include "mz_strm_aes.h"
30#endif
31#ifdef HAVE_BZIP2
32# include "mz_strm_bzip.h"
33#endif
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -070034#ifdef HAVE_CRYPT
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080035# include "mz_strm_crypt.h"
36#endif
37#ifdef HAVE_LZMA
38# include "mz_strm_lzma.h"
39#endif
40#include "mz_strm_mem.h"
41#include "mz_strm_zlib.h"
42
43#include "mz_zip.h"
44
45/***************************************************************************/
46
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070047#define MZ_ZIP_MAGIC_LOCALHEADER (0x04034b50)
48#define MZ_ZIP_MAGIC_CENTRALHEADER (0x02014b50)
49#define MZ_ZIP_MAGIC_ENDHEADER (0x06054b50)
50#define MZ_ZIP_MAGIC_ENDHEADER64 (0x06064b50)
51#define MZ_ZIP_MAGIC_ENDLOCHEADER64 (0x07064b50)
52#define MZ_ZIP_MAGIC_DATADESCRIPTOR (0x08074b50)
53
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -070054#define MZ_ZIP_SIZE_CD_ITEM (0x2e)
55#define MZ_ZIP_SIZE_CD_LOCATOR64 (0x14)
56#define MZ_ZIP_SIZE_LOCALHEADER (0x1e)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080057
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080058/***************************************************************************/
59
60typedef struct mz_zip_s
61{
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070062 mz_zip_global global_info;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080063 mz_zip_file file_info;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070064 mz_zip_file local_file_info;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080065
66 void *stream; // main stream
67 void *cd_stream; // memory stream for central directory
68 void *compress_stream; // compression stream
69 void *crc32_stream; // crc32 stream
70 void *crypt_stream; // encryption stream
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070071 void *file_info_stream; // memory stream for storing file info
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080072
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070073 int32_t open_mode;
74
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070075 uint64_t disk_offset; // byte before the zip file, (>0 for sfx)
76
77 uint64_t cd_current_pos; // pos of the current file in the central dir
78 uint64_t cd_pos; // position of the beginning of the central dir
79 uint64_t cd_offset; // offset of start of central directory
80 uint64_t cd_size; // size of the central directory
81
82 uint16_t entry_scanned;
83 uint16_t entry_opened; // 1 if a file in the zip is currently writ.
84 uint64_t entry_read;
85
86#ifdef HAVE_AES
87 uint16_t aes_version;
88 uint8_t aes_encryption_mode;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080089#endif
90} mz_zip;
91
92/***************************************************************************/
93
94// Locate the central directory of a zip file (at the end, just before the global comment)
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -070095static int32_t mz_zip_search_cd(void *stream, uint64_t *central_pos)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080096{
Nathan Moinvaziri9ab31ba2017-10-16 07:51:09 -070097 uint8_t buf[1024 + 4];
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080098 uint64_t file_size = 0;
Nathan Moinvaziri77008c62017-10-16 14:50:31 -070099 uint64_t back_read = 0;
Nathan Moinvazirif6e81cd2017-10-10 18:24:03 -0700100 uint64_t max_back = UINT16_MAX; // maximum size of global comment
Nathan Moinvaziri77008c62017-10-16 14:50:31 -0700101 uint32_t read_size = sizeof(buf);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800102 uint64_t read_pos = 0;
103 uint32_t i = 0;
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700104
Nathan Moinvazirif6e81cd2017-10-10 18:24:03 -0700105 *central_pos = 0;
106
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800107 if (mz_stream_seek(stream, 0, MZ_STREAM_SEEK_END) != MZ_OK)
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700108 return MZ_STREAM_ERROR;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800109
110 file_size = mz_stream_tell(stream);
111
112 if (max_back > file_size)
113 max_back = file_size;
114
115 while (back_read < max_back)
116 {
Nathan Moinvaziri9ab31ba2017-10-16 07:51:09 -0700117 back_read += (sizeof(buf) - 4);
118 if (back_read > max_back)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800119 back_read = max_back;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800120
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700121 read_pos = file_size - back_read;
Nathan Moinvaziri9ab31ba2017-10-16 07:51:09 -0700122 if (read_size > (file_size - read_pos))
123 read_size = (uint32_t)(file_size - read_pos);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800124
125 if (mz_stream_seek(stream, read_pos, MZ_STREAM_SEEK_SET) != MZ_OK)
126 break;
127 if (mz_stream_read(stream, buf, read_size) != read_size)
128 break;
129
130 for (i = read_size - 3; (i--) > 0;)
131 {
Nathan Moinvaziri9ab31ba2017-10-16 07:51:09 -0700132 if (((*(buf + i)) == (MZ_ZIP_MAGIC_ENDHEADER & 0xff)) &&
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700133 ((*(buf + i + 1)) == (MZ_ZIP_MAGIC_ENDHEADER >> 8 & 0xff)) &&
134 ((*(buf + i + 2)) == (MZ_ZIP_MAGIC_ENDHEADER >> 16 & 0xff)) &&
135 ((*(buf + i + 3)) == (MZ_ZIP_MAGIC_ENDHEADER >> 24 & 0xff)))
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800136 {
Nathan Moinvazirif6e81cd2017-10-10 18:24:03 -0700137 *central_pos = read_pos + i;
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700138 return MZ_OK;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800139 }
140 }
141
Nathan Moinvazirif6e81cd2017-10-10 18:24:03 -0700142 if (*central_pos != 0)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800143 break;
144 }
145
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700146 return MZ_EXIST_ERROR;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800147}
148
149// Locate the central directory 64 of a zip file (at the end, just before the global comment)
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700150static int32_t mz_zip_search_zip64_cd(void *stream, const uint64_t end_central_offset, uint64_t *central_pos)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800151{
152 uint64_t offset = 0;
153 uint32_t value32 = 0;
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700154 int16_t err = MZ_OK;
155
156
157 *central_pos = 0;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800158
159 // Zip64 end of central directory locator
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700160 err = mz_stream_seek(stream, end_central_offset - MZ_ZIP_SIZE_CD_LOCATOR64, MZ_STREAM_SEEK_SET);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800161 // Read locator signature
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700162 if (err == MZ_OK)
163 {
164 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700165 if (value32 != MZ_ZIP_MAGIC_ENDLOCHEADER64)
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700166 err = MZ_FORMAT_ERROR;
167 }
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800168 // Number of the disk with the start of the zip64 end of central directory
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700169 if (err == MZ_OK)
170 err = mz_stream_read_uint32(stream, &value32);
171 // Relative offset of the zip64 end of central directory record8
172 if (err == MZ_OK)
173 err = mz_stream_read_uint64(stream, &offset);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800174 // Total number of disks
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700175 if (err == MZ_OK)
176 err = mz_stream_read_uint32(stream, &value32);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800177 // Goto end of central directory record
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700178 if (err == MZ_OK)
179 err = mz_stream_seek(stream, offset, MZ_STREAM_SEEK_SET);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800180 // The signature
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700181 if (err == MZ_OK)
182 {
183 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700184 if (value32 != MZ_ZIP_MAGIC_ENDHEADER64)
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700185 err = MZ_FORMAT_ERROR;
186 }
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800187
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700188 if (err == MZ_OK)
189 *central_pos = offset;
190
191 return err;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800192}
193
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700194static int mz_zip_read_cd(void *handle)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800195{
196 mz_zip *zip = NULL;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700197 uint64_t number_entry_cd = 0;
198 uint64_t number_entry_cd64 = 0;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800199 uint64_t number_entry = 0;
200 uint64_t central_pos = 0;
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700201 uint64_t central_pos64 = 0;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800202 uint16_t value16 = 0;
203 uint32_t value32 = 0;
204 uint64_t value64 = 0;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800205 int16_t err = MZ_OK;
206
207
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700208 zip = (mz_zip *)handle;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800209 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700210 return MZ_PARAM_ERROR;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800211
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700212 // Read and cache central directory records
213 if (mz_zip_search_cd(zip->stream, &central_pos) == MZ_OK)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800214 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700215 // Read end of central directory info
216 err = mz_stream_seek(zip->stream, central_pos, MZ_STREAM_SEEK_SET);
217 // The signature, already checked
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800218 if (err == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700219 err = mz_stream_read_uint32(zip->stream, &value32);
220 // Number of this disk
221 if (err == MZ_OK)
222 err = mz_stream_read_uint16(zip->stream, &value16);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700223 // Number of the disk with the start of the central directory
224 if (err == MZ_OK)
225 err = mz_stream_read_uint16(zip->stream, &value16);
226 zip->global_info.number_disk_with_cd = value16;
227 // Total number of entries in the central dir on this disk
228 if (err == MZ_OK)
229 err = mz_stream_read_uint16(zip->stream, &value16);
230 zip->global_info.number_entry = value16;
231 // Total number of entries in the central dir
232 if (err == MZ_OK)
233 err = mz_stream_read_uint16(zip->stream, &value16);
234 number_entry_cd = value16;
235 if (number_entry_cd != zip->global_info.number_entry)
236 err = MZ_FORMAT_ERROR;
237 // Size of the central directory
238 if (err == MZ_OK)
239 err = mz_stream_read_uint32(zip->stream, &value32);
240 if (err == MZ_OK)
241 zip->cd_size = value32;
242 // Offset of start of central directory with respect to the starting disk number
243 if (err == MZ_OK)
244 err = mz_stream_read_uint32(zip->stream, &value32);
245 zip->cd_offset = value32;
246 // Zip file global comment length
247 if (err == MZ_OK)
248 err = mz_stream_read_uint16(zip->stream, &zip->global_info.comment_size);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800249
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700250 if ((err == MZ_OK) && ((number_entry_cd == UINT16_MAX) || (zip->cd_offset == UINT32_MAX)))
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800251 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700252 // Format should be Zip64, as the central directory or file size is too large
253 if (mz_zip_search_zip64_cd(zip->stream, central_pos, &central_pos64) == MZ_OK)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800254 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700255 central_pos = central_pos64;
256
257 err = mz_stream_seek(zip->stream, central_pos, MZ_STREAM_SEEK_SET);
258 // The signature, already checked
259 if (err == MZ_OK)
260 err = mz_stream_read_uint32(zip->stream, &value32);
261 // Size of zip64 end of central directory record
262 if (err == MZ_OK)
263 err = mz_stream_read_uint64(zip->stream, &value64);
264 // Version made by
265 if (err == MZ_OK)
266 err = mz_stream_read_uint16(zip->stream, &value16);
267 // Version needed to extract
268 if (err == MZ_OK)
269 err = mz_stream_read_uint16(zip->stream, &value16);
270 // Number of this disk
271 if (err == MZ_OK)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700272 err = mz_stream_read_uint32(zip->stream, &value32);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700273 // Number of the disk with the start of the central directory
274 if (err == MZ_OK)
275 err = mz_stream_read_uint32(zip->stream, &zip->global_info.number_disk_with_cd);
276 // Total number of entries in the central directory on this disk
277 if (err == MZ_OK)
278 err = mz_stream_read_uint64(zip->stream, &number_entry);
279 // Total number of entries in the central directory
280 if (err == MZ_OK)
281 err = mz_stream_read_uint64(zip->stream, &number_entry_cd64);
282 if (number_entry == UINT32_MAX)
283 zip->global_info.number_entry = number_entry_cd64;
284 // Size of the central directory
285 if (err == MZ_OK)
286 err = mz_stream_read_uint64(zip->stream, &zip->cd_size);
287 // Offset of start of central directory with respect to the starting disk number
288 if (err == MZ_OK)
289 err = mz_stream_read_uint64(zip->stream, &zip->cd_offset);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800290 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700291 else if ((zip->global_info.number_entry == UINT16_MAX) || (number_entry_cd != zip->global_info.number_entry) ||
292 (zip->cd_size == UINT16_MAX) || (zip->cd_offset == UINT32_MAX))
Nathan Moinvaziri016ad472017-10-09 23:36:30 -0700293 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700294 err = MZ_FORMAT_ERROR;
Nathan Moinvaziri016ad472017-10-09 23:36:30 -0700295 }
296 }
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800297 }
298
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700299 if (err == MZ_OK)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800300 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700301 if (central_pos < zip->cd_offset + zip->cd_size)
302 err = MZ_FORMAT_ERROR;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800303 }
304
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700305 if ((err == MZ_OK) && (zip->global_info.comment_size > 0))
306 {
307 zip->global_info.comment = (char *)malloc(zip->global_info.comment_size + 1);
308 if (zip->global_info.comment)
309 {
310 if (mz_stream_read(zip->stream, zip->global_info.comment,
311 zip->global_info.comment_size) != zip->global_info.comment_size)
312 err = MZ_STREAM_ERROR;
313
314 zip->global_info.comment[zip->global_info.comment_size] = 0;
315 }
316 }
317
318 if (err == MZ_OK)
319 {
320 zip->disk_offset = central_pos - (zip->cd_offset + zip->cd_size);
321 zip->cd_pos = zip->disk_offset;
322 }
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800323
324 return err;
325}
326
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700327static int mz_zip_write_cd(void *handle, const char *global_comment, uint16_t version_madeby)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800328{
329 mz_zip *zip = NULL;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800330 uint16_t comment_size = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700331 uint64_t zip64_eocd_pos_inzip = 0;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800332 uint64_t pos = 0;
333 uint64_t cd_pos = 0;
Nathan Moinvaziri016ad472017-10-09 23:36:30 -0700334 int64_t disk_number = 0;
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700335 int64_t disk_size = 0;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800336 int16_t err = MZ_OK;
337
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700338
339 zip = (mz_zip *)handle;
340 if (zip == NULL)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800341 return MZ_PARAM_ERROR;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800342
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800343 if (global_comment == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700344 global_comment = zip->global_info.comment;
Nathan Moinvaziri016ad472017-10-09 23:36:30 -0700345
346 if (mz_stream_get_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, &disk_number) == MZ_OK)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700347 zip->global_info.number_disk_with_cd = (uint32_t)disk_number;
348 if (mz_stream_get_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_SIZE, &disk_size) == MZ_OK && disk_size > 0)
349 zip->global_info.number_disk_with_cd += 1;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700350 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800351
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700352 zip->cd_pos = mz_stream_tell(zip->stream);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800353 mz_stream_seek(zip->cd_stream, 0, MZ_STREAM_SEEK_END);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700354 zip->cd_size = (uint32_t)mz_stream_tell(zip->cd_stream);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800355 mz_stream_seek(zip->cd_stream, 0, MZ_STREAM_SEEK_SET);
356
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700357 err = mz_stream_copy(zip->stream, zip->cd_stream, (int32_t)zip->cd_size);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800358
359 mz_stream_close(zip->cd_stream);
360 mz_stream_delete(&zip->cd_stream);
361
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800362 // Write the ZIP64 central directory header
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700363 if (pos >= UINT32_MAX || zip->global_info.number_entry > UINT32_MAX)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800364 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700365 zip64_eocd_pos_inzip = mz_stream_tell(zip->stream);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800366
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700367 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_ENDHEADER64);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800368
369 // Size of this 'zip64 end of central directory'
370 if (err == MZ_OK)
371 err = mz_stream_write_uint64(zip->stream, (uint64_t)44);
372 // Version made by
373 if (err == MZ_OK)
374 err = mz_stream_write_uint16(zip->stream, version_madeby);
375 // Version needed
376 if (err == MZ_OK)
377 err = mz_stream_write_uint16(zip->stream, (uint16_t)45);
378 // Number of this disk
379 if (err == MZ_OK)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700380 err = mz_stream_write_uint32(zip->stream, zip->global_info.number_disk_with_cd);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800381 // Number of the disk with the start of the central directory
382 if (err == MZ_OK)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700383 err = mz_stream_write_uint32(zip->stream, zip->global_info.number_disk_with_cd);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800384 // Total number of entries in the central dir on this disk
385 if (err == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700386 err = mz_stream_write_uint64(zip->stream, zip->global_info.number_entry);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800387 // Total number of entries in the central dir
388 if (err == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700389 err = mz_stream_write_uint64(zip->stream, zip->global_info.number_entry);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800390 // Size of the central directory
391 if (err == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700392 err = mz_stream_write_uint64(zip->stream, (uint64_t)zip->cd_size);
393 // Offset of start of central directory with respect to the starting disk number
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800394 if (err == MZ_OK)
395 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700396 cd_pos = zip->cd_pos - zip->disk_offset;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800397 err = mz_stream_write_uint64(zip->stream, cd_pos);
398 }
399 if (err == MZ_OK)
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700400 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_ENDLOCHEADER64);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800401 // Number of the disk with the start of the central directory
402 if (err == MZ_OK)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700403 err = mz_stream_write_uint32(zip->stream, zip->global_info.number_disk_with_cd);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800404 // Relative offset to the end of zip64 central directory
405 if (err == MZ_OK)
406 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700407 cd_pos = zip64_eocd_pos_inzip - zip->cd_pos;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800408 err = mz_stream_write_uint64(zip->stream, cd_pos);
409 }
410 // Number of the disk with the start of the central directory
411 if (err == MZ_OK)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700412 err = mz_stream_write_uint32(zip->stream, zip->global_info.number_disk_with_cd);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800413 }
414
415 // Write the central directory header
416
417 // Signature
418 if (err == MZ_OK)
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700419 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_ENDHEADER);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800420 // Number of this disk
421 if (err == MZ_OK)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700422 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->global_info.number_disk_with_cd);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800423 // Number of the disk with the start of the central directory
424 if (err == MZ_OK)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700425 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->global_info.number_disk_with_cd);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800426 // Total number of entries in the central dir on this disk
427 if (err == MZ_OK)
428 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700429 if (zip->global_info.number_entry >= UINT16_MAX)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800430 err = mz_stream_write_uint16(zip->stream, UINT16_MAX);
431 else
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700432 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->global_info.number_entry);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800433 }
434 // Total number of entries in the central dir
435 if (err == MZ_OK)
436 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700437 if (zip->global_info.number_entry >= UINT16_MAX)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800438 err = mz_stream_write_uint16(zip->stream, UINT16_MAX);
439 else
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700440 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->global_info.number_entry);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800441 }
442 // Size of the central directory
443 if (err == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700444 err = mz_stream_write_uint32(zip->stream, (uint32_t)zip->cd_size);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800445 // Offset of start of central directory with respect to the starting disk number
446 if (err == MZ_OK)
447 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700448 cd_pos = zip->cd_pos - zip->disk_offset;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800449 if (pos >= UINT32_MAX)
450 err = mz_stream_write_uint32(zip->stream, UINT32_MAX);
451 else
452 err = mz_stream_write_uint32(zip->stream, (uint32_t)cd_pos);
453 }
454
455 // Write global comment
456 if (global_comment != NULL)
457 comment_size = (uint16_t)strlen(global_comment);
458 if (err == MZ_OK)
459 err = mz_stream_write_uint16(zip->stream, comment_size);
460 if (err == MZ_OK)
461 {
462 if (mz_stream_write(zip->stream, global_comment, comment_size) != comment_size)
463 err = MZ_STREAM_ERROR;
464 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700465 return err;
466}
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800467
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700468extern void* ZEXPORT mz_zip_open(void *stream, int32_t mode)
469{
470 mz_zip *zip = NULL;
471 int16_t err = MZ_OK;
472
473
474 zip = (mz_zip *)malloc(sizeof(mz_zip));
475 if (zip == NULL)
476 return NULL;
477
478 memset(zip, 0, sizeof(mz_zip));
479
480 zip->stream = stream;
481 zip->open_mode = mode;
482
483 if (zip->open_mode & MZ_STREAM_MODE_WRITE)
484 {
485 mz_stream_mem_create(&zip->cd_stream);
486 mz_stream_mem_set_grow(zip->cd_stream, 1);
487 mz_stream_mem_open(zip->cd_stream, NULL, MZ_STREAM_MODE_CREATE);
488 }
489
490 if ((zip->open_mode & MZ_STREAM_MODE_READ) || (mode & MZ_STREAM_MODE_APPEND))
491 {
492 err = mz_zip_read_cd(zip);
493
494 if ((err == MZ_OK) && (mode & MZ_STREAM_MODE_APPEND))
495 {
496 // Store central directory in memory
497 if (mz_stream_seek(zip->stream, zip->cd_offset + zip->disk_offset, MZ_STREAM_SEEK_SET) != MZ_OK)
498 err = MZ_STREAM_ERROR;
499 if (mz_stream_copy(zip->cd_stream, zip->stream, (uint32_t)zip->cd_size) != MZ_OK)
500 err = MZ_STREAM_ERROR;
501 if (mz_stream_seek(zip->stream, zip->cd_offset + zip->disk_offset, MZ_STREAM_SEEK_SET) != MZ_OK)
502 err = MZ_STREAM_ERROR;
503 }
504 }
505
506 if (err == MZ_OK)
507 {
508 mz_stream_mem_create(&zip->file_info_stream);
509 mz_stream_mem_set_grow(zip->file_info_stream, 1);
510 mz_stream_mem_set_grow_size(zip->file_info_stream, 4096);
511
512 err = mz_stream_mem_open(zip->file_info_stream, NULL, MZ_STREAM_MODE_CREATE);
513 }
514
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700515 if (err != MZ_OK)
516 {
517 if (zip->file_info_stream != NULL)
518 mz_stream_mem_delete(&zip->file_info_stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700519
520 mz_stream_close(zip->cd_stream);
521 mz_stream_delete(&zip->cd_stream);
522
523 if (zip->global_info.comment)
524 free(zip->global_info.comment);
525
526 free(zip);
527 return NULL;
528 }
529
530 return zip;
531}
532
533extern int ZEXPORT mz_zip_close(void *handle, const char *global_comment, uint16_t version_madeby)
534{
535 mz_zip *zip = NULL;
536
537 int16_t err = MZ_OK;
538
539 if (handle == NULL)
540 return MZ_PARAM_ERROR;
541 zip = (mz_zip *)handle;
542
543 if (zip->entry_opened == 1)
544 {
545 err = mz_zip_entry_close(handle);
546 if (err != MZ_OK)
547 return err;
548 }
549
550 if (zip->open_mode & MZ_STREAM_MODE_WRITE)
551 err = mz_zip_write_cd(handle, global_comment, version_madeby);
552
553 mz_stream_mem_close(zip->file_info_stream);
554 mz_stream_mem_delete(&zip->file_info_stream);
555
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700556 if (zip->global_info.comment)
557 free(zip->global_info.comment);
558
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800559 free(zip);
560
561 return err;
562}
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700563
564extern int ZEXPORT mz_zip_get_global_info(void *handle, mz_zip_global **global_info)
565{
566 mz_zip *zip = NULL;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700567 if (handle == NULL || global_info == NULL)
568 return MZ_PARAM_ERROR;
569 zip = (mz_zip *)handle;
570 *global_info = &zip->global_info;
571 return MZ_OK;
572}
573
574static int mz_zip_entry_get_version_needed(mz_zip_file *file_info)
575{
576 mz_zip *zip = NULL;
577 int16_t version_needed = 20;
578
579 if (file_info == NULL)
580 return MZ_PARAM_ERROR;
581
582 if (file_info->version_needed > 0)
583 version_needed = file_info->version_needed;
584
585 // Calculate extra field size
586 if (file_info->zip_64)
587 version_needed = 45;
588#ifdef HAVE_AES
589 if ((file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
590 version_needed = 51;
591#endif
592#ifdef HAVE_LZMA
593 if (file_info->compression_method == MZ_COMPRESS_METHOD_LZMA)
594 version_needed = 63;
595#endif
596 return version_needed;
597}
598
599// Get info about the current file in the zip file
600static int mz_zip_entry_read_header(void *stream, uint8_t local, mz_zip_file *file_info, void *file_info_stream)
601{
602 uint32_t magic = 0;
603 uint32_t extra_pos = 0;
604 uint16_t extra_header_id = 0;
605 uint16_t extra_data_size = 0;
606 uint16_t value16 = 0;
607 uint32_t value32 = 0;
608 uint64_t value64 = 0;
609 int64_t seek = 0;
610 int16_t err = MZ_OK;
611
612
613 memset(file_info, 0, sizeof(mz_zip_file));
614
615 // Check the magic
616 err = mz_stream_read_uint32(stream, &magic);
617 if (magic == MZ_ZIP_MAGIC_ENDHEADER || magic == MZ_ZIP_MAGIC_ENDHEADER64)
618 err = MZ_END_OF_LIST;
619 else if ((local) && (magic != MZ_ZIP_MAGIC_LOCALHEADER))
620 err = MZ_FORMAT_ERROR;
621 else if ((!local) && (magic != MZ_ZIP_MAGIC_CENTRALHEADER))
622 err = MZ_FORMAT_ERROR;
623
624 // Read header fields
625 if (err == MZ_OK)
626 {
627 if (!local)
628 err = mz_stream_read_uint16(stream, &file_info->version_madeby);
629 if (err == MZ_OK)
630 err = mz_stream_read_uint16(stream, &file_info->version_needed);
631 if (err == MZ_OK)
632 err = mz_stream_read_uint16(stream, &file_info->flag);
633 if (err == MZ_OK)
634 err = mz_stream_read_uint16(stream, &file_info->compression_method);
635 if (err == MZ_OK)
636 err = mz_stream_read_uint32(stream, &file_info->dos_date);
637 if (err == MZ_OK)
638 err = mz_stream_read_uint32(stream, &file_info->crc);
639 if (err == MZ_OK)
640 err = mz_stream_read_uint32(stream, &value32);
641 file_info->compressed_size = value32;
642 if (err == MZ_OK)
643 err = mz_stream_read_uint32(stream, &value32);
644 file_info->uncompressed_size = value32;
645 if (err == MZ_OK)
646 err = mz_stream_read_uint16(stream, &file_info->filename_size);
647 if (err == MZ_OK)
648 err = mz_stream_read_uint16(stream, &file_info->extrafield_size);
649 if (!local)
650 {
651 if (err == MZ_OK)
652 err = mz_stream_read_uint16(stream, &file_info->comment_size);
653 if (err == MZ_OK)
654 err = mz_stream_read_uint16(stream, &value16);
655 file_info->disk_num_start = value16;
656 if (err == MZ_OK)
657 err = mz_stream_read_uint16(stream, &file_info->internal_fa);
658 if (err == MZ_OK)
659 err = mz_stream_read_uint32(stream, &file_info->external_fa);
660 if (err == MZ_OK)
661 err = mz_stream_read_uint32(stream, &value32);
662 file_info->disk_offset = value32;
663 }
664 }
665
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700666 if ((err == MZ_OK) && (!local))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700667 err = mz_stream_seek(file_info_stream, 0, MZ_STREAM_SEEK_SET);
668
669 if ((err == MZ_OK) && (file_info->filename_size > 0))
670 {
671 // Read filename in our memory stream buffer
672 mz_stream_mem_get_buffer(file_info_stream, (void **)&file_info->filename);
673
674 if (err == MZ_OK)
675 err = mz_stream_copy(file_info_stream, stream, file_info->filename_size);
676 if (err == MZ_OK)
677 err = mz_stream_write_uint8(file_info_stream, 0);
678
679 seek += file_info->filename_size + 1;
680 }
681
682 if ((err == MZ_OK) && (file_info->extrafield_size > 0))
683 {
684 mz_stream_mem_get_buffer_at(file_info_stream, seek, (void **)&file_info->extrafield);
685
686 err = mz_stream_copy(file_info_stream, stream, file_info->extrafield_size);
687 if (err == MZ_OK)
688 err = mz_stream_write_uint8(file_info_stream, 0);
689
690 // Seek back and parse the extra field
691 if (err == MZ_OK)
692 err = mz_stream_seek(file_info_stream, seek, MZ_STREAM_SEEK_SET);
693
694 seek += file_info->extrafield_size + 1;
695
696 while ((err == MZ_OK) && (extra_pos < file_info->extrafield_size))
697 {
698 err = mz_stream_read_uint16(file_info_stream, &extra_header_id);
699 if (err == MZ_OK)
700 err = mz_stream_read_uint16(file_info_stream, &extra_data_size);
701
702 // ZIP64 extra field
703 if (extra_header_id == 0x0001)
704 {
705 if ((err == MZ_OK) && (file_info->uncompressed_size == UINT32_MAX))
706 err = mz_stream_read_uint64(file_info_stream, &file_info->uncompressed_size);
707 if ((err == MZ_OK) && (file_info->compressed_size == UINT32_MAX))
708 err = mz_stream_read_uint64(file_info_stream, &file_info->compressed_size);
709 if ((err == MZ_OK) && (file_info->disk_offset == UINT32_MAX))
710 err = mz_stream_read_uint64(file_info_stream, &value64);
711 file_info->disk_offset = value64;
712 if ((err == MZ_OK) && (file_info->disk_num_start == UINT32_MAX))
713 err = mz_stream_read_uint32(file_info_stream, &file_info->disk_num_start);
714 }
715#ifdef HAVE_AES
716 // AES extra field
717 else if (extra_header_id == 0x9901)
718 {
719 uint8_t value8 = 0;
720 // Verify version info
721 err = mz_stream_read_uint16(file_info_stream, &value16);
722 // Support AE-1 and AE-2
723 if (value16 != 1 && value16 != 2)
724 err = MZ_FORMAT_ERROR;
725 file_info->aes_version = value16;
726 if (err == MZ_OK)
727 err = mz_stream_read_uint8(file_info_stream, &value8);
728 if ((char)value8 != 'A')
729 err = MZ_FORMAT_ERROR;
730 if (err == MZ_OK)
731 err = mz_stream_read_uint8(file_info_stream, &value8);
732 if ((char)value8 != 'E')
733 err = MZ_FORMAT_ERROR;
734 // Get AES encryption strength and actual compression method
735 if (err == MZ_OK)
736 err = mz_stream_read_uint8(file_info_stream, &value8);
737 file_info->aes_encryption_mode = value8;
738 if (err == MZ_OK)
739 err = mz_stream_read_uint16(file_info_stream, &value16);
740 file_info->compression_method = value16;
741 }
742#endif
743 else
744 {
745 err = mz_stream_seek(file_info_stream, extra_data_size, MZ_STREAM_SEEK_CUR);
746 }
747
748 extra_pos += 4 + extra_data_size;
749 }
750 }
751
752 if ((err == MZ_OK) && (file_info->comment_size > 0))
753 {
754 mz_stream_mem_get_buffer_at(file_info_stream, seek, (void **)&file_info->comment);
755
756 err = mz_stream_copy(file_info_stream, stream, file_info->comment_size);
757 if (err == MZ_OK)
758 err = mz_stream_write_uint8(file_info_stream, 0);
759 }
760
761 return err;
762}
763
764static int mz_zip_entry_write_header(void *stream, uint8_t local, mz_zip_file *file_info)
765{
766 uint16_t extrafield_size = 0;
767 uint16_t extrafield_zip64_size = 0;
768 uint16_t filename_size = 0;
769 uint16_t comment_size = 0;
770 int16_t err = MZ_OK;
771
772 if (file_info == NULL)
773 return MZ_PARAM_ERROR;
774
775 if (file_info->disk_offset >= UINT32_MAX)
776 file_info->zip_64 = 1;
777
778 extrafield_size = file_info->extrafield_size;
779 if (file_info->zip_64)
780 {
781 extrafield_size += 4;
782 if (file_info->uncompressed_size >= UINT32_MAX)
783 extrafield_zip64_size += 8;
784 if (file_info->compressed_size >= UINT32_MAX)
785 extrafield_zip64_size += 8;
786 if (file_info->disk_offset >= UINT32_MAX)
787 extrafield_zip64_size += 8;
788 extrafield_size += extrafield_zip64_size;
789 }
790#ifdef HAVE_AES
791 if ((file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
792 extrafield_size += 4 + 7;
793#endif
794
795 file_info->version_needed = mz_zip_entry_get_version_needed(file_info);
796
797 if (local)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700798 err = mz_stream_write_uint32(stream, MZ_ZIP_MAGIC_LOCALHEADER);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700799 else
800 {
801 err = mz_stream_write_uint32(stream, MZ_ZIP_MAGIC_CENTRALHEADER);
802 if (err == MZ_OK)
803 err = mz_stream_write_uint16(stream, file_info->version_madeby);
804 }
805
806 if (err == MZ_OK)
807 err = mz_stream_write_uint16(stream, file_info->version_needed);
808 if (err == MZ_OK)
809 err = mz_stream_write_uint16(stream, file_info->flag);
810 if (err == MZ_OK)
811 {
812#ifdef HAVE_AES
813 if (file_info->aes_version > 0)
814 err = mz_stream_write_uint16(stream, MZ_AES_METHOD);
815 else
816#endif
817 err = mz_stream_write_uint16(stream, file_info->compression_method);
818 }
819 if (err == MZ_OK)
820 err = mz_stream_write_uint32(stream, file_info->dos_date);
821
822 if (err == MZ_OK)
823 err = mz_stream_write_uint32(stream, file_info->crc); // crc
824 if (err == MZ_OK)
825 {
826 if (file_info->compressed_size >= UINT32_MAX) // compr size
827 err = mz_stream_write_uint32(stream, UINT32_MAX);
828 else
829 err = mz_stream_write_uint32(stream, (uint32_t)file_info->compressed_size);
830 }
831 if (err == MZ_OK)
832 {
833 if (file_info->uncompressed_size >= UINT32_MAX) // uncompr size
834 err = mz_stream_write_uint32(stream, UINT32_MAX);
835 else
836 err = mz_stream_write_uint32(stream, (uint32_t)file_info->uncompressed_size);
837 }
838
839 filename_size = (uint16_t)strlen(file_info->filename);
840 if (err == MZ_OK)
841 err = mz_stream_write_uint16(stream, filename_size);
842 if (err == MZ_OK)
843 err = mz_stream_write_uint16(stream, extrafield_size);
844
845 if (!local)
846 {
847 if (file_info->comment != NULL)
848 comment_size = (uint16_t)strlen(file_info->comment);
849 if (err == MZ_OK)
850 err = mz_stream_write_uint16(stream, comment_size);
851 if (err == MZ_OK)
852 err = mz_stream_write_uint16(stream, (uint16_t)file_info->disk_num_start);
853 if (err == MZ_OK)
854 err = mz_stream_write_uint16(stream, file_info->internal_fa);
855 if (err == MZ_OK)
856 err = mz_stream_write_uint32(stream, file_info->external_fa);
857 if (err == MZ_OK)
858 {
859 if (file_info->disk_offset >= UINT32_MAX)
860 err = mz_stream_write_uint32(stream, UINT32_MAX);
861 else
862 err = mz_stream_write_uint32(stream, (uint32_t)file_info->disk_offset);
863 }
864 }
865
866 if (err == MZ_OK)
867 {
868 if (mz_stream_write(stream, file_info->filename, filename_size) != filename_size)
869 err = MZ_STREAM_ERROR;
870 }
871 if (err == MZ_OK)
872 {
873 if (mz_stream_write(stream, file_info->extrafield, file_info->extrafield_size) != file_info->extrafield_size)
874 err = MZ_STREAM_ERROR;
875 }
876 // Add ZIP64 extra info header to central directory
877 if ((err == MZ_OK) && (file_info->zip_64))
878 {
879 err = mz_stream_write_uint16(stream, 0x0001);
880 if (err == MZ_OK)
881 err = mz_stream_write_uint16(stream, extrafield_zip64_size);
882 if ((err == MZ_OK) && (file_info->uncompressed_size >= UINT32_MAX))
883 err = mz_stream_write_uint64(stream, file_info->uncompressed_size);
884 if ((err == MZ_OK) && (file_info->compressed_size >= UINT32_MAX))
885 err = mz_stream_write_uint64(stream, file_info->compressed_size);
886 if ((err == MZ_OK) && (file_info->disk_offset >= UINT32_MAX))
887 err = mz_stream_write_uint64(stream, file_info->disk_offset);
888 }
889
890#ifdef HAVE_AES
891 // Write AES extra info header to central directory
892 if ((err == MZ_OK) && (file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
893 {
894 err = mz_stream_write_uint16(stream, 0x9901);
895 if (err == MZ_OK)
896 err = mz_stream_write_uint16(stream, 7);
897 if (err == MZ_OK)
898 err = mz_stream_write_uint16(stream, file_info->aes_version);
899 if (err == MZ_OK)
900 err = mz_stream_write_uint8(stream, 'A');
901 if (err == MZ_OK)
902 err = mz_stream_write_uint8(stream, 'E');
903 if (err == MZ_OK)
904 err = mz_stream_write_uint8(stream, file_info->aes_encryption_mode);
905 if (err == MZ_OK)
906 err = mz_stream_write_uint16(stream, file_info->compression_method);
907 }
908#endif
909 if ((err == MZ_OK) && (file_info->comment != NULL))
910 {
911 if (mz_stream_write(stream, file_info->comment, file_info->comment_size) != MZ_OK)
912 err = MZ_STREAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700913 }
914
915 return err;
916}
917
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700918static int mz_zip_entry_open_int(void *handle, int16_t compress_level, const char *password)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700919{
920 mz_zip *zip = NULL;
921 uint64_t extrafield_local_offset = 0;
922 uint16_t extrafield_local_size = 0;
923 uint32_t size_variable = 0;
924 int64_t max_total_in = 0;
925 int64_t total_in = 0;
926 int64_t footer_size = 0;
927 int16_t err = MZ_OK;
928
929 if (handle == NULL)
930 return MZ_PARAM_ERROR;
931 zip = (mz_zip *)handle;
932
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700933 switch (zip->file_info.compression_method)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700934 {
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700935 case MZ_COMPRESS_METHOD_RAW:
936 case MZ_COMPRESS_METHOD_DEFLATE:
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700937#ifdef HAVE_BZIP2
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700938 case MZ_COMPRESS_METHOD_BZIP2:
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700939#endif
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700940#if HAVE_LZMA
941 case MZ_COMPRESS_METHOD_LZMA:
942#endif
943 err = MZ_OK;
944 break;
945 default:
946 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700947 }
948
949 if ((zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED) && (password == NULL))
950 return MZ_PARAM_ERROR;
951
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700952 if ((err == Z_OK) && (zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED))
953 {
954#ifdef HAVE_AES
955 if (zip->file_info.aes_version)
956 {
957 mz_stream_aes_create(&zip->crypt_stream);
958 mz_stream_aes_set_password(zip->crypt_stream, password);
959 mz_stream_aes_set_encryption_mode(zip->crypt_stream, zip->file_info.aes_encryption_mode);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700960 }
961 else
962#endif
963 {
964#ifdef HAVE_CRYPT
965 uint8_t verify1 = 0;
966 uint8_t verify2 = 0;
967
968 // Info-ZIP modification to ZipCrypto format:
969 // If bit 3 of the general purpose bit flag is set, it uses high byte of 16-bit File Time.
970
971 verify1 = (uint8_t)((zip->file_info.dos_date >> 16) & 0xff);
972 verify2 = (uint8_t)((zip->file_info.dos_date >> 8) & 0xff);
973
974 mz_stream_crypt_create(&zip->crypt_stream);
975 mz_stream_crypt_set_password(zip->crypt_stream, password);
976 mz_stream_crypt_set_verify(zip->crypt_stream, verify1, verify2);
977#endif
978 }
979 }
980
981 if (err == MZ_OK)
982 {
983 if (zip->crypt_stream == NULL)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700984 mz_stream_raw_create(&zip->crypt_stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700985
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700986 mz_stream_set_base(zip->crypt_stream, zip->stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700987
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700988 err = mz_stream_open(zip->crypt_stream, NULL, zip->open_mode);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700989 }
990
991 if (err == MZ_OK)
992 {
993 if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_RAW)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700994 mz_stream_raw_create(&zip->compress_stream);
995 else if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_DEFLATE)
996 mz_stream_zlib_create(&zip->compress_stream);
997#ifdef HAVE_BZIP2
998 else if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_BZIP2)
999 mz_stream_bzip_create(&zip->compress_stream);
1000#endif
1001#ifdef HAVE_LZMA
1002 else if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_LZMA)
1003 mz_stream_lzma_create(&zip->compress_stream);
1004#endif
1005 else
1006 err = MZ_PARAM_ERROR;
1007 }
1008
1009 if (err == MZ_OK)
1010 {
1011 if (zip->open_mode & MZ_STREAM_MODE_WRITE)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001012 {
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001013 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_COMPRESS_LEVEL, compress_level);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001014 }
1015 else
1016 {
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001017 if (zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED || zip->file_info.compression_method == MZ_COMPRESS_METHOD_RAW)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001018 {
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001019 max_total_in = zip->file_info.compressed_size;
1020 if (mz_stream_get_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_FOOTER_SIZE, &footer_size) == MZ_OK)
1021 max_total_in -= footer_size;
1022 if (mz_stream_get_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_TOTAL_IN, &total_in) == MZ_OK)
1023 max_total_in -= total_in;
1024 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_IN_MAX, max_total_in);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001025 }
1026 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001027
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001028 mz_stream_set_base(zip->compress_stream, zip->crypt_stream);
1029
1030 err = mz_stream_open(zip->compress_stream, NULL, zip->open_mode);
1031 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001032 if (err == Z_OK)
1033 {
1034 mz_stream_crc32_create(&zip->crc32_stream);
1035 mz_stream_set_base(zip->crc32_stream, zip->compress_stream);
1036
1037 err = mz_stream_open(zip->crc32_stream, NULL, zip->open_mode);
1038 }
1039
1040 if (err == MZ_OK)
1041 {
1042 zip->entry_opened = 1;
1043 }
1044
1045 return err;
1046}
1047
1048extern int ZEXPORT mz_zip_entry_read_open(void *handle, int raw, const char *password)
1049{
1050 mz_zip *zip = NULL;
1051 uint64_t extrafield_local_offset = 0;
1052 uint16_t extrafield_local_size = 0;
1053 uint32_t size_variable = 0;
1054 int64_t max_total_in = 0;
1055 int64_t total_in = 0;
1056 int64_t footer_size = 0;
1057 int16_t err = MZ_OK;
1058
1059#if !defined(HAVE_CRYPT) && !defined(HAVE_AES)
1060 if (password != NULL)
1061 return MZ_PARAM_ERROR;
1062#endif
1063 if (handle == NULL)
1064 return MZ_PARAM_ERROR;
1065
1066 zip = (mz_zip *)handle;
1067
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001068 if ((zip->open_mode & MZ_STREAM_MODE_READ) == 0)
1069 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001070 if (zip->entry_scanned == 0)
1071 return MZ_PARAM_ERROR;
1072 if ((zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED) && (password == NULL))
1073 return MZ_PARAM_ERROR;
1074
1075 if (zip->file_info.disk_num_start == zip->global_info.number_disk_with_cd)
1076 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
1077 else
1078 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, zip->file_info.disk_num_start);
1079
1080 err = mz_stream_seek(zip->stream, zip->disk_offset + zip->file_info.disk_offset, MZ_STREAM_SEEK_SET);
1081 if (err == MZ_OK)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001082 err = mz_zip_entry_read_header(zip->stream, 1, &zip->local_file_info, zip->file_info_stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001083 if (err == MZ_OK)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001084 err = mz_zip_entry_open_int(handle, 0, password);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001085
1086 return err;
1087}
1088
1089extern int ZEXPORT mz_zip_entry_write_open(void *handle, const mz_zip_file *file_info,
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001090 int16_t compress_level, const char *password)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001091{
1092 mz_zip *zip = NULL;
1093 int64_t disk_number = 0;
1094 int16_t err = MZ_OK;
1095
1096#if !defined(HAVE_CRYPT) && !defined(HAVE_AES)
1097 if (crypt_info != NULL)
1098 return MZ_PARAM_ERROR;
1099#endif
1100 if (handle == NULL)
1101 return MZ_PARAM_ERROR;
1102 if (file_info == NULL || file_info->filename == NULL)
1103 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001104
1105 zip = (mz_zip *)handle;
1106
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001107 if ((zip->open_mode & MZ_STREAM_MODE_WRITE) == 0)
1108 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001109 if (zip->entry_opened == 1)
1110 {
1111 err = mz_zip_entry_close(handle);
1112 if (err != MZ_OK)
1113 return err;
1114 }
1115
1116 memcpy(&zip->file_info, file_info, sizeof(mz_zip_file));
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001117
1118 zip->file_info.flag |= MZ_ZIP_FLAG_DATA_DESCRIPTOR;
1119#ifdef HAVE_LZMA
1120 zip->file_info.flag |= MZ_ZIP_FLAG_LZMA_EOS_MARKER;
1121#endif
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001122 if ((compress_level == 8) || (compress_level == 9))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001123 zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_MAX;
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001124 if (compress_level == 2)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001125 zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_FAST;
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001126 if (compress_level == 1)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001127 zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_SUPER_FAST;
1128
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001129 if (password != NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001130 zip->file_info.flag |= MZ_ZIP_FLAG_ENCRYPTED;
1131 else
1132 zip->file_info.flag &= ~MZ_ZIP_FLAG_ENCRYPTED;
1133
1134 if (mz_stream_get_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, &disk_number) == MZ_OK)
1135 zip->file_info.disk_num_start = (uint32_t)disk_number;
1136
1137 zip->file_info.disk_offset = mz_stream_tell(zip->stream);
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001138 zip->file_info.crc = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001139 zip->file_info.compressed_size = 0;
1140 zip->file_info.uncompressed_size = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001141#ifdef HAVE_AES
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001142 if (file_info->aes_version)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001143 {
1144 zip->file_info.aes_version = MZ_AES_VERSION;
1145 zip->file_info.aes_encryption_mode = MZ_AES_ENCRYPTIONMODE;
1146 }
1147#endif
1148
1149 if (err == MZ_OK)
1150 err = mz_zip_entry_write_header(zip->stream, 1, &zip->file_info);
1151 if (err == MZ_OK)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001152 err = mz_zip_entry_open_int(handle, compress_level, password);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001153
1154 return err;
1155}
1156
1157extern int ZEXPORT mz_zip_entry_read(void *handle, void *buf, uint32_t len)
1158{
1159 mz_zip *zip = NULL;
1160 int32_t read = 0;
1161
1162 if (handle == NULL)
1163 return MZ_PARAM_ERROR;
1164
1165 zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001166 if (zip->entry_opened == 0)
1167 return MZ_PARAM_ERROR;
1168
1169 if (len == 0)
1170 return 0;
1171 if (len > UINT16_MAX) // Zlib limitation
1172 return MZ_PARAM_ERROR;
1173
1174 read = mz_stream_read(zip->crc32_stream, buf, len);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001175 if (read > 0)
1176 zip->entry_read += read;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001177 return read;
1178}
1179
1180extern int ZEXPORT mz_zip_entry_write(void *handle, const void *buf, uint32_t len)
1181{
1182 mz_zip *zip = NULL;
1183
1184 if (handle == NULL)
1185 return MZ_PARAM_ERROR;
1186
1187 zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001188 if (zip->entry_opened == 0)
1189 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001190 return mz_stream_write(zip->crc32_stream, buf, len);
1191}
1192
1193extern int ZEXPORT mz_zip_entry_close_raw(void *handle, uint64_t uncompressed_size, uint32_t crc32)
1194{
1195 mz_zip *zip = NULL;
1196 uint64_t compressed_size = 0;
1197 uint16_t extrafield_size = 0;
1198 uint16_t extrafield_zip64_size = 0;
1199 uint16_t filename_size = 0;
1200 uint16_t comment_size = 0;
1201 uint16_t version_needed = 0;
1202 int16_t err = MZ_OK;
1203
1204 if (handle == NULL)
1205 return MZ_PARAM_ERROR;
1206
1207 zip = (mz_zip *)handle;
1208
1209 if (zip->entry_opened == 0)
1210 return MZ_PARAM_ERROR;
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001211
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001212 mz_stream_close(zip->compress_stream);
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001213 if (crc32 == 0)
1214 crc32 = mz_stream_crc32_get_value(zip->crc32_stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001215
1216 if ((zip->open_mode & MZ_STREAM_MODE_WRITE) == 0)
1217 {
1218#ifdef HAVE_AES
1219 // AES zip version AE-1 will expect a valid crc as well
1220 if (zip->aes_version <= 0x0001)
1221#endif
1222 {
1223 if ((zip->entry_read > 0) && (zip->file_info.compression_method != MZ_COMPRESS_METHOD_RAW))
1224 {
1225 if (crc32 != zip->file_info.crc)
1226 err = MZ_CRC_ERROR;
1227 }
1228 }
1229 }
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001230
1231 mz_stream_get_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_OUT, (int64_t *)&compressed_size);
1232 if ((zip->file_info.compression_method != MZ_COMPRESS_METHOD_RAW) || (uncompressed_size == 0))
1233 mz_stream_get_prop_int64(zip->crc32_stream, MZ_STREAM_PROP_TOTAL_OUT, (int64_t *)&uncompressed_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001234
1235 if (zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED)
1236 {
1237 mz_stream_set_base(zip->crypt_stream, zip->stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001238 err = mz_stream_close(zip->crypt_stream);
1239
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001240 mz_stream_get_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_TOTAL_OUT, (int64_t *)&compressed_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001241 }
1242
1243 mz_stream_delete(&zip->crypt_stream);
1244
1245 mz_stream_delete(&zip->compress_stream);
1246 mz_stream_crc32_delete(&zip->crc32_stream);
1247
1248 if (zip->open_mode & MZ_STREAM_MODE_WRITE)
1249 {
1250 if (err == MZ_OK)
1251 {
1252 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_DATADESCRIPTOR);
1253 if (err == MZ_OK)
1254 err = mz_stream_write_uint32(zip->stream, crc32);
1255 if (err == MZ_OK)
1256 {
1257 if (zip->file_info.zip_64)
1258 err = mz_stream_write_uint64(zip->stream, compressed_size);
1259 else
1260 err = mz_stream_write_uint32(zip->stream, (uint32_t)compressed_size);
1261 }
1262 if (err == MZ_OK)
1263 {
1264 if (zip->file_info.zip_64)
1265 err = mz_stream_write_uint64(zip->stream, uncompressed_size);
1266 else
1267 err = mz_stream_write_uint32(zip->stream, (uint32_t)uncompressed_size);
1268 }
1269 }
1270
1271 zip->file_info.crc = crc32;
1272 zip->file_info.compressed_size = compressed_size;
1273 zip->file_info.uncompressed_size = uncompressed_size;
1274
1275 if (err == MZ_OK)
1276 err = mz_zip_entry_write_header(zip->cd_stream, 0, &zip->file_info);
1277
1278 zip->global_info.number_entry += 1;
1279 }
1280
1281 zip->entry_opened = 0;
1282
1283 return err;
1284}
1285
1286extern int ZEXPORT mz_zip_entry_close(void *handle)
1287{
1288 return mz_zip_entry_close_raw(handle, 0, 0);
1289}
1290
1291extern int ZEXPORT mz_zip_entry_get_info(void *handle, mz_zip_file **file_info)
1292{
1293 mz_zip *zip = NULL;
1294
1295 if (handle == NULL)
1296 return MZ_PARAM_ERROR;
1297
1298 zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001299 if (zip->entry_scanned == 0)
1300 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001301 *file_info = &zip->file_info;
1302 return MZ_OK;
1303}
1304
1305extern int ZEXPORT mz_zip_entry_get_local_info(void *handle, mz_zip_file **local_file_info)
1306{
1307 mz_zip *zip = NULL;
1308
1309 if (handle == NULL)
1310 return MZ_PARAM_ERROR;
1311
1312 zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001313 if (zip->entry_scanned == 0 || zip->entry_opened == 0)
1314 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001315 *local_file_info = &zip->local_file_info;
1316 return MZ_OK;
1317}
1318
1319static int mz_zip_goto_next_entry_int(void *handle)
1320{
1321 mz_zip *zip = NULL;
1322 int16_t err = MZ_OK;
1323
1324 if (handle == NULL)
1325 return MZ_PARAM_ERROR;
1326
1327 zip = (mz_zip *)handle;
1328 zip->entry_scanned = 0;
1329
1330 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
1331
1332 err = mz_stream_seek(zip->stream, zip->disk_offset + zip->cd_current_pos, MZ_STREAM_SEEK_SET);
1333 if (err == MZ_OK)
1334 err = mz_zip_entry_read_header(zip->stream, 0, &zip->file_info, zip->file_info_stream);
1335 if (err == MZ_OK)
1336 zip->entry_scanned = 1;
1337 return err;
1338}
1339
1340extern int ZEXPORT mz_zip_goto_first_entry(void *handle)
1341{
1342 mz_zip *zip = NULL;
1343
1344 if (handle == NULL)
1345 return MZ_PARAM_ERROR;
1346
1347 zip = (mz_zip *)handle;
1348 zip->cd_current_pos = zip->cd_offset;
1349
1350 return mz_zip_goto_next_entry_int(handle);
1351}
1352
1353extern int ZEXPORT mz_zip_goto_next_entry(void *handle)
1354{
1355 mz_zip *zip = NULL;
1356
1357 if (handle == NULL)
1358 return MZ_PARAM_ERROR;
1359
1360 zip = (mz_zip *)handle;
1361 zip->cd_current_pos += MZ_ZIP_SIZE_CD_ITEM + zip->file_info.filename_size +
1362 zip->file_info.extrafield_size + zip->file_info.comment_size;
1363
1364 return mz_zip_goto_next_entry_int(handle);
1365}
1366
1367extern int ZEXPORT mz_zip_locate_entry(void *handle, const char *filename, mz_filename_compare_cb filename_compare_cb)
1368{
1369 mz_zip *zip = NULL;
1370 int16_t err = MZ_OK;
1371 int32_t result = 0;
1372
1373 if (handle == NULL)
1374 return MZ_PARAM_ERROR;
1375
1376 zip = (mz_zip *)handle;
1377
1378 err = mz_zip_goto_first_entry(handle);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001379 while (err == MZ_OK)
1380 {
1381 if (filename_compare_cb != NULL)
1382 result = filename_compare_cb(handle, zip->file_info.filename, filename);
1383 else
1384 result = strcmp(zip->file_info.filename, filename);
1385
1386 if (result == 0)
1387 return MZ_OK;
1388
1389 err = mz_zip_goto_next_entry(handle);
1390 }
1391
1392 return err;
1393}