blob: 4a7fb916885b1829bf72e8ee583503591c6c78a3 [file] [log] [blame]
Antoine Cœur0c2fceb2017-10-05 15:30:37 +08001/* zip.c -- Zip manipulation
Nathan Moinvaziriffa830f2018-07-11 16:34:01 -07002 Version 2.3.6, July 11, 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
Nathan Moinvazirib47b41b2018-07-11 09:44:29 -070033#include "mz_strm_crc32.h"
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080034#ifdef HAVE_LZMA
35# include "mz_strm_lzma.h"
36#endif
Nathan Moinvazirib47b41b2018-07-11 09:44:29 -070037#include "mz_strm_mem.h"
Nathan Moinvaziri0e5c9dc2018-05-23 20:21:48 -070038#ifdef HAVE_PKCRYPT
39# include "mz_strm_pkcrypt.h"
40#endif
Nathan Moinvaziri4f6a0e32017-11-10 08:45:14 -080041#ifdef HAVE_ZLIB
42# include "mz_strm_zlib.h"
43#endif
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080044
45#include "mz_zip.h"
46
47/***************************************************************************/
48
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070049#define MZ_ZIP_MAGIC_LOCALHEADER (0x04034b50)
50#define MZ_ZIP_MAGIC_CENTRALHEADER (0x02014b50)
51#define MZ_ZIP_MAGIC_ENDHEADER (0x06054b50)
52#define MZ_ZIP_MAGIC_ENDHEADER64 (0x06064b50)
53#define MZ_ZIP_MAGIC_ENDLOCHEADER64 (0x07064b50)
54#define MZ_ZIP_MAGIC_DATADESCRIPTOR (0x08074b50)
55
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -070056#define MZ_ZIP_SIZE_CD_ITEM (0x2e)
57#define MZ_ZIP_SIZE_CD_LOCATOR64 (0x14)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080058
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -070059#define MZ_ZIP_EXTENSION_ZIP64 (0x0001)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -070060#define MZ_ZIP_EXTENSION_NTFS (0x000a)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -070061#define MZ_ZIP_EXTENSION_AES (0x9901)
62
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080063/***************************************************************************/
64
65typedef struct mz_zip_s
66{
67 mz_zip_file file_info;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070068 mz_zip_file local_file_info;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080069
70 void *stream; // main stream
Nathan Moinvaziricda36002017-10-21 09:37:18 -070071 void *cd_stream; // pointer to the stream with the cd
72 void *cd_mem_stream; // memory stream for central directory
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080073 void *compress_stream; // compression stream
74 void *crc32_stream; // crc32 stream
75 void *crypt_stream; // encryption stream
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070076 void *file_info_stream; // memory stream for storing file info
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -070077 void *local_file_info_stream; // memory stream for storing local file info
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080078
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070079 int32_t open_mode;
80
Nathan Moinvazirifd039e32017-10-22 14:40:39 -070081 uint32_t disk_number_with_cd; // number of the disk with the central dir
Nathan Moinvaziriee614ff2018-07-12 12:36:33 -070082 uint64_t disk_offset_shift; // correction for zips that have wrong offset start of cd
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070083
Nathan Moinvaziricda36002017-10-21 09:37:18 -070084 uint64_t cd_start_pos; // pos of the first file in the central dir stream
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070085 uint64_t cd_current_pos; // pos of the current file in the central dir
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070086 uint64_t cd_offset; // offset of start of central directory
87 uint64_t cd_size; // size of the central directory
88
Viktor Szakats915b82e2018-04-24 10:02:39 +000089 uint16_t entry_scanned;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070090 uint16_t entry_opened; // 1 if a file in the zip is currently writ.
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070091
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -070092 int64_t number_entry;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -070093
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -070094 int16_t compression_method;
95
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -070096 uint16_t version_madeby;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -070097 char *comment;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080098} mz_zip;
99
100/***************************************************************************/
101
102// Locate the central directory of a zip file (at the end, just before the global comment)
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700103static int32_t mz_zip_search_eocd(void *stream, uint64_t *central_pos)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800104{
Nathan Moinvaziri9ab31ba2017-10-16 07:51:09 -0700105 uint8_t buf[1024 + 4];
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700106 int64_t file_size = 0;
107 int64_t back_read = 0;
108 int64_t max_back = UINT16_MAX; // maximum size of global comment
109 int32_t read_size = sizeof(buf);
110 int64_t read_pos = 0;
111 int32_t i = 0;
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700112
Nathan Moinvazirif6e81cd2017-10-10 18:24:03 -0700113 *central_pos = 0;
114
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700115 if (mz_stream_seek(stream, 0, MZ_SEEK_END) != MZ_OK)
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700116 return MZ_STREAM_ERROR;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800117
118 file_size = mz_stream_tell(stream);
119
120 if (max_back > file_size)
121 max_back = file_size;
122
123 while (back_read < max_back)
124 {
Nathan Moinvaziri9ab31ba2017-10-16 07:51:09 -0700125 back_read += (sizeof(buf) - 4);
126 if (back_read > max_back)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800127 back_read = max_back;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800128
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700129 read_pos = file_size - back_read;
Nathan Moinvaziri9ab31ba2017-10-16 07:51:09 -0700130 if (read_size > (file_size - read_pos))
131 read_size = (uint32_t)(file_size - read_pos);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800132
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700133 if (mz_stream_seek(stream, read_pos, MZ_SEEK_SET) != MZ_OK)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800134 break;
135 if (mz_stream_read(stream, buf, read_size) != read_size)
136 break;
137
138 for (i = read_size - 3; (i--) > 0;)
139 {
Nathan Moinvaziri9ab31ba2017-10-16 07:51:09 -0700140 if (((*(buf + i)) == (MZ_ZIP_MAGIC_ENDHEADER & 0xff)) &&
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700141 ((*(buf + i + 1)) == (MZ_ZIP_MAGIC_ENDHEADER >> 8 & 0xff)) &&
142 ((*(buf + i + 2)) == (MZ_ZIP_MAGIC_ENDHEADER >> 16 & 0xff)) &&
143 ((*(buf + i + 3)) == (MZ_ZIP_MAGIC_ENDHEADER >> 24 & 0xff)))
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800144 {
Nathan Moinvazirif6e81cd2017-10-10 18:24:03 -0700145 *central_pos = read_pos + i;
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700146 return MZ_OK;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800147 }
148 }
149
Nathan Moinvazirif6e81cd2017-10-10 18:24:03 -0700150 if (*central_pos != 0)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800151 break;
152 }
153
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700154 return MZ_EXIST_ERROR;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800155}
156
157// Locate the central directory 64 of a zip file (at the end, just before the global comment)
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700158static 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 +0800159{
160 uint64_t offset = 0;
161 uint32_t value32 = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700162 int32_t err = MZ_OK;
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700163
164
165 *central_pos = 0;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800166
167 // Zip64 end of central directory locator
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700168 err = mz_stream_seek(stream, end_central_offset - MZ_ZIP_SIZE_CD_LOCATOR64, MZ_SEEK_SET);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800169 // Read locator signature
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700170 if (err == MZ_OK)
171 {
172 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700173 if (value32 != MZ_ZIP_MAGIC_ENDLOCHEADER64)
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700174 err = MZ_FORMAT_ERROR;
175 }
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800176 // Number of the disk with the start of the zip64 end of central directory
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700177 if (err == MZ_OK)
178 err = mz_stream_read_uint32(stream, &value32);
179 // Relative offset of the zip64 end of central directory record8
180 if (err == MZ_OK)
181 err = mz_stream_read_uint64(stream, &offset);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800182 // Total number of disks
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700183 if (err == MZ_OK)
184 err = mz_stream_read_uint32(stream, &value32);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800185 // Goto end of central directory record
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700186 if (err == MZ_OK)
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700187 err = mz_stream_seek(stream, offset, MZ_SEEK_SET);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800188 // The signature
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700189 if (err == MZ_OK)
190 {
191 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700192 if (value32 != MZ_ZIP_MAGIC_ENDHEADER64)
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700193 err = MZ_FORMAT_ERROR;
194 }
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800195
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700196 if (err == MZ_OK)
197 *central_pos = offset;
198
199 return err;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800200}
201
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700202static int32_t mz_zip_read_cd(void *handle)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800203{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700204 mz_zip *zip = (mz_zip *)handle;
205 int64_t number_entry_cd = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700206 uint64_t number_entry_cd64 = 0;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800207 uint64_t number_entry = 0;
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700208 uint64_t eocd_pos = 0;
209 uint64_t eocd_pos64 = 0;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800210 uint16_t value16 = 0;
211 uint32_t value32 = 0;
212 uint64_t value64 = 0;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700213 uint16_t comment_size = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700214 int32_t err = MZ_OK;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800215
216
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800217 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700218 return MZ_PARAM_ERROR;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800219
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700220 // Read and cache central directory records
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700221 if (mz_zip_search_eocd(zip->stream, &eocd_pos) == MZ_OK)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800222 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700223 // Read end of central directory info
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700224 err = mz_stream_seek(zip->stream, eocd_pos, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700225 // The signature, already checked
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800226 if (err == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700227 err = mz_stream_read_uint32(zip->stream, &value32);
228 // Number of this disk
229 if (err == MZ_OK)
230 err = mz_stream_read_uint16(zip->stream, &value16);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700231 // Number of the disk with the start of the central directory
232 if (err == MZ_OK)
233 err = mz_stream_read_uint16(zip->stream, &value16);
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700234 zip->disk_number_with_cd = value16;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700235 // Total number of entries in the central dir on this disk
236 if (err == MZ_OK)
237 err = mz_stream_read_uint16(zip->stream, &value16);
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700238 zip->number_entry = value16;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700239 // Total number of entries in the central dir
240 if (err == MZ_OK)
241 err = mz_stream_read_uint16(zip->stream, &value16);
242 number_entry_cd = value16;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700243 if (number_entry_cd != zip->number_entry)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700244 err = MZ_FORMAT_ERROR;
245 // Size of the central directory
246 if (err == MZ_OK)
247 err = mz_stream_read_uint32(zip->stream, &value32);
248 if (err == MZ_OK)
249 zip->cd_size = value32;
250 // Offset of start of central directory with respect to the starting disk number
251 if (err == MZ_OK)
252 err = mz_stream_read_uint32(zip->stream, &value32);
Nathan Moinvaziriee614ff2018-07-12 12:36:33 -0700253 if (err == MZ_OK)
254 zip->cd_offset = value32;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700255 // Zip file global comment length
256 if (err == MZ_OK)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700257 err = mz_stream_read_uint16(zip->stream, &comment_size);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800258
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700259 if ((err == MZ_OK) && ((number_entry_cd == UINT16_MAX) || (zip->cd_offset == UINT32_MAX)))
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800260 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700261 // Format should be Zip64, as the central directory or file size is too large
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700262 if (mz_zip_search_zip64_eocd(zip->stream, eocd_pos, &eocd_pos64) == MZ_OK)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800263 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700264 eocd_pos = eocd_pos64;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700265
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700266 err = mz_stream_seek(zip->stream, eocd_pos, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700267 // The signature, already checked
268 if (err == MZ_OK)
269 err = mz_stream_read_uint32(zip->stream, &value32);
270 // Size of zip64 end of central directory record
271 if (err == MZ_OK)
272 err = mz_stream_read_uint64(zip->stream, &value64);
273 // Version made by
274 if (err == MZ_OK)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700275 err = mz_stream_read_uint16(zip->stream, &zip->version_madeby);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700276 // Version needed to extract
277 if (err == MZ_OK)
278 err = mz_stream_read_uint16(zip->stream, &value16);
279 // Number of this disk
280 if (err == MZ_OK)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700281 err = mz_stream_read_uint32(zip->stream, &value32);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700282 // Number of the disk with the start of the central directory
283 if (err == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700284 err = mz_stream_read_uint32(zip->stream, &zip->disk_number_with_cd);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700285 // Total number of entries in the central directory on this disk
286 if (err == MZ_OK)
287 err = mz_stream_read_uint64(zip->stream, &number_entry);
288 // Total number of entries in the central directory
289 if (err == MZ_OK)
290 err = mz_stream_read_uint64(zip->stream, &number_entry_cd64);
291 if (number_entry == UINT32_MAX)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700292 zip->number_entry = number_entry_cd64;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700293 // Size of the central directory
294 if (err == MZ_OK)
295 err = mz_stream_read_uint64(zip->stream, &zip->cd_size);
296 // Offset of start of central directory with respect to the starting disk number
297 if (err == MZ_OK)
298 err = mz_stream_read_uint64(zip->stream, &zip->cd_offset);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800299 }
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700300 else if ((zip->number_entry == UINT16_MAX) || (number_entry_cd != zip->number_entry) ||
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700301 (zip->cd_size == UINT16_MAX) || (zip->cd_offset == UINT32_MAX))
Nathan Moinvaziri016ad472017-10-09 23:36:30 -0700302 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700303 err = MZ_FORMAT_ERROR;
Nathan Moinvaziri016ad472017-10-09 23:36:30 -0700304 }
305 }
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800306 }
307
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700308 if (err == MZ_OK)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800309 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700310 if (eocd_pos < zip->cd_offset + zip->cd_size)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700311 err = MZ_FORMAT_ERROR;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800312 }
313
Nathan Moinvaziriee614ff2018-07-12 12:36:33 -0700314 if (err == MZ_OK)
315 {
316 // Verify central directory signature exists at offset
317 err = mz_stream_seek(zip->stream, zip->cd_offset, MZ_SEEK_SET);
318 if (err == MZ_OK)
319 err = mz_stream_read_uint32(zip->stream, &value32);
320 if (value32 != MZ_ZIP_MAGIC_CENTRALHEADER)
321 {
322 // If not found attempt to seek backward to find it
323 err = mz_stream_seek(zip->stream, eocd_pos - zip->cd_size, MZ_SEEK_SET);
324 if (err == MZ_OK)
325 err = mz_stream_read_uint32(zip->stream, &value32);
326 if (value32 == MZ_ZIP_MAGIC_CENTRALHEADER)
327 {
328 // If found compensate for incorrect locations
329 value64 = zip->cd_offset;
330 zip->cd_offset = eocd_pos - zip->cd_size;
331 zip->disk_offset_shift = zip->cd_offset - value64;
332 }
333 }
334 }
335
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700336 if ((err == MZ_OK) && (comment_size > 0))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700337 {
Nathan Moinvaziri5244fc02018-05-02 20:01:35 -0700338 zip->comment = (char *)MZ_ALLOC(comment_size + 1);
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700339 if (zip->comment)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700340 {
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700341 if (mz_stream_read(zip->stream, zip->comment, comment_size) != comment_size)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700342 err = MZ_STREAM_ERROR;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700343 zip->comment[comment_size] = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700344 }
345 }
346
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800347 return err;
348}
349
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700350static int32_t mz_zip_write_cd(void *handle)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800351{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700352 mz_zip *zip = (mz_zip *)handle;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800353 uint16_t comment_size = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700354 uint64_t zip64_eocd_pos_inzip = 0;
Nathan Moinvaziri016ad472017-10-09 23:36:30 -0700355 int64_t disk_number = 0;
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700356 int64_t disk_size = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700357 int32_t err = MZ_OK;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800358
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700359
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700360 if (zip == NULL)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800361 return MZ_PARAM_ERROR;
Viktor Szakats915b82e2018-04-24 10:02:39 +0000362
Nathan Moinvaziri016ad472017-10-09 23:36:30 -0700363 if (mz_stream_get_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, &disk_number) == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700364 zip->disk_number_with_cd = (uint32_t)disk_number;
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700365 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 -0700366 zip->disk_number_with_cd += 1;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700367 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800368
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700369 zip->cd_offset = mz_stream_tell(zip->stream);
370 mz_stream_seek(zip->cd_mem_stream, 0, MZ_SEEK_END);
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700371 zip->cd_size = (uint32_t)mz_stream_tell(zip->cd_mem_stream);
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700372 mz_stream_seek(zip->cd_mem_stream, 0, MZ_SEEK_SET);
Viktor Szakats915b82e2018-04-24 10:02:39 +0000373
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700374 err = mz_stream_copy(zip->stream, zip->cd_mem_stream, (int32_t)zip->cd_size);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800375
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800376 // Write the ZIP64 central directory header
juanii3f59ffc2018-02-08 12:44:17 -0300377 if (zip->cd_offset >= UINT32_MAX || zip->number_entry > UINT16_MAX)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800378 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700379 zip64_eocd_pos_inzip = mz_stream_tell(zip->stream);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800380
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700381 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_ENDHEADER64);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800382
383 // Size of this 'zip64 end of central directory'
384 if (err == MZ_OK)
385 err = mz_stream_write_uint64(zip->stream, (uint64_t)44);
386 // Version made by
387 if (err == MZ_OK)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700388 err = mz_stream_write_uint16(zip->stream, zip->version_madeby);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800389 // Version needed
390 if (err == MZ_OK)
391 err = mz_stream_write_uint16(zip->stream, (uint16_t)45);
392 // Number of this disk
393 if (err == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700394 err = mz_stream_write_uint32(zip->stream, zip->disk_number_with_cd);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800395 // Number of the disk with the start of the central directory
396 if (err == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700397 err = mz_stream_write_uint32(zip->stream, zip->disk_number_with_cd);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800398 // Total number of entries in the central dir on this disk
399 if (err == MZ_OK)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700400 err = mz_stream_write_uint64(zip->stream, zip->number_entry);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800401 // Total number of entries in the central dir
402 if (err == MZ_OK)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700403 err = mz_stream_write_uint64(zip->stream, zip->number_entry);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800404 // Size of the central directory
405 if (err == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700406 err = mz_stream_write_uint64(zip->stream, (uint64_t)zip->cd_size);
407 // Offset of start of central directory with respect to the starting disk number
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800408 if (err == MZ_OK)
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700409 err = mz_stream_write_uint64(zip->stream, zip->cd_offset);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800410 if (err == MZ_OK)
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700411 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_ENDLOCHEADER64);
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700412
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800413 // Number of the disk with the start of the central directory
414 if (err == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700415 err = mz_stream_write_uint32(zip->stream, zip->disk_number_with_cd);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800416 // Relative offset to the end of zip64 central directory
417 if (err == MZ_OK)
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700418 err = mz_stream_write_uint64(zip->stream, zip64_eocd_pos_inzip);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800419 // Number of the disk with the start of the central directory
420 if (err == MZ_OK)
juaniic65486d2018-02-08 17:07:07 -0300421 err = mz_stream_write_uint32(zip->stream, zip->disk_number_with_cd + 1);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800422 }
423
424 // Write the central directory header
425
Viktor Szakats915b82e2018-04-24 10:02:39 +0000426 // Signature
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800427 if (err == MZ_OK)
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700428 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_ENDHEADER);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800429 // Number of this disk
430 if (err == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700431 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->disk_number_with_cd);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800432 // Number of the disk with the start of the central directory
433 if (err == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700434 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->disk_number_with_cd);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800435 // Total number of entries in the central dir on this disk
436 if (err == MZ_OK)
437 {
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700438 if (zip->number_entry >= UINT16_MAX)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800439 err = mz_stream_write_uint16(zip->stream, UINT16_MAX);
440 else
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700441 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->number_entry);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800442 }
443 // Total number of entries in the central dir
444 if (err == MZ_OK)
445 {
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700446 if (zip->number_entry >= UINT16_MAX)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800447 err = mz_stream_write_uint16(zip->stream, UINT16_MAX);
448 else
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700449 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->number_entry);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800450 }
451 // Size of the central directory
452 if (err == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700453 err = mz_stream_write_uint32(zip->stream, (uint32_t)zip->cd_size);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800454 // Offset of start of central directory with respect to the starting disk number
455 if (err == MZ_OK)
456 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700457 if (zip->cd_offset >= UINT32_MAX)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800458 err = mz_stream_write_uint32(zip->stream, UINT32_MAX);
459 else
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700460 err = mz_stream_write_uint32(zip->stream, (uint32_t)zip->cd_offset);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800461 }
462
463 // Write global comment
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700464 if (zip->comment != NULL)
465 comment_size = (uint16_t)strlen(zip->comment);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800466 if (err == MZ_OK)
467 err = mz_stream_write_uint16(zip->stream, comment_size);
468 if (err == MZ_OK)
469 {
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700470 if (mz_stream_write(zip->stream, zip->comment, comment_size) != comment_size)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800471 err = MZ_STREAM_ERROR;
472 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700473 return err;
474}
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800475
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -0800476extern void* mz_zip_open(void *stream, int32_t mode)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700477{
478 mz_zip *zip = NULL;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700479 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700480
481
Nathan Moinvaziri5244fc02018-05-02 20:01:35 -0700482 zip = (mz_zip *)MZ_ALLOC(sizeof(mz_zip));
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700483 if (zip == NULL)
484 return NULL;
485
486 memset(zip, 0, sizeof(mz_zip));
487
488 zip->stream = stream;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700489
Nathan Moinvaziri3f7de8e2017-12-11 21:09:52 -0800490 if (mode & MZ_OPEN_MODE_WRITE)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700491 {
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700492 mz_stream_mem_create(&zip->cd_mem_stream);
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700493 mz_stream_mem_open(zip->cd_mem_stream, NULL, MZ_OPEN_MODE_CREATE);
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700494
495 zip->cd_stream = zip->cd_mem_stream;
496 }
497 else
498 {
499 zip->cd_stream = stream;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700500 }
501
Nathan Moinvaziri3f7de8e2017-12-11 21:09:52 -0800502 if ((mode & MZ_OPEN_MODE_READ) || (mode & MZ_OPEN_MODE_APPEND))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700503 {
504 err = mz_zip_read_cd(zip);
505
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700506 if ((err == MZ_OK) && (mode & MZ_OPEN_MODE_APPEND))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700507 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700508 if (zip->cd_size > 0)
509 {
510 // Store central directory in memory
511 err = mz_stream_seek(zip->stream, zip->cd_offset, MZ_SEEK_SET);
512 if (err == MZ_OK)
513 err = mz_stream_copy(zip->cd_mem_stream, zip->stream, (uint32_t)zip->cd_size);
514 if (err == MZ_OK)
515 err = mz_stream_seek(zip->stream, zip->cd_offset, MZ_SEEK_SET);
516 }
517 else
518 {
519 // If no central directory, append new zip to end of file
520 err = mz_stream_seek(zip->stream, 0, MZ_SEEK_END);
521 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700522 }
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700523 else
524 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700525 zip->cd_start_pos = zip->cd_offset;
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700526 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700527 }
528
529 if (err == MZ_OK)
530 {
531 mz_stream_mem_create(&zip->file_info_stream);
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700532 mz_stream_mem_open(zip->file_info_stream, NULL, MZ_OPEN_MODE_CREATE);
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700533 mz_stream_mem_create(&zip->local_file_info_stream);
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700534 mz_stream_mem_open(zip->local_file_info_stream, NULL, MZ_OPEN_MODE_CREATE);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700535 }
536
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700537 if (err != MZ_OK)
538 {
Nathan Moinvaziri3f7de8e2017-12-11 21:09:52 -0800539 mz_zip_close(zip);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700540 return NULL;
541 }
542
Nathan Moinvaziri3f7de8e2017-12-11 21:09:52 -0800543 zip->open_mode = mode;
544
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700545 return zip;
546}
547
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -0800548extern int32_t mz_zip_close(void *handle)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700549{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700550 mz_zip *zip = (mz_zip *)handle;
551 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700552
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700553 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700554 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700555
556 if (zip->entry_opened == 1)
557 {
558 err = mz_zip_entry_close(handle);
559 if (err != MZ_OK)
560 return err;
561 }
562
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700563 if (zip->open_mode & MZ_OPEN_MODE_WRITE)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700564 err = mz_zip_write_cd(handle);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700565
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700566 if (zip->cd_mem_stream != NULL)
Nathan Moinvaziri51bf64a2017-10-20 14:23:37 -0700567 {
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700568 mz_stream_close(zip->cd_mem_stream);
569 mz_stream_delete(&zip->cd_mem_stream);
Nathan Moinvaziri51bf64a2017-10-20 14:23:37 -0700570 }
571
Nathan Moinvaziri3f7de8e2017-12-11 21:09:52 -0800572 if (zip->file_info_stream != NULL)
573 {
574 mz_stream_mem_close(zip->file_info_stream);
575 mz_stream_mem_delete(&zip->file_info_stream);
576 }
577 if (zip->local_file_info_stream != NULL)
578 {
579 mz_stream_mem_close(zip->local_file_info_stream);
580 mz_stream_mem_delete(&zip->local_file_info_stream);
581 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700582
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700583 if (zip->comment)
Nathan Moinvaziri5244fc02018-05-02 20:01:35 -0700584 MZ_FREE(zip->comment);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700585
Nathan Moinvaziri5244fc02018-05-02 20:01:35 -0700586 MZ_FREE(zip);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800587
588 return err;
589}
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700590
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -0800591extern int32_t mz_zip_get_comment(void *handle, const char **comment)
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 || comment == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700595 return MZ_PARAM_ERROR;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700596 if (zip->comment == NULL)
597 return MZ_EXIST_ERROR;
598 *comment = zip->comment;
599 return MZ_OK;
600}
601
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -0800602extern int32_t mz_zip_set_comment(void *handle, const char *comment)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700603{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700604 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700605 uint16_t comment_size = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700606 if (zip == NULL || comment == NULL)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700607 return MZ_PARAM_ERROR;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700608 if (zip->comment != NULL)
Nathan Moinvaziri5244fc02018-05-02 20:01:35 -0700609 MZ_FREE(zip->comment);
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700610 comment_size = (uint16_t)(strlen(comment) + 1);
Nathan Moinvaziri5244fc02018-05-02 20:01:35 -0700611 zip->comment = (char *)MZ_ALLOC(comment_size);
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700612 strncpy(zip->comment, comment, comment_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700613 return MZ_OK;
614}
615
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -0800616extern int32_t mz_zip_get_version_madeby(void *handle, uint16_t *version_madeby)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700617{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700618 mz_zip *zip = (mz_zip *)handle;
619 if (zip == NULL || version_madeby == NULL)
620 return MZ_PARAM_ERROR;
621 *version_madeby = zip->version_madeby;
622 return MZ_OK;
623}
624
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -0800625extern int32_t mz_zip_set_version_madeby(void *handle, uint16_t version_madeby)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700626{
627 mz_zip *zip = (mz_zip *)handle;
628 if (zip == NULL)
629 return MZ_PARAM_ERROR;
630 zip->version_madeby = version_madeby;
631 return MZ_OK;
632}
633
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700634// Get info about the current file in the zip file
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700635static 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 -0700636{
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700637 uint64_t ntfs_time = 0;
638 uint32_t reserved = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700639 uint32_t magic = 0;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700640 uint32_t dos_date = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700641 uint32_t extra_pos = 0;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700642 uint32_t extra_data_size_read = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700643 uint16_t extra_header_id = 0;
644 uint16_t extra_data_size = 0;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700645 uint16_t ntfs_attrib_id = 0;
646 uint16_t ntfs_attrib_size = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700647 uint16_t value16 = 0;
648 uint32_t value32 = 0;
649 uint64_t value64 = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700650 int64_t max_seek = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700651 int64_t seek = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700652 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700653
654
655 memset(file_info, 0, sizeof(mz_zip_file));
656
657 // Check the magic
658 err = mz_stream_read_uint32(stream, &magic);
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700659 if (err == MZ_END_OF_STREAM)
660 err = MZ_END_OF_LIST;
661 else if (magic == MZ_ZIP_MAGIC_ENDHEADER || magic == MZ_ZIP_MAGIC_ENDHEADER64)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700662 err = MZ_END_OF_LIST;
663 else if ((local) && (magic != MZ_ZIP_MAGIC_LOCALHEADER))
664 err = MZ_FORMAT_ERROR;
665 else if ((!local) && (magic != MZ_ZIP_MAGIC_CENTRALHEADER))
666 err = MZ_FORMAT_ERROR;
Viktor Szakats915b82e2018-04-24 10:02:39 +0000667
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700668 // Read header fields
669 if (err == MZ_OK)
670 {
671 if (!local)
672 err = mz_stream_read_uint16(stream, &file_info->version_madeby);
673 if (err == MZ_OK)
674 err = mz_stream_read_uint16(stream, &file_info->version_needed);
675 if (err == MZ_OK)
676 err = mz_stream_read_uint16(stream, &file_info->flag);
677 if (err == MZ_OK)
678 err = mz_stream_read_uint16(stream, &file_info->compression_method);
679 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700680 {
681 err = mz_stream_read_uint32(stream, &dos_date);
682 file_info->modified_date = mz_zip_dosdate_to_time_t(dos_date);
683 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700684 if (err == MZ_OK)
685 err = mz_stream_read_uint32(stream, &file_info->crc);
686 if (err == MZ_OK)
687 err = mz_stream_read_uint32(stream, &value32);
688 file_info->compressed_size = value32;
689 if (err == MZ_OK)
690 err = mz_stream_read_uint32(stream, &value32);
691 file_info->uncompressed_size = value32;
692 if (err == MZ_OK)
693 err = mz_stream_read_uint16(stream, &file_info->filename_size);
694 if (err == MZ_OK)
695 err = mz_stream_read_uint16(stream, &file_info->extrafield_size);
696 if (!local)
697 {
698 if (err == MZ_OK)
699 err = mz_stream_read_uint16(stream, &file_info->comment_size);
700 if (err == MZ_OK)
701 err = mz_stream_read_uint16(stream, &value16);
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700702 file_info->disk_number = value16;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700703 if (err == MZ_OK)
704 err = mz_stream_read_uint16(stream, &file_info->internal_fa);
705 if (err == MZ_OK)
706 err = mz_stream_read_uint32(stream, &file_info->external_fa);
707 if (err == MZ_OK)
708 err = mz_stream_read_uint32(stream, &value32);
709 file_info->disk_offset = value32;
710 }
711 }
712
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700713 max_seek = file_info->filename_size + file_info->extrafield_size + file_info->comment_size + 3;
714 if (err == MZ_OK)
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700715 err = mz_stream_seek(file_info_stream, max_seek, MZ_SEEK_SET);
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700716 if (err == MZ_OK)
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700717 err = mz_stream_seek(file_info_stream, 0, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700718
719 if ((err == MZ_OK) && (file_info->filename_size > 0))
720 {
Nathan Moinvaziria710bd72018-05-09 00:46:47 -0700721 mz_stream_mem_get_buffer(file_info_stream, (const void **)&file_info->filename);
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700722
723 err = mz_stream_copy(file_info_stream, stream, file_info->filename_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700724 if (err == MZ_OK)
725 err = mz_stream_write_uint8(file_info_stream, 0);
726
727 seek += file_info->filename_size + 1;
728 }
729
730 if ((err == MZ_OK) && (file_info->extrafield_size > 0))
731 {
Nathan Moinvaziria710bd72018-05-09 00:46:47 -0700732 mz_stream_mem_get_buffer_at(file_info_stream, seek, (const void **)&file_info->extrafield);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700733
734 err = mz_stream_copy(file_info_stream, stream, file_info->extrafield_size);
735 if (err == MZ_OK)
736 err = mz_stream_write_uint8(file_info_stream, 0);
737
738 // Seek back and parse the extra field
739 if (err == MZ_OK)
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700740 err = mz_stream_seek(file_info_stream, seek, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700741
742 seek += file_info->extrafield_size + 1;
743
744 while ((err == MZ_OK) && (extra_pos < file_info->extrafield_size))
745 {
746 err = mz_stream_read_uint16(file_info_stream, &extra_header_id);
747 if (err == MZ_OK)
748 err = mz_stream_read_uint16(file_info_stream, &extra_data_size);
749
750 // ZIP64 extra field
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700751 if (extra_header_id == MZ_ZIP_EXTENSION_ZIP64)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700752 {
753 if ((err == MZ_OK) && (file_info->uncompressed_size == UINT32_MAX))
754 err = mz_stream_read_uint64(file_info_stream, &file_info->uncompressed_size);
755 if ((err == MZ_OK) && (file_info->compressed_size == UINT32_MAX))
756 err = mz_stream_read_uint64(file_info_stream, &file_info->compressed_size);
757 if ((err == MZ_OK) && (file_info->disk_offset == UINT32_MAX))
758 err = mz_stream_read_uint64(file_info_stream, &value64);
759 file_info->disk_offset = value64;
juanii4ae79922018-02-11 14:29:36 -0300760 if ((err == MZ_OK) && (file_info->disk_number == UINT16_MAX))
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700761 err = mz_stream_read_uint32(file_info_stream, &file_info->disk_number);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700762 }
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700763 // NTFS extra field
764 else if (extra_header_id == MZ_ZIP_EXTENSION_NTFS)
765 {
Nathan Moinvaziri7a3b6982018-05-09 00:45:02 -0700766 if (err == MZ_OK)
767 err = mz_stream_read_uint32(file_info_stream, &reserved);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700768 extra_data_size_read = 4;
769
770 while ((err == MZ_OK) && (extra_data_size_read < extra_data_size))
771 {
772 err = mz_stream_read_uint16(file_info_stream, &ntfs_attrib_id);
773 if (err == MZ_OK)
774 err = mz_stream_read_uint16(file_info_stream, &ntfs_attrib_size);
775
Nathan Moinvaziri7a3b6982018-05-09 00:45:02 -0700776 if ((err == MZ_OK) && (ntfs_attrib_id == 0x01) && (ntfs_attrib_size == 24))
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700777 {
778 err = mz_stream_read_uint64(file_info_stream, &ntfs_time);
779 mz_zip_ntfs_to_unix_time(ntfs_time, &file_info->modified_date);
780
juanii7063b0e2018-02-11 13:56:21 -0300781 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700782 {
783 err = mz_stream_read_uint64(file_info_stream, &ntfs_time);
784 mz_zip_ntfs_to_unix_time(ntfs_time, &file_info->accessed_date);
785 }
juanii7063b0e2018-02-11 13:56:21 -0300786 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700787 {
788 err = mz_stream_read_uint64(file_info_stream, &ntfs_time);
789 mz_zip_ntfs_to_unix_time(ntfs_time, &file_info->creation_date);
790 }
791 }
792 else
793 {
Nathan Moinvaziri7a3b6982018-05-09 00:45:02 -0700794 if (err == MZ_OK)
795 err = mz_stream_seek(file_info_stream, ntfs_attrib_size, MZ_SEEK_CUR);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700796 }
797
798 extra_data_size_read += ntfs_attrib_size + 4;
799 }
800 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700801#ifdef HAVE_AES
802 // AES extra field
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700803 else if (extra_header_id == MZ_ZIP_EXTENSION_AES)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700804 {
805 uint8_t value8 = 0;
806 // Verify version info
807 err = mz_stream_read_uint16(file_info_stream, &value16);
808 // Support AE-1 and AE-2
809 if (value16 != 1 && value16 != 2)
810 err = MZ_FORMAT_ERROR;
811 file_info->aes_version = value16;
812 if (err == MZ_OK)
813 err = mz_stream_read_uint8(file_info_stream, &value8);
814 if ((char)value8 != 'A')
815 err = MZ_FORMAT_ERROR;
816 if (err == MZ_OK)
817 err = mz_stream_read_uint8(file_info_stream, &value8);
818 if ((char)value8 != 'E')
819 err = MZ_FORMAT_ERROR;
820 // Get AES encryption strength and actual compression method
821 if (err == MZ_OK)
822 err = mz_stream_read_uint8(file_info_stream, &value8);
823 file_info->aes_encryption_mode = value8;
824 if (err == MZ_OK)
825 err = mz_stream_read_uint16(file_info_stream, &value16);
826 file_info->compression_method = value16;
827 }
828#endif
829 else
830 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700831 err = mz_stream_seek(file_info_stream, extra_data_size, MZ_SEEK_CUR);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700832 }
833
834 extra_pos += 4 + extra_data_size;
835 }
836 }
837
838 if ((err == MZ_OK) && (file_info->comment_size > 0))
839 {
Nathan Moinvaziria710bd72018-05-09 00:46:47 -0700840 mz_stream_mem_get_buffer_at(file_info_stream, seek, (const void **)&file_info->comment);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700841
842 err = mz_stream_copy(file_info_stream, stream, file_info->comment_size);
843 if (err == MZ_OK)
844 err = mz_stream_write_uint8(file_info_stream, 0);
845 }
846
847 return err;
848}
849
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700850static int32_t mz_zip_entry_write_header(void *stream, uint8_t local, mz_zip_file *file_info)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700851{
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700852 uint64_t ntfs_time = 0;
853 uint32_t reserved = 0;
854 uint32_t dos_date = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700855 uint16_t extrafield_size = 0;
856 uint16_t extrafield_zip64_size = 0;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700857 uint16_t extrafield_ntfs_size = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700858 uint16_t filename_size = 0;
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -0700859 uint16_t filename_length = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700860 uint16_t comment_size = 0;
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700861 uint16_t version_needed = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700862 uint8_t zip64 = 0;
863 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700864
865 if (file_info == NULL)
866 return MZ_PARAM_ERROR;
867
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700868 // Calculate extra field sizes
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700869 extrafield_size = file_info->extrafield_size;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700870
871 if (file_info->uncompressed_size >= UINT32_MAX)
872 extrafield_zip64_size += 8;
873 if (file_info->compressed_size >= UINT32_MAX)
874 extrafield_zip64_size += 8;
875 if (file_info->disk_offset >= UINT32_MAX)
876 extrafield_zip64_size += 8;
877
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700878 if (file_info->zip64 == MZ_ZIP64_AUTO)
Nathan Moinvaziri121e0882018-05-03 17:57:20 -0700879 {
880 // If uncompressed size is unknown, assume zip64 for 64-bit data descriptors
881 zip64 = (local && file_info->uncompressed_size == 0) || (extrafield_zip64_size > 0);
882 }
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700883 else if (file_info->zip64 == MZ_ZIP64_FORCE)
Nathan Moinvaziria56a08c2018-05-03 09:35:37 -0700884 zip64 = 1;
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700885 else if (file_info->zip64 == MZ_ZIP64_DISABLE)
Nathan Moinvaziria56a08c2018-05-03 09:35:37 -0700886 {
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700887 // Zip64 extension is required to zip file
888 if (extrafield_zip64_size > 0)
889 return MZ_PARAM_ERROR;
Nathan Moinvaziria56a08c2018-05-03 09:35:37 -0700890 }
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700891
892 if (zip64)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700893 {
894 extrafield_size += 4;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700895 extrafield_size += extrafield_zip64_size;
896 }
897#ifdef HAVE_AES
898 if ((file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
899 extrafield_size += 4 + 7;
900#endif
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700901 // NTFS timestamps
Nathan Moinvazirid9e159a2018-02-13 15:30:30 -0800902 if ((file_info->modified_date != 0) &&
903 (file_info->accessed_date != 0) &&
904 (file_info->creation_date != 0))
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700905 {
juanii3679a3d2018-02-11 13:55:38 -0300906 extrafield_ntfs_size += 8 + 8 + 8 + 4 + 2 + 2;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700907 extrafield_size += 4;
908 extrafield_size += extrafield_ntfs_size;
909 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700910
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700911 if (local)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700912 err = mz_stream_write_uint32(stream, MZ_ZIP_MAGIC_LOCALHEADER);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700913 else
914 {
915 err = mz_stream_write_uint32(stream, MZ_ZIP_MAGIC_CENTRALHEADER);
916 if (err == MZ_OK)
917 err = mz_stream_write_uint16(stream, file_info->version_madeby);
918 }
919
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700920 // Calculate version needed to extract
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700921 if (err == MZ_OK)
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700922 {
923 version_needed = file_info->version_needed;
924 if (version_needed == 0)
925 {
926 version_needed = 20;
927 if (zip64)
928 version_needed = 45;
929#ifdef HAVE_AES
930 if ((file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
931 version_needed = 51;
932#endif
933#ifdef HAVE_LZMA
934 if (file_info->compression_method == MZ_COMPRESS_METHOD_LZMA)
935 version_needed = 63;
936#endif
937 }
938 err = mz_stream_write_uint16(stream, version_needed);
939 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700940 if (err == MZ_OK)
941 err = mz_stream_write_uint16(stream, file_info->flag);
942 if (err == MZ_OK)
943 {
944#ifdef HAVE_AES
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700945 if (file_info->aes_version)
946 err = mz_stream_write_uint16(stream, MZ_COMPRESS_METHOD_AES);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700947 else
948#endif
949 err = mz_stream_write_uint16(stream, file_info->compression_method);
950 }
951 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700952 {
Nathan Moinvaziri17cdaec2017-10-26 10:18:41 -0700953 if (file_info->modified_date != 0)
954 dos_date = mz_zip_time_t_to_dos_date(file_info->modified_date);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700955 err = mz_stream_write_uint32(stream, dos_date);
956 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700957
958 if (err == MZ_OK)
959 err = mz_stream_write_uint32(stream, file_info->crc); // crc
960 if (err == MZ_OK)
961 {
962 if (file_info->compressed_size >= UINT32_MAX) // compr size
963 err = mz_stream_write_uint32(stream, UINT32_MAX);
964 else
965 err = mz_stream_write_uint32(stream, (uint32_t)file_info->compressed_size);
966 }
967 if (err == MZ_OK)
968 {
969 if (file_info->uncompressed_size >= UINT32_MAX) // uncompr size
970 err = mz_stream_write_uint32(stream, UINT32_MAX);
971 else
972 err = mz_stream_write_uint32(stream, (uint32_t)file_info->uncompressed_size);
973 }
974
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -0700975 filename_length = (uint16_t)strlen(file_info->filename);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700976 if (err == MZ_OK)
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -0700977 {
978 filename_size = filename_length;
979 if (mz_zip_attrib_is_dir(file_info->external_fa, file_info->version_madeby) == MZ_OK)
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700980 {
981 if ((file_info->filename[filename_length - 1] == '/') ||
982 (file_info->filename[filename_length - 1] == '\\'))
983 filename_length -= 1;
984 else
985 filename_size += 1;
986 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700987 err = mz_stream_write_uint16(stream, filename_size);
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -0700988 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700989 if (err == MZ_OK)
990 err = mz_stream_write_uint16(stream, extrafield_size);
991
992 if (!local)
993 {
994 if (file_info->comment != NULL)
995 comment_size = (uint16_t)strlen(file_info->comment);
996 if (err == MZ_OK)
997 err = mz_stream_write_uint16(stream, comment_size);
998 if (err == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700999 err = mz_stream_write_uint16(stream, (uint16_t)file_info->disk_number);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001000 if (err == MZ_OK)
1001 err = mz_stream_write_uint16(stream, file_info->internal_fa);
1002 if (err == MZ_OK)
1003 err = mz_stream_write_uint32(stream, file_info->external_fa);
1004 if (err == MZ_OK)
1005 {
1006 if (file_info->disk_offset >= UINT32_MAX)
1007 err = mz_stream_write_uint32(stream, UINT32_MAX);
1008 else
1009 err = mz_stream_write_uint32(stream, (uint32_t)file_info->disk_offset);
1010 }
1011 }
1012
1013 if (err == MZ_OK)
1014 {
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -07001015 if (mz_stream_write(stream, file_info->filename, filename_length) != filename_length)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001016 err = MZ_STREAM_ERROR;
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -07001017 if (err == MZ_OK)
1018 {
Nathan Moinvaziri240b3b62018-05-02 13:38:14 -07001019 // Ensure that directories have a slash appended to them for compatibility
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -07001020 if (mz_zip_attrib_is_dir(file_info->external_fa, file_info->version_madeby) == MZ_OK)
1021 err = mz_stream_write_uint8(stream, '/');
1022 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001023 }
1024 if (err == MZ_OK)
1025 {
1026 if (mz_stream_write(stream, file_info->extrafield, file_info->extrafield_size) != file_info->extrafield_size)
1027 err = MZ_STREAM_ERROR;
1028 }
1029 // Add ZIP64 extra info header to central directory
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001030 if ((err == MZ_OK) && (zip64))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001031 {
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07001032 err = mz_stream_write_uint16(stream, MZ_ZIP_EXTENSION_ZIP64);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001033 if (err == MZ_OK)
1034 err = mz_stream_write_uint16(stream, extrafield_zip64_size);
1035 if ((err == MZ_OK) && (file_info->uncompressed_size >= UINT32_MAX))
1036 err = mz_stream_write_uint64(stream, file_info->uncompressed_size);
1037 if ((err == MZ_OK) && (file_info->compressed_size >= UINT32_MAX))
1038 err = mz_stream_write_uint64(stream, file_info->compressed_size);
1039 if ((err == MZ_OK) && (file_info->disk_offset >= UINT32_MAX))
1040 err = mz_stream_write_uint64(stream, file_info->disk_offset);
1041 }
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001042 // Write NTFS timestamps
1043 if ((err == MZ_OK) && (extrafield_ntfs_size > 0))
1044 {
1045 err = mz_stream_write_uint16(stream, MZ_ZIP_EXTENSION_NTFS);
1046 if (err == MZ_OK)
1047 err = mz_stream_write_uint16(stream, extrafield_ntfs_size);
1048 if (err == MZ_OK)
1049 err = mz_stream_write_uint32(stream, reserved);
1050 if (err == MZ_OK)
1051 err = mz_stream_write_uint16(stream, 0x01);
1052 if (err == MZ_OK)
1053 err = mz_stream_write_uint16(stream, extrafield_ntfs_size - 8);
juanii3679a3d2018-02-11 13:55:38 -03001054 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001055 {
1056 mz_zip_unix_to_ntfs_time(file_info->modified_date, &ntfs_time);
1057 err = mz_stream_write_uint64(stream, ntfs_time);
1058 }
juanii3679a3d2018-02-11 13:55:38 -03001059 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001060 {
1061 mz_zip_unix_to_ntfs_time(file_info->accessed_date, &ntfs_time);
1062 err = mz_stream_write_uint64(stream, ntfs_time);
1063 }
juanii3679a3d2018-02-11 13:55:38 -03001064 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001065 {
1066 mz_zip_unix_to_ntfs_time(file_info->creation_date, &ntfs_time);
1067 err = mz_stream_write_uint64(stream, ntfs_time);
1068 }
1069 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001070#ifdef HAVE_AES
1071 // Write AES extra info header to central directory
1072 if ((err == MZ_OK) && (file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
1073 {
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07001074 err = mz_stream_write_uint16(stream, MZ_ZIP_EXTENSION_AES);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001075 if (err == MZ_OK)
1076 err = mz_stream_write_uint16(stream, 7);
1077 if (err == MZ_OK)
1078 err = mz_stream_write_uint16(stream, file_info->aes_version);
1079 if (err == MZ_OK)
1080 err = mz_stream_write_uint8(stream, 'A');
1081 if (err == MZ_OK)
1082 err = mz_stream_write_uint8(stream, 'E');
1083 if (err == MZ_OK)
1084 err = mz_stream_write_uint8(stream, file_info->aes_encryption_mode);
1085 if (err == MZ_OK)
1086 err = mz_stream_write_uint16(stream, file_info->compression_method);
1087 }
1088#endif
1089 if ((err == MZ_OK) && (file_info->comment != NULL))
1090 {
1091 if (mz_stream_write(stream, file_info->comment, file_info->comment_size) != MZ_OK)
1092 err = MZ_STREAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001093 }
1094
1095 return err;
1096}
1097
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001098static 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 -07001099{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001100 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001101 int64_t max_total_in = 0;
Nathan Moinvazirifa8105b2018-07-08 19:05:00 -07001102 int64_t header_size = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001103 int64_t footer_size = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001104 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001105
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001106 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001107 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001108
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001109 zip->compression_method = compression_method;
1110
1111 switch (zip->compression_method)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001112 {
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001113 case MZ_COMPRESS_METHOD_RAW:
1114 case MZ_COMPRESS_METHOD_DEFLATE:
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001115#ifdef HAVE_BZIP2
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001116 case MZ_COMPRESS_METHOD_BZIP2:
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001117#endif
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001118#if HAVE_LZMA
1119 case MZ_COMPRESS_METHOD_LZMA:
1120#endif
1121 err = MZ_OK;
1122 break;
1123 default:
1124 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001125 }
1126
Nathan Moinvaziriee614ff2018-07-12 12:36:33 -07001127 if ((err == MZ_OK) && (zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED) &&
1128 (zip->compression_method != MZ_COMPRESS_METHOD_RAW || password != NULL))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001129 {
1130#ifdef HAVE_AES
1131 if (zip->file_info.aes_version)
1132 {
1133 mz_stream_aes_create(&zip->crypt_stream);
1134 mz_stream_aes_set_password(zip->crypt_stream, password);
1135 mz_stream_aes_set_encryption_mode(zip->crypt_stream, zip->file_info.aes_encryption_mode);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001136 }
1137 else
1138#endif
1139 {
Nathan Moinvaziri0e5c9dc2018-05-23 20:21:48 -07001140#ifdef HAVE_PKCRYPT
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001141 uint8_t verify1 = 0;
1142 uint8_t verify2 = 0;
1143
1144 // Info-ZIP modification to ZipCrypto format:
1145 // If bit 3 of the general purpose bit flag is set, it uses high byte of 16-bit File Time.
1146
Nathan Moinvaziri18a30652017-12-07 06:59:53 -08001147 if (zip->file_info.flag & MZ_ZIP_FLAG_DATA_DESCRIPTOR)
1148 {
1149 uint32_t dos_date = 0;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001150
Nathan Moinvaziri18a30652017-12-07 06:59:53 -08001151 dos_date = mz_zip_time_t_to_dos_date(zip->file_info.modified_date);
1152
1153 verify1 = (uint8_t)((dos_date >> 16) & 0xff);
1154 verify2 = (uint8_t)((dos_date >> 8) & 0xff);
1155 }
1156 else
1157 {
1158 verify1 = (uint8_t)((zip->file_info.crc >> 16) & 0xff);
1159 verify2 = (uint8_t)((zip->file_info.crc >> 24) & 0xff);
1160 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001161
Nathan Moinvaziri0e5c9dc2018-05-23 20:21:48 -07001162 mz_stream_pkcrypt_create(&zip->crypt_stream);
1163 mz_stream_pkcrypt_set_password(zip->crypt_stream, password);
1164 mz_stream_pkcrypt_set_verify(zip->crypt_stream, verify1, verify2);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001165#endif
1166 }
1167 }
1168
1169 if (err == MZ_OK)
1170 {
1171 if (zip->crypt_stream == NULL)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001172 mz_stream_raw_create(&zip->crypt_stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001173
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001174 mz_stream_set_base(zip->crypt_stream, zip->stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001175
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001176 err = mz_stream_open(zip->crypt_stream, NULL, zip->open_mode);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001177 }
1178
1179 if (err == MZ_OK)
1180 {
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001181 if (zip->compression_method == MZ_COMPRESS_METHOD_RAW)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001182 mz_stream_raw_create(&zip->compress_stream);
Andy Maloney3909c232018-05-22 09:02:09 -04001183#ifdef HAVE_ZLIB
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001184 else if (zip->compression_method == MZ_COMPRESS_METHOD_DEFLATE)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001185 mz_stream_zlib_create(&zip->compress_stream);
Andy Maloney3909c232018-05-22 09:02:09 -04001186#endif
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001187#ifdef HAVE_BZIP2
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001188 else if (zip->compression_method == MZ_COMPRESS_METHOD_BZIP2)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001189 mz_stream_bzip_create(&zip->compress_stream);
1190#endif
1191#ifdef HAVE_LZMA
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001192 else if (zip->compression_method == MZ_COMPRESS_METHOD_LZMA)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001193 mz_stream_lzma_create(&zip->compress_stream);
1194#endif
1195 else
1196 err = MZ_PARAM_ERROR;
1197 }
1198
1199 if (err == MZ_OK)
1200 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001201 if (zip->open_mode & MZ_OPEN_MODE_WRITE)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001202 {
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001203 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_COMPRESS_LEVEL, compress_level);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001204 }
1205 else
1206 {
Nathan Moinvaziri5e8f4d72018-03-15 10:43:18 -07001207 if (zip->compression_method == MZ_COMPRESS_METHOD_RAW || zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001208 {
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001209 max_total_in = zip->file_info.compressed_size;
Nathan Moinvazirifa8105b2018-07-08 19:05:00 -07001210 mz_stream_set_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_TOTAL_IN_MAX, max_total_in);
1211
1212 if (mz_stream_get_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_HEADER_SIZE, &header_size) == MZ_OK)
1213 max_total_in -= header_size;
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001214 if (mz_stream_get_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_FOOTER_SIZE, &footer_size) == MZ_OK)
1215 max_total_in -= footer_size;
Nathan Moinvazirifa8105b2018-07-08 19:05:00 -07001216
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001217 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_IN_MAX, max_total_in);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001218 }
Nathan Moinvaziri5e8f4d72018-03-15 10:43:18 -07001219 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 -03001220 {
1221 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_IN_MAX, zip->file_info.compressed_size);
1222 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_OUT_MAX, zip->file_info.uncompressed_size);
1223 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001224 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001225
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001226 mz_stream_set_base(zip->compress_stream, zip->crypt_stream);
1227
1228 err = mz_stream_open(zip->compress_stream, NULL, zip->open_mode);
1229 }
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001230 if (err == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001231 {
1232 mz_stream_crc32_create(&zip->crc32_stream);
1233 mz_stream_set_base(zip->crc32_stream, zip->compress_stream);
1234
1235 err = mz_stream_open(zip->crc32_stream, NULL, zip->open_mode);
1236 }
1237
1238 if (err == MZ_OK)
1239 {
1240 zip->entry_opened = 1;
1241 }
1242
1243 return err;
1244}
1245
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001246extern int32_t mz_zip_entry_read_open(void *handle, int16_t raw, const char *password)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001247{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001248 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001249 int16_t compression_method = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001250 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001251
Nathan Moinvaziri0e5c9dc2018-05-23 20:21:48 -07001252#if !defined(HAVE_PKCRYPT) && !defined(HAVE_AES)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001253 if (password != NULL)
1254 return MZ_PARAM_ERROR;
1255#endif
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001256 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001257 return MZ_PARAM_ERROR;
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001258 if ((zip->open_mode & MZ_OPEN_MODE_READ) == 0)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001259 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001260 if (zip->entry_scanned == 0)
1261 return MZ_PARAM_ERROR;
Nathan Moinvaziri0f09a002018-04-23 18:48:30 -07001262 if ((zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED) && (password == NULL) && (!raw))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001263 return MZ_PARAM_ERROR;
1264
Nathan Moinvazirifd039e32017-10-22 14:40:39 -07001265 if (zip->file_info.disk_number == zip->disk_number_with_cd)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001266 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
1267 else
Nathan Moinvazirifd039e32017-10-22 14:40:39 -07001268 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, zip->file_info.disk_number);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001269
Nathan Moinvaziriee614ff2018-07-12 12:36:33 -07001270 err = mz_stream_seek(zip->stream, zip->file_info.disk_offset + zip->disk_offset_shift, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001271 if (err == MZ_OK)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001272 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 -07001273
Nathan Moinvaziri51f72502017-10-26 10:15:21 -07001274 compression_method = zip->file_info.compression_method;
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001275 if (raw)
1276 compression_method = MZ_COMPRESS_METHOD_RAW;
1277
Nathan Moinvaziri0a9282d2018-06-19 11:59:07 -07001278#ifdef MZ_ZIP_COMPRESS_ONLY
1279 if (compression_method != MZ_COMPRESS_METHOD_RAW)
1280 err = MZ_SUPPORT_ERROR;
1281#endif
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001282 if (err == MZ_OK)
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001283 err = mz_zip_entry_open_int(handle, compression_method, 0, password);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001284
1285 return err;
1286}
1287
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -07001288extern 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 -07001289{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001290 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001291 int64_t disk_number = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001292 int32_t err = MZ_OK;
Nathan Moinvaziri51bf64a2017-10-20 14:23:37 -07001293 int16_t compression_method = 0;
1294
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001295
Nathan Moinvaziri0e5c9dc2018-05-23 20:21:48 -07001296#if !defined(HAVE_PKCRYPT) && !defined(HAVE_AES)
tz-lomb1b25802017-11-10 15:03:02 +03001297 if (password != NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001298 return MZ_PARAM_ERROR;
1299#endif
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001300 if (zip == NULL || file_info == NULL || file_info->filename == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001301 return MZ_PARAM_ERROR;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001302
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001303 if (zip->entry_opened == 1)
1304 {
1305 err = mz_zip_entry_close(handle);
1306 if (err != MZ_OK)
1307 return err;
1308 }
1309
1310 memcpy(&zip->file_info, file_info, sizeof(mz_zip_file));
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001311
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001312 compression_method = zip->file_info.compression_method;
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001313
1314 if (compression_method == MZ_COMPRESS_METHOD_DEFLATE)
1315 {
1316 if ((compress_level == 8) || (compress_level == 9))
1317 zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_MAX;
1318 if (compress_level == 2)
1319 zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_FAST;
1320 if (compress_level == 1)
1321 zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_SUPER_FAST;
1322 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001323#ifdef HAVE_LZMA
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001324 else if (compression_method == MZ_COMPRESS_METHOD_LZMA)
1325 zip->file_info.flag |= MZ_ZIP_FLAG_LZMA_EOS_MARKER;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001326#endif
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001327
1328 zip->file_info.flag |= MZ_ZIP_FLAG_DATA_DESCRIPTOR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001329
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001330 if (password != NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001331 zip->file_info.flag |= MZ_ZIP_FLAG_ENCRYPTED;
1332 else
1333 zip->file_info.flag &= ~MZ_ZIP_FLAG_ENCRYPTED;
1334
juaniib8887e92018-02-14 00:51:05 -03001335 mz_stream_get_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, &disk_number);
1336 zip->file_info.disk_number = (uint32_t)disk_number;
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001337
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001338 zip->file_info.disk_offset = mz_stream_tell(zip->stream);
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001339 zip->file_info.crc = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001340 zip->file_info.compressed_size = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001341
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001342#ifdef HAVE_AES
1343 if (zip->file_info.aes_version && zip->file_info.aes_encryption_mode == 0)
1344 zip->file_info.aes_encryption_mode = MZ_AES_ENCRYPTION_MODE_256;
1345#endif
1346
Nathan Moinvaziri4827f712018-05-02 10:50:47 -07001347 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 +00001348 compression_method = MZ_COMPRESS_METHOD_RAW;
1349
Nathan Moinvaziri0a9282d2018-06-19 11:59:07 -07001350#ifdef MZ_ZIP_DECOMPRESS_ONLY
1351 if (compression_method != MZ_COMPRESS_METHOD_RAW)
1352 err = MZ_SUPPORT_ERROR;
1353#endif
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001354 if (err == MZ_OK)
1355 err = mz_zip_entry_write_header(zip->stream, 1, &zip->file_info);
1356 if (err == MZ_OK)
Nathan Moinvaziri51bf64a2017-10-20 14:23:37 -07001357 err = mz_zip_entry_open_int(handle, compression_method, compress_level, password);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001358
1359 return err;
1360}
1361
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001362extern int32_t mz_zip_entry_read(void *handle, void *buf, uint32_t len)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001363{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001364 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001365 int32_t read = 0;
1366
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001367 if (zip == NULL || zip->entry_opened == 0)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001368 return MZ_PARAM_ERROR;
Nathan Moinvaziri930f9182018-01-22 08:51:07 -08001369 if (UINT_MAX == UINT16_MAX && len > UINT16_MAX) // Zlib limitation
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001370 return MZ_PARAM_ERROR;
Nathan Moinvazirid7814e92018-07-11 14:54:14 -07001371 if (len == 0)
1372 return MZ_PARAM_ERROR;
1373
Nathan Moinvazirieb80cd92018-07-11 16:45:09 -07001374 if (zip->file_info.compressed_size == 0)
1375 return 0;
1376
Nathan Moinvazirid7814e92018-07-11 14:54:14 -07001377 // Read entire entry even if uncompressed_size = 0, otherwise
1378 // aes encryption validation will fail if compressed_size > 0
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001379 read = mz_stream_read(zip->crc32_stream, buf, len);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001380 return read;
1381}
1382
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001383extern int32_t mz_zip_entry_write(void *handle, const void *buf, uint32_t len)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001384{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001385 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri0a9282d2018-06-19 11:59:07 -07001386 int32_t written = 0;
1387
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001388 if (zip == NULL || zip->entry_opened == 0)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001389 return MZ_PARAM_ERROR;
Nathan Moinvaziri0a9282d2018-06-19 11:59:07 -07001390 written = mz_stream_write(zip->crc32_stream, buf, len);
1391 return written;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001392}
1393
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001394extern int32_t mz_zip_entry_get_info(void *handle, mz_zip_file **file_info)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001395{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001396 mz_zip *zip = (mz_zip *)handle;
1397 if (zip == NULL || zip->entry_scanned == 0)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001398 return MZ_PARAM_ERROR;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001399 *file_info = &zip->file_info;
1400 return MZ_OK;
1401}
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001402
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001403extern int32_t mz_zip_entry_get_local_info(void *handle, mz_zip_file **local_file_info)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001404{
1405 mz_zip *zip = (mz_zip *)handle;
1406 if (zip == NULL || zip->entry_scanned == 0 || zip->entry_opened == 0)
1407 return MZ_PARAM_ERROR;
1408 *local_file_info = &zip->local_file_info;
1409 return MZ_OK;
1410}
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001411
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001412extern int32_t mz_zip_entry_close_raw(void *handle, uint64_t uncompressed_size, uint32_t crc32)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001413{
1414 mz_zip *zip = (mz_zip *)handle;
1415 uint64_t compressed_size = 0;
Nathan Moinvaziri8871f6c2018-07-08 19:23:56 -07001416 int64_t total_in = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001417 int32_t err = MZ_OK;
1418
1419 if (zip == NULL || zip->entry_opened == 0)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001420 return MZ_PARAM_ERROR;
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001421
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001422 mz_stream_close(zip->compress_stream);
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001423 if (crc32 == 0)
1424 crc32 = mz_stream_crc32_get_value(zip->crc32_stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001425
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001426 if ((zip->open_mode & MZ_OPEN_MODE_WRITE) == 0)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001427 {
1428#ifdef HAVE_AES
1429 // AES zip version AE-1 will expect a valid crc as well
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001430 if (zip->file_info.aes_version <= 0x0001)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001431#endif
1432 {
Nathan Moinvaziri8871f6c2018-07-08 19:23:56 -07001433 mz_stream_crc32_get_prop_int64(zip->crc32_stream, MZ_STREAM_PROP_TOTAL_IN, &total_in);
Nathan Moinvaziri184f4cb2018-07-09 07:53:17 -07001434 // If entire entry was not read this will fail
Nathan Moinvaziri8871f6c2018-07-08 19:23:56 -07001435 if ((total_in > 0) && (zip->compression_method != MZ_COMPRESS_METHOD_RAW))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001436 {
1437 if (crc32 != zip->file_info.crc)
1438 err = MZ_CRC_ERROR;
1439 }
1440 }
1441 }
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001442
1443 mz_stream_get_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_OUT, (int64_t *)&compressed_size);
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001444 if ((zip->compression_method != MZ_COMPRESS_METHOD_RAW) || (uncompressed_size == 0))
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001445 mz_stream_get_prop_int64(zip->crc32_stream, MZ_STREAM_PROP_TOTAL_OUT, (int64_t *)&uncompressed_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001446
1447 if (zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED)
1448 {
1449 mz_stream_set_base(zip->crypt_stream, zip->stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001450 err = mz_stream_close(zip->crypt_stream);
1451
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001452 mz_stream_get_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_TOTAL_OUT, (int64_t *)&compressed_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001453 }
1454
1455 mz_stream_delete(&zip->crypt_stream);
1456
1457 mz_stream_delete(&zip->compress_stream);
1458 mz_stream_crc32_delete(&zip->crc32_stream);
1459
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001460 if (zip->open_mode & MZ_OPEN_MODE_WRITE)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001461 {
1462 if (err == MZ_OK)
1463 {
1464 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_DATADESCRIPTOR);
1465 if (err == MZ_OK)
1466 err = mz_stream_write_uint32(zip->stream, crc32);
1467 if (err == MZ_OK)
1468 {
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001469 if (zip->file_info.uncompressed_size <= UINT32_MAX)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001470 err = mz_stream_write_uint32(zip->stream, (uint32_t)compressed_size);
Andrew Gunnerson3c52d212018-01-05 22:28:12 -05001471 else
1472 err = mz_stream_write_uint64(zip->stream, compressed_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001473 }
1474 if (err == MZ_OK)
1475 {
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001476 if (zip->file_info.uncompressed_size <= UINT32_MAX)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001477 err = mz_stream_write_uint32(zip->stream, (uint32_t)uncompressed_size);
Andrew Gunnerson3c52d212018-01-05 22:28:12 -05001478 else
1479 err = mz_stream_write_uint64(zip->stream, uncompressed_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001480 }
1481 }
1482
1483 zip->file_info.crc = crc32;
1484 zip->file_info.compressed_size = compressed_size;
1485 zip->file_info.uncompressed_size = uncompressed_size;
1486
1487 if (err == MZ_OK)
Nathan Moinvaziricda36002017-10-21 09:37:18 -07001488 err = mz_zip_entry_write_header(zip->cd_mem_stream, 0, &zip->file_info);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001489
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07001490 zip->number_entry += 1;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001491 }
1492
1493 zip->entry_opened = 0;
1494
1495 return err;
1496}
1497
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001498extern int32_t mz_zip_entry_close(void *handle)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001499{
1500 return mz_zip_entry_close_raw(handle, 0, 0);
1501}
1502
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001503static int32_t mz_zip_goto_next_entry_int(void *handle)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001504{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001505 mz_zip *zip = (mz_zip *)handle;
1506 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001507
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001508 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001509 return MZ_PARAM_ERROR;
1510
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001511 zip->entry_scanned = 0;
1512
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001513 mz_stream_set_prop_int64(zip->cd_stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
Viktor Szakats915b82e2018-04-24 10:02:39 +00001514
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001515 err = mz_stream_seek(zip->cd_stream, zip->cd_current_pos, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001516 if (err == MZ_OK)
Nathan Moinvaziricda36002017-10-21 09:37:18 -07001517 err = mz_zip_entry_read_header(zip->cd_stream, 0, &zip->file_info, zip->file_info_stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001518 if (err == MZ_OK)
1519 zip->entry_scanned = 1;
1520 return err;
1521}
1522
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001523extern int32_t mz_zip_get_number_entry(void *handle, int64_t *number_entry)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07001524{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001525 mz_zip *zip = (mz_zip *)handle;
1526 if (zip == NULL || number_entry == NULL)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07001527 return MZ_PARAM_ERROR;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07001528 *number_entry = zip->number_entry;
1529 return MZ_OK;
1530}
1531
Viktor Szakats0f0535f2018-05-02 13:15:58 +00001532extern int32_t mz_zip_get_disk_number_with_cd(void *handle, uint32_t *disk_number_with_cd)
juanii566b9782018-02-10 15:07:54 -03001533{
1534 mz_zip *zip = (mz_zip *)handle;
1535 if (zip == NULL || disk_number_with_cd == NULL)
1536 return MZ_PARAM_ERROR;
1537 *disk_number_with_cd = zip->disk_number_with_cd;
1538 return MZ_OK;
1539}
1540
Viktor Szakatsce26dba2018-04-22 19:30:34 +00001541extern int64_t mz_zip_get_entry(void *handle)
1542{
1543 mz_zip *zip = (mz_zip *)handle;
1544
1545 if (zip == NULL)
1546 return MZ_PARAM_ERROR;
1547
1548 return zip->cd_current_pos;
1549}
1550
1551extern int32_t mz_zip_goto_entry(void *handle, uint64_t cd_pos)
1552{
1553 mz_zip *zip = (mz_zip *)handle;
1554
1555 if (zip == NULL)
1556 return MZ_PARAM_ERROR;
1557
1558 if (cd_pos < zip->cd_start_pos || cd_pos > zip->cd_start_pos + zip->cd_size)
1559 return MZ_PARAM_ERROR;
1560
1561 zip->cd_current_pos = cd_pos;
1562
1563 return mz_zip_goto_next_entry_int(handle);
1564}
1565
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001566extern int32_t mz_zip_goto_first_entry(void *handle)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001567{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001568 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001569
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001570 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001571 return MZ_PARAM_ERROR;
1572
Nathan Moinvaziricda36002017-10-21 09:37:18 -07001573 zip->cd_current_pos = zip->cd_start_pos;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001574
1575 return mz_zip_goto_next_entry_int(handle);
1576}
1577
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001578extern int32_t mz_zip_goto_next_entry(void *handle)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001579{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001580 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001581
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001582 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001583 return MZ_PARAM_ERROR;
1584
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001585 zip->cd_current_pos += MZ_ZIP_SIZE_CD_ITEM + zip->file_info.filename_size +
1586 zip->file_info.extrafield_size + zip->file_info.comment_size;
1587
1588 return mz_zip_goto_next_entry_int(handle);
1589}
1590
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001591extern 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 -07001592{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001593 mz_zip *zip = (mz_zip *)handle;
1594 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001595 int32_t result = 0;
1596
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001597 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001598 return MZ_PARAM_ERROR;
1599
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001600 err = mz_zip_goto_first_entry(handle);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001601 while (err == MZ_OK)
1602 {
1603 if (filename_compare_cb != NULL)
1604 result = filename_compare_cb(handle, zip->file_info.filename, filename);
1605 else
1606 result = strcmp(zip->file_info.filename, filename);
1607
1608 if (result == 0)
1609 return MZ_OK;
1610
1611 err = mz_zip_goto_next_entry(handle);
1612 }
1613
1614 return err;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001615}
1616
1617/***************************************************************************/
1618
Nathan Moinvaziribb3b75b2018-05-02 10:48:51 -07001619int32_t mz_zip_attrib_is_dir(int32_t attributes, int32_t version_madeby)
1620{
1621 int32_t host_system = (uint8_t)(version_madeby >> 8);
1622
1623 if (host_system == MZ_HOST_SYSTEM_MSDOS || host_system == MZ_HOST_SYSTEM_WINDOWS_NTFS)
1624 {
1625 if ((attributes & 0x10) == 0x10) // FILE_ATTRIBUTE_DIRECTORY
1626 return MZ_OK;
1627 }
1628 else if (host_system == MZ_HOST_SYSTEM_UNIX || host_system == MZ_HOST_SYSTEM_OSX_DARWIN)
1629 {
1630 if ((attributes & 00170000) == 0040000) // S_ISDIR
1631 return MZ_OK;
1632 }
1633
1634 return MZ_EXIST_ERROR;
1635}
1636
1637/***************************************************************************/
1638
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001639static int32_t mz_zip_invalid_date(const struct tm *ptm)
1640{
1641#define datevalue_in_range(min, max, value) ((min) <= (value) && (value) <= (max))
Viktor Szakatsce26dba2018-04-22 19:30:34 +00001642 return (!datevalue_in_range(0, 127 + 80, ptm->tm_year) || // 1980-based year, allow 80 extra
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001643 !datevalue_in_range(0, 11, ptm->tm_mon) ||
1644 !datevalue_in_range(1, 31, ptm->tm_mday) ||
1645 !datevalue_in_range(0, 23, ptm->tm_hour) ||
1646 !datevalue_in_range(0, 59, ptm->tm_min) ||
1647 !datevalue_in_range(0, 59, ptm->tm_sec));
1648#undef datevalue_in_range
1649}
1650
1651static void mz_zip_dosdate_to_raw_tm(uint64_t dos_date, struct tm *ptm)
1652{
1653 uint64_t date = (uint64_t)(dos_date >> 16);
1654
1655 ptm->tm_mday = (uint16_t)(date & 0x1f);
1656 ptm->tm_mon = (uint16_t)(((date & 0x1E0) / 0x20) - 1);
1657 ptm->tm_year = (uint16_t)(((date & 0x0FE00) / 0x0200) + 80);
1658 ptm->tm_hour = (uint16_t)((dos_date & 0xF800) / 0x800);
1659 ptm->tm_min = (uint16_t)((dos_date & 0x7E0) / 0x20);
1660 ptm->tm_sec = (uint16_t)(2 * (dos_date & 0x1f));
1661 ptm->tm_isdst = -1;
1662}
1663
1664int32_t mz_zip_dosdate_to_tm(uint64_t dos_date, struct tm *ptm)
1665{
1666 if (ptm == NULL)
1667 return MZ_PARAM_ERROR;
1668
1669 mz_zip_dosdate_to_raw_tm(dos_date, ptm);
1670
1671 if (mz_zip_invalid_date(ptm))
1672 {
1673 // Invalid date stored, so don't return it
1674 memset(ptm, 0, sizeof(struct tm));
1675 return MZ_FORMAT_ERROR;
1676 }
1677 return MZ_OK;
1678}
1679
1680time_t mz_zip_dosdate_to_time_t(uint64_t dos_date)
1681{
1682 struct tm ptm;
1683 mz_zip_dosdate_to_raw_tm(dos_date, &ptm);
1684 return mktime(&ptm);
1685}
1686
1687int32_t mz_zip_time_t_to_tm(time_t unix_time, struct tm *ptm)
1688{
Nathan Moinvaziri17fcdcd2017-10-26 08:13:13 -07001689 struct tm *ltm = NULL;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001690 if (ptm == NULL)
1691 return MZ_PARAM_ERROR;
Viktor Szakatsce26dba2018-04-22 19:30:34 +00001692 ltm = localtime(&unix_time); // Returns a 1900-based year
Nathan Moinvaziri17fcdcd2017-10-26 08:13:13 -07001693 if (ltm == NULL)
Viktor Szakatsce26dba2018-04-22 19:30:34 +00001694 {
1695 // Invalid date stored, so don't return it
1696 memset(ptm, 0, sizeof(struct tm));
Nathan Moinvaziri17fcdcd2017-10-26 08:13:13 -07001697 return MZ_INTERNAL_ERROR;
Viktor Szakatsce26dba2018-04-22 19:30:34 +00001698 }
Nathan Moinvaziri17fcdcd2017-10-26 08:13:13 -07001699 memcpy(ptm, ltm, sizeof(struct tm));
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001700 return MZ_OK;
1701}
1702
1703uint32_t mz_zip_time_t_to_dos_date(time_t unix_time)
1704{
1705 struct tm ptm;
1706 mz_zip_time_t_to_tm(unix_time, &ptm);
1707 return mz_zip_tm_to_dosdate((const struct tm *)&ptm);
1708}
1709
1710uint32_t mz_zip_tm_to_dosdate(const struct tm *ptm)
1711{
Nathan Moinvazirie33916b2018-05-01 13:45:08 -07001712 struct tm fixed_tm;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001713
1714 // Years supported:
1715
1716 // [00, 79] (assumed to be between 2000 and 2079)
1717 // [80, 207] (assumed to be between 1980 and 2107, typical output of old
1718 // software that does 'year-1900' to get a double digit year)
1719 // [1980, 2107] (due to format limitations, only years 1980-2107 can be stored.)
1720
1721 memcpy(&fixed_tm, ptm, sizeof(struct tm));
1722 if (fixed_tm.tm_year >= 1980) // range [1980, 2107]
1723 fixed_tm.tm_year -= 1980;
Viktor Szakatsce26dba2018-04-22 19:30:34 +00001724 else if (fixed_tm.tm_year >= 80) // range [80, 207]
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001725 fixed_tm.tm_year -= 80;
1726 else // range [00, 79]
1727 fixed_tm.tm_year += 20;
1728
Viktor Szakatsce26dba2018-04-22 19:30:34 +00001729 if (mz_zip_invalid_date(&fixed_tm))
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001730 return 0;
1731
1732 return (uint32_t)(((fixed_tm.tm_mday) + (32 * (fixed_tm.tm_mon + 1)) + (512 * fixed_tm.tm_year)) << 16) |
1733 ((fixed_tm.tm_sec / 2) + (32 * fixed_tm.tm_min) + (2048 * (uint32_t)fixed_tm.tm_hour));
1734}
1735
1736int32_t mz_zip_ntfs_to_unix_time(uint64_t ntfs_time, time_t *unix_time)
1737{
1738 *unix_time = (time_t)((ntfs_time - 116444736000000000LL) / 10000000);
1739 return MZ_OK;
1740}
1741
1742int32_t mz_zip_unix_to_ntfs_time(time_t unix_time, uint64_t *ntfs_time)
1743{
1744 *ntfs_time = ((uint64_t)unix_time * 10000000) + 116444736000000000LL;
1745 return MZ_OK;
1746}