blob: a2edf7812303bc962b89b8b394275053f889daa2 [file] [log] [blame]
Antoine Cœur0c2fceb2017-10-05 15:30:37 +08001/* zip.c -- Zip manipulation
Nathan Moinvaziriae724ef2018-07-14 09:53:56 -07002 Version 2.3.8, July 14, 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 {
Nathan Moinvaziri446d0622018-07-16 19:17:46 -0700504 if ((mode & MZ_OPEN_MODE_CREATE) == 0)
505 err = mz_zip_read_cd(zip);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700506
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700507 if ((err == MZ_OK) && (mode & MZ_OPEN_MODE_APPEND))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700508 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700509 if (zip->cd_size > 0)
510 {
511 // Store central directory in memory
512 err = mz_stream_seek(zip->stream, zip->cd_offset, MZ_SEEK_SET);
513 if (err == MZ_OK)
514 err = mz_stream_copy(zip->cd_mem_stream, zip->stream, (uint32_t)zip->cd_size);
515 if (err == MZ_OK)
516 err = mz_stream_seek(zip->stream, zip->cd_offset, MZ_SEEK_SET);
517 }
518 else
519 {
520 // If no central directory, append new zip to end of file
521 err = mz_stream_seek(zip->stream, 0, MZ_SEEK_END);
522 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700523 }
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700524 else
525 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700526 zip->cd_start_pos = zip->cd_offset;
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700527 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700528 }
529
530 if (err == MZ_OK)
531 {
532 mz_stream_mem_create(&zip->file_info_stream);
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700533 mz_stream_mem_open(zip->file_info_stream, NULL, MZ_OPEN_MODE_CREATE);
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700534 mz_stream_mem_create(&zip->local_file_info_stream);
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700535 mz_stream_mem_open(zip->local_file_info_stream, NULL, MZ_OPEN_MODE_CREATE);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700536 }
537
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700538 if (err != MZ_OK)
539 {
Nathan Moinvaziri3f7de8e2017-12-11 21:09:52 -0800540 mz_zip_close(zip);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700541 return NULL;
542 }
543
Nathan Moinvaziri3f7de8e2017-12-11 21:09:52 -0800544 zip->open_mode = mode;
545
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700546 return zip;
547}
548
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -0800549extern int32_t mz_zip_close(void *handle)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700550{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700551 mz_zip *zip = (mz_zip *)handle;
552 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700553
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700554 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700555 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700556
557 if (zip->entry_opened == 1)
558 {
559 err = mz_zip_entry_close(handle);
560 if (err != MZ_OK)
561 return err;
562 }
563
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700564 if (zip->open_mode & MZ_OPEN_MODE_WRITE)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700565 err = mz_zip_write_cd(handle);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700566
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700567 if (zip->cd_mem_stream != NULL)
Nathan Moinvaziri51bf64a2017-10-20 14:23:37 -0700568 {
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700569 mz_stream_close(zip->cd_mem_stream);
570 mz_stream_delete(&zip->cd_mem_stream);
Nathan Moinvaziri51bf64a2017-10-20 14:23:37 -0700571 }
572
Nathan Moinvaziri3f7de8e2017-12-11 21:09:52 -0800573 if (zip->file_info_stream != NULL)
574 {
575 mz_stream_mem_close(zip->file_info_stream);
576 mz_stream_mem_delete(&zip->file_info_stream);
577 }
578 if (zip->local_file_info_stream != NULL)
579 {
580 mz_stream_mem_close(zip->local_file_info_stream);
581 mz_stream_mem_delete(&zip->local_file_info_stream);
582 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700583
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700584 if (zip->comment)
Nathan Moinvaziri5244fc02018-05-02 20:01:35 -0700585 MZ_FREE(zip->comment);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700586
Nathan Moinvaziri5244fc02018-05-02 20:01:35 -0700587 MZ_FREE(zip);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800588
589 return err;
590}
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700591
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -0800592extern int32_t mz_zip_get_comment(void *handle, const char **comment)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700593{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700594 mz_zip *zip = (mz_zip *)handle;
595 if (zip == NULL || comment == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700596 return MZ_PARAM_ERROR;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700597 if (zip->comment == NULL)
598 return MZ_EXIST_ERROR;
599 *comment = zip->comment;
600 return MZ_OK;
601}
602
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -0800603extern int32_t mz_zip_set_comment(void *handle, const char *comment)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700604{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700605 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700606 uint16_t comment_size = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700607 if (zip == NULL || comment == NULL)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700608 return MZ_PARAM_ERROR;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700609 if (zip->comment != NULL)
Nathan Moinvaziri5244fc02018-05-02 20:01:35 -0700610 MZ_FREE(zip->comment);
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700611 comment_size = (uint16_t)(strlen(comment) + 1);
Nathan Moinvaziri5244fc02018-05-02 20:01:35 -0700612 zip->comment = (char *)MZ_ALLOC(comment_size);
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700613 strncpy(zip->comment, comment, comment_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700614 return MZ_OK;
615}
616
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -0800617extern int32_t mz_zip_get_version_madeby(void *handle, uint16_t *version_madeby)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700618{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700619 mz_zip *zip = (mz_zip *)handle;
620 if (zip == NULL || version_madeby == NULL)
621 return MZ_PARAM_ERROR;
622 *version_madeby = zip->version_madeby;
623 return MZ_OK;
624}
625
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -0800626extern int32_t mz_zip_set_version_madeby(void *handle, uint16_t version_madeby)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700627{
628 mz_zip *zip = (mz_zip *)handle;
629 if (zip == NULL)
630 return MZ_PARAM_ERROR;
631 zip->version_madeby = version_madeby;
632 return MZ_OK;
633}
634
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700635// Get info about the current file in the zip file
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700636static 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 -0700637{
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700638 uint64_t ntfs_time = 0;
639 uint32_t reserved = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700640 uint32_t magic = 0;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700641 uint32_t dos_date = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700642 uint32_t extra_pos = 0;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700643 uint32_t extra_data_size_read = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700644 uint16_t extra_header_id = 0;
645 uint16_t extra_data_size = 0;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700646 uint16_t ntfs_attrib_id = 0;
647 uint16_t ntfs_attrib_size = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700648 uint16_t value16 = 0;
649 uint32_t value32 = 0;
650 uint64_t value64 = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700651 int64_t max_seek = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700652 int64_t seek = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700653 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700654
655
656 memset(file_info, 0, sizeof(mz_zip_file));
657
658 // Check the magic
659 err = mz_stream_read_uint32(stream, &magic);
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700660 if (err == MZ_END_OF_STREAM)
661 err = MZ_END_OF_LIST;
662 else if (magic == MZ_ZIP_MAGIC_ENDHEADER || magic == MZ_ZIP_MAGIC_ENDHEADER64)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700663 err = MZ_END_OF_LIST;
664 else if ((local) && (magic != MZ_ZIP_MAGIC_LOCALHEADER))
665 err = MZ_FORMAT_ERROR;
666 else if ((!local) && (magic != MZ_ZIP_MAGIC_CENTRALHEADER))
667 err = MZ_FORMAT_ERROR;
Viktor Szakats915b82e2018-04-24 10:02:39 +0000668
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700669 // Read header fields
670 if (err == MZ_OK)
671 {
672 if (!local)
673 err = mz_stream_read_uint16(stream, &file_info->version_madeby);
674 if (err == MZ_OK)
675 err = mz_stream_read_uint16(stream, &file_info->version_needed);
676 if (err == MZ_OK)
677 err = mz_stream_read_uint16(stream, &file_info->flag);
678 if (err == MZ_OK)
679 err = mz_stream_read_uint16(stream, &file_info->compression_method);
680 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700681 {
682 err = mz_stream_read_uint32(stream, &dos_date);
683 file_info->modified_date = mz_zip_dosdate_to_time_t(dos_date);
684 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700685 if (err == MZ_OK)
686 err = mz_stream_read_uint32(stream, &file_info->crc);
687 if (err == MZ_OK)
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700688 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700689 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700690 file_info->compressed_size = value32;
691 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700692 if (err == MZ_OK)
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700693 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700694 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700695 file_info->uncompressed_size = value32;
696 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700697 if (err == MZ_OK)
698 err = mz_stream_read_uint16(stream, &file_info->filename_size);
699 if (err == MZ_OK)
700 err = mz_stream_read_uint16(stream, &file_info->extrafield_size);
701 if (!local)
702 {
703 if (err == MZ_OK)
704 err = mz_stream_read_uint16(stream, &file_info->comment_size);
705 if (err == MZ_OK)
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700706 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700707 err = mz_stream_read_uint16(stream, &value16);
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700708 file_info->disk_number = value16;
709 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700710 if (err == MZ_OK)
711 err = mz_stream_read_uint16(stream, &file_info->internal_fa);
712 if (err == MZ_OK)
713 err = mz_stream_read_uint32(stream, &file_info->external_fa);
714 if (err == MZ_OK)
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700715 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700716 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700717 file_info->disk_offset = value32;
718 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700719 }
720 }
721
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700722 max_seek = file_info->filename_size + file_info->extrafield_size + file_info->comment_size + 3;
723 if (err == MZ_OK)
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700724 err = mz_stream_seek(file_info_stream, max_seek, MZ_SEEK_SET);
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700725 if (err == MZ_OK)
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700726 err = mz_stream_seek(file_info_stream, 0, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700727
728 if ((err == MZ_OK) && (file_info->filename_size > 0))
729 {
Nathan Moinvaziria710bd72018-05-09 00:46:47 -0700730 mz_stream_mem_get_buffer(file_info_stream, (const void **)&file_info->filename);
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700731
732 err = mz_stream_copy(file_info_stream, stream, file_info->filename_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700733 if (err == MZ_OK)
734 err = mz_stream_write_uint8(file_info_stream, 0);
735
736 seek += file_info->filename_size + 1;
737 }
738
739 if ((err == MZ_OK) && (file_info->extrafield_size > 0))
740 {
Nathan Moinvaziria710bd72018-05-09 00:46:47 -0700741 mz_stream_mem_get_buffer_at(file_info_stream, seek, (const void **)&file_info->extrafield);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700742
743 err = mz_stream_copy(file_info_stream, stream, file_info->extrafield_size);
744 if (err == MZ_OK)
745 err = mz_stream_write_uint8(file_info_stream, 0);
746
747 // Seek back and parse the extra field
748 if (err == MZ_OK)
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700749 err = mz_stream_seek(file_info_stream, seek, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700750
751 seek += file_info->extrafield_size + 1;
752
753 while ((err == MZ_OK) && (extra_pos < file_info->extrafield_size))
754 {
755 err = mz_stream_read_uint16(file_info_stream, &extra_header_id);
756 if (err == MZ_OK)
757 err = mz_stream_read_uint16(file_info_stream, &extra_data_size);
758
759 // ZIP64 extra field
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700760 if (extra_header_id == MZ_ZIP_EXTENSION_ZIP64)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700761 {
762 if ((err == MZ_OK) && (file_info->uncompressed_size == UINT32_MAX))
763 err = mz_stream_read_uint64(file_info_stream, &file_info->uncompressed_size);
764 if ((err == MZ_OK) && (file_info->compressed_size == UINT32_MAX))
765 err = mz_stream_read_uint64(file_info_stream, &file_info->compressed_size);
766 if ((err == MZ_OK) && (file_info->disk_offset == UINT32_MAX))
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700767 err = mz_stream_read_uint64(file_info_stream, &file_info->disk_offset);
juanii4ae79922018-02-11 14:29:36 -0300768 if ((err == MZ_OK) && (file_info->disk_number == UINT16_MAX))
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700769 err = mz_stream_read_uint32(file_info_stream, &file_info->disk_number);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700770 }
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700771 // NTFS extra field
772 else if (extra_header_id == MZ_ZIP_EXTENSION_NTFS)
773 {
Nathan Moinvaziri7a3b6982018-05-09 00:45:02 -0700774 if (err == MZ_OK)
775 err = mz_stream_read_uint32(file_info_stream, &reserved);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700776 extra_data_size_read = 4;
777
778 while ((err == MZ_OK) && (extra_data_size_read < extra_data_size))
779 {
780 err = mz_stream_read_uint16(file_info_stream, &ntfs_attrib_id);
781 if (err == MZ_OK)
782 err = mz_stream_read_uint16(file_info_stream, &ntfs_attrib_size);
783
Nathan Moinvaziri7a3b6982018-05-09 00:45:02 -0700784 if ((err == MZ_OK) && (ntfs_attrib_id == 0x01) && (ntfs_attrib_size == 24))
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700785 {
786 err = mz_stream_read_uint64(file_info_stream, &ntfs_time);
787 mz_zip_ntfs_to_unix_time(ntfs_time, &file_info->modified_date);
788
juanii7063b0e2018-02-11 13:56:21 -0300789 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700790 {
791 err = mz_stream_read_uint64(file_info_stream, &ntfs_time);
792 mz_zip_ntfs_to_unix_time(ntfs_time, &file_info->accessed_date);
793 }
juanii7063b0e2018-02-11 13:56:21 -0300794 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700795 {
796 err = mz_stream_read_uint64(file_info_stream, &ntfs_time);
797 mz_zip_ntfs_to_unix_time(ntfs_time, &file_info->creation_date);
798 }
799 }
800 else
801 {
Nathan Moinvaziri7a3b6982018-05-09 00:45:02 -0700802 if (err == MZ_OK)
803 err = mz_stream_seek(file_info_stream, ntfs_attrib_size, MZ_SEEK_CUR);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700804 }
805
806 extra_data_size_read += ntfs_attrib_size + 4;
807 }
808 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700809#ifdef HAVE_AES
810 // AES extra field
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700811 else if (extra_header_id == MZ_ZIP_EXTENSION_AES)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700812 {
813 uint8_t value8 = 0;
814 // Verify version info
815 err = mz_stream_read_uint16(file_info_stream, &value16);
816 // Support AE-1 and AE-2
817 if (value16 != 1 && value16 != 2)
818 err = MZ_FORMAT_ERROR;
819 file_info->aes_version = value16;
820 if (err == MZ_OK)
821 err = mz_stream_read_uint8(file_info_stream, &value8);
822 if ((char)value8 != 'A')
823 err = MZ_FORMAT_ERROR;
824 if (err == MZ_OK)
825 err = mz_stream_read_uint8(file_info_stream, &value8);
826 if ((char)value8 != 'E')
827 err = MZ_FORMAT_ERROR;
828 // Get AES encryption strength and actual compression method
829 if (err == MZ_OK)
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700830 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700831 err = mz_stream_read_uint8(file_info_stream, &value8);
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700832 file_info->aes_encryption_mode = value8;
833 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700834 if (err == MZ_OK)
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700835 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700836 err = mz_stream_read_uint16(file_info_stream, &value16);
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700837 file_info->compression_method = value16;
838 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700839 }
840#endif
841 else
842 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700843 err = mz_stream_seek(file_info_stream, extra_data_size, MZ_SEEK_CUR);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700844 }
845
846 extra_pos += 4 + extra_data_size;
847 }
848 }
849
850 if ((err == MZ_OK) && (file_info->comment_size > 0))
851 {
Nathan Moinvaziria710bd72018-05-09 00:46:47 -0700852 mz_stream_mem_get_buffer_at(file_info_stream, seek, (const void **)&file_info->comment);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700853
854 err = mz_stream_copy(file_info_stream, stream, file_info->comment_size);
855 if (err == MZ_OK)
856 err = mz_stream_write_uint8(file_info_stream, 0);
857 }
858
859 return err;
860}
861
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700862static int32_t mz_zip_entry_write_header(void *stream, uint8_t local, mz_zip_file *file_info)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700863{
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700864 uint64_t ntfs_time = 0;
865 uint32_t reserved = 0;
866 uint32_t dos_date = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700867 uint16_t extrafield_size = 0;
868 uint16_t extrafield_zip64_size = 0;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700869 uint16_t extrafield_ntfs_size = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700870 uint16_t filename_size = 0;
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -0700871 uint16_t filename_length = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700872 uint16_t comment_size = 0;
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700873 uint16_t version_needed = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700874 uint8_t zip64 = 0;
875 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700876
877 if (file_info == NULL)
878 return MZ_PARAM_ERROR;
879
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700880 // Calculate extra field sizes
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700881 extrafield_size = file_info->extrafield_size;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700882
883 if (file_info->uncompressed_size >= UINT32_MAX)
884 extrafield_zip64_size += 8;
885 if (file_info->compressed_size >= UINT32_MAX)
886 extrafield_zip64_size += 8;
887 if (file_info->disk_offset >= UINT32_MAX)
888 extrafield_zip64_size += 8;
889
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700890 if (file_info->zip64 == MZ_ZIP64_AUTO)
Nathan Moinvaziri121e0882018-05-03 17:57:20 -0700891 {
892 // If uncompressed size is unknown, assume zip64 for 64-bit data descriptors
893 zip64 = (local && file_info->uncompressed_size == 0) || (extrafield_zip64_size > 0);
894 }
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700895 else if (file_info->zip64 == MZ_ZIP64_FORCE)
Nathan Moinvaziria56a08c2018-05-03 09:35:37 -0700896 zip64 = 1;
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700897 else if (file_info->zip64 == MZ_ZIP64_DISABLE)
Nathan Moinvaziria56a08c2018-05-03 09:35:37 -0700898 {
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700899 // Zip64 extension is required to zip file
900 if (extrafield_zip64_size > 0)
901 return MZ_PARAM_ERROR;
Nathan Moinvaziria56a08c2018-05-03 09:35:37 -0700902 }
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700903
904 if (zip64)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700905 {
906 extrafield_size += 4;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700907 extrafield_size += extrafield_zip64_size;
908 }
909#ifdef HAVE_AES
910 if ((file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
911 extrafield_size += 4 + 7;
912#endif
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700913 // NTFS timestamps
Nathan Moinvazirid9e159a2018-02-13 15:30:30 -0800914 if ((file_info->modified_date != 0) &&
915 (file_info->accessed_date != 0) &&
916 (file_info->creation_date != 0))
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700917 {
juanii3679a3d2018-02-11 13:55:38 -0300918 extrafield_ntfs_size += 8 + 8 + 8 + 4 + 2 + 2;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700919 extrafield_size += 4;
920 extrafield_size += extrafield_ntfs_size;
921 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700922
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700923 if (local)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700924 err = mz_stream_write_uint32(stream, MZ_ZIP_MAGIC_LOCALHEADER);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700925 else
926 {
927 err = mz_stream_write_uint32(stream, MZ_ZIP_MAGIC_CENTRALHEADER);
928 if (err == MZ_OK)
929 err = mz_stream_write_uint16(stream, file_info->version_madeby);
930 }
931
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700932 // Calculate version needed to extract
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700933 if (err == MZ_OK)
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700934 {
935 version_needed = file_info->version_needed;
936 if (version_needed == 0)
937 {
938 version_needed = 20;
939 if (zip64)
940 version_needed = 45;
941#ifdef HAVE_AES
942 if ((file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
943 version_needed = 51;
944#endif
945#ifdef HAVE_LZMA
946 if (file_info->compression_method == MZ_COMPRESS_METHOD_LZMA)
947 version_needed = 63;
948#endif
949 }
950 err = mz_stream_write_uint16(stream, version_needed);
951 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700952 if (err == MZ_OK)
953 err = mz_stream_write_uint16(stream, file_info->flag);
954 if (err == MZ_OK)
955 {
956#ifdef HAVE_AES
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700957 if (file_info->aes_version)
958 err = mz_stream_write_uint16(stream, MZ_COMPRESS_METHOD_AES);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700959 else
960#endif
961 err = mz_stream_write_uint16(stream, file_info->compression_method);
962 }
963 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700964 {
Nathan Moinvaziri17cdaec2017-10-26 10:18:41 -0700965 if (file_info->modified_date != 0)
966 dos_date = mz_zip_time_t_to_dos_date(file_info->modified_date);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700967 err = mz_stream_write_uint32(stream, dos_date);
968 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700969
970 if (err == MZ_OK)
971 err = mz_stream_write_uint32(stream, file_info->crc); // crc
972 if (err == MZ_OK)
973 {
974 if (file_info->compressed_size >= UINT32_MAX) // compr size
975 err = mz_stream_write_uint32(stream, UINT32_MAX);
976 else
977 err = mz_stream_write_uint32(stream, (uint32_t)file_info->compressed_size);
978 }
979 if (err == MZ_OK)
980 {
981 if (file_info->uncompressed_size >= UINT32_MAX) // uncompr size
982 err = mz_stream_write_uint32(stream, UINT32_MAX);
983 else
984 err = mz_stream_write_uint32(stream, (uint32_t)file_info->uncompressed_size);
985 }
986
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -0700987 filename_length = (uint16_t)strlen(file_info->filename);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700988 if (err == MZ_OK)
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -0700989 {
990 filename_size = filename_length;
991 if (mz_zip_attrib_is_dir(file_info->external_fa, file_info->version_madeby) == MZ_OK)
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700992 {
993 if ((file_info->filename[filename_length - 1] == '/') ||
994 (file_info->filename[filename_length - 1] == '\\'))
995 filename_length -= 1;
996 else
997 filename_size += 1;
998 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700999 err = mz_stream_write_uint16(stream, filename_size);
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -07001000 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001001 if (err == MZ_OK)
1002 err = mz_stream_write_uint16(stream, extrafield_size);
1003
1004 if (!local)
1005 {
1006 if (file_info->comment != NULL)
1007 comment_size = (uint16_t)strlen(file_info->comment);
1008 if (err == MZ_OK)
1009 err = mz_stream_write_uint16(stream, comment_size);
1010 if (err == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -07001011 err = mz_stream_write_uint16(stream, (uint16_t)file_info->disk_number);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001012 if (err == MZ_OK)
1013 err = mz_stream_write_uint16(stream, file_info->internal_fa);
1014 if (err == MZ_OK)
1015 err = mz_stream_write_uint32(stream, file_info->external_fa);
1016 if (err == MZ_OK)
1017 {
1018 if (file_info->disk_offset >= UINT32_MAX)
1019 err = mz_stream_write_uint32(stream, UINT32_MAX);
1020 else
1021 err = mz_stream_write_uint32(stream, (uint32_t)file_info->disk_offset);
1022 }
1023 }
1024
1025 if (err == MZ_OK)
1026 {
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -07001027 if (mz_stream_write(stream, file_info->filename, filename_length) != filename_length)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001028 err = MZ_STREAM_ERROR;
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -07001029 if (err == MZ_OK)
1030 {
Nathan Moinvaziri240b3b62018-05-02 13:38:14 -07001031 // Ensure that directories have a slash appended to them for compatibility
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -07001032 if (mz_zip_attrib_is_dir(file_info->external_fa, file_info->version_madeby) == MZ_OK)
1033 err = mz_stream_write_uint8(stream, '/');
1034 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001035 }
1036 if (err == MZ_OK)
1037 {
1038 if (mz_stream_write(stream, file_info->extrafield, file_info->extrafield_size) != file_info->extrafield_size)
1039 err = MZ_STREAM_ERROR;
1040 }
1041 // Add ZIP64 extra info header to central directory
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001042 if ((err == MZ_OK) && (zip64))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001043 {
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07001044 err = mz_stream_write_uint16(stream, MZ_ZIP_EXTENSION_ZIP64);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001045 if (err == MZ_OK)
1046 err = mz_stream_write_uint16(stream, extrafield_zip64_size);
1047 if ((err == MZ_OK) && (file_info->uncompressed_size >= UINT32_MAX))
1048 err = mz_stream_write_uint64(stream, file_info->uncompressed_size);
1049 if ((err == MZ_OK) && (file_info->compressed_size >= UINT32_MAX))
1050 err = mz_stream_write_uint64(stream, file_info->compressed_size);
1051 if ((err == MZ_OK) && (file_info->disk_offset >= UINT32_MAX))
1052 err = mz_stream_write_uint64(stream, file_info->disk_offset);
1053 }
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001054 // Write NTFS timestamps
1055 if ((err == MZ_OK) && (extrafield_ntfs_size > 0))
1056 {
1057 err = mz_stream_write_uint16(stream, MZ_ZIP_EXTENSION_NTFS);
1058 if (err == MZ_OK)
1059 err = mz_stream_write_uint16(stream, extrafield_ntfs_size);
1060 if (err == MZ_OK)
1061 err = mz_stream_write_uint32(stream, reserved);
1062 if (err == MZ_OK)
1063 err = mz_stream_write_uint16(stream, 0x01);
1064 if (err == MZ_OK)
1065 err = mz_stream_write_uint16(stream, extrafield_ntfs_size - 8);
juanii3679a3d2018-02-11 13:55:38 -03001066 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001067 {
1068 mz_zip_unix_to_ntfs_time(file_info->modified_date, &ntfs_time);
1069 err = mz_stream_write_uint64(stream, ntfs_time);
1070 }
juanii3679a3d2018-02-11 13:55:38 -03001071 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001072 {
1073 mz_zip_unix_to_ntfs_time(file_info->accessed_date, &ntfs_time);
1074 err = mz_stream_write_uint64(stream, ntfs_time);
1075 }
juanii3679a3d2018-02-11 13:55:38 -03001076 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001077 {
1078 mz_zip_unix_to_ntfs_time(file_info->creation_date, &ntfs_time);
1079 err = mz_stream_write_uint64(stream, ntfs_time);
1080 }
1081 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001082#ifdef HAVE_AES
1083 // Write AES extra info header to central directory
1084 if ((err == MZ_OK) && (file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
1085 {
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07001086 err = mz_stream_write_uint16(stream, MZ_ZIP_EXTENSION_AES);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001087 if (err == MZ_OK)
1088 err = mz_stream_write_uint16(stream, 7);
1089 if (err == MZ_OK)
1090 err = mz_stream_write_uint16(stream, file_info->aes_version);
1091 if (err == MZ_OK)
1092 err = mz_stream_write_uint8(stream, 'A');
1093 if (err == MZ_OK)
1094 err = mz_stream_write_uint8(stream, 'E');
1095 if (err == MZ_OK)
1096 err = mz_stream_write_uint8(stream, file_info->aes_encryption_mode);
1097 if (err == MZ_OK)
1098 err = mz_stream_write_uint16(stream, file_info->compression_method);
1099 }
1100#endif
1101 if ((err == MZ_OK) && (file_info->comment != NULL))
1102 {
1103 if (mz_stream_write(stream, file_info->comment, file_info->comment_size) != MZ_OK)
1104 err = MZ_STREAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001105 }
1106
1107 return err;
1108}
1109
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001110static 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 -07001111{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001112 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001113 int64_t max_total_in = 0;
Nathan Moinvazirifa8105b2018-07-08 19:05:00 -07001114 int64_t header_size = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001115 int64_t footer_size = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001116 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001117
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001118 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001119 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001120
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001121 zip->compression_method = compression_method;
1122
1123 switch (zip->compression_method)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001124 {
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001125 case MZ_COMPRESS_METHOD_RAW:
1126 case MZ_COMPRESS_METHOD_DEFLATE:
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001127#ifdef HAVE_BZIP2
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001128 case MZ_COMPRESS_METHOD_BZIP2:
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001129#endif
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001130#if HAVE_LZMA
1131 case MZ_COMPRESS_METHOD_LZMA:
1132#endif
1133 err = MZ_OK;
1134 break;
1135 default:
1136 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001137 }
1138
Nathan Moinvaziriee614ff2018-07-12 12:36:33 -07001139 if ((err == MZ_OK) && (zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED) &&
1140 (zip->compression_method != MZ_COMPRESS_METHOD_RAW || password != NULL))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001141 {
1142#ifdef HAVE_AES
1143 if (zip->file_info.aes_version)
1144 {
1145 mz_stream_aes_create(&zip->crypt_stream);
1146 mz_stream_aes_set_password(zip->crypt_stream, password);
1147 mz_stream_aes_set_encryption_mode(zip->crypt_stream, zip->file_info.aes_encryption_mode);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001148 }
1149 else
1150#endif
1151 {
Nathan Moinvaziri0e5c9dc2018-05-23 20:21:48 -07001152#ifdef HAVE_PKCRYPT
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001153 uint8_t verify1 = 0;
1154 uint8_t verify2 = 0;
1155
1156 // Info-ZIP modification to ZipCrypto format:
1157 // If bit 3 of the general purpose bit flag is set, it uses high byte of 16-bit File Time.
1158
Nathan Moinvaziri18a30652017-12-07 06:59:53 -08001159 if (zip->file_info.flag & MZ_ZIP_FLAG_DATA_DESCRIPTOR)
1160 {
1161 uint32_t dos_date = 0;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001162
Nathan Moinvaziri18a30652017-12-07 06:59:53 -08001163 dos_date = mz_zip_time_t_to_dos_date(zip->file_info.modified_date);
1164
1165 verify1 = (uint8_t)((dos_date >> 16) & 0xff);
1166 verify2 = (uint8_t)((dos_date >> 8) & 0xff);
1167 }
1168 else
1169 {
1170 verify1 = (uint8_t)((zip->file_info.crc >> 16) & 0xff);
1171 verify2 = (uint8_t)((zip->file_info.crc >> 24) & 0xff);
1172 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001173
Nathan Moinvaziri0e5c9dc2018-05-23 20:21:48 -07001174 mz_stream_pkcrypt_create(&zip->crypt_stream);
1175 mz_stream_pkcrypt_set_password(zip->crypt_stream, password);
1176 mz_stream_pkcrypt_set_verify(zip->crypt_stream, verify1, verify2);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001177#endif
1178 }
1179 }
1180
1181 if (err == MZ_OK)
1182 {
1183 if (zip->crypt_stream == NULL)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001184 mz_stream_raw_create(&zip->crypt_stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001185
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001186 mz_stream_set_base(zip->crypt_stream, zip->stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001187
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001188 err = mz_stream_open(zip->crypt_stream, NULL, zip->open_mode);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001189 }
1190
1191 if (err == MZ_OK)
1192 {
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001193 if (zip->compression_method == MZ_COMPRESS_METHOD_RAW)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001194 mz_stream_raw_create(&zip->compress_stream);
Andy Maloney3909c232018-05-22 09:02:09 -04001195#ifdef HAVE_ZLIB
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001196 else if (zip->compression_method == MZ_COMPRESS_METHOD_DEFLATE)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001197 mz_stream_zlib_create(&zip->compress_stream);
Andy Maloney3909c232018-05-22 09:02:09 -04001198#endif
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001199#ifdef HAVE_BZIP2
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001200 else if (zip->compression_method == MZ_COMPRESS_METHOD_BZIP2)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001201 mz_stream_bzip_create(&zip->compress_stream);
1202#endif
1203#ifdef HAVE_LZMA
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001204 else if (zip->compression_method == MZ_COMPRESS_METHOD_LZMA)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001205 mz_stream_lzma_create(&zip->compress_stream);
1206#endif
1207 else
1208 err = MZ_PARAM_ERROR;
1209 }
1210
1211 if (err == MZ_OK)
1212 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001213 if (zip->open_mode & MZ_OPEN_MODE_WRITE)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001214 {
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001215 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_COMPRESS_LEVEL, compress_level);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001216 }
1217 else
1218 {
Nathan Moinvaziri5e8f4d72018-03-15 10:43:18 -07001219 if (zip->compression_method == MZ_COMPRESS_METHOD_RAW || zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001220 {
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001221 max_total_in = zip->file_info.compressed_size;
Nathan Moinvazirifa8105b2018-07-08 19:05:00 -07001222 mz_stream_set_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_TOTAL_IN_MAX, max_total_in);
1223
1224 if (mz_stream_get_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_HEADER_SIZE, &header_size) == MZ_OK)
1225 max_total_in -= header_size;
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001226 if (mz_stream_get_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_FOOTER_SIZE, &footer_size) == MZ_OK)
1227 max_total_in -= footer_size;
Nathan Moinvazirifa8105b2018-07-08 19:05:00 -07001228
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001229 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_IN_MAX, max_total_in);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001230 }
Nathan Moinvaziri5e8f4d72018-03-15 10:43:18 -07001231 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 -03001232 {
1233 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_IN_MAX, zip->file_info.compressed_size);
1234 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_OUT_MAX, zip->file_info.uncompressed_size);
1235 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001236 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001237
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001238 mz_stream_set_base(zip->compress_stream, zip->crypt_stream);
1239
1240 err = mz_stream_open(zip->compress_stream, NULL, zip->open_mode);
1241 }
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001242 if (err == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001243 {
1244 mz_stream_crc32_create(&zip->crc32_stream);
1245 mz_stream_set_base(zip->crc32_stream, zip->compress_stream);
1246
1247 err = mz_stream_open(zip->crc32_stream, NULL, zip->open_mode);
1248 }
1249
1250 if (err == MZ_OK)
1251 {
1252 zip->entry_opened = 1;
1253 }
1254
1255 return err;
1256}
1257
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001258extern int32_t mz_zip_entry_read_open(void *handle, int16_t raw, const char *password)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001259{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001260 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001261 int16_t compression_method = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001262 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001263
Nathan Moinvaziri40427632018-07-22 11:12:40 -07001264#if defined(MZ_ZIP_NO_ENCRYPTION)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001265 if (password != NULL)
1266 return MZ_PARAM_ERROR;
1267#endif
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001268 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001269 return MZ_PARAM_ERROR;
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001270 if ((zip->open_mode & MZ_OPEN_MODE_READ) == 0)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001271 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001272 if (zip->entry_scanned == 0)
1273 return MZ_PARAM_ERROR;
Nathan Moinvaziri0f09a002018-04-23 18:48:30 -07001274 if ((zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED) && (password == NULL) && (!raw))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001275 return MZ_PARAM_ERROR;
1276
Nathan Moinvazirifd039e32017-10-22 14:40:39 -07001277 if (zip->file_info.disk_number == zip->disk_number_with_cd)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001278 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
1279 else
Nathan Moinvazirifd039e32017-10-22 14:40:39 -07001280 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, zip->file_info.disk_number);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001281
Nathan Moinvaziriee614ff2018-07-12 12:36:33 -07001282 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 -07001283 if (err == MZ_OK)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001284 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 -07001285
Nathan Moinvaziri51f72502017-10-26 10:15:21 -07001286 compression_method = zip->file_info.compression_method;
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001287 if (raw)
1288 compression_method = MZ_COMPRESS_METHOD_RAW;
1289
Nathan Moinvaziria6d1f662018-07-22 10:35:49 -07001290#ifdef MZ_ZIP_NO_DECOMPRESSION
Nathan Moinvaziri0a9282d2018-06-19 11:59:07 -07001291 if (compression_method != MZ_COMPRESS_METHOD_RAW)
1292 err = MZ_SUPPORT_ERROR;
1293#endif
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001294 if (err == MZ_OK)
Nathan Moinvaziri903b73d2017-10-20 08:47:19 -07001295 err = mz_zip_entry_open_int(handle, compression_method, 0, password);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001296
1297 return err;
1298}
1299
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -07001300extern 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 -07001301{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001302 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001303 int64_t disk_number = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001304 int32_t err = MZ_OK;
Nathan Moinvaziri51bf64a2017-10-20 14:23:37 -07001305 int16_t compression_method = 0;
1306
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001307
Nathan Moinvaziri40427632018-07-22 11:12:40 -07001308#if defined(MZ_ZIP_NO_ENCRYPTION)
tz-lomb1b25802017-11-10 15:03:02 +03001309 if (password != NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001310 return MZ_PARAM_ERROR;
1311#endif
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001312 if (zip == NULL || file_info == NULL || file_info->filename == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001313 return MZ_PARAM_ERROR;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001314
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001315 if (zip->entry_opened == 1)
1316 {
1317 err = mz_zip_entry_close(handle);
1318 if (err != MZ_OK)
1319 return err;
1320 }
1321
1322 memcpy(&zip->file_info, file_info, sizeof(mz_zip_file));
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001323
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001324 compression_method = zip->file_info.compression_method;
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001325
1326 if (compression_method == MZ_COMPRESS_METHOD_DEFLATE)
1327 {
1328 if ((compress_level == 8) || (compress_level == 9))
1329 zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_MAX;
1330 if (compress_level == 2)
1331 zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_FAST;
1332 if (compress_level == 1)
1333 zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_SUPER_FAST;
1334 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001335#ifdef HAVE_LZMA
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001336 else if (compression_method == MZ_COMPRESS_METHOD_LZMA)
1337 zip->file_info.flag |= MZ_ZIP_FLAG_LZMA_EOS_MARKER;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001338#endif
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001339
1340 zip->file_info.flag |= MZ_ZIP_FLAG_DATA_DESCRIPTOR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001341
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001342 if (password != NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001343 zip->file_info.flag |= MZ_ZIP_FLAG_ENCRYPTED;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001344
juaniib8887e92018-02-14 00:51:05 -03001345 mz_stream_get_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, &disk_number);
1346 zip->file_info.disk_number = (uint32_t)disk_number;
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001347
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001348 zip->file_info.disk_offset = mz_stream_tell(zip->stream);
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001349 zip->file_info.crc = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001350 zip->file_info.compressed_size = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001351
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001352#ifdef HAVE_AES
1353 if (zip->file_info.aes_version && zip->file_info.aes_encryption_mode == 0)
1354 zip->file_info.aes_encryption_mode = MZ_AES_ENCRYPTION_MODE_256;
1355#endif
1356
Nathan Moinvaziri4827f712018-05-02 10:50:47 -07001357 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 +00001358 compression_method = MZ_COMPRESS_METHOD_RAW;
1359
Nathan Moinvaziria6d1f662018-07-22 10:35:49 -07001360#ifdef MZ_ZIP_NO_COMPRESSION
Nathan Moinvaziri0a9282d2018-06-19 11:59:07 -07001361 if (compression_method != MZ_COMPRESS_METHOD_RAW)
1362 err = MZ_SUPPORT_ERROR;
1363#endif
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001364 if (err == MZ_OK)
1365 err = mz_zip_entry_write_header(zip->stream, 1, &zip->file_info);
1366 if (err == MZ_OK)
Nathan Moinvaziri51bf64a2017-10-20 14:23:37 -07001367 err = mz_zip_entry_open_int(handle, compression_method, compress_level, password);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001368
1369 return err;
1370}
1371
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001372extern int32_t mz_zip_entry_read(void *handle, void *buf, uint32_t len)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001373{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001374 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001375 int32_t read = 0;
1376
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001377 if (zip == NULL || zip->entry_opened == 0)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001378 return MZ_PARAM_ERROR;
Nathan Moinvaziri930f9182018-01-22 08:51:07 -08001379 if (UINT_MAX == UINT16_MAX && len > UINT16_MAX) // Zlib limitation
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001380 return MZ_PARAM_ERROR;
Nathan Moinvazirid7814e92018-07-11 14:54:14 -07001381 if (len == 0)
1382 return MZ_PARAM_ERROR;
1383
Nathan Moinvazirieb80cd92018-07-11 16:45:09 -07001384 if (zip->file_info.compressed_size == 0)
1385 return 0;
1386
Nathan Moinvazirid7814e92018-07-11 14:54:14 -07001387 // Read entire entry even if uncompressed_size = 0, otherwise
1388 // aes encryption validation will fail if compressed_size > 0
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001389 read = mz_stream_read(zip->crc32_stream, buf, len);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001390 return read;
1391}
1392
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001393extern int32_t mz_zip_entry_write(void *handle, const void *buf, uint32_t len)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001394{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001395 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri0a9282d2018-06-19 11:59:07 -07001396 int32_t written = 0;
1397
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001398 if (zip == NULL || zip->entry_opened == 0)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001399 return MZ_PARAM_ERROR;
Nathan Moinvaziri0a9282d2018-06-19 11:59:07 -07001400 written = mz_stream_write(zip->crc32_stream, buf, len);
1401 return written;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001402}
1403
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001404extern int32_t mz_zip_entry_get_info(void *handle, mz_zip_file **file_info)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001405{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001406 mz_zip *zip = (mz_zip *)handle;
1407 if (zip == NULL || zip->entry_scanned == 0)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001408 return MZ_PARAM_ERROR;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001409 *file_info = &zip->file_info;
1410 return MZ_OK;
1411}
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001412
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001413extern int32_t mz_zip_entry_get_local_info(void *handle, mz_zip_file **local_file_info)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001414{
1415 mz_zip *zip = (mz_zip *)handle;
1416 if (zip == NULL || zip->entry_scanned == 0 || zip->entry_opened == 0)
1417 return MZ_PARAM_ERROR;
1418 *local_file_info = &zip->local_file_info;
1419 return MZ_OK;
1420}
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001421
Nathan Moinvaziri6ac2ff42018-07-21 14:29:29 -07001422static int32_t mz_zip_entry_close_int(void *handle, int16_t raw, uint64_t uncompressed_size, uint32_t crc32)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001423{
1424 mz_zip *zip = (mz_zip *)handle;
1425 uint64_t compressed_size = 0;
Nathan Moinvaziri8871f6c2018-07-08 19:23:56 -07001426 int64_t total_in = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001427 int32_t err = MZ_OK;
1428
1429 if (zip == NULL || zip->entry_opened == 0)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001430 return MZ_PARAM_ERROR;
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001431
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001432 mz_stream_close(zip->compress_stream);
Nathan Moinvaziri6ac2ff42018-07-21 14:29:29 -07001433
Nathan Moinvaziri16d9f472018-07-21 14:37:56 -07001434 if (raw == 0)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001435 crc32 = mz_stream_crc32_get_value(zip->crc32_stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001436
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001437 if ((zip->open_mode & MZ_OPEN_MODE_WRITE) == 0)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001438 {
1439#ifdef HAVE_AES
1440 // AES zip version AE-1 will expect a valid crc as well
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001441 if (zip->file_info.aes_version <= 0x0001)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001442#endif
1443 {
Nathan Moinvaziri6ac2ff42018-07-21 14:29:29 -07001444 mz_stream_get_prop_int64(zip->crc32_stream, MZ_STREAM_PROP_TOTAL_IN, &total_in);
Nathan Moinvaziri184f4cb2018-07-09 07:53:17 -07001445 // If entire entry was not read this will fail
Nathan Moinvaziri8871f6c2018-07-08 19:23:56 -07001446 if ((total_in > 0) && (zip->compression_method != MZ_COMPRESS_METHOD_RAW))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001447 {
1448 if (crc32 != zip->file_info.crc)
1449 err = MZ_CRC_ERROR;
1450 }
1451 }
1452 }
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001453
1454 mz_stream_get_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_OUT, (int64_t *)&compressed_size);
Nathan Moinvaziri16d9f472018-07-21 14:37:56 -07001455 if (raw == 0)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001456 mz_stream_get_prop_int64(zip->crc32_stream, MZ_STREAM_PROP_TOTAL_OUT, (int64_t *)&uncompressed_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001457
1458 if (zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED)
1459 {
1460 mz_stream_set_base(zip->crypt_stream, zip->stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001461 err = mz_stream_close(zip->crypt_stream);
1462
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001463 mz_stream_get_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_TOTAL_OUT, (int64_t *)&compressed_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001464 }
1465
1466 mz_stream_delete(&zip->crypt_stream);
1467
1468 mz_stream_delete(&zip->compress_stream);
1469 mz_stream_crc32_delete(&zip->crc32_stream);
1470
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001471 if (zip->open_mode & MZ_OPEN_MODE_WRITE)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001472 {
1473 if (err == MZ_OK)
1474 {
1475 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_DATADESCRIPTOR);
1476 if (err == MZ_OK)
1477 err = mz_stream_write_uint32(zip->stream, crc32);
1478 if (err == MZ_OK)
1479 {
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001480 if (zip->file_info.uncompressed_size <= UINT32_MAX)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001481 err = mz_stream_write_uint32(zip->stream, (uint32_t)compressed_size);
Andrew Gunnerson3c52d212018-01-05 22:28:12 -05001482 else
1483 err = mz_stream_write_uint64(zip->stream, compressed_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001484 }
1485 if (err == MZ_OK)
1486 {
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001487 if (zip->file_info.uncompressed_size <= UINT32_MAX)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001488 err = mz_stream_write_uint32(zip->stream, (uint32_t)uncompressed_size);
Andrew Gunnerson3c52d212018-01-05 22:28:12 -05001489 else
1490 err = mz_stream_write_uint64(zip->stream, uncompressed_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001491 }
1492 }
1493
1494 zip->file_info.crc = crc32;
1495 zip->file_info.compressed_size = compressed_size;
1496 zip->file_info.uncompressed_size = uncompressed_size;
1497
1498 if (err == MZ_OK)
Nathan Moinvaziricda36002017-10-21 09:37:18 -07001499 err = mz_zip_entry_write_header(zip->cd_mem_stream, 0, &zip->file_info);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001500
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07001501 zip->number_entry += 1;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001502 }
1503
1504 zip->entry_opened = 0;
1505
1506 return err;
1507}
1508
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001509extern int32_t mz_zip_entry_close(void *handle)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001510{
Nathan Moinvaziri6ac2ff42018-07-21 14:29:29 -07001511 return mz_zip_entry_close_int(handle, 0, 0, 0);
1512}
1513
1514extern int32_t mz_zip_entry_close_raw(void *handle, uint64_t uncompressed_size, uint32_t crc32)
1515{
1516 return mz_zip_entry_close_int(handle, 1, uncompressed_size, crc32);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001517}
1518
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001519static int32_t mz_zip_goto_next_entry_int(void *handle)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001520{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001521 mz_zip *zip = (mz_zip *)handle;
1522 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001523
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001524 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001525 return MZ_PARAM_ERROR;
1526
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001527 zip->entry_scanned = 0;
1528
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001529 mz_stream_set_prop_int64(zip->cd_stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
Viktor Szakats915b82e2018-04-24 10:02:39 +00001530
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001531 err = mz_stream_seek(zip->cd_stream, zip->cd_current_pos, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001532 if (err == MZ_OK)
Nathan Moinvaziricda36002017-10-21 09:37:18 -07001533 err = mz_zip_entry_read_header(zip->cd_stream, 0, &zip->file_info, zip->file_info_stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001534 if (err == MZ_OK)
1535 zip->entry_scanned = 1;
1536 return err;
1537}
1538
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001539extern int32_t mz_zip_get_number_entry(void *handle, int64_t *number_entry)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07001540{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001541 mz_zip *zip = (mz_zip *)handle;
1542 if (zip == NULL || number_entry == NULL)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07001543 return MZ_PARAM_ERROR;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07001544 *number_entry = zip->number_entry;
1545 return MZ_OK;
1546}
1547
Viktor Szakats0f0535f2018-05-02 13:15:58 +00001548extern int32_t mz_zip_get_disk_number_with_cd(void *handle, uint32_t *disk_number_with_cd)
juanii566b9782018-02-10 15:07:54 -03001549{
1550 mz_zip *zip = (mz_zip *)handle;
1551 if (zip == NULL || disk_number_with_cd == NULL)
1552 return MZ_PARAM_ERROR;
1553 *disk_number_with_cd = zip->disk_number_with_cd;
1554 return MZ_OK;
1555}
1556
Viktor Szakatsce26dba2018-04-22 19:30:34 +00001557extern int64_t mz_zip_get_entry(void *handle)
1558{
1559 mz_zip *zip = (mz_zip *)handle;
1560
1561 if (zip == NULL)
1562 return MZ_PARAM_ERROR;
1563
1564 return zip->cd_current_pos;
1565}
1566
1567extern int32_t mz_zip_goto_entry(void *handle, uint64_t cd_pos)
1568{
1569 mz_zip *zip = (mz_zip *)handle;
1570
1571 if (zip == NULL)
1572 return MZ_PARAM_ERROR;
1573
1574 if (cd_pos < zip->cd_start_pos || cd_pos > zip->cd_start_pos + zip->cd_size)
1575 return MZ_PARAM_ERROR;
1576
1577 zip->cd_current_pos = cd_pos;
1578
1579 return mz_zip_goto_next_entry_int(handle);
1580}
1581
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001582extern int32_t mz_zip_goto_first_entry(void *handle)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001583{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001584 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001585
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001586 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001587 return MZ_PARAM_ERROR;
1588
Nathan Moinvaziricda36002017-10-21 09:37:18 -07001589 zip->cd_current_pos = zip->cd_start_pos;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001590
1591 return mz_zip_goto_next_entry_int(handle);
1592}
1593
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001594extern int32_t mz_zip_goto_next_entry(void *handle)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001595{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001596 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001597
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001598 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001599 return MZ_PARAM_ERROR;
1600
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001601 zip->cd_current_pos += MZ_ZIP_SIZE_CD_ITEM + zip->file_info.filename_size +
1602 zip->file_info.extrafield_size + zip->file_info.comment_size;
1603
1604 return mz_zip_goto_next_entry_int(handle);
1605}
1606
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001607extern 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 -07001608{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001609 mz_zip *zip = (mz_zip *)handle;
1610 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001611 int32_t result = 0;
1612
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001613 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001614 return MZ_PARAM_ERROR;
1615
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001616 err = mz_zip_goto_first_entry(handle);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001617 while (err == MZ_OK)
1618 {
1619 if (filename_compare_cb != NULL)
1620 result = filename_compare_cb(handle, zip->file_info.filename, filename);
1621 else
1622 result = strcmp(zip->file_info.filename, filename);
1623
1624 if (result == 0)
1625 return MZ_OK;
1626
1627 err = mz_zip_goto_next_entry(handle);
1628 }
1629
1630 return err;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001631}
1632
1633/***************************************************************************/
1634
Nathan Moinvaziribb3b75b2018-05-02 10:48:51 -07001635int32_t mz_zip_attrib_is_dir(int32_t attributes, int32_t version_madeby)
1636{
1637 int32_t host_system = (uint8_t)(version_madeby >> 8);
1638
1639 if (host_system == MZ_HOST_SYSTEM_MSDOS || host_system == MZ_HOST_SYSTEM_WINDOWS_NTFS)
1640 {
1641 if ((attributes & 0x10) == 0x10) // FILE_ATTRIBUTE_DIRECTORY
1642 return MZ_OK;
1643 }
1644 else if (host_system == MZ_HOST_SYSTEM_UNIX || host_system == MZ_HOST_SYSTEM_OSX_DARWIN)
1645 {
1646 if ((attributes & 00170000) == 0040000) // S_ISDIR
1647 return MZ_OK;
1648 }
1649
1650 return MZ_EXIST_ERROR;
1651}
1652
1653/***************************************************************************/
1654
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001655static int32_t mz_zip_invalid_date(const struct tm *ptm)
1656{
1657#define datevalue_in_range(min, max, value) ((min) <= (value) && (value) <= (max))
Viktor Szakatsce26dba2018-04-22 19:30:34 +00001658 return (!datevalue_in_range(0, 127 + 80, ptm->tm_year) || // 1980-based year, allow 80 extra
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001659 !datevalue_in_range(0, 11, ptm->tm_mon) ||
1660 !datevalue_in_range(1, 31, ptm->tm_mday) ||
1661 !datevalue_in_range(0, 23, ptm->tm_hour) ||
1662 !datevalue_in_range(0, 59, ptm->tm_min) ||
1663 !datevalue_in_range(0, 59, ptm->tm_sec));
1664#undef datevalue_in_range
1665}
1666
1667static void mz_zip_dosdate_to_raw_tm(uint64_t dos_date, struct tm *ptm)
1668{
1669 uint64_t date = (uint64_t)(dos_date >> 16);
1670
1671 ptm->tm_mday = (uint16_t)(date & 0x1f);
1672 ptm->tm_mon = (uint16_t)(((date & 0x1E0) / 0x20) - 1);
1673 ptm->tm_year = (uint16_t)(((date & 0x0FE00) / 0x0200) + 80);
1674 ptm->tm_hour = (uint16_t)((dos_date & 0xF800) / 0x800);
1675 ptm->tm_min = (uint16_t)((dos_date & 0x7E0) / 0x20);
1676 ptm->tm_sec = (uint16_t)(2 * (dos_date & 0x1f));
1677 ptm->tm_isdst = -1;
1678}
1679
1680int32_t mz_zip_dosdate_to_tm(uint64_t dos_date, struct tm *ptm)
1681{
1682 if (ptm == NULL)
1683 return MZ_PARAM_ERROR;
1684
1685 mz_zip_dosdate_to_raw_tm(dos_date, ptm);
1686
1687 if (mz_zip_invalid_date(ptm))
1688 {
1689 // Invalid date stored, so don't return it
1690 memset(ptm, 0, sizeof(struct tm));
1691 return MZ_FORMAT_ERROR;
1692 }
1693 return MZ_OK;
1694}
1695
1696time_t mz_zip_dosdate_to_time_t(uint64_t dos_date)
1697{
1698 struct tm ptm;
1699 mz_zip_dosdate_to_raw_tm(dos_date, &ptm);
1700 return mktime(&ptm);
1701}
1702
1703int32_t mz_zip_time_t_to_tm(time_t unix_time, struct tm *ptm)
1704{
Nathan Moinvaziri17fcdcd2017-10-26 08:13:13 -07001705 struct tm *ltm = NULL;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001706 if (ptm == NULL)
1707 return MZ_PARAM_ERROR;
Viktor Szakatsce26dba2018-04-22 19:30:34 +00001708 ltm = localtime(&unix_time); // Returns a 1900-based year
Nathan Moinvaziri17fcdcd2017-10-26 08:13:13 -07001709 if (ltm == NULL)
Viktor Szakatsce26dba2018-04-22 19:30:34 +00001710 {
1711 // Invalid date stored, so don't return it
1712 memset(ptm, 0, sizeof(struct tm));
Nathan Moinvaziri17fcdcd2017-10-26 08:13:13 -07001713 return MZ_INTERNAL_ERROR;
Viktor Szakatsce26dba2018-04-22 19:30:34 +00001714 }
Nathan Moinvaziri17fcdcd2017-10-26 08:13:13 -07001715 memcpy(ptm, ltm, sizeof(struct tm));
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001716 return MZ_OK;
1717}
1718
1719uint32_t mz_zip_time_t_to_dos_date(time_t unix_time)
1720{
1721 struct tm ptm;
1722 mz_zip_time_t_to_tm(unix_time, &ptm);
1723 return mz_zip_tm_to_dosdate((const struct tm *)&ptm);
1724}
1725
1726uint32_t mz_zip_tm_to_dosdate(const struct tm *ptm)
1727{
Nathan Moinvazirie33916b2018-05-01 13:45:08 -07001728 struct tm fixed_tm;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001729
1730 // Years supported:
1731
1732 // [00, 79] (assumed to be between 2000 and 2079)
1733 // [80, 207] (assumed to be between 1980 and 2107, typical output of old
1734 // software that does 'year-1900' to get a double digit year)
1735 // [1980, 2107] (due to format limitations, only years 1980-2107 can be stored.)
1736
1737 memcpy(&fixed_tm, ptm, sizeof(struct tm));
1738 if (fixed_tm.tm_year >= 1980) // range [1980, 2107]
1739 fixed_tm.tm_year -= 1980;
Viktor Szakatsce26dba2018-04-22 19:30:34 +00001740 else if (fixed_tm.tm_year >= 80) // range [80, 207]
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001741 fixed_tm.tm_year -= 80;
1742 else // range [00, 79]
1743 fixed_tm.tm_year += 20;
1744
Viktor Szakatsce26dba2018-04-22 19:30:34 +00001745 if (mz_zip_invalid_date(&fixed_tm))
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001746 return 0;
1747
1748 return (uint32_t)(((fixed_tm.tm_mday) + (32 * (fixed_tm.tm_mon + 1)) + (512 * fixed_tm.tm_year)) << 16) |
1749 ((fixed_tm.tm_sec / 2) + (32 * fixed_tm.tm_min) + (2048 * (uint32_t)fixed_tm.tm_hour));
1750}
1751
1752int32_t mz_zip_ntfs_to_unix_time(uint64_t ntfs_time, time_t *unix_time)
1753{
1754 *unix_time = (time_t)((ntfs_time - 116444736000000000LL) / 10000000);
1755 return MZ_OK;
1756}
1757
1758int32_t mz_zip_unix_to_ntfs_time(time_t unix_time, uint64_t *ntfs_time)
1759{
1760 *ntfs_time = ((uint64_t)unix_time * 10000000) + 116444736000000000LL;
1761 return MZ_OK;
1762}