blob: bb7a51693fed527f1026af5ba349833958c27f03 [file] [log] [blame]
Antoine Cœur0c2fceb2017-10-05 15:30:37 +08001/* zip.c -- Zip manipulation
Nathan Moinvazirib70b8242018-06-19 12:30:12 -07002 Version 2.3.4, June 19, 2018
Antoine Cœur0c2fceb2017-10-05 15:30:37 +08003 part of the MiniZip project
4
Nathan Moinvazirifee885a2018-01-06 08:49:03 -08005 Copyright (C) 2010-2018 Nathan Moinvaziri
Antoine Cœur0c2fceb2017-10-05 15:30:37 +08006 https://github.com/nmoinvaz/minizip
7 Copyright (C) 2009-2010 Mathias Svensson
8 Modifications for Zip64 support
9 http://result42.com
10 Copyright (C) 1998-2010 Gilles Vollant
11 http://www.winimage.com/zLibDll/minizip.html
12
13 This program is distributed under the terms of the same license as zlib.
14 See the accompanying LICENSE file for the full text of the license.
15*/
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <stdint.h>
20#include <string.h>
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -070021#include <time.h>
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080022#include <errno.h>
Nathan Moinvaziri34eff622018-01-22 09:25:15 -080023#include <limits.h>
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080024
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -070025#include "mz.h"
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080026#include "mz_strm.h"
27#ifdef HAVE_AES
28# include "mz_strm_aes.h"
29#endif
30#ifdef HAVE_BZIP2
31# include "mz_strm_bzip.h"
32#endif
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080033#ifdef HAVE_LZMA
34# include "mz_strm_lzma.h"
35#endif
Nathan Moinvaziri0e5c9dc2018-05-23 20:21:48 -070036#ifdef HAVE_PKCRYPT
37# include "mz_strm_pkcrypt.h"
38#endif
Nathan Moinvaziri4f6a0e32017-11-10 08:45:14 -080039#ifdef HAVE_ZLIB
40# include "mz_strm_zlib.h"
41#endif
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080042#include "mz_strm_mem.h"
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080043
44#include "mz_zip.h"
45
46/***************************************************************************/
47
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070048#define MZ_ZIP_MAGIC_LOCALHEADER (0x04034b50)
49#define MZ_ZIP_MAGIC_CENTRALHEADER (0x02014b50)
50#define MZ_ZIP_MAGIC_ENDHEADER (0x06054b50)
51#define MZ_ZIP_MAGIC_ENDHEADER64 (0x06064b50)
52#define MZ_ZIP_MAGIC_ENDLOCHEADER64 (0x07064b50)
53#define MZ_ZIP_MAGIC_DATADESCRIPTOR (0x08074b50)
54
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -070055#define MZ_ZIP_SIZE_CD_ITEM (0x2e)
56#define MZ_ZIP_SIZE_CD_LOCATOR64 (0x14)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080057
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -070058#define MZ_ZIP_EXTENSION_ZIP64 (0x0001)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -070059#define MZ_ZIP_EXTENSION_NTFS (0x000a)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -070060#define MZ_ZIP_EXTENSION_AES (0x9901)
61
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080062/***************************************************************************/
63
64typedef struct mz_zip_s
65{
66 mz_zip_file file_info;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070067 mz_zip_file local_file_info;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080068
69 void *stream; // main stream
Nathan Moinvaziricda36002017-10-21 09:37:18 -070070 void *cd_stream; // pointer to the stream with the cd
71 void *cd_mem_stream; // memory stream for central directory
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080072 void *compress_stream; // compression stream
73 void *crc32_stream; // crc32 stream
74 void *crypt_stream; // encryption stream
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070075 void *file_info_stream; // memory stream for storing file info
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -070076 void *local_file_info_stream; // memory stream for storing local file info
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080077
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070078 int32_t open_mode;
79
Nathan Moinvazirifd039e32017-10-22 14:40:39 -070080 uint32_t disk_number_with_cd; // number of the disk with the central dir
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070081
Nathan Moinvaziricda36002017-10-21 09:37:18 -070082 uint64_t cd_start_pos; // pos of the first file in the central dir stream
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070083 uint64_t cd_current_pos; // pos of the current file in the central dir
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070084 uint64_t cd_offset; // offset of start of central directory
85 uint64_t cd_size; // size of the central directory
86
Viktor Szakats915b82e2018-04-24 10:02:39 +000087 uint16_t entry_scanned;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070088 uint16_t entry_opened; // 1 if a file in the zip is currently writ.
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070089
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -070090 int64_t number_entry;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -070091
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -070092 int16_t compression_method;
93
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -070094 uint16_t version_madeby;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -070095 char *comment;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080096} mz_zip;
97
98/***************************************************************************/
99
100// Locate the central directory of a zip file (at the end, just before the global comment)
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700101static int32_t mz_zip_search_eocd(void *stream, uint64_t *central_pos)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800102{
Nathan Moinvaziri9ab31ba2017-10-16 07:51:09 -0700103 uint8_t buf[1024 + 4];
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700104 int64_t file_size = 0;
105 int64_t back_read = 0;
106 int64_t max_back = UINT16_MAX; // maximum size of global comment
107 int32_t read_size = sizeof(buf);
108 int64_t read_pos = 0;
109 int32_t i = 0;
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700110
Nathan Moinvazirif6e81cd2017-10-10 18:24:03 -0700111 *central_pos = 0;
112
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700113 if (mz_stream_seek(stream, 0, MZ_SEEK_END) != MZ_OK)
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700114 return MZ_STREAM_ERROR;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800115
116 file_size = mz_stream_tell(stream);
117
118 if (max_back > file_size)
119 max_back = file_size;
120
121 while (back_read < max_back)
122 {
Nathan Moinvaziri9ab31ba2017-10-16 07:51:09 -0700123 back_read += (sizeof(buf) - 4);
124 if (back_read > max_back)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800125 back_read = max_back;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800126
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700127 read_pos = file_size - back_read;
Nathan Moinvaziri9ab31ba2017-10-16 07:51:09 -0700128 if (read_size > (file_size - read_pos))
129 read_size = (uint32_t)(file_size - read_pos);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800130
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700131 if (mz_stream_seek(stream, read_pos, MZ_SEEK_SET) != MZ_OK)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800132 break;
133 if (mz_stream_read(stream, buf, read_size) != read_size)
134 break;
135
136 for (i = read_size - 3; (i--) > 0;)
137 {
Nathan Moinvaziri9ab31ba2017-10-16 07:51:09 -0700138 if (((*(buf + i)) == (MZ_ZIP_MAGIC_ENDHEADER & 0xff)) &&
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700139 ((*(buf + i + 1)) == (MZ_ZIP_MAGIC_ENDHEADER >> 8 & 0xff)) &&
140 ((*(buf + i + 2)) == (MZ_ZIP_MAGIC_ENDHEADER >> 16 & 0xff)) &&
141 ((*(buf + i + 3)) == (MZ_ZIP_MAGIC_ENDHEADER >> 24 & 0xff)))
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800142 {
Nathan Moinvazirif6e81cd2017-10-10 18:24:03 -0700143 *central_pos = read_pos + i;
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700144 return MZ_OK;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800145 }
146 }
147
Nathan Moinvazirif6e81cd2017-10-10 18:24:03 -0700148 if (*central_pos != 0)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800149 break;
150 }
151
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700152 return MZ_EXIST_ERROR;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800153}
154
155// Locate the central directory 64 of a zip file (at the end, just before the global comment)
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700156static int32_t mz_zip_search_zip64_eocd(void *stream, const uint64_t end_central_offset, uint64_t *central_pos)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800157{
158 uint64_t offset = 0;
159 uint32_t value32 = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700160 int32_t err = MZ_OK;
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700161
162
163 *central_pos = 0;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800164
165 // Zip64 end of central directory locator
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700166 err = mz_stream_seek(stream, end_central_offset - MZ_ZIP_SIZE_CD_LOCATOR64, MZ_SEEK_SET);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800167 // Read locator signature
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700168 if (err == MZ_OK)
169 {
170 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700171 if (value32 != MZ_ZIP_MAGIC_ENDLOCHEADER64)
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700172 err = MZ_FORMAT_ERROR;
173 }
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800174 // Number of the disk with the start of the zip64 end of central directory
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700175 if (err == MZ_OK)
176 err = mz_stream_read_uint32(stream, &value32);
177 // Relative offset of the zip64 end of central directory record8
178 if (err == MZ_OK)
179 err = mz_stream_read_uint64(stream, &offset);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800180 // Total number of disks
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700181 if (err == MZ_OK)
182 err = mz_stream_read_uint32(stream, &value32);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800183 // Goto end of central directory record
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700184 if (err == MZ_OK)
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700185 err = mz_stream_seek(stream, offset, MZ_SEEK_SET);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800186 // The signature
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700187 if (err == MZ_OK)
188 {
189 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700190 if (value32 != MZ_ZIP_MAGIC_ENDHEADER64)
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700191 err = MZ_FORMAT_ERROR;
192 }
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800193
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700194 if (err == MZ_OK)
195 *central_pos = offset;
196
197 return err;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800198}
199
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700200static int32_t mz_zip_read_cd(void *handle)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800201{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700202 mz_zip *zip = (mz_zip *)handle;
203 int64_t number_entry_cd = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700204 uint64_t number_entry_cd64 = 0;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800205 uint64_t number_entry = 0;
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700206 uint64_t eocd_pos = 0;
207 uint64_t eocd_pos64 = 0;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800208 uint16_t value16 = 0;
209 uint32_t value32 = 0;
210 uint64_t value64 = 0;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700211 uint16_t comment_size = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700212 int32_t err = MZ_OK;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800213
214
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800215 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700216 return MZ_PARAM_ERROR;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800217
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700218 // Read and cache central directory records
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700219 if (mz_zip_search_eocd(zip->stream, &eocd_pos) == MZ_OK)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800220 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700221 // Read end of central directory info
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700222 err = mz_stream_seek(zip->stream, eocd_pos, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700223 // The signature, already checked
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800224 if (err == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700225 err = mz_stream_read_uint32(zip->stream, &value32);
226 // Number of this disk
227 if (err == MZ_OK)
228 err = mz_stream_read_uint16(zip->stream, &value16);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700229 // Number of the disk with the start of the central directory
230 if (err == MZ_OK)
231 err = mz_stream_read_uint16(zip->stream, &value16);
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700232 zip->disk_number_with_cd = value16;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700233 // Total number of entries in the central dir on this disk
234 if (err == MZ_OK)
235 err = mz_stream_read_uint16(zip->stream, &value16);
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700236 zip->number_entry = value16;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700237 // Total number of entries in the central dir
238 if (err == MZ_OK)
239 err = mz_stream_read_uint16(zip->stream, &value16);
240 number_entry_cd = value16;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700241 if (number_entry_cd != zip->number_entry)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700242 err = MZ_FORMAT_ERROR;
243 // Size of the central directory
244 if (err == MZ_OK)
245 err = mz_stream_read_uint32(zip->stream, &value32);
246 if (err == MZ_OK)
247 zip->cd_size = value32;
248 // Offset of start of central directory with respect to the starting disk number
249 if (err == MZ_OK)
250 err = mz_stream_read_uint32(zip->stream, &value32);
251 zip->cd_offset = value32;
252 // Zip file global comment length
253 if (err == MZ_OK)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700254 err = mz_stream_read_uint16(zip->stream, &comment_size);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800255
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700256 if ((err == MZ_OK) && ((number_entry_cd == UINT16_MAX) || (zip->cd_offset == UINT32_MAX)))
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800257 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700258 // Format should be Zip64, as the central directory or file size is too large
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700259 if (mz_zip_search_zip64_eocd(zip->stream, eocd_pos, &eocd_pos64) == MZ_OK)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800260 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700261 eocd_pos = eocd_pos64;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700262
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700263 err = mz_stream_seek(zip->stream, eocd_pos, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700264 // The signature, already checked
265 if (err == MZ_OK)
266 err = mz_stream_read_uint32(zip->stream, &value32);
267 // Size of zip64 end of central directory record
268 if (err == MZ_OK)
269 err = mz_stream_read_uint64(zip->stream, &value64);
270 // Version made by
271 if (err == MZ_OK)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700272 err = mz_stream_read_uint16(zip->stream, &zip->version_madeby);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700273 // Version needed to extract
274 if (err == MZ_OK)
275 err = mz_stream_read_uint16(zip->stream, &value16);
276 // Number of this disk
277 if (err == MZ_OK)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700278 err = mz_stream_read_uint32(zip->stream, &value32);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700279 // Number of the disk with the start of the central directory
280 if (err == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700281 err = mz_stream_read_uint32(zip->stream, &zip->disk_number_with_cd);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700282 // Total number of entries in the central directory on this disk
283 if (err == MZ_OK)
284 err = mz_stream_read_uint64(zip->stream, &number_entry);
285 // Total number of entries in the central directory
286 if (err == MZ_OK)
287 err = mz_stream_read_uint64(zip->stream, &number_entry_cd64);
288 if (number_entry == UINT32_MAX)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700289 zip->number_entry = number_entry_cd64;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700290 // Size of the central directory
291 if (err == MZ_OK)
292 err = mz_stream_read_uint64(zip->stream, &zip->cd_size);
293 // Offset of start of central directory with respect to the starting disk number
294 if (err == MZ_OK)
295 err = mz_stream_read_uint64(zip->stream, &zip->cd_offset);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800296 }
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700297 else if ((zip->number_entry == UINT16_MAX) || (number_entry_cd != zip->number_entry) ||
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700298 (zip->cd_size == UINT16_MAX) || (zip->cd_offset == UINT32_MAX))
Nathan Moinvaziri016ad472017-10-09 23:36:30 -0700299 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700300 err = MZ_FORMAT_ERROR;
Nathan Moinvaziri016ad472017-10-09 23:36:30 -0700301 }
302 }
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800303 }
304
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700305 if (err == MZ_OK)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800306 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700307 if (eocd_pos < zip->cd_offset + zip->cd_size)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700308 err = MZ_FORMAT_ERROR;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800309 }
310
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700311 if ((err == MZ_OK) && (comment_size > 0))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700312 {
Nathan Moinvaziri5244fc02018-05-02 20:01:35 -0700313 zip->comment = (char *)MZ_ALLOC(comment_size + 1);
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700314 if (zip->comment)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700315 {
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700316 if (mz_stream_read(zip->stream, zip->comment, comment_size) != comment_size)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700317 err = MZ_STREAM_ERROR;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700318 zip->comment[comment_size] = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700319 }
320 }
321
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800322 return err;
323}
324
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700325static int32_t mz_zip_write_cd(void *handle)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800326{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700327 mz_zip *zip = (mz_zip *)handle;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800328 uint16_t comment_size = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700329 uint64_t zip64_eocd_pos_inzip = 0;
Nathan Moinvaziri016ad472017-10-09 23:36:30 -0700330 int64_t disk_number = 0;
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700331 int64_t disk_size = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700332 int32_t err = MZ_OK;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800333
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700334
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700335 if (zip == NULL)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800336 return MZ_PARAM_ERROR;
Viktor Szakats915b82e2018-04-24 10:02:39 +0000337
Nathan Moinvaziri016ad472017-10-09 23:36:30 -0700338 if (mz_stream_get_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, &disk_number) == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700339 zip->disk_number_with_cd = (uint32_t)disk_number;
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700340 if (mz_stream_get_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_SIZE, &disk_size) == MZ_OK && disk_size > 0)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700341 zip->disk_number_with_cd += 1;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700342 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800343
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700344 zip->cd_offset = mz_stream_tell(zip->stream);
345 mz_stream_seek(zip->cd_mem_stream, 0, MZ_SEEK_END);
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700346 zip->cd_size = (uint32_t)mz_stream_tell(zip->cd_mem_stream);
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700347 mz_stream_seek(zip->cd_mem_stream, 0, MZ_SEEK_SET);
Viktor Szakats915b82e2018-04-24 10:02:39 +0000348
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700349 err = mz_stream_copy(zip->stream, zip->cd_mem_stream, (int32_t)zip->cd_size);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800350
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800351 // Write the ZIP64 central directory header
juanii3f59ffc2018-02-08 12:44:17 -0300352 if (zip->cd_offset >= UINT32_MAX || zip->number_entry > UINT16_MAX)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800353 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700354 zip64_eocd_pos_inzip = mz_stream_tell(zip->stream);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800355
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700356 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_ENDHEADER64);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800357
358 // Size of this 'zip64 end of central directory'
359 if (err == MZ_OK)
360 err = mz_stream_write_uint64(zip->stream, (uint64_t)44);
361 // Version made by
362 if (err == MZ_OK)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700363 err = mz_stream_write_uint16(zip->stream, zip->version_madeby);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800364 // Version needed
365 if (err == MZ_OK)
366 err = mz_stream_write_uint16(zip->stream, (uint16_t)45);
367 // Number of this disk
368 if (err == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700369 err = mz_stream_write_uint32(zip->stream, zip->disk_number_with_cd);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800370 // Number of the disk with the start of the central directory
371 if (err == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700372 err = mz_stream_write_uint32(zip->stream, zip->disk_number_with_cd);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800373 // Total number of entries in the central dir on this disk
374 if (err == MZ_OK)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700375 err = mz_stream_write_uint64(zip->stream, zip->number_entry);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800376 // Total number of entries in the central dir
377 if (err == MZ_OK)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700378 err = mz_stream_write_uint64(zip->stream, zip->number_entry);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800379 // Size of the central directory
380 if (err == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700381 err = mz_stream_write_uint64(zip->stream, (uint64_t)zip->cd_size);
382 // Offset of start of central directory with respect to the starting disk number
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800383 if (err == MZ_OK)
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700384 err = mz_stream_write_uint64(zip->stream, zip->cd_offset);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800385 if (err == MZ_OK)
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700386 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_ENDLOCHEADER64);
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700387
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800388 // Number of the disk with the start of the central directory
389 if (err == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700390 err = mz_stream_write_uint32(zip->stream, zip->disk_number_with_cd);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800391 // Relative offset to the end of zip64 central directory
392 if (err == MZ_OK)
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700393 err = mz_stream_write_uint64(zip->stream, zip64_eocd_pos_inzip);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800394 // Number of the disk with the start of the central directory
395 if (err == MZ_OK)
juaniic65486d2018-02-08 17:07:07 -0300396 err = mz_stream_write_uint32(zip->stream, zip->disk_number_with_cd + 1);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800397 }
398
399 // Write the central directory header
400
Viktor Szakats915b82e2018-04-24 10:02:39 +0000401 // Signature
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800402 if (err == MZ_OK)
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700403 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_ENDHEADER);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800404 // Number of this disk
405 if (err == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700406 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->disk_number_with_cd);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800407 // Number of the disk with the start of the central directory
408 if (err == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700409 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->disk_number_with_cd);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800410 // Total number of entries in the central dir on this disk
411 if (err == MZ_OK)
412 {
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700413 if (zip->number_entry >= UINT16_MAX)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800414 err = mz_stream_write_uint16(zip->stream, UINT16_MAX);
415 else
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700416 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->number_entry);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800417 }
418 // Total number of entries in the central dir
419 if (err == MZ_OK)
420 {
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700421 if (zip->number_entry >= UINT16_MAX)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800422 err = mz_stream_write_uint16(zip->stream, UINT16_MAX);
423 else
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700424 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->number_entry);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800425 }
426 // Size of the central directory
427 if (err == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700428 err = mz_stream_write_uint32(zip->stream, (uint32_t)zip->cd_size);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800429 // Offset of start of central directory with respect to the starting disk number
430 if (err == MZ_OK)
431 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700432 if (zip->cd_offset >= UINT32_MAX)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800433 err = mz_stream_write_uint32(zip->stream, UINT32_MAX);
434 else
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700435 err = mz_stream_write_uint32(zip->stream, (uint32_t)zip->cd_offset);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800436 }
437
438 // Write global comment
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700439 if (zip->comment != NULL)
440 comment_size = (uint16_t)strlen(zip->comment);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800441 if (err == MZ_OK)
442 err = mz_stream_write_uint16(zip->stream, comment_size);
443 if (err == MZ_OK)
444 {
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700445 if (mz_stream_write(zip->stream, zip->comment, comment_size) != comment_size)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800446 err = MZ_STREAM_ERROR;
447 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700448 return err;
449}
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800450
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -0800451extern void* mz_zip_open(void *stream, int32_t mode)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700452{
453 mz_zip *zip = NULL;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700454 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700455
456
Nathan Moinvaziri5244fc02018-05-02 20:01:35 -0700457 zip = (mz_zip *)MZ_ALLOC(sizeof(mz_zip));
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700458 if (zip == NULL)
459 return NULL;
460
461 memset(zip, 0, sizeof(mz_zip));
462
463 zip->stream = stream;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700464
Nathan Moinvaziri3f7de8e2017-12-11 21:09:52 -0800465 if (mode & MZ_OPEN_MODE_WRITE)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700466 {
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700467 mz_stream_mem_create(&zip->cd_mem_stream);
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700468 mz_stream_mem_open(zip->cd_mem_stream, NULL, MZ_OPEN_MODE_CREATE);
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700469
470 zip->cd_stream = zip->cd_mem_stream;
471 }
472 else
473 {
474 zip->cd_stream = stream;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700475 }
476
Nathan Moinvaziri3f7de8e2017-12-11 21:09:52 -0800477 if ((mode & MZ_OPEN_MODE_READ) || (mode & MZ_OPEN_MODE_APPEND))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700478 {
479 err = mz_zip_read_cd(zip);
480
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700481 if ((err == MZ_OK) && (mode & MZ_OPEN_MODE_APPEND))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700482 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700483 if (zip->cd_size > 0)
484 {
485 // Store central directory in memory
486 err = mz_stream_seek(zip->stream, zip->cd_offset, MZ_SEEK_SET);
487 if (err == MZ_OK)
488 err = mz_stream_copy(zip->cd_mem_stream, zip->stream, (uint32_t)zip->cd_size);
489 if (err == MZ_OK)
490 err = mz_stream_seek(zip->stream, zip->cd_offset, MZ_SEEK_SET);
491 }
492 else
493 {
494 // If no central directory, append new zip to end of file
495 err = mz_stream_seek(zip->stream, 0, MZ_SEEK_END);
496 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700497 }
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700498 else
499 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700500 zip->cd_start_pos = zip->cd_offset;
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700501 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700502 }
503
504 if (err == MZ_OK)
505 {
506 mz_stream_mem_create(&zip->file_info_stream);
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700507 mz_stream_mem_open(zip->file_info_stream, NULL, MZ_OPEN_MODE_CREATE);
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700508 mz_stream_mem_create(&zip->local_file_info_stream);
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700509 mz_stream_mem_open(zip->local_file_info_stream, NULL, MZ_OPEN_MODE_CREATE);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700510 }
511
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700512 if (err != MZ_OK)
513 {
Nathan Moinvaziri3f7de8e2017-12-11 21:09:52 -0800514 mz_zip_close(zip);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700515 return NULL;
516 }
517
Nathan Moinvaziri3f7de8e2017-12-11 21:09:52 -0800518 zip->open_mode = mode;
519
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700520 return zip;
521}
522
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -0800523extern int32_t mz_zip_close(void *handle)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700524{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700525 mz_zip *zip = (mz_zip *)handle;
526 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700527
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700528 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700529 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700530
531 if (zip->entry_opened == 1)
532 {
533 err = mz_zip_entry_close(handle);
534 if (err != MZ_OK)
535 return err;
536 }
537
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700538 if (zip->open_mode & MZ_OPEN_MODE_WRITE)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700539 err = mz_zip_write_cd(handle);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700540
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700541 if (zip->cd_mem_stream != NULL)
Nathan Moinvaziri51bf64a2017-10-20 14:23:37 -0700542 {
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700543 mz_stream_close(zip->cd_mem_stream);
544 mz_stream_delete(&zip->cd_mem_stream);
Nathan Moinvaziri51bf64a2017-10-20 14:23:37 -0700545 }
546
Nathan Moinvaziri3f7de8e2017-12-11 21:09:52 -0800547 if (zip->file_info_stream != NULL)
548 {
549 mz_stream_mem_close(zip->file_info_stream);
550 mz_stream_mem_delete(&zip->file_info_stream);
551 }
552 if (zip->local_file_info_stream != NULL)
553 {
554 mz_stream_mem_close(zip->local_file_info_stream);
555 mz_stream_mem_delete(&zip->local_file_info_stream);
556 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700557
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700558 if (zip->comment)
Nathan Moinvaziri5244fc02018-05-02 20:01:35 -0700559 MZ_FREE(zip->comment);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700560
Nathan Moinvaziri5244fc02018-05-02 20:01:35 -0700561 MZ_FREE(zip);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800562
563 return err;
564}
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700565
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -0800566extern int32_t mz_zip_get_comment(void *handle, const char **comment)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700567{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700568 mz_zip *zip = (mz_zip *)handle;
569 if (zip == NULL || comment == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700570 return MZ_PARAM_ERROR;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700571 if (zip->comment == NULL)
572 return MZ_EXIST_ERROR;
573 *comment = zip->comment;
574 return MZ_OK;
575}
576
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -0800577extern int32_t mz_zip_set_comment(void *handle, const char *comment)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700578{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700579 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700580 uint16_t comment_size = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700581 if (zip == NULL || comment == NULL)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700582 return MZ_PARAM_ERROR;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700583 if (zip->comment != NULL)
Nathan Moinvaziri5244fc02018-05-02 20:01:35 -0700584 MZ_FREE(zip->comment);
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700585 comment_size = (uint16_t)(strlen(comment) + 1);
Nathan Moinvaziri5244fc02018-05-02 20:01:35 -0700586 zip->comment = (char *)MZ_ALLOC(comment_size);
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700587 strncpy(zip->comment, comment, comment_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700588 return MZ_OK;
589}
590
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -0800591extern int32_t mz_zip_get_version_madeby(void *handle, uint16_t *version_madeby)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700592{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700593 mz_zip *zip = (mz_zip *)handle;
594 if (zip == NULL || version_madeby == NULL)
595 return MZ_PARAM_ERROR;
596 *version_madeby = zip->version_madeby;
597 return MZ_OK;
598}
599
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -0800600extern int32_t mz_zip_set_version_madeby(void *handle, uint16_t version_madeby)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700601{
602 mz_zip *zip = (mz_zip *)handle;
603 if (zip == NULL)
604 return MZ_PARAM_ERROR;
605 zip->version_madeby = version_madeby;
606 return MZ_OK;
607}
608
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700609// Get info about the current file in the zip file
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700610static int32_t mz_zip_entry_read_header(void *stream, uint8_t local, mz_zip_file *file_info, void *file_info_stream)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700611{
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700612 uint64_t ntfs_time = 0;
613 uint32_t reserved = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700614 uint32_t magic = 0;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700615 uint32_t dos_date = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700616 uint32_t extra_pos = 0;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700617 uint32_t extra_data_size_read = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700618 uint16_t extra_header_id = 0;
619 uint16_t extra_data_size = 0;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700620 uint16_t ntfs_attrib_id = 0;
621 uint16_t ntfs_attrib_size = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700622 uint16_t value16 = 0;
623 uint32_t value32 = 0;
624 uint64_t value64 = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700625 int64_t max_seek = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700626 int64_t seek = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700627 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700628
629
630 memset(file_info, 0, sizeof(mz_zip_file));
631
632 // Check the magic
633 err = mz_stream_read_uint32(stream, &magic);
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700634 if (err == MZ_END_OF_STREAM)
635 err = MZ_END_OF_LIST;
636 else if (magic == MZ_ZIP_MAGIC_ENDHEADER || magic == MZ_ZIP_MAGIC_ENDHEADER64)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700637 err = MZ_END_OF_LIST;
638 else if ((local) && (magic != MZ_ZIP_MAGIC_LOCALHEADER))
639 err = MZ_FORMAT_ERROR;
640 else if ((!local) && (magic != MZ_ZIP_MAGIC_CENTRALHEADER))
641 err = MZ_FORMAT_ERROR;
Viktor Szakats915b82e2018-04-24 10:02:39 +0000642
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700643 // Read header fields
644 if (err == MZ_OK)
645 {
646 if (!local)
647 err = mz_stream_read_uint16(stream, &file_info->version_madeby);
648 if (err == MZ_OK)
649 err = mz_stream_read_uint16(stream, &file_info->version_needed);
650 if (err == MZ_OK)
651 err = mz_stream_read_uint16(stream, &file_info->flag);
652 if (err == MZ_OK)
653 err = mz_stream_read_uint16(stream, &file_info->compression_method);
654 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700655 {
656 err = mz_stream_read_uint32(stream, &dos_date);
657 file_info->modified_date = mz_zip_dosdate_to_time_t(dos_date);
658 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700659 if (err == MZ_OK)
660 err = mz_stream_read_uint32(stream, &file_info->crc);
661 if (err == MZ_OK)
662 err = mz_stream_read_uint32(stream, &value32);
663 file_info->compressed_size = value32;
664 if (err == MZ_OK)
665 err = mz_stream_read_uint32(stream, &value32);
666 file_info->uncompressed_size = value32;
667 if (err == MZ_OK)
668 err = mz_stream_read_uint16(stream, &file_info->filename_size);
669 if (err == MZ_OK)
670 err = mz_stream_read_uint16(stream, &file_info->extrafield_size);
671 if (!local)
672 {
673 if (err == MZ_OK)
674 err = mz_stream_read_uint16(stream, &file_info->comment_size);
675 if (err == MZ_OK)
676 err = mz_stream_read_uint16(stream, &value16);
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700677 file_info->disk_number = value16;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700678 if (err == MZ_OK)
679 err = mz_stream_read_uint16(stream, &file_info->internal_fa);
680 if (err == MZ_OK)
681 err = mz_stream_read_uint32(stream, &file_info->external_fa);
682 if (err == MZ_OK)
683 err = mz_stream_read_uint32(stream, &value32);
684 file_info->disk_offset = value32;
685 }
686 }
687
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700688 max_seek = file_info->filename_size + file_info->extrafield_size + file_info->comment_size + 3;
689 if (err == MZ_OK)
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700690 err = mz_stream_seek(file_info_stream, max_seek, MZ_SEEK_SET);
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700691 if (err == MZ_OK)
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700692 err = mz_stream_seek(file_info_stream, 0, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700693
694 if ((err == MZ_OK) && (file_info->filename_size > 0))
695 {
Nathan Moinvaziria710bd72018-05-09 00:46:47 -0700696 mz_stream_mem_get_buffer(file_info_stream, (const void **)&file_info->filename);
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700697
698 err = mz_stream_copy(file_info_stream, stream, file_info->filename_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700699 if (err == MZ_OK)
700 err = mz_stream_write_uint8(file_info_stream, 0);
701
702 seek += file_info->filename_size + 1;
703 }
704
705 if ((err == MZ_OK) && (file_info->extrafield_size > 0))
706 {
Nathan Moinvaziria710bd72018-05-09 00:46:47 -0700707 mz_stream_mem_get_buffer_at(file_info_stream, seek, (const void **)&file_info->extrafield);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700708
709 err = mz_stream_copy(file_info_stream, stream, file_info->extrafield_size);
710 if (err == MZ_OK)
711 err = mz_stream_write_uint8(file_info_stream, 0);
712
713 // Seek back and parse the extra field
714 if (err == MZ_OK)
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700715 err = mz_stream_seek(file_info_stream, seek, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700716
717 seek += file_info->extrafield_size + 1;
718
719 while ((err == MZ_OK) && (extra_pos < file_info->extrafield_size))
720 {
721 err = mz_stream_read_uint16(file_info_stream, &extra_header_id);
722 if (err == MZ_OK)
723 err = mz_stream_read_uint16(file_info_stream, &extra_data_size);
724
725 // ZIP64 extra field
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700726 if (extra_header_id == MZ_ZIP_EXTENSION_ZIP64)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700727 {
728 if ((err == MZ_OK) && (file_info->uncompressed_size == UINT32_MAX))
729 err = mz_stream_read_uint64(file_info_stream, &file_info->uncompressed_size);
730 if ((err == MZ_OK) && (file_info->compressed_size == UINT32_MAX))
731 err = mz_stream_read_uint64(file_info_stream, &file_info->compressed_size);
732 if ((err == MZ_OK) && (file_info->disk_offset == UINT32_MAX))
733 err = mz_stream_read_uint64(file_info_stream, &value64);
734 file_info->disk_offset = value64;
juanii4ae79922018-02-11 14:29:36 -0300735 if ((err == MZ_OK) && (file_info->disk_number == UINT16_MAX))
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700736 err = mz_stream_read_uint32(file_info_stream, &file_info->disk_number);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700737 }
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700738 // NTFS extra field
739 else if (extra_header_id == MZ_ZIP_EXTENSION_NTFS)
740 {
Nathan Moinvaziri7a3b6982018-05-09 00:45:02 -0700741 if (err == MZ_OK)
742 err = mz_stream_read_uint32(file_info_stream, &reserved);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700743 extra_data_size_read = 4;
744
745 while ((err == MZ_OK) && (extra_data_size_read < extra_data_size))
746 {
747 err = mz_stream_read_uint16(file_info_stream, &ntfs_attrib_id);
748 if (err == MZ_OK)
749 err = mz_stream_read_uint16(file_info_stream, &ntfs_attrib_size);
750
Nathan Moinvaziri7a3b6982018-05-09 00:45:02 -0700751 if ((err == MZ_OK) && (ntfs_attrib_id == 0x01) && (ntfs_attrib_size == 24))
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700752 {
753 err = mz_stream_read_uint64(file_info_stream, &ntfs_time);
754 mz_zip_ntfs_to_unix_time(ntfs_time, &file_info->modified_date);
755
juanii7063b0e2018-02-11 13:56:21 -0300756 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700757 {
758 err = mz_stream_read_uint64(file_info_stream, &ntfs_time);
759 mz_zip_ntfs_to_unix_time(ntfs_time, &file_info->accessed_date);
760 }
juanii7063b0e2018-02-11 13:56:21 -0300761 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700762 {
763 err = mz_stream_read_uint64(file_info_stream, &ntfs_time);
764 mz_zip_ntfs_to_unix_time(ntfs_time, &file_info->creation_date);
765 }
766 }
767 else
768 {
Nathan Moinvaziri7a3b6982018-05-09 00:45:02 -0700769 if (err == MZ_OK)
770 err = mz_stream_seek(file_info_stream, ntfs_attrib_size, MZ_SEEK_CUR);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700771 }
772
773 extra_data_size_read += ntfs_attrib_size + 4;
774 }
775 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700776#ifdef HAVE_AES
777 // AES extra field
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700778 else if (extra_header_id == MZ_ZIP_EXTENSION_AES)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700779 {
780 uint8_t value8 = 0;
781 // Verify version info
782 err = mz_stream_read_uint16(file_info_stream, &value16);
783 // Support AE-1 and AE-2
784 if (value16 != 1 && value16 != 2)
785 err = MZ_FORMAT_ERROR;
786 file_info->aes_version = value16;
787 if (err == MZ_OK)
788 err = mz_stream_read_uint8(file_info_stream, &value8);
789 if ((char)value8 != 'A')
790 err = MZ_FORMAT_ERROR;
791 if (err == MZ_OK)
792 err = mz_stream_read_uint8(file_info_stream, &value8);
793 if ((char)value8 != 'E')
794 err = MZ_FORMAT_ERROR;
795 // Get AES encryption strength and actual compression method
796 if (err == MZ_OK)
797 err = mz_stream_read_uint8(file_info_stream, &value8);
798 file_info->aes_encryption_mode = value8;
799 if (err == MZ_OK)
800 err = mz_stream_read_uint16(file_info_stream, &value16);
801 file_info->compression_method = value16;
802 }
803#endif
804 else
805 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700806 err = mz_stream_seek(file_info_stream, extra_data_size, MZ_SEEK_CUR);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700807 }
808
809 extra_pos += 4 + extra_data_size;
810 }
811 }
812
813 if ((err == MZ_OK) && (file_info->comment_size > 0))
814 {
Nathan Moinvaziria710bd72018-05-09 00:46:47 -0700815 mz_stream_mem_get_buffer_at(file_info_stream, seek, (const void **)&file_info->comment);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700816
817 err = mz_stream_copy(file_info_stream, stream, file_info->comment_size);
818 if (err == MZ_OK)
819 err = mz_stream_write_uint8(file_info_stream, 0);
820 }
821
822 return err;
823}
824
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700825static int32_t mz_zip_entry_write_header(void *stream, uint8_t local, mz_zip_file *file_info)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700826{
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700827 uint64_t ntfs_time = 0;
828 uint32_t reserved = 0;
829 uint32_t dos_date = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700830 uint16_t extrafield_size = 0;
831 uint16_t extrafield_zip64_size = 0;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700832 uint16_t extrafield_ntfs_size = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700833 uint16_t filename_size = 0;
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -0700834 uint16_t filename_length = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700835 uint16_t comment_size = 0;
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700836 uint16_t version_needed = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700837 uint8_t zip64 = 0;
838 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700839
840 if (file_info == NULL)
841 return MZ_PARAM_ERROR;
842
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700843 // Calculate extra field sizes
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700844 extrafield_size = file_info->extrafield_size;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700845
846 if (file_info->uncompressed_size >= UINT32_MAX)
847 extrafield_zip64_size += 8;
848 if (file_info->compressed_size >= UINT32_MAX)
849 extrafield_zip64_size += 8;
850 if (file_info->disk_offset >= UINT32_MAX)
851 extrafield_zip64_size += 8;
852
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700853 if (file_info->zip64 == MZ_ZIP64_AUTO)
Nathan Moinvaziri121e0882018-05-03 17:57:20 -0700854 {
855 // If uncompressed size is unknown, assume zip64 for 64-bit data descriptors
856 zip64 = (local && file_info->uncompressed_size == 0) || (extrafield_zip64_size > 0);
857 }
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700858 else if (file_info->zip64 == MZ_ZIP64_FORCE)
Nathan Moinvaziria56a08c2018-05-03 09:35:37 -0700859 zip64 = 1;
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700860 else if (file_info->zip64 == MZ_ZIP64_DISABLE)
Nathan Moinvaziria56a08c2018-05-03 09:35:37 -0700861 {
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700862 // Zip64 extension is required to zip file
863 if (extrafield_zip64_size > 0)
864 return MZ_PARAM_ERROR;
Nathan Moinvaziria56a08c2018-05-03 09:35:37 -0700865 }
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700866
867 if (zip64)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700868 {
869 extrafield_size += 4;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700870 extrafield_size += extrafield_zip64_size;
871 }
872#ifdef HAVE_AES
873 if ((file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
874 extrafield_size += 4 + 7;
875#endif
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700876 // NTFS timestamps
Nathan Moinvazirid9e159a2018-02-13 15:30:30 -0800877 if ((file_info->modified_date != 0) &&
878 (file_info->accessed_date != 0) &&
879 (file_info->creation_date != 0))
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700880 {
juanii3679a3d2018-02-11 13:55:38 -0300881 extrafield_ntfs_size += 8 + 8 + 8 + 4 + 2 + 2;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700882 extrafield_size += 4;
883 extrafield_size += extrafield_ntfs_size;
884 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700885
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700886 if (local)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700887 err = mz_stream_write_uint32(stream, MZ_ZIP_MAGIC_LOCALHEADER);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700888 else
889 {
890 err = mz_stream_write_uint32(stream, MZ_ZIP_MAGIC_CENTRALHEADER);
891 if (err == MZ_OK)
892 err = mz_stream_write_uint16(stream, file_info->version_madeby);
893 }
894
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700895 // Calculate version needed to extract
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700896 if (err == MZ_OK)
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700897 {
898 version_needed = file_info->version_needed;
899 if (version_needed == 0)
900 {
901 version_needed = 20;
902 if (zip64)
903 version_needed = 45;
904#ifdef HAVE_AES
905 if ((file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
906 version_needed = 51;
907#endif
908#ifdef HAVE_LZMA
909 if (file_info->compression_method == MZ_COMPRESS_METHOD_LZMA)
910 version_needed = 63;
911#endif
912 }
913 err = mz_stream_write_uint16(stream, version_needed);
914 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700915 if (err == MZ_OK)
916 err = mz_stream_write_uint16(stream, file_info->flag);
917 if (err == MZ_OK)
918 {
919#ifdef HAVE_AES
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700920 if (file_info->aes_version)
921 err = mz_stream_write_uint16(stream, MZ_COMPRESS_METHOD_AES);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700922 else
923#endif
924 err = mz_stream_write_uint16(stream, file_info->compression_method);
925 }
926 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700927 {
Nathan Moinvaziri17cdaec2017-10-26 10:18:41 -0700928 if (file_info->modified_date != 0)
929 dos_date = mz_zip_time_t_to_dos_date(file_info->modified_date);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700930 err = mz_stream_write_uint32(stream, dos_date);
931 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700932
933 if (err == MZ_OK)
934 err = mz_stream_write_uint32(stream, file_info->crc); // crc
935 if (err == MZ_OK)
936 {
937 if (file_info->compressed_size >= UINT32_MAX) // compr size
938 err = mz_stream_write_uint32(stream, UINT32_MAX);
939 else
940 err = mz_stream_write_uint32(stream, (uint32_t)file_info->compressed_size);
941 }
942 if (err == MZ_OK)
943 {
944 if (file_info->uncompressed_size >= UINT32_MAX) // uncompr size
945 err = mz_stream_write_uint32(stream, UINT32_MAX);
946 else
947 err = mz_stream_write_uint32(stream, (uint32_t)file_info->uncompressed_size);
948 }
949
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -0700950 filename_length = (uint16_t)strlen(file_info->filename);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700951 if (err == MZ_OK)
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -0700952 {
953 filename_size = filename_length;
954 if (mz_zip_attrib_is_dir(file_info->external_fa, file_info->version_madeby) == MZ_OK)
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700955 {
956 if ((file_info->filename[filename_length - 1] == '/') ||
957 (file_info->filename[filename_length - 1] == '\\'))
958 filename_length -= 1;
959 else
960 filename_size += 1;
961 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700962 err = mz_stream_write_uint16(stream, filename_size);
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -0700963 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700964 if (err == MZ_OK)
965 err = mz_stream_write_uint16(stream, extrafield_size);
966
967 if (!local)
968 {
969 if (file_info->comment != NULL)
970 comment_size = (uint16_t)strlen(file_info->comment);
971 if (err == MZ_OK)
972 err = mz_stream_write_uint16(stream, comment_size);
973 if (err == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700974 err = mz_stream_write_uint16(stream, (uint16_t)file_info->disk_number);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700975 if (err == MZ_OK)
976 err = mz_stream_write_uint16(stream, file_info->internal_fa);
977 if (err == MZ_OK)
978 err = mz_stream_write_uint32(stream, file_info->external_fa);
979 if (err == MZ_OK)
980 {
981 if (file_info->disk_offset >= UINT32_MAX)
982 err = mz_stream_write_uint32(stream, UINT32_MAX);
983 else
984 err = mz_stream_write_uint32(stream, (uint32_t)file_info->disk_offset);
985 }
986 }
987
988 if (err == MZ_OK)
989 {
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -0700990 if (mz_stream_write(stream, file_info->filename, filename_length) != filename_length)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700991 err = MZ_STREAM_ERROR;
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -0700992 if (err == MZ_OK)
993 {
Nathan Moinvaziri240b3b62018-05-02 13:38:14 -0700994 // Ensure that directories have a slash appended to them for compatibility
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -0700995 if (mz_zip_attrib_is_dir(file_info->external_fa, file_info->version_madeby) == MZ_OK)
996 err = mz_stream_write_uint8(stream, '/');
997 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700998 }
999 if (err == MZ_OK)
1000 {
1001 if (mz_stream_write(stream, file_info->extrafield, file_info->extrafield_size) != file_info->extrafield_size)
1002 err = MZ_STREAM_ERROR;
1003 }
1004 // Add ZIP64 extra info header to central directory
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001005 if ((err == MZ_OK) && (zip64))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001006 {
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07001007 err = mz_stream_write_uint16(stream, MZ_ZIP_EXTENSION_ZIP64);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001008 if (err == MZ_OK)
1009 err = mz_stream_write_uint16(stream, extrafield_zip64_size);
1010 if ((err == MZ_OK) && (file_info->uncompressed_size >= UINT32_MAX))
1011 err = mz_stream_write_uint64(stream, file_info->uncompressed_size);
1012 if ((err == MZ_OK) && (file_info->compressed_size >= UINT32_MAX))
1013 err = mz_stream_write_uint64(stream, file_info->compressed_size);
1014 if ((err == MZ_OK) && (file_info->disk_offset >= UINT32_MAX))
1015 err = mz_stream_write_uint64(stream, file_info->disk_offset);
1016 }
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001017 // Write NTFS timestamps
1018 if ((err == MZ_OK) && (extrafield_ntfs_size > 0))
1019 {
1020 err = mz_stream_write_uint16(stream, MZ_ZIP_EXTENSION_NTFS);
1021 if (err == MZ_OK)
1022 err = mz_stream_write_uint16(stream, extrafield_ntfs_size);
1023 if (err == MZ_OK)
1024 err = mz_stream_write_uint32(stream, reserved);
1025 if (err == MZ_OK)
1026 err = mz_stream_write_uint16(stream, 0x01);
1027 if (err == MZ_OK)
1028 err = mz_stream_write_uint16(stream, extrafield_ntfs_size - 8);
juanii3679a3d2018-02-11 13:55:38 -03001029 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001030 {
1031 mz_zip_unix_to_ntfs_time(file_info->modified_date, &ntfs_time);
1032 err = mz_stream_write_uint64(stream, ntfs_time);
1033 }
juanii3679a3d2018-02-11 13:55:38 -03001034 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001035 {
1036 mz_zip_unix_to_ntfs_time(file_info->accessed_date, &ntfs_time);
1037 err = mz_stream_write_uint64(stream, ntfs_time);
1038 }
juanii3679a3d2018-02-11 13:55:38 -03001039 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001040 {
1041 mz_zip_unix_to_ntfs_time(file_info->creation_date, &ntfs_time);
1042 err = mz_stream_write_uint64(stream, ntfs_time);
1043 }
1044 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001045#ifdef HAVE_AES
1046 // Write AES extra info header to central directory
1047 if ((err == MZ_OK) && (file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
1048 {
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07001049 err = mz_stream_write_uint16(stream, MZ_ZIP_EXTENSION_AES);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001050 if (err == MZ_OK)
1051 err = mz_stream_write_uint16(stream, 7);
1052 if (err == MZ_OK)
1053 err = mz_stream_write_uint16(stream, file_info->aes_version);
1054 if (err == MZ_OK)
1055 err = mz_stream_write_uint8(stream, 'A');
1056 if (err == MZ_OK)
1057 err = mz_stream_write_uint8(stream, 'E');
1058 if (err == MZ_OK)
1059 err = mz_stream_write_uint8(stream, file_info->aes_encryption_mode);
1060 if (err == MZ_OK)
1061 err = mz_stream_write_uint16(stream, file_info->compression_method);
1062 }
1063#endif
1064 if ((err == MZ_OK) && (file_info->comment != NULL))
1065 {
1066 if (mz_stream_write(stream, file_info->comment, file_info->comment_size) != MZ_OK)
1067 err = MZ_STREAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001068 }
1069
1070 return err;
1071}
1072
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001073static int32_t mz_zip_entry_open_int(void *handle, int16_t compression_method, int16_t compress_level, const char *password)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001074{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001075 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001076 int64_t max_total_in = 0;
Nathan Moinvazirifa8105b2018-07-08 19:05:00 -07001077 int64_t header_size = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001078 int64_t footer_size = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001079 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001080
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001081 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001082 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001083
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001084 zip->compression_method = compression_method;
1085
1086 switch (zip->compression_method)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001087 {
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001088 case MZ_COMPRESS_METHOD_RAW:
1089 case MZ_COMPRESS_METHOD_DEFLATE:
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001090#ifdef HAVE_BZIP2
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001091 case MZ_COMPRESS_METHOD_BZIP2:
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001092#endif
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001093#if HAVE_LZMA
1094 case MZ_COMPRESS_METHOD_LZMA:
1095#endif
1096 err = MZ_OK;
1097 break;
1098 default:
1099 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001100 }
1101
Nathan Moinvaziri0f09a002018-04-23 18:48:30 -07001102 if ((zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED) && (password == NULL) && (zip->compression_method != MZ_COMPRESS_METHOD_RAW))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001103 return MZ_PARAM_ERROR;
1104
Nathan Moinvaziri0f09a002018-04-23 18:48:30 -07001105 if ((err == MZ_OK) && (zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED) && (zip->compression_method != MZ_COMPRESS_METHOD_RAW))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001106 {
1107#ifdef HAVE_AES
1108 if (zip->file_info.aes_version)
1109 {
1110 mz_stream_aes_create(&zip->crypt_stream);
1111 mz_stream_aes_set_password(zip->crypt_stream, password);
1112 mz_stream_aes_set_encryption_mode(zip->crypt_stream, zip->file_info.aes_encryption_mode);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001113 }
1114 else
1115#endif
1116 {
Nathan Moinvaziri0e5c9dc2018-05-23 20:21:48 -07001117#ifdef HAVE_PKCRYPT
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001118 uint8_t verify1 = 0;
1119 uint8_t verify2 = 0;
1120
1121 // Info-ZIP modification to ZipCrypto format:
1122 // If bit 3 of the general purpose bit flag is set, it uses high byte of 16-bit File Time.
1123
Nathan Moinvaziri18a30652017-12-07 06:59:53 -08001124 if (zip->file_info.flag & MZ_ZIP_FLAG_DATA_DESCRIPTOR)
1125 {
1126 uint32_t dos_date = 0;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001127
Nathan Moinvaziri18a30652017-12-07 06:59:53 -08001128 dos_date = mz_zip_time_t_to_dos_date(zip->file_info.modified_date);
1129
1130 verify1 = (uint8_t)((dos_date >> 16) & 0xff);
1131 verify2 = (uint8_t)((dos_date >> 8) & 0xff);
1132 }
1133 else
1134 {
1135 verify1 = (uint8_t)((zip->file_info.crc >> 16) & 0xff);
1136 verify2 = (uint8_t)((zip->file_info.crc >> 24) & 0xff);
1137 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001138
Nathan Moinvaziri0e5c9dc2018-05-23 20:21:48 -07001139 mz_stream_pkcrypt_create(&zip->crypt_stream);
1140 mz_stream_pkcrypt_set_password(zip->crypt_stream, password);
1141 mz_stream_pkcrypt_set_verify(zip->crypt_stream, verify1, verify2);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001142#endif
1143 }
1144 }
1145
1146 if (err == MZ_OK)
1147 {
1148 if (zip->crypt_stream == NULL)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001149 mz_stream_raw_create(&zip->crypt_stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001150
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001151 mz_stream_set_base(zip->crypt_stream, zip->stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001152
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001153 err = mz_stream_open(zip->crypt_stream, NULL, zip->open_mode);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001154 }
1155
1156 if (err == MZ_OK)
1157 {
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001158 if (zip->compression_method == MZ_COMPRESS_METHOD_RAW)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001159 mz_stream_raw_create(&zip->compress_stream);
Andy Maloney3909c232018-05-22 09:02:09 -04001160#ifdef HAVE_ZLIB
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001161 else if (zip->compression_method == MZ_COMPRESS_METHOD_DEFLATE)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001162 mz_stream_zlib_create(&zip->compress_stream);
Andy Maloney3909c232018-05-22 09:02:09 -04001163#endif
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001164#ifdef HAVE_BZIP2
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001165 else if (zip->compression_method == MZ_COMPRESS_METHOD_BZIP2)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001166 mz_stream_bzip_create(&zip->compress_stream);
1167#endif
1168#ifdef HAVE_LZMA
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001169 else if (zip->compression_method == MZ_COMPRESS_METHOD_LZMA)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001170 mz_stream_lzma_create(&zip->compress_stream);
1171#endif
1172 else
1173 err = MZ_PARAM_ERROR;
1174 }
1175
1176 if (err == MZ_OK)
1177 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001178 if (zip->open_mode & MZ_OPEN_MODE_WRITE)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001179 {
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001180 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_COMPRESS_LEVEL, compress_level);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001181 }
1182 else
1183 {
Nathan Moinvaziri5e8f4d72018-03-15 10:43:18 -07001184 if (zip->compression_method == MZ_COMPRESS_METHOD_RAW || zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001185 {
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001186 max_total_in = zip->file_info.compressed_size;
Nathan Moinvazirifa8105b2018-07-08 19:05:00 -07001187 mz_stream_set_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_TOTAL_IN_MAX, max_total_in);
1188
1189 if (mz_stream_get_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_HEADER_SIZE, &header_size) == MZ_OK)
1190 max_total_in -= header_size;
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001191 if (mz_stream_get_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_FOOTER_SIZE, &footer_size) == MZ_OK)
1192 max_total_in -= footer_size;
Nathan Moinvazirifa8105b2018-07-08 19:05:00 -07001193
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001194 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_IN_MAX, max_total_in);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001195 }
Nathan Moinvaziri5e8f4d72018-03-15 10:43:18 -07001196 if (zip->compression_method == MZ_COMPRESS_METHOD_LZMA && (zip->file_info.flag & MZ_ZIP_FLAG_LZMA_EOS_MARKER) == 0)
juanii55bcdaf2018-02-11 20:55:57 -03001197 {
1198 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_IN_MAX, zip->file_info.compressed_size);
1199 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_OUT_MAX, zip->file_info.uncompressed_size);
1200 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001201 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001202
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001203 mz_stream_set_base(zip->compress_stream, zip->crypt_stream);
1204
1205 err = mz_stream_open(zip->compress_stream, NULL, zip->open_mode);
1206 }
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001207 if (err == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001208 {
1209 mz_stream_crc32_create(&zip->crc32_stream);
Nathan Moinvaziri4f6a0e32017-11-10 08:45:14 -08001210#ifdef HAVE_ZLIB
1211 mz_stream_crc32_set_update_func(zip->crc32_stream,
1212 (mz_stream_crc32_update)mz_stream_zlib_get_crc32_update());
1213#elif defined(HAVE_LZMA)
1214 mz_stream_crc32_set_update_func(zip->crc32_stream,
1215 (mz_stream_crc32_update)mz_stream_lzma_get_crc32_update());
1216#else
1217 #error ZLIB or LZMA required for CRC32
1218#endif
1219
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001220 mz_stream_set_base(zip->crc32_stream, zip->compress_stream);
1221
1222 err = mz_stream_open(zip->crc32_stream, NULL, zip->open_mode);
1223 }
1224
1225 if (err == MZ_OK)
1226 {
1227 zip->entry_opened = 1;
1228 }
1229
1230 return err;
1231}
1232
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001233extern int32_t mz_zip_entry_read_open(void *handle, int16_t raw, const char *password)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001234{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001235 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001236 int16_t compression_method = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001237 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001238
Nathan Moinvaziri0e5c9dc2018-05-23 20:21:48 -07001239#if !defined(HAVE_PKCRYPT) && !defined(HAVE_AES)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001240 if (password != NULL)
1241 return MZ_PARAM_ERROR;
1242#endif
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001243 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001244 return MZ_PARAM_ERROR;
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001245 if ((zip->open_mode & MZ_OPEN_MODE_READ) == 0)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001246 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001247 if (zip->entry_scanned == 0)
1248 return MZ_PARAM_ERROR;
Nathan Moinvaziri0f09a002018-04-23 18:48:30 -07001249 if ((zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED) && (password == NULL) && (!raw))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001250 return MZ_PARAM_ERROR;
1251
Nathan Moinvazirifd039e32017-10-22 14:40:39 -07001252 if (zip->file_info.disk_number == zip->disk_number_with_cd)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001253 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
1254 else
Nathan Moinvazirifd039e32017-10-22 14:40:39 -07001255 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, zip->file_info.disk_number);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001256
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001257 err = mz_stream_seek(zip->stream, zip->file_info.disk_offset, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001258 if (err == MZ_OK)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001259 err = mz_zip_entry_read_header(zip->stream, 1, &zip->local_file_info, zip->local_file_info_stream);
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001260
Nathan Moinvaziri51f72502017-10-26 10:15:21 -07001261 compression_method = zip->file_info.compression_method;
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001262 if (raw)
1263 compression_method = MZ_COMPRESS_METHOD_RAW;
1264
Nathan Moinvaziri0a9282d2018-06-19 11:59:07 -07001265#ifdef MZ_ZIP_COMPRESS_ONLY
1266 if (compression_method != MZ_COMPRESS_METHOD_RAW)
1267 err = MZ_SUPPORT_ERROR;
1268#endif
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001269 if (err == MZ_OK)
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001270 err = mz_zip_entry_open_int(handle, compression_method, 0, password);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001271
1272 return err;
1273}
1274
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -07001275extern int32_t mz_zip_entry_write_open(void *handle, const mz_zip_file *file_info, int16_t compress_level, const char *password)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001276{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001277 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001278 int64_t disk_number = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001279 int32_t err = MZ_OK;
Nathan Moinvaziri51bf64a2017-10-20 14:23:37 -07001280 int16_t compression_method = 0;
1281
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001282
Nathan Moinvaziri0e5c9dc2018-05-23 20:21:48 -07001283#if !defined(HAVE_PKCRYPT) && !defined(HAVE_AES)
tz-lomb1b25802017-11-10 15:03:02 +03001284 if (password != NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001285 return MZ_PARAM_ERROR;
1286#endif
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001287 if (zip == NULL || file_info == NULL || file_info->filename == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001288 return MZ_PARAM_ERROR;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001289
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001290 if (zip->entry_opened == 1)
1291 {
1292 err = mz_zip_entry_close(handle);
1293 if (err != MZ_OK)
1294 return err;
1295 }
1296
1297 memcpy(&zip->file_info, file_info, sizeof(mz_zip_file));
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001298
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001299 compression_method = zip->file_info.compression_method;
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001300
1301 if (compression_method == MZ_COMPRESS_METHOD_DEFLATE)
1302 {
1303 if ((compress_level == 8) || (compress_level == 9))
1304 zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_MAX;
1305 if (compress_level == 2)
1306 zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_FAST;
1307 if (compress_level == 1)
1308 zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_SUPER_FAST;
1309 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001310#ifdef HAVE_LZMA
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001311 else if (compression_method == MZ_COMPRESS_METHOD_LZMA)
1312 zip->file_info.flag |= MZ_ZIP_FLAG_LZMA_EOS_MARKER;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001313#endif
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001314
1315 zip->file_info.flag |= MZ_ZIP_FLAG_DATA_DESCRIPTOR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001316
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001317 if (password != NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001318 zip->file_info.flag |= MZ_ZIP_FLAG_ENCRYPTED;
1319 else
1320 zip->file_info.flag &= ~MZ_ZIP_FLAG_ENCRYPTED;
1321
juaniib8887e92018-02-14 00:51:05 -03001322 mz_stream_get_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, &disk_number);
1323 zip->file_info.disk_number = (uint32_t)disk_number;
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001324
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001325 zip->file_info.disk_offset = mz_stream_tell(zip->stream);
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001326 zip->file_info.crc = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001327 zip->file_info.compressed_size = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001328
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001329#ifdef HAVE_AES
1330 if (zip->file_info.aes_version && zip->file_info.aes_encryption_mode == 0)
1331 zip->file_info.aes_encryption_mode = MZ_AES_ENCRYPTION_MODE_256;
1332#endif
1333
Nathan Moinvaziri4827f712018-05-02 10:50:47 -07001334 if ((compress_level == 0) || (mz_zip_attrib_is_dir(zip->file_info.external_fa, zip->file_info.version_madeby) == MZ_OK))
Viktor Szakats2884e672018-04-30 08:12:13 +00001335 compression_method = MZ_COMPRESS_METHOD_RAW;
1336
Nathan Moinvaziri0a9282d2018-06-19 11:59:07 -07001337#ifdef MZ_ZIP_DECOMPRESS_ONLY
1338 if (compression_method != MZ_COMPRESS_METHOD_RAW)
1339 err = MZ_SUPPORT_ERROR;
1340#endif
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001341 if (err == MZ_OK)
1342 err = mz_zip_entry_write_header(zip->stream, 1, &zip->file_info);
1343 if (err == MZ_OK)
Nathan Moinvaziri51bf64a2017-10-20 14:23:37 -07001344 err = mz_zip_entry_open_int(handle, compression_method, compress_level, password);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001345
1346 return err;
1347}
1348
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001349extern int32_t mz_zip_entry_read(void *handle, void *buf, uint32_t len)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001350{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001351 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001352 int32_t read = 0;
1353
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001354 if (zip == NULL || zip->entry_opened == 0)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001355 return MZ_PARAM_ERROR;
Nathan Moinvaziri930f9182018-01-22 08:51:07 -08001356 if (UINT_MAX == UINT16_MAX && len > UINT16_MAX) // Zlib limitation
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001357 return MZ_PARAM_ERROR;
Nathan Moinvaziria86a4352018-01-02 13:12:07 -08001358 if (len == 0 || zip->file_info.uncompressed_size == 0)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001359 return 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001360 read = mz_stream_read(zip->crc32_stream, buf, len);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001361 return read;
1362}
1363
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001364extern int32_t mz_zip_entry_write(void *handle, const void *buf, uint32_t len)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001365{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001366 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri0a9282d2018-06-19 11:59:07 -07001367 int32_t written = 0;
1368
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001369 if (zip == NULL || zip->entry_opened == 0)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001370 return MZ_PARAM_ERROR;
Nathan Moinvaziri0a9282d2018-06-19 11:59:07 -07001371 written = mz_stream_write(zip->crc32_stream, buf, len);
1372 return written;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001373}
1374
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001375extern int32_t mz_zip_entry_get_info(void *handle, mz_zip_file **file_info)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001376{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001377 mz_zip *zip = (mz_zip *)handle;
1378 if (zip == NULL || zip->entry_scanned == 0)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001379 return MZ_PARAM_ERROR;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001380 *file_info = &zip->file_info;
1381 return MZ_OK;
1382}
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001383
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001384extern int32_t mz_zip_entry_get_local_info(void *handle, mz_zip_file **local_file_info)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001385{
1386 mz_zip *zip = (mz_zip *)handle;
1387 if (zip == NULL || zip->entry_scanned == 0 || zip->entry_opened == 0)
1388 return MZ_PARAM_ERROR;
1389 *local_file_info = &zip->local_file_info;
1390 return MZ_OK;
1391}
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001392
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001393extern int32_t mz_zip_entry_close_raw(void *handle, uint64_t uncompressed_size, uint32_t crc32)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001394{
1395 mz_zip *zip = (mz_zip *)handle;
1396 uint64_t compressed_size = 0;
Nathan Moinvaziri8871f6c2018-07-08 19:23:56 -07001397 int64_t total_in = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001398 int32_t err = MZ_OK;
1399
1400 if (zip == NULL || zip->entry_opened == 0)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001401 return MZ_PARAM_ERROR;
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001402
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001403 mz_stream_close(zip->compress_stream);
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001404 if (crc32 == 0)
1405 crc32 = mz_stream_crc32_get_value(zip->crc32_stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001406
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001407 if ((zip->open_mode & MZ_OPEN_MODE_WRITE) == 0)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001408 {
1409#ifdef HAVE_AES
1410 // AES zip version AE-1 will expect a valid crc as well
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001411 if (zip->file_info.aes_version <= 0x0001)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001412#endif
1413 {
Nathan Moinvaziri8871f6c2018-07-08 19:23:56 -07001414 mz_stream_crc32_get_prop_int64(zip->crc32_stream, MZ_STREAM_PROP_TOTAL_IN, &total_in);
1415 if ((total_in > 0) && (zip->compression_method != MZ_COMPRESS_METHOD_RAW))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001416 {
1417 if (crc32 != zip->file_info.crc)
1418 err = MZ_CRC_ERROR;
1419 }
1420 }
1421 }
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001422
1423 mz_stream_get_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_OUT, (int64_t *)&compressed_size);
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001424 if ((zip->compression_method != MZ_COMPRESS_METHOD_RAW) || (uncompressed_size == 0))
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001425 mz_stream_get_prop_int64(zip->crc32_stream, MZ_STREAM_PROP_TOTAL_OUT, (int64_t *)&uncompressed_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001426
1427 if (zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED)
1428 {
1429 mz_stream_set_base(zip->crypt_stream, zip->stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001430 err = mz_stream_close(zip->crypt_stream);
1431
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001432 mz_stream_get_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_TOTAL_OUT, (int64_t *)&compressed_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001433 }
1434
1435 mz_stream_delete(&zip->crypt_stream);
1436
1437 mz_stream_delete(&zip->compress_stream);
1438 mz_stream_crc32_delete(&zip->crc32_stream);
1439
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001440 if (zip->open_mode & MZ_OPEN_MODE_WRITE)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001441 {
1442 if (err == MZ_OK)
1443 {
1444 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_DATADESCRIPTOR);
1445 if (err == MZ_OK)
1446 err = mz_stream_write_uint32(zip->stream, crc32);
1447 if (err == MZ_OK)
1448 {
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001449 if (zip->file_info.uncompressed_size <= UINT32_MAX)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001450 err = mz_stream_write_uint32(zip->stream, (uint32_t)compressed_size);
Andrew Gunnerson3c52d212018-01-05 22:28:12 -05001451 else
1452 err = mz_stream_write_uint64(zip->stream, compressed_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001453 }
1454 if (err == MZ_OK)
1455 {
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001456 if (zip->file_info.uncompressed_size <= UINT32_MAX)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001457 err = mz_stream_write_uint32(zip->stream, (uint32_t)uncompressed_size);
Andrew Gunnerson3c52d212018-01-05 22:28:12 -05001458 else
1459 err = mz_stream_write_uint64(zip->stream, uncompressed_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001460 }
1461 }
1462
1463 zip->file_info.crc = crc32;
1464 zip->file_info.compressed_size = compressed_size;
1465 zip->file_info.uncompressed_size = uncompressed_size;
1466
1467 if (err == MZ_OK)
Nathan Moinvaziricda36002017-10-21 09:37:18 -07001468 err = mz_zip_entry_write_header(zip->cd_mem_stream, 0, &zip->file_info);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001469
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07001470 zip->number_entry += 1;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001471 }
1472
1473 zip->entry_opened = 0;
1474
1475 return err;
1476}
1477
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001478extern int32_t mz_zip_entry_close(void *handle)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001479{
1480 return mz_zip_entry_close_raw(handle, 0, 0);
1481}
1482
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001483static int32_t mz_zip_goto_next_entry_int(void *handle)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001484{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001485 mz_zip *zip = (mz_zip *)handle;
1486 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001487
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001488 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001489 return MZ_PARAM_ERROR;
1490
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001491 zip->entry_scanned = 0;
1492
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001493 mz_stream_set_prop_int64(zip->cd_stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
Viktor Szakats915b82e2018-04-24 10:02:39 +00001494
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001495 err = mz_stream_seek(zip->cd_stream, zip->cd_current_pos, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001496 if (err == MZ_OK)
Nathan Moinvaziricda36002017-10-21 09:37:18 -07001497 err = mz_zip_entry_read_header(zip->cd_stream, 0, &zip->file_info, zip->file_info_stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001498 if (err == MZ_OK)
1499 zip->entry_scanned = 1;
1500 return err;
1501}
1502
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001503extern int32_t mz_zip_get_number_entry(void *handle, int64_t *number_entry)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07001504{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001505 mz_zip *zip = (mz_zip *)handle;
1506 if (zip == NULL || number_entry == NULL)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07001507 return MZ_PARAM_ERROR;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07001508 *number_entry = zip->number_entry;
1509 return MZ_OK;
1510}
1511
Viktor Szakats0f0535f2018-05-02 13:15:58 +00001512extern int32_t mz_zip_get_disk_number_with_cd(void *handle, uint32_t *disk_number_with_cd)
juanii566b9782018-02-10 15:07:54 -03001513{
1514 mz_zip *zip = (mz_zip *)handle;
1515 if (zip == NULL || disk_number_with_cd == NULL)
1516 return MZ_PARAM_ERROR;
1517 *disk_number_with_cd = zip->disk_number_with_cd;
1518 return MZ_OK;
1519}
1520
Viktor Szakatsce26dba2018-04-22 19:30:34 +00001521extern int64_t mz_zip_get_entry(void *handle)
1522{
1523 mz_zip *zip = (mz_zip *)handle;
1524
1525 if (zip == NULL)
1526 return MZ_PARAM_ERROR;
1527
1528 return zip->cd_current_pos;
1529}
1530
1531extern int32_t mz_zip_goto_entry(void *handle, uint64_t cd_pos)
1532{
1533 mz_zip *zip = (mz_zip *)handle;
1534
1535 if (zip == NULL)
1536 return MZ_PARAM_ERROR;
1537
1538 if (cd_pos < zip->cd_start_pos || cd_pos > zip->cd_start_pos + zip->cd_size)
1539 return MZ_PARAM_ERROR;
1540
1541 zip->cd_current_pos = cd_pos;
1542
1543 return mz_zip_goto_next_entry_int(handle);
1544}
1545
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001546extern int32_t mz_zip_goto_first_entry(void *handle)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001547{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001548 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001549
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001550 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001551 return MZ_PARAM_ERROR;
1552
Nathan Moinvaziricda36002017-10-21 09:37:18 -07001553 zip->cd_current_pos = zip->cd_start_pos;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001554
1555 return mz_zip_goto_next_entry_int(handle);
1556}
1557
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001558extern int32_t mz_zip_goto_next_entry(void *handle)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001559{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001560 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001561
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001562 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001563 return MZ_PARAM_ERROR;
1564
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001565 zip->cd_current_pos += MZ_ZIP_SIZE_CD_ITEM + zip->file_info.filename_size +
1566 zip->file_info.extrafield_size + zip->file_info.comment_size;
1567
1568 return mz_zip_goto_next_entry_int(handle);
1569}
1570
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001571extern int32_t mz_zip_locate_entry(void *handle, const char *filename, mz_filename_compare_cb filename_compare_cb)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001572{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001573 mz_zip *zip = (mz_zip *)handle;
1574 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001575 int32_t result = 0;
1576
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001577 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001578 return MZ_PARAM_ERROR;
1579
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001580 err = mz_zip_goto_first_entry(handle);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001581 while (err == MZ_OK)
1582 {
1583 if (filename_compare_cb != NULL)
1584 result = filename_compare_cb(handle, zip->file_info.filename, filename);
1585 else
1586 result = strcmp(zip->file_info.filename, filename);
1587
1588 if (result == 0)
1589 return MZ_OK;
1590
1591 err = mz_zip_goto_next_entry(handle);
1592 }
1593
1594 return err;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001595}
1596
1597/***************************************************************************/
1598
Nathan Moinvaziribb3b75b2018-05-02 10:48:51 -07001599int32_t mz_zip_attrib_is_dir(int32_t attributes, int32_t version_madeby)
1600{
1601 int32_t host_system = (uint8_t)(version_madeby >> 8);
1602
1603 if (host_system == MZ_HOST_SYSTEM_MSDOS || host_system == MZ_HOST_SYSTEM_WINDOWS_NTFS)
1604 {
1605 if ((attributes & 0x10) == 0x10) // FILE_ATTRIBUTE_DIRECTORY
1606 return MZ_OK;
1607 }
1608 else if (host_system == MZ_HOST_SYSTEM_UNIX || host_system == MZ_HOST_SYSTEM_OSX_DARWIN)
1609 {
1610 if ((attributes & 00170000) == 0040000) // S_ISDIR
1611 return MZ_OK;
1612 }
1613
1614 return MZ_EXIST_ERROR;
1615}
1616
1617/***************************************************************************/
1618
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001619static int32_t mz_zip_invalid_date(const struct tm *ptm)
1620{
1621#define datevalue_in_range(min, max, value) ((min) <= (value) && (value) <= (max))
Viktor Szakatsce26dba2018-04-22 19:30:34 +00001622 return (!datevalue_in_range(0, 127 + 80, ptm->tm_year) || // 1980-based year, allow 80 extra
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001623 !datevalue_in_range(0, 11, ptm->tm_mon) ||
1624 !datevalue_in_range(1, 31, ptm->tm_mday) ||
1625 !datevalue_in_range(0, 23, ptm->tm_hour) ||
1626 !datevalue_in_range(0, 59, ptm->tm_min) ||
1627 !datevalue_in_range(0, 59, ptm->tm_sec));
1628#undef datevalue_in_range
1629}
1630
1631static void mz_zip_dosdate_to_raw_tm(uint64_t dos_date, struct tm *ptm)
1632{
1633 uint64_t date = (uint64_t)(dos_date >> 16);
1634
1635 ptm->tm_mday = (uint16_t)(date & 0x1f);
1636 ptm->tm_mon = (uint16_t)(((date & 0x1E0) / 0x20) - 1);
1637 ptm->tm_year = (uint16_t)(((date & 0x0FE00) / 0x0200) + 80);
1638 ptm->tm_hour = (uint16_t)((dos_date & 0xF800) / 0x800);
1639 ptm->tm_min = (uint16_t)((dos_date & 0x7E0) / 0x20);
1640 ptm->tm_sec = (uint16_t)(2 * (dos_date & 0x1f));
1641 ptm->tm_isdst = -1;
1642}
1643
1644int32_t mz_zip_dosdate_to_tm(uint64_t dos_date, struct tm *ptm)
1645{
1646 if (ptm == NULL)
1647 return MZ_PARAM_ERROR;
1648
1649 mz_zip_dosdate_to_raw_tm(dos_date, ptm);
1650
1651 if (mz_zip_invalid_date(ptm))
1652 {
1653 // Invalid date stored, so don't return it
1654 memset(ptm, 0, sizeof(struct tm));
1655 return MZ_FORMAT_ERROR;
1656 }
1657 return MZ_OK;
1658}
1659
1660time_t mz_zip_dosdate_to_time_t(uint64_t dos_date)
1661{
1662 struct tm ptm;
1663 mz_zip_dosdate_to_raw_tm(dos_date, &ptm);
1664 return mktime(&ptm);
1665}
1666
1667int32_t mz_zip_time_t_to_tm(time_t unix_time, struct tm *ptm)
1668{
Nathan Moinvaziri17fcdcd2017-10-26 08:13:13 -07001669 struct tm *ltm = NULL;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001670 if (ptm == NULL)
1671 return MZ_PARAM_ERROR;
Viktor Szakatsce26dba2018-04-22 19:30:34 +00001672 ltm = localtime(&unix_time); // Returns a 1900-based year
Nathan Moinvaziri17fcdcd2017-10-26 08:13:13 -07001673 if (ltm == NULL)
Viktor Szakatsce26dba2018-04-22 19:30:34 +00001674 {
1675 // Invalid date stored, so don't return it
1676 memset(ptm, 0, sizeof(struct tm));
Nathan Moinvaziri17fcdcd2017-10-26 08:13:13 -07001677 return MZ_INTERNAL_ERROR;
Viktor Szakatsce26dba2018-04-22 19:30:34 +00001678 }
Nathan Moinvaziri17fcdcd2017-10-26 08:13:13 -07001679 memcpy(ptm, ltm, sizeof(struct tm));
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001680 return MZ_OK;
1681}
1682
1683uint32_t mz_zip_time_t_to_dos_date(time_t unix_time)
1684{
1685 struct tm ptm;
1686 mz_zip_time_t_to_tm(unix_time, &ptm);
1687 return mz_zip_tm_to_dosdate((const struct tm *)&ptm);
1688}
1689
1690uint32_t mz_zip_tm_to_dosdate(const struct tm *ptm)
1691{
Nathan Moinvazirie33916b2018-05-01 13:45:08 -07001692 struct tm fixed_tm;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001693
1694 // Years supported:
1695
1696 // [00, 79] (assumed to be between 2000 and 2079)
1697 // [80, 207] (assumed to be between 1980 and 2107, typical output of old
1698 // software that does 'year-1900' to get a double digit year)
1699 // [1980, 2107] (due to format limitations, only years 1980-2107 can be stored.)
1700
1701 memcpy(&fixed_tm, ptm, sizeof(struct tm));
1702 if (fixed_tm.tm_year >= 1980) // range [1980, 2107]
1703 fixed_tm.tm_year -= 1980;
Viktor Szakatsce26dba2018-04-22 19:30:34 +00001704 else if (fixed_tm.tm_year >= 80) // range [80, 207]
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001705 fixed_tm.tm_year -= 80;
1706 else // range [00, 79]
1707 fixed_tm.tm_year += 20;
1708
Viktor Szakatsce26dba2018-04-22 19:30:34 +00001709 if (mz_zip_invalid_date(&fixed_tm))
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001710 return 0;
1711
1712 return (uint32_t)(((fixed_tm.tm_mday) + (32 * (fixed_tm.tm_mon + 1)) + (512 * fixed_tm.tm_year)) << 16) |
1713 ((fixed_tm.tm_sec / 2) + (32 * fixed_tm.tm_min) + (2048 * (uint32_t)fixed_tm.tm_hour));
1714}
1715
1716int32_t mz_zip_ntfs_to_unix_time(uint64_t ntfs_time, time_t *unix_time)
1717{
1718 *unix_time = (time_t)((ntfs_time - 116444736000000000LL) / 10000000);
1719 return MZ_OK;
1720}
1721
1722int32_t mz_zip_unix_to_ntfs_time(time_t unix_time, uint64_t *ntfs_time)
1723{
1724 *ntfs_time = ((uint64_t)unix_time * 10000000) + 116444736000000000LL;
1725 return MZ_OK;
1726}