blob: 5eb23ae26f26ec4e24db42d65c601de8a3376f92 [file] [log] [blame]
Antoine Cœur0c2fceb2017-10-05 15:30:37 +08001/* zip.c -- Zip manipulation
Nathan Moinvaziriff6f2a12018-10-08 23:48:58 -07002 Version 2.6.0, October 8, 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
Nathan Moinvaziri79cfab02018-08-17 12:21:06 -07007 Copyright (C) 2009-2010 Mathias Svensson
Antoine Cœur0c2fceb2017-10-05 15:30:37 +08008 Modifications for Zip64 support
9 http://result42.com
Nathan Moinvaziri79cfab02018-08-17 12:21:06 -070010 Copyright (C) 2007-2008 Even Rouault
11 Modifications of Unzip for Zip64
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080012 Copyright (C) 1998-2010 Gilles Vollant
Viktor Szakats9dea6f22018-10-17 22:39:01 +000013 https://www.winimage.com/zLibDll/minizip.html
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080014
15 This program is distributed under the terms of the same license as zlib.
16 See the accompanying LICENSE file for the full text of the license.
17*/
18
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080019#include <stdlib.h>
20#include <stdint.h>
Nathan Moinvaziri85d36c52018-08-31 16:43:41 -070021#include <stdio.h>
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080022#include <string.h>
Nathan Moinvaziric565fa82018-10-19 08:48:33 -070023#include <inttypes.h>
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -070024#include <ctype.h>
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -070025#include <time.h>
Nathan Moinvaziri34eff622018-01-22 09:25:15 -080026#include <limits.h>
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080027
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -070028#include "mz.h"
Nathan Moinvaziri5f091882018-10-24 18:06:08 -070029#include "mz_crypt.h"
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080030#include "mz_strm.h"
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080031#ifdef HAVE_BZIP2
32# include "mz_strm_bzip.h"
33#endif
Nathan Moinvazirib47b41b2018-07-11 09:44:29 -070034#include "mz_strm_crc32.h"
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080035#ifdef HAVE_LZMA
36# include "mz_strm_lzma.h"
37#endif
Nathan Moinvazirib47b41b2018-07-11 09:44:29 -070038#include "mz_strm_mem.h"
Nathan Moinvaziri0e5c9dc2018-05-23 20:21:48 -070039#ifdef HAVE_PKCRYPT
40# include "mz_strm_pkcrypt.h"
41#endif
Nathan Moinvaziri21a31022018-10-24 09:50:16 -070042#ifdef HAVE_AES
43# include "mz_strm_wzaes.h"
44#endif
Nathan Moinvaziri4f6a0e32017-11-10 08:45:14 -080045#ifdef HAVE_ZLIB
46# include "mz_strm_zlib.h"
47#endif
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080048
49#include "mz_zip.h"
50
Nathan Moinvaziric565fa82018-10-19 08:48:33 -070051#if defined(_MSC_VER) && _MSC_VER < 1900
52# define snprintf _snprintf
53#endif
54
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080055/***************************************************************************/
56
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070057#define MZ_ZIP_MAGIC_LOCALHEADER (0x04034b50)
58#define MZ_ZIP_MAGIC_CENTRALHEADER (0x02014b50)
59#define MZ_ZIP_MAGIC_ENDHEADER (0x06054b50)
60#define MZ_ZIP_MAGIC_ENDHEADER64 (0x06064b50)
61#define MZ_ZIP_MAGIC_ENDLOCHEADER64 (0x07064b50)
62#define MZ_ZIP_MAGIC_DATADESCRIPTOR (0x08074b50)
63
Nathan Moinvaziri638f31f2018-08-27 08:17:16 -070064#define MZ_ZIP_SIZE_LD_ITEM (32)
65#define MZ_ZIP_SIZE_CD_ITEM (46)
66#define MZ_ZIP_SIZE_CD_LOCATOR64 (20)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080067
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080068/***************************************************************************/
69
70typedef struct mz_zip_s
71{
72 mz_zip_file file_info;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070073 mz_zip_file local_file_info;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080074
75 void *stream; // main stream
Nathan Moinvaziricda36002017-10-21 09:37:18 -070076 void *cd_stream; // pointer to the stream with the cd
77 void *cd_mem_stream; // memory stream for central directory
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080078 void *compress_stream; // compression stream
79 void *crc32_stream; // crc32 stream
80 void *crypt_stream; // encryption stream
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070081 void *file_info_stream; // memory stream for storing file info
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -070082 void *local_file_info_stream; // memory stream for storing local file info
Antoine Cœur0c2fceb2017-10-05 15:30:37 +080083
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070084 int32_t open_mode;
85
Nathan Moinvazirifd039e32017-10-22 14:40:39 -070086 uint32_t disk_number_with_cd; // number of the disk with the central dir
Nathan Moinvaziri413822a2018-10-20 09:45:07 -070087 int64_t disk_offset_shift; // correction for zips that have wrong offset start of cd
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070088
Nathan Moinvaziri413822a2018-10-20 09:45:07 -070089 int64_t cd_start_pos; // pos of the first file in the central dir stream
90 int64_t cd_current_pos; // pos of the current file in the central dir
91 int64_t cd_offset; // offset of start of central directory
92 int64_t cd_size; // size of the central directory
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070093
Nathan Moinvaziri9eb113d2018-08-08 17:38:38 -070094 uint8_t entry_scanned; // entry header information read ok
95 uint8_t entry_opened; // entry is open for read/write
96 uint8_t entry_raw; // entry opened with raw mode
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -070097
Nathan Moinvaziri904c4082018-10-08 23:31:21 -070098 uint64_t number_entry;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -070099
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700100 uint16_t version_madeby;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700101 char *comment;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800102} mz_zip;
103
104/***************************************************************************/
105
Nathan Moinvazirie824da82018-07-26 17:53:15 -0700106// Locate the end of central directory of a zip file (at the end, just before the global comment)
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700107static int32_t mz_zip_search_eocd(void *stream, int64_t *central_pos)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800108{
Nathan Moinvaziri9ab31ba2017-10-16 07:51:09 -0700109 uint8_t buf[1024 + 4];
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700110 int64_t file_size = 0;
111 int64_t back_read = 0;
112 int64_t max_back = UINT16_MAX; // maximum size of global comment
113 int32_t read_size = sizeof(buf);
114 int64_t read_pos = 0;
115 int32_t i = 0;
Nathan Moinvaziri05e03ca2018-10-28 16:15:13 -0700116 int32_t err = MZ_OK;
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700117
Nathan Moinvazirif6e81cd2017-10-10 18:24:03 -0700118 *central_pos = 0;
119
Nathan Moinvaziri05e03ca2018-10-28 16:15:13 -0700120 err = mz_stream_seek(stream, 0, MZ_SEEK_END);
121 if (err != MZ_OK)
122 return err;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800123
124 file_size = mz_stream_tell(stream);
125
126 if (max_back > file_size)
127 max_back = file_size;
128
129 while (back_read < max_back)
130 {
Nathan Moinvaziri9ab31ba2017-10-16 07:51:09 -0700131 back_read += (sizeof(buf) - 4);
132 if (back_read > max_back)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800133 back_read = max_back;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800134
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700135 read_pos = file_size - back_read;
Nathan Moinvaziri9ab31ba2017-10-16 07:51:09 -0700136 if (read_size > (file_size - read_pos))
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700137 read_size = (int32_t)(file_size - read_pos);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800138
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700139 if (mz_stream_seek(stream, read_pos, MZ_SEEK_SET) != MZ_OK)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800140 break;
141 if (mz_stream_read(stream, buf, read_size) != read_size)
142 break;
143
144 for (i = read_size - 3; (i--) > 0;)
145 {
Nathan Moinvaziri9ab31ba2017-10-16 07:51:09 -0700146 if (((*(buf + i)) == (MZ_ZIP_MAGIC_ENDHEADER & 0xff)) &&
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700147 ((*(buf + i + 1)) == (MZ_ZIP_MAGIC_ENDHEADER >> 8 & 0xff)) &&
148 ((*(buf + i + 2)) == (MZ_ZIP_MAGIC_ENDHEADER >> 16 & 0xff)) &&
149 ((*(buf + i + 3)) == (MZ_ZIP_MAGIC_ENDHEADER >> 24 & 0xff)))
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800150 {
Nathan Moinvazirif6e81cd2017-10-10 18:24:03 -0700151 *central_pos = read_pos + i;
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700152 return MZ_OK;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800153 }
154 }
155
Nathan Moinvazirif6e81cd2017-10-10 18:24:03 -0700156 if (*central_pos != 0)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800157 break;
158 }
159
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700160 return MZ_EXIST_ERROR;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800161}
162
Nathan Moinvazirie824da82018-07-26 17:53:15 -0700163// Locate the end of central directory 64 of a zip file
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700164static int32_t mz_zip_search_zip64_eocd(void *stream, const int64_t end_central_offset, int64_t *central_pos)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800165{
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700166 int64_t offset = 0;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800167 uint32_t value32 = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700168 int32_t err = MZ_OK;
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700169
170
171 *central_pos = 0;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800172
173 // Zip64 end of central directory locator
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700174 err = mz_stream_seek(stream, end_central_offset - MZ_ZIP_SIZE_CD_LOCATOR64, MZ_SEEK_SET);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800175 // Read locator signature
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700176 if (err == MZ_OK)
177 {
178 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700179 if (value32 != MZ_ZIP_MAGIC_ENDLOCHEADER64)
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700180 err = MZ_FORMAT_ERROR;
181 }
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800182 // Number of the disk with the start of the zip64 end of central directory
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700183 if (err == MZ_OK)
184 err = mz_stream_read_uint32(stream, &value32);
185 // Relative offset of the zip64 end of central directory record8
186 if (err == MZ_OK)
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700187 err = mz_stream_read_uint64(stream, (uint64_t *)&offset);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800188 // Total number of disks
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700189 if (err == MZ_OK)
190 err = mz_stream_read_uint32(stream, &value32);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800191 // Goto end of central directory record
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700192 if (err == MZ_OK)
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700193 err = mz_stream_seek(stream, (int64_t)offset, MZ_SEEK_SET);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800194 // The signature
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700195 if (err == MZ_OK)
196 {
197 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700198 if (value32 != MZ_ZIP_MAGIC_ENDHEADER64)
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700199 err = MZ_FORMAT_ERROR;
200 }
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800201
Nathan Moinvaziri3a0fb552017-10-10 12:47:48 -0700202 if (err == MZ_OK)
203 *central_pos = offset;
204
205 return err;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800206}
207
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700208static int32_t mz_zip_read_cd(void *handle)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800209{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700210 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700211 uint64_t number_entry_cd64 = 0;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800212 uint64_t number_entry = 0;
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700213 uint64_t number_entry_cd = 0;
214 int64_t eocd_pos = 0;
215 int64_t eocd_pos64 = 0;
216 int64_t value64i = 0;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800217 uint16_t value16 = 0;
218 uint32_t value32 = 0;
219 uint64_t value64 = 0;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700220 uint16_t comment_size = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700221 int32_t err = MZ_OK;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800222
223
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800224 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700225 return MZ_PARAM_ERROR;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800226
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700227 // Read and cache central directory records
Nathan Moinvaziri77687f32018-08-09 16:29:26 -0700228 err = mz_zip_search_eocd(zip->stream, &eocd_pos);
229 if (err == MZ_OK)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800230 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700231 // Read end of central directory info
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700232 err = mz_stream_seek(zip->stream, eocd_pos, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700233 // The signature, already checked
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800234 if (err == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700235 err = mz_stream_read_uint32(zip->stream, &value32);
236 // Number of this disk
237 if (err == MZ_OK)
238 err = mz_stream_read_uint16(zip->stream, &value16);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700239 // Number of the disk with the start of the central directory
240 if (err == MZ_OK)
241 err = mz_stream_read_uint16(zip->stream, &value16);
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700242 zip->disk_number_with_cd = value16;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700243 // Total number of entries in the central dir on this disk
244 if (err == MZ_OK)
245 err = mz_stream_read_uint16(zip->stream, &value16);
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700246 zip->number_entry = value16;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700247 // Total number of entries in the central dir
248 if (err == MZ_OK)
249 err = mz_stream_read_uint16(zip->stream, &value16);
250 number_entry_cd = value16;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700251 if (number_entry_cd != zip->number_entry)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700252 err = MZ_FORMAT_ERROR;
253 // Size of the central directory
254 if (err == MZ_OK)
255 err = mz_stream_read_uint32(zip->stream, &value32);
256 if (err == MZ_OK)
257 zip->cd_size = value32;
258 // Offset of start of central directory with respect to the starting disk number
259 if (err == MZ_OK)
260 err = mz_stream_read_uint32(zip->stream, &value32);
Nathan Moinvaziriee614ff2018-07-12 12:36:33 -0700261 if (err == MZ_OK)
262 zip->cd_offset = value32;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700263 // Zip file global comment length
264 if (err == MZ_OK)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700265 err = mz_stream_read_uint16(zip->stream, &comment_size);
Nathan Moinvaziri51bf61d2018-07-26 16:41:54 -0700266 if ((err == MZ_OK) && (comment_size > 0))
267 {
268 zip->comment = (char *)MZ_ALLOC(comment_size + 1);
Nathan Moinvaziri85d36c52018-08-31 16:43:41 -0700269 if (zip->comment != NULL)
Nathan Moinvaziri51bf61d2018-07-26 16:41:54 -0700270 {
271 if (mz_stream_read(zip->stream, zip->comment, comment_size) != comment_size)
Nathan Moinvaziri05e03ca2018-10-28 16:15:13 -0700272 err = MZ_READ_ERROR;
Nathan Moinvaziri51bf61d2018-07-26 16:41:54 -0700273 zip->comment[comment_size] = 0;
274 }
275 }
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800276
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700277 if ((err == MZ_OK) && ((number_entry_cd == UINT16_MAX) || (zip->cd_offset == UINT32_MAX)))
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800278 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700279 // Format should be Zip64, as the central directory or file size is too large
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700280 if (mz_zip_search_zip64_eocd(zip->stream, eocd_pos, &eocd_pos64) == MZ_OK)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800281 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700282 eocd_pos = eocd_pos64;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700283
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700284 err = mz_stream_seek(zip->stream, eocd_pos, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700285 // The signature, already checked
286 if (err == MZ_OK)
287 err = mz_stream_read_uint32(zip->stream, &value32);
288 // Size of zip64 end of central directory record
289 if (err == MZ_OK)
290 err = mz_stream_read_uint64(zip->stream, &value64);
291 // Version made by
292 if (err == MZ_OK)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700293 err = mz_stream_read_uint16(zip->stream, &zip->version_madeby);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700294 // Version needed to extract
295 if (err == MZ_OK)
296 err = mz_stream_read_uint16(zip->stream, &value16);
297 // Number of this disk
298 if (err == MZ_OK)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700299 err = mz_stream_read_uint32(zip->stream, &value32);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700300 // Number of the disk with the start of the central directory
301 if (err == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700302 err = mz_stream_read_uint32(zip->stream, &zip->disk_number_with_cd);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700303 // Total number of entries in the central directory on this disk
304 if (err == MZ_OK)
305 err = mz_stream_read_uint64(zip->stream, &number_entry);
306 // Total number of entries in the central directory
307 if (err == MZ_OK)
308 err = mz_stream_read_uint64(zip->stream, &number_entry_cd64);
309 if (number_entry == UINT32_MAX)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700310 zip->number_entry = number_entry_cd64;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700311 // Size of the central directory
312 if (err == MZ_OK)
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700313 err = mz_stream_read_int64(zip->stream, &zip->cd_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700314 // Offset of start of central directory with respect to the starting disk number
315 if (err == MZ_OK)
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700316 err = mz_stream_read_int64(zip->stream, &zip->cd_offset);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800317 }
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700318 else if ((zip->number_entry == UINT16_MAX) || (number_entry_cd != zip->number_entry) ||
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700319 (zip->cd_size == UINT16_MAX) || (zip->cd_offset == UINT32_MAX))
Nathan Moinvaziri016ad472017-10-09 23:36:30 -0700320 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700321 err = MZ_FORMAT_ERROR;
Nathan Moinvaziri016ad472017-10-09 23:36:30 -0700322 }
323 }
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800324 }
325
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700326 if (err == MZ_OK)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800327 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700328 if (eocd_pos < zip->cd_offset + zip->cd_size)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700329 err = MZ_FORMAT_ERROR;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800330 }
331
Nathan Moinvaziriee614ff2018-07-12 12:36:33 -0700332 if (err == MZ_OK)
333 {
334 // Verify central directory signature exists at offset
335 err = mz_stream_seek(zip->stream, zip->cd_offset, MZ_SEEK_SET);
336 if (err == MZ_OK)
337 err = mz_stream_read_uint32(zip->stream, &value32);
338 if (value32 != MZ_ZIP_MAGIC_CENTRALHEADER)
339 {
340 // If not found attempt to seek backward to find it
341 err = mz_stream_seek(zip->stream, eocd_pos - zip->cd_size, MZ_SEEK_SET);
342 if (err == MZ_OK)
343 err = mz_stream_read_uint32(zip->stream, &value32);
344 if (value32 == MZ_ZIP_MAGIC_CENTRALHEADER)
345 {
346 // If found compensate for incorrect locations
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700347 value64i = zip->cd_offset;
Nathan Moinvaziriee614ff2018-07-12 12:36:33 -0700348 zip->cd_offset = eocd_pos - zip->cd_size;
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700349 zip->disk_offset_shift = zip->cd_offset - value64i;
Nathan Moinvaziriee614ff2018-07-12 12:36:33 -0700350 }
351 }
352 }
353
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800354 return err;
355}
356
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700357static int32_t mz_zip_write_cd(void *handle)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800358{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700359 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700360 int64_t zip64_eocd_pos_inzip = 0;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800361 uint16_t comment_size = 0;
Nathan Moinvaziri016ad472017-10-09 23:36:30 -0700362 int64_t disk_number = 0;
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700363 int64_t disk_size = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700364 int32_t err = MZ_OK;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800365
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700366
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700367 if (zip == NULL)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800368 return MZ_PARAM_ERROR;
Viktor Szakats915b82e2018-04-24 10:02:39 +0000369
Nathan Moinvaziri016ad472017-10-09 23:36:30 -0700370 if (mz_stream_get_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, &disk_number) == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700371 zip->disk_number_with_cd = (uint32_t)disk_number;
Nathan Moinvaziria66cc312017-10-18 16:51:10 -0700372 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 -0700373 zip->disk_number_with_cd += 1;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700374 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800375
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700376 zip->cd_offset = mz_stream_tell(zip->stream);
377 mz_stream_seek(zip->cd_mem_stream, 0, MZ_SEEK_END);
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700378 zip->cd_size = (uint32_t)mz_stream_tell(zip->cd_mem_stream);
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700379 mz_stream_seek(zip->cd_mem_stream, 0, MZ_SEEK_SET);
Viktor Szakats915b82e2018-04-24 10:02:39 +0000380
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700381 err = mz_stream_copy(zip->stream, zip->cd_mem_stream, (int32_t)zip->cd_size);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800382
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800383 // Write the ZIP64 central directory header
juanii3f59ffc2018-02-08 12:44:17 -0300384 if (zip->cd_offset >= UINT32_MAX || zip->number_entry > UINT16_MAX)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800385 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700386 zip64_eocd_pos_inzip = mz_stream_tell(zip->stream);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800387
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700388 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_ENDHEADER64);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800389
390 // Size of this 'zip64 end of central directory'
391 if (err == MZ_OK)
392 err = mz_stream_write_uint64(zip->stream, (uint64_t)44);
393 // Version made by
394 if (err == MZ_OK)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700395 err = mz_stream_write_uint16(zip->stream, zip->version_madeby);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800396 // Version needed
397 if (err == MZ_OK)
398 err = mz_stream_write_uint16(zip->stream, (uint16_t)45);
399 // Number of this disk
400 if (err == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700401 err = mz_stream_write_uint32(zip->stream, zip->disk_number_with_cd);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800402 // Number of the disk with the start of the central directory
403 if (err == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700404 err = mz_stream_write_uint32(zip->stream, zip->disk_number_with_cd);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800405 // Total number of entries in the central dir on this disk
406 if (err == MZ_OK)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700407 err = mz_stream_write_uint64(zip->stream, zip->number_entry);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800408 // Total number of entries in the central dir
409 if (err == MZ_OK)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700410 err = mz_stream_write_uint64(zip->stream, zip->number_entry);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800411 // Size of the central directory
412 if (err == MZ_OK)
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700413 err = mz_stream_write_int64(zip->stream, zip->cd_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700414 // Offset of start of central directory with respect to the starting disk number
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800415 if (err == MZ_OK)
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700416 err = mz_stream_write_int64(zip->stream, zip->cd_offset);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800417 if (err == MZ_OK)
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700418 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_ENDLOCHEADER64);
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700419
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800420 // Number of the disk with the start of the central directory
421 if (err == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700422 err = mz_stream_write_uint32(zip->stream, zip->disk_number_with_cd);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800423 // Relative offset to the end of zip64 central directory
424 if (err == MZ_OK)
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700425 err = mz_stream_write_int64(zip->stream, zip64_eocd_pos_inzip);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800426 // Number of the disk with the start of the central directory
427 if (err == MZ_OK)
juaniic65486d2018-02-08 17:07:07 -0300428 err = mz_stream_write_uint32(zip->stream, zip->disk_number_with_cd + 1);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800429 }
430
431 // Write the central directory header
432
Viktor Szakats915b82e2018-04-24 10:02:39 +0000433 // Signature
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800434 if (err == MZ_OK)
Nathan Moinvaziri00cca9f2017-10-16 07:37:11 -0700435 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_ENDHEADER);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800436 // Number of this disk
437 if (err == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700438 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->disk_number_with_cd);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800439 // Number of the disk with the start of the central directory
440 if (err == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -0700441 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->disk_number_with_cd);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800442 // Total number of entries in the central dir on this disk
443 if (err == MZ_OK)
444 {
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700445 if (zip->number_entry >= UINT16_MAX)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800446 err = mz_stream_write_uint16(zip->stream, UINT16_MAX);
447 else
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700448 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->number_entry);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800449 }
450 // Total number of entries in the central dir
451 if (err == MZ_OK)
452 {
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700453 if (zip->number_entry >= UINT16_MAX)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800454 err = mz_stream_write_uint16(zip->stream, UINT16_MAX);
455 else
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700456 err = mz_stream_write_uint16(zip->stream, (uint16_t)zip->number_entry);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800457 }
458 // Size of the central directory
459 if (err == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700460 err = mz_stream_write_uint32(zip->stream, (uint32_t)zip->cd_size);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800461 // Offset of start of central directory with respect to the starting disk number
462 if (err == MZ_OK)
463 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700464 if (zip->cd_offset >= UINT32_MAX)
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800465 err = mz_stream_write_uint32(zip->stream, UINT32_MAX);
466 else
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700467 err = mz_stream_write_uint32(zip->stream, (uint32_t)zip->cd_offset);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800468 }
469
470 // Write global comment
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700471 if (zip->comment != NULL)
472 comment_size = (uint16_t)strlen(zip->comment);
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800473 if (err == MZ_OK)
474 err = mz_stream_write_uint16(zip->stream, comment_size);
475 if (err == MZ_OK)
476 {
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700477 if (mz_stream_write(zip->stream, zip->comment, comment_size) != comment_size)
Nathan Moinvaziri05e03ca2018-10-28 16:15:13 -0700478 err = MZ_READ_ERROR;
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800479 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700480 return err;
481}
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800482
Nathan Moinvaziri590e5902018-08-17 11:10:35 -0700483void *mz_zip_create(void **handle)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700484{
485 mz_zip *zip = NULL;
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -0700486
487 zip = (mz_zip *)MZ_ALLOC(sizeof(mz_zip));
488 if (zip != NULL)
489 {
490 memset(zip, 0, sizeof(mz_zip));
491 }
492 if (handle != NULL)
493 *handle = zip;
494
495 return zip;
496}
497
Nathan Moinvaziri590e5902018-08-17 11:10:35 -0700498void mz_zip_delete(void **handle)
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -0700499{
500 mz_zip *zip = NULL;
501 if (handle == NULL)
502 return;
503 zip = (mz_zip *)*handle;
504 if (zip != NULL)
505 {
506 MZ_FREE(zip);
507 }
508 *handle = NULL;
509}
510
Nathan Moinvaziri590e5902018-08-17 11:10:35 -0700511int32_t mz_zip_open(void *handle, void *stream, int32_t mode)
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -0700512{
513 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700514 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700515
516
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700517 if (zip == NULL)
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -0700518 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700519
520 zip->stream = stream;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700521
Nathan Moinvaziric565fa82018-10-19 08:48:33 -0700522 mz_stream_mem_create(&zip->cd_mem_stream);
523
Nathan Moinvaziri3f7de8e2017-12-11 21:09:52 -0800524 if (mode & MZ_OPEN_MODE_WRITE)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700525 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700526 mz_stream_mem_open(zip->cd_mem_stream, NULL, MZ_OPEN_MODE_CREATE);
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700527 zip->cd_stream = zip->cd_mem_stream;
528 }
529 else
530 {
531 zip->cd_stream = stream;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700532 }
533
Nathan Moinvaziri3f7de8e2017-12-11 21:09:52 -0800534 if ((mode & MZ_OPEN_MODE_READ) || (mode & MZ_OPEN_MODE_APPEND))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700535 {
Nathan Moinvaziri446d0622018-07-16 19:17:46 -0700536 if ((mode & MZ_OPEN_MODE_CREATE) == 0)
537 err = mz_zip_read_cd(zip);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700538
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700539 if ((err == MZ_OK) && (mode & MZ_OPEN_MODE_APPEND))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700540 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700541 if (zip->cd_size > 0)
542 {
543 // Store central directory in memory
544 err = mz_stream_seek(zip->stream, zip->cd_offset, MZ_SEEK_SET);
545 if (err == MZ_OK)
Nathan Moinvaziri904c4082018-10-08 23:31:21 -0700546 err = mz_stream_copy(zip->cd_mem_stream, zip->stream, (int32_t)zip->cd_size);
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700547 if (err == MZ_OK)
548 err = mz_stream_seek(zip->stream, zip->cd_offset, MZ_SEEK_SET);
549 }
550 else
551 {
552 // If no central directory, append new zip to end of file
553 err = mz_stream_seek(zip->stream, 0, MZ_SEEK_END);
554 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700555 }
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700556 else
557 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700558 zip->cd_start_pos = zip->cd_offset;
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700559 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700560 }
561
562 if (err == MZ_OK)
563 {
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700564 // Memory streams used to store variable length file info data
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700565 mz_stream_mem_create(&zip->file_info_stream);
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700566 mz_stream_mem_open(zip->file_info_stream, NULL, MZ_OPEN_MODE_CREATE);
Viktor Szakatse7215072018-09-04 15:06:53 +0000567
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700568 mz_stream_mem_create(&zip->local_file_info_stream);
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700569 mz_stream_mem_open(zip->local_file_info_stream, NULL, MZ_OPEN_MODE_CREATE);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700570 }
571
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700572 if (err != MZ_OK)
573 {
Nathan Moinvaziri3f7de8e2017-12-11 21:09:52 -0800574 mz_zip_close(zip);
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -0700575 return err;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700576 }
577
Nathan Moinvaziri3f7de8e2017-12-11 21:09:52 -0800578 zip->open_mode = mode;
579
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -0700580 return err;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700581}
582
Nathan Moinvaziri590e5902018-08-17 11:10:35 -0700583int32_t mz_zip_close(void *handle)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700584{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700585 mz_zip *zip = (mz_zip *)handle;
586 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700587
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700588 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700589 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700590
Nathan Moinvaziri77687f32018-08-09 16:29:26 -0700591 if (mz_zip_entry_is_open(handle) == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700592 {
593 err = mz_zip_entry_close(handle);
594 if (err != MZ_OK)
595 return err;
596 }
597
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -0700598 if (zip->open_mode & MZ_OPEN_MODE_WRITE)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700599 err = mz_zip_write_cd(handle);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700600
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700601 if (zip->cd_mem_stream != NULL)
Nathan Moinvaziri51bf64a2017-10-20 14:23:37 -0700602 {
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700603 mz_stream_close(zip->cd_mem_stream);
604 mz_stream_delete(&zip->cd_mem_stream);
Nathan Moinvaziri51bf64a2017-10-20 14:23:37 -0700605 }
606
Nathan Moinvaziri3f7de8e2017-12-11 21:09:52 -0800607 if (zip->file_info_stream != NULL)
608 {
609 mz_stream_mem_close(zip->file_info_stream);
610 mz_stream_mem_delete(&zip->file_info_stream);
611 }
612 if (zip->local_file_info_stream != NULL)
613 {
614 mz_stream_mem_close(zip->local_file_info_stream);
615 mz_stream_mem_delete(&zip->local_file_info_stream);
616 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700617
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700618 if (zip->comment)
Damien Riquet9098b6c2018-10-08 14:24:38 +0200619 {
Nathan Moinvaziri5244fc02018-05-02 20:01:35 -0700620 MZ_FREE(zip->comment);
Damien Riquet9098b6c2018-10-08 14:24:38 +0200621 zip->comment = NULL;
622 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700623
Nathan Moinvazirib994cae2018-08-20 16:01:08 -0700624 zip->stream = NULL;
625 zip->cd_stream = NULL;
626
Antoine Cœur0c2fceb2017-10-05 15:30:37 +0800627 return err;
628}
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700629
Nathan Moinvaziri590e5902018-08-17 11:10:35 -0700630int32_t mz_zip_get_comment(void *handle, const char **comment)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700631{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700632 mz_zip *zip = (mz_zip *)handle;
633 if (zip == NULL || comment == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700634 return MZ_PARAM_ERROR;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700635 if (zip->comment == NULL)
636 return MZ_EXIST_ERROR;
637 *comment = zip->comment;
638 return MZ_OK;
639}
640
Nathan Moinvaziri590e5902018-08-17 11:10:35 -0700641int32_t mz_zip_set_comment(void *handle, const char *comment)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700642{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700643 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700644 uint16_t comment_size = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700645 if (zip == NULL || comment == NULL)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700646 return MZ_PARAM_ERROR;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700647 if (zip->comment != NULL)
Nathan Moinvaziri5244fc02018-05-02 20:01:35 -0700648 MZ_FREE(zip->comment);
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -0700649 comment_size = (uint16_t)(strlen(comment) + 1);
Nathan Moinvaziri5244fc02018-05-02 20:01:35 -0700650 zip->comment = (char *)MZ_ALLOC(comment_size);
Nathan Moinvaziri5d9f95f2018-08-30 16:22:25 -0700651 if (zip->comment == NULL)
652 return MZ_MEM_ERROR;
Viktor Szakatsab850762018-09-06 22:12:49 +0000653 strncpy(zip->comment, comment, comment_size - 1);
Nathan Moinvaziri980b3ab2018-09-17 07:46:25 -0700654 zip->comment[comment_size - 1] = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700655 return MZ_OK;
656}
657
Nathan Moinvaziri590e5902018-08-17 11:10:35 -0700658int32_t mz_zip_get_version_madeby(void *handle, uint16_t *version_madeby)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700659{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700660 mz_zip *zip = (mz_zip *)handle;
661 if (zip == NULL || version_madeby == NULL)
662 return MZ_PARAM_ERROR;
663 *version_madeby = zip->version_madeby;
664 return MZ_OK;
665}
666
Nathan Moinvaziri590e5902018-08-17 11:10:35 -0700667int32_t mz_zip_set_version_madeby(void *handle, uint16_t version_madeby)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700668{
669 mz_zip *zip = (mz_zip *)handle;
670 if (zip == NULL)
671 return MZ_PARAM_ERROR;
672 zip->version_madeby = version_madeby;
673 return MZ_OK;
674}
675
Nathan Moinvazirib994cae2018-08-20 16:01:08 -0700676int32_t mz_zip_get_stream(void *handle, void **stream)
677{
678 mz_zip *zip = (mz_zip *)handle;
679 if (zip == NULL || stream == NULL)
680 return MZ_PARAM_ERROR;
681 *stream = zip->stream;
682 if (*stream == NULL)
683 return MZ_EXIST_ERROR;
684 return MZ_OK;
685}
686
Nathan Moinvaziric565fa82018-10-19 08:48:33 -0700687int32_t mz_zip_set_cd_stream(void *handle, int64_t cd_start_pos, void *cd_stream)
688{
689 mz_zip *zip = (mz_zip *)handle;
690 if (zip == NULL || cd_stream == NULL)
691 return MZ_PARAM_ERROR;
692 zip->cd_stream = cd_stream;
693 zip->cd_start_pos = cd_start_pos;
694 return MZ_OK;
695}
696
697int32_t mz_zip_get_cd_mem_stream(void *handle, void **cd_mem_stream)
698{
699 mz_zip *zip = (mz_zip *)handle;
700 if (zip == NULL || cd_mem_stream == NULL)
701 return MZ_PARAM_ERROR;
702 *cd_mem_stream = zip->cd_mem_stream;
703 if (*cd_mem_stream == NULL)
704 return MZ_EXIST_ERROR;
705 return MZ_OK;
706}
707
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700708// Get info about the current file in the zip file
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700709static int32_t mz_zip_entry_read_header(void *stream, uint8_t local, mz_zip_file *file_info, void *file_extra_stream)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700710{
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700711 uint64_t ntfs_time = 0;
712 uint32_t reserved = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700713 uint32_t magic = 0;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700714 uint32_t dos_date = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700715 uint32_t extra_pos = 0;
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700716 uint16_t field_type = 0;
717 uint16_t field_length = 0;
718 uint32_t field_length_read = 0;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700719 uint16_t ntfs_attrib_id = 0;
720 uint16_t ntfs_attrib_size = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700721 uint16_t value16 = 0;
722 uint32_t value32 = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700723 int64_t max_seek = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700724 int64_t seek = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700725 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700726
727
728 memset(file_info, 0, sizeof(mz_zip_file));
729
730 // Check the magic
731 err = mz_stream_read_uint32(stream, &magic);
Nathan Moinvaziricda36002017-10-21 09:37:18 -0700732 if (err == MZ_END_OF_STREAM)
733 err = MZ_END_OF_LIST;
734 else if (magic == MZ_ZIP_MAGIC_ENDHEADER || magic == MZ_ZIP_MAGIC_ENDHEADER64)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700735 err = MZ_END_OF_LIST;
736 else if ((local) && (magic != MZ_ZIP_MAGIC_LOCALHEADER))
737 err = MZ_FORMAT_ERROR;
738 else if ((!local) && (magic != MZ_ZIP_MAGIC_CENTRALHEADER))
739 err = MZ_FORMAT_ERROR;
Viktor Szakats915b82e2018-04-24 10:02:39 +0000740
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700741 // Read header fields
742 if (err == MZ_OK)
743 {
744 if (!local)
745 err = mz_stream_read_uint16(stream, &file_info->version_madeby);
746 if (err == MZ_OK)
747 err = mz_stream_read_uint16(stream, &file_info->version_needed);
748 if (err == MZ_OK)
749 err = mz_stream_read_uint16(stream, &file_info->flag);
750 if (err == MZ_OK)
751 err = mz_stream_read_uint16(stream, &file_info->compression_method);
752 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700753 {
754 err = mz_stream_read_uint32(stream, &dos_date);
755 file_info->modified_date = mz_zip_dosdate_to_time_t(dos_date);
756 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700757 if (err == MZ_OK)
758 err = mz_stream_read_uint32(stream, &file_info->crc);
759 if (err == MZ_OK)
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700760 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700761 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700762 file_info->compressed_size = value32;
763 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700764 if (err == MZ_OK)
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700765 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700766 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700767 file_info->uncompressed_size = value32;
768 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700769 if (err == MZ_OK)
770 err = mz_stream_read_uint16(stream, &file_info->filename_size);
771 if (err == MZ_OK)
772 err = mz_stream_read_uint16(stream, &file_info->extrafield_size);
773 if (!local)
774 {
775 if (err == MZ_OK)
776 err = mz_stream_read_uint16(stream, &file_info->comment_size);
777 if (err == MZ_OK)
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700778 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700779 err = mz_stream_read_uint16(stream, &value16);
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700780 file_info->disk_number = value16;
781 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700782 if (err == MZ_OK)
783 err = mz_stream_read_uint16(stream, &file_info->internal_fa);
784 if (err == MZ_OK)
785 err = mz_stream_read_uint32(stream, &file_info->external_fa);
786 if (err == MZ_OK)
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700787 {
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700788 err = mz_stream_read_uint32(stream, &value32);
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700789 file_info->disk_offset = value32;
790 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700791 }
792 }
793
Nathan Moinvaziri5d9f95f2018-08-30 16:22:25 -0700794 max_seek = (int64_t)file_info->filename_size + file_info->extrafield_size + file_info->comment_size + 3;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700795 if (err == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700796 err = mz_stream_seek(file_extra_stream, max_seek, MZ_SEEK_SET);
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700797 if (err == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700798 err = mz_stream_seek(file_extra_stream, 0, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700799
800 if ((err == MZ_OK) && (file_info->filename_size > 0))
801 {
Nathan Moinvaziric714ef62018-10-25 22:01:02 -0700802 mz_stream_mem_get_buffer(file_extra_stream, (const void **)&file_info->filename);
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700803
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700804 err = mz_stream_copy(file_extra_stream, stream, file_info->filename_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700805 if (err == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700806 err = mz_stream_write_uint8(file_extra_stream, 0);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700807
Nathan Moinvaziri5d9f95f2018-08-30 16:22:25 -0700808 seek += (int64_t)file_info->filename_size + 1;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700809 }
810
811 if ((err == MZ_OK) && (file_info->extrafield_size > 0))
812 {
Nathan Moinvaziri6bde0342018-10-25 22:05:13 -0700813 mz_stream_mem_get_buffer_at(file_extra_stream, seek, (const void **)&file_info->extrafield);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700814
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700815 err = mz_stream_copy(file_extra_stream, stream, file_info->extrafield_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700816 if (err == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700817 err = mz_stream_write_uint8(file_extra_stream, 0);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700818
819 // Seek back and parse the extra field
820 if (err == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700821 err = mz_stream_seek(file_extra_stream, seek, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700822
Nathan Moinvaziri5d9f95f2018-08-30 16:22:25 -0700823 seek += (int64_t)file_info->extrafield_size + 1;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700824
825 while ((err == MZ_OK) && (extra_pos < file_info->extrafield_size))
826 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700827 err = mz_zip_extrafield_read(file_extra_stream, &field_type, &field_length);
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -0700828 // Read ZIP64 extra field
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700829 if (field_type == MZ_ZIP_EXTENSION_ZIP64)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700830 {
831 if ((err == MZ_OK) && (file_info->uncompressed_size == UINT32_MAX))
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700832 err = mz_stream_read_int64(file_extra_stream, &file_info->uncompressed_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700833 if ((err == MZ_OK) && (file_info->compressed_size == UINT32_MAX))
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700834 err = mz_stream_read_int64(file_extra_stream, &file_info->compressed_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700835 if ((err == MZ_OK) && (file_info->disk_offset == UINT32_MAX))
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700836 err = mz_stream_read_int64(file_extra_stream, &file_info->disk_offset);
juanii4ae79922018-02-11 14:29:36 -0300837 if ((err == MZ_OK) && (file_info->disk_number == UINT16_MAX))
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700838 err = mz_stream_read_uint32(file_extra_stream, &file_info->disk_number);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700839 }
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -0700840 // Read NTFS extra field
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700841 else if (field_type == MZ_ZIP_EXTENSION_NTFS)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700842 {
Nathan Moinvaziri7a3b6982018-05-09 00:45:02 -0700843 if (err == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700844 err = mz_stream_read_uint32(file_extra_stream, &reserved);
845 field_length_read = 4;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700846
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700847 while ((err == MZ_OK) && (field_length_read < field_length))
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700848 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700849 err = mz_stream_read_uint16(file_extra_stream, &ntfs_attrib_id);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700850 if (err == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700851 err = mz_stream_read_uint16(file_extra_stream, &ntfs_attrib_size);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700852
Nathan Moinvaziri7a3b6982018-05-09 00:45:02 -0700853 if ((err == MZ_OK) && (ntfs_attrib_id == 0x01) && (ntfs_attrib_size == 24))
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700854 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700855 err = mz_stream_read_uint64(file_extra_stream, &ntfs_time);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700856 mz_zip_ntfs_to_unix_time(ntfs_time, &file_info->modified_date);
857
juanii7063b0e2018-02-11 13:56:21 -0300858 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700859 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700860 err = mz_stream_read_uint64(file_extra_stream, &ntfs_time);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700861 mz_zip_ntfs_to_unix_time(ntfs_time, &file_info->accessed_date);
862 }
juanii7063b0e2018-02-11 13:56:21 -0300863 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700864 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700865 err = mz_stream_read_uint64(file_extra_stream, &ntfs_time);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700866 mz_zip_ntfs_to_unix_time(ntfs_time, &file_info->creation_date);
867 }
868 }
869 else
870 {
Nathan Moinvaziri7a3b6982018-05-09 00:45:02 -0700871 if (err == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700872 err = mz_stream_seek(file_extra_stream, ntfs_attrib_size, MZ_SEEK_CUR);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700873 }
874
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700875 field_length_read += ntfs_attrib_size + 4;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700876 }
877 }
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -0700878 // Read UNIX1 extra field
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700879 else if (field_type == MZ_ZIP_EXTENSION_UNIX1)
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -0700880 {
881 if (err == MZ_OK && file_info->accessed_date == 0)
882 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700883 err = mz_stream_read_uint32(file_extra_stream, &value32);
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -0700884 if (err == MZ_OK)
885 file_info->accessed_date = value32;
886 }
887 if (err == MZ_OK && file_info->modified_date == 0)
888 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700889 err = mz_stream_read_uint32(file_extra_stream, &value32);
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -0700890 if (err == MZ_OK)
891 file_info->modified_date = value32;
892 }
893 if (err == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700894 err = mz_stream_read_uint16(file_extra_stream, &value16); // User id
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -0700895 if (err == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700896 err = mz_stream_read_uint16(file_extra_stream, &value16); // Group id
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -0700897
898 // Skip variable data
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700899 mz_stream_seek(file_extra_stream, field_length - (4 + 4 + 2 + 2), SEEK_CUR);
Nathan Moinvaziri413822a2018-10-20 09:45:07 -0700900 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700901#ifdef HAVE_AES
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -0700902 // Read AES extra field
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700903 else if (field_type == MZ_ZIP_EXTENSION_AES)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700904 {
905 uint8_t value8 = 0;
906 // Verify version info
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700907 err = mz_stream_read_uint16(file_extra_stream, &value16);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700908 // Support AE-1 and AE-2
909 if (value16 != 1 && value16 != 2)
910 err = MZ_FORMAT_ERROR;
911 file_info->aes_version = value16;
912 if (err == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700913 err = mz_stream_read_uint8(file_extra_stream, &value8);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700914 if ((char)value8 != 'A')
915 err = MZ_FORMAT_ERROR;
916 if (err == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700917 err = mz_stream_read_uint8(file_extra_stream, &value8);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700918 if ((char)value8 != 'E')
919 err = MZ_FORMAT_ERROR;
920 // Get AES encryption strength and actual compression method
921 if (err == MZ_OK)
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700922 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700923 err = mz_stream_read_uint8(file_extra_stream, &value8);
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700924 file_info->aes_encryption_mode = value8;
925 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700926 if (err == MZ_OK)
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700927 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700928 err = mz_stream_read_uint16(file_extra_stream, &value16);
Nathan Moinvaziri2ecd8a02018-07-13 12:31:39 -0700929 file_info->compression_method = value16;
930 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700931 }
932#endif
933 else
934 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700935 err = mz_stream_seek(file_extra_stream, field_length, MZ_SEEK_CUR);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700936 }
937
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700938 extra_pos += 4 + field_length;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700939 }
940 }
941
942 if ((err == MZ_OK) && (file_info->comment_size > 0))
943 {
Nathan Moinvaziric714ef62018-10-25 22:01:02 -0700944 mz_stream_mem_get_buffer_at(file_extra_stream, seek, (const void **)&file_info->comment);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700945
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700946 err = mz_stream_copy(file_extra_stream, stream, file_info->comment_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700947 if (err == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700948 err = mz_stream_write_uint8(file_extra_stream, 0);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700949 }
950
951 return err;
952}
953
Nathan Moinvazirifa620e42018-10-20 10:37:10 -0700954static int32_t mz_zip_entry_write_header(void *stream, uint8_t local, mz_zip_file *file_info)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700955{
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -0700956 uint64_t ntfs_time = 0;
957 uint32_t reserved = 0;
958 uint32_t dos_date = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700959 uint16_t extrafield_size = 0;
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700960 uint16_t field_type = 0;
961 uint16_t field_length = 0;
962 uint16_t field_length_zip64 = 0;
963 uint16_t field_length_ntfs = 0;
964 uint16_t field_length_aes = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700965 uint16_t filename_size = 0;
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -0700966 uint16_t filename_length = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700967 uint16_t comment_size = 0;
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700968 uint16_t version_needed = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700969 int32_t err = MZ_OK;
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700970 int32_t err_mem = MZ_OK;
971 uint8_t zip64 = 0;
972 uint8_t skip_aes = 0;
Nathan Moinvaziric565fa82018-10-19 08:48:33 -0700973 uint8_t mask = 0;
974 uint8_t write_end_slash = 0;
975 const char *filename = NULL;
Nathan Moinvazirifa620e42018-10-20 10:37:10 -0700976 char masked_name[64];
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700977 void *file_extra_stream = NULL;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -0700978
979 if (file_info == NULL)
980 return MZ_PARAM_ERROR;
981
Nathan Moinvaziric565fa82018-10-19 08:48:33 -0700982 if ((local) && (file_info->flag & MZ_ZIP_FLAG_MASK_LOCAL_INFO))
983 mask = 1;
984
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700985 // Calculate extra field sizes
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700986 if (file_info->uncompressed_size >= UINT32_MAX)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700987 field_length_zip64 += 8;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700988 if (file_info->compressed_size >= UINT32_MAX)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700989 field_length_zip64 += 8;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700990 if (file_info->disk_offset >= UINT32_MAX)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700991 field_length_zip64 += 8;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -0700992
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700993 if (file_info->zip64 == MZ_ZIP64_AUTO)
Nathan Moinvaziri121e0882018-05-03 17:57:20 -0700994 {
995 // If uncompressed size is unknown, assume zip64 for 64-bit data descriptors
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -0700996 zip64 = (local && file_info->uncompressed_size == 0) || (field_length_zip64 > 0);
Nathan Moinvaziri121e0882018-05-03 17:57:20 -0700997 }
Nathan Moinvaziri46f71432018-05-03 17:24:32 -0700998 else if (file_info->zip64 == MZ_ZIP64_FORCE)
Nathan Moinvaziri298126a2018-07-31 10:19:48 -0700999 {
Nathan Moinvaziria56a08c2018-05-03 09:35:37 -07001000 zip64 = 1;
Nathan Moinvaziri298126a2018-07-31 10:19:48 -07001001 }
Nathan Moinvaziri46f71432018-05-03 17:24:32 -07001002 else if (file_info->zip64 == MZ_ZIP64_DISABLE)
Nathan Moinvaziria56a08c2018-05-03 09:35:37 -07001003 {
Nathan Moinvaziri46f71432018-05-03 17:24:32 -07001004 // Zip64 extension is required to zip file
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07001005 if (field_length_zip64 > 0)
Nathan Moinvaziri46f71432018-05-03 17:24:32 -07001006 return MZ_PARAM_ERROR;
Nathan Moinvaziria56a08c2018-05-03 09:35:37 -07001007 }
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001008
1009 if (zip64)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001010 {
1011 extrafield_size += 4;
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07001012 extrafield_size += field_length_zip64;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001013 }
Nathan Moinvaziri298126a2018-07-31 10:19:48 -07001014
1015 // Calculate extra field size and check for duplicates
1016 if (file_info->extrafield_size > 0)
1017 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07001018 mz_stream_mem_create(&file_extra_stream);
Nathan Moinvaziri915c5132018-10-26 20:00:52 -07001019 mz_stream_mem_set_buffer(file_extra_stream, (void *)file_info->extrafield,
1020 file_info->extrafield_size);
Nathan Moinvaziri298126a2018-07-31 10:19:48 -07001021
1022 do
1023 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07001024 err_mem = mz_stream_read_uint16(file_extra_stream, &field_type);
Nathan Moinvaziri298126a2018-07-31 10:19:48 -07001025 if (err_mem == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07001026 err_mem = mz_stream_read_uint16(file_extra_stream, &field_length);
Nathan Moinvaziri298126a2018-07-31 10:19:48 -07001027 if (err_mem != MZ_OK)
1028 break;
1029
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07001030 // Prefer incoming aes extensions over ours
Nathan Moinvaziri298126a2018-07-31 10:19:48 -07001031 if (field_type == MZ_ZIP_EXTENSION_AES)
1032 skip_aes = 1;
1033
1034 // Prefer our zip64, ntfs extension over incoming
1035 if (field_type != MZ_ZIP_EXTENSION_ZIP64 && field_type != MZ_ZIP_EXTENSION_NTFS)
1036 extrafield_size += 4 + field_length;
1037
1038 if (err_mem == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07001039 err_mem = mz_stream_seek(file_extra_stream, field_length, SEEK_CUR);
Nathan Moinvaziri298126a2018-07-31 10:19:48 -07001040 }
1041 while (err_mem == MZ_OK);
1042 }
1043
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001044#ifdef HAVE_AES
Nathan Moinvaziri298126a2018-07-31 10:19:48 -07001045 if (!skip_aes)
1046 {
1047 if ((file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
Nathan Moinvaziri413822a2018-10-20 09:45:07 -07001048 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07001049 field_length_aes = 1 + 1 + 1 + 2 + 2;
1050 extrafield_size += 4 + field_length_aes;
Nathan Moinvaziri413822a2018-10-20 09:45:07 -07001051 }
1052 }
Nathan Moinvaziria2bb08c2018-10-28 13:45:30 -07001053#else
Nathan Moinvaziri4ae469a2018-10-28 15:32:04 -07001054 MZ_UNUSED(skip_aes);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001055#endif
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001056 // NTFS timestamps
Nathan Moinvazirid9e159a2018-02-13 15:30:30 -08001057 if ((file_info->modified_date != 0) &&
1058 (file_info->accessed_date != 0) &&
Nathan Moinvaziric565fa82018-10-19 08:48:33 -07001059 (file_info->creation_date != 0) && (!mask))
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001060 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07001061 field_length_ntfs = 8 + 8 + 8 + 4 + 2 + 2;
1062 extrafield_size += 4 + field_length_ntfs;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001063 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001064
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001065 if (local)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001066 err = mz_stream_write_uint32(stream, MZ_ZIP_MAGIC_LOCALHEADER);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001067 else
1068 {
1069 err = mz_stream_write_uint32(stream, MZ_ZIP_MAGIC_CENTRALHEADER);
1070 if (err == MZ_OK)
1071 err = mz_stream_write_uint16(stream, file_info->version_madeby);
1072 }
1073
Nathan Moinvaziri46f71432018-05-03 17:24:32 -07001074 // Calculate version needed to extract
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001075 if (err == MZ_OK)
Nathan Moinvaziri46f71432018-05-03 17:24:32 -07001076 {
1077 version_needed = file_info->version_needed;
1078 if (version_needed == 0)
1079 {
1080 version_needed = 20;
1081 if (zip64)
1082 version_needed = 45;
1083#ifdef HAVE_AES
1084 if ((file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
1085 version_needed = 51;
1086#endif
1087#ifdef HAVE_LZMA
1088 if (file_info->compression_method == MZ_COMPRESS_METHOD_LZMA)
1089 version_needed = 63;
1090#endif
1091 }
1092 err = mz_stream_write_uint16(stream, version_needed);
1093 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001094 if (err == MZ_OK)
1095 err = mz_stream_write_uint16(stream, file_info->flag);
1096 if (err == MZ_OK)
1097 {
1098#ifdef HAVE_AES
Nathan Moinvaziri413822a2018-10-20 09:45:07 -07001099 if ((file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07001100 err = mz_stream_write_uint16(stream, MZ_COMPRESS_METHOD_AES);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001101 else
1102#endif
1103 err = mz_stream_write_uint16(stream, file_info->compression_method);
1104 }
1105 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001106 {
Nathan Moinvaziric565fa82018-10-19 08:48:33 -07001107 if (file_info->modified_date != 0 && !mask)
Nathan Moinvaziri17cdaec2017-10-26 10:18:41 -07001108 dos_date = mz_zip_time_t_to_dos_date(file_info->modified_date);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001109 err = mz_stream_write_uint32(stream, dos_date);
1110 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001111
1112 if (err == MZ_OK)
Nathan Moinvaziric565fa82018-10-19 08:48:33 -07001113 {
1114 if (mask)
1115 err = mz_stream_write_uint32(stream, 0);
1116 else
1117 err = mz_stream_write_uint32(stream, file_info->crc); // crc
1118 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001119 if (err == MZ_OK)
1120 {
1121 if (file_info->compressed_size >= UINT32_MAX) // compr size
1122 err = mz_stream_write_uint32(stream, UINT32_MAX);
1123 else
1124 err = mz_stream_write_uint32(stream, (uint32_t)file_info->compressed_size);
1125 }
1126 if (err == MZ_OK)
1127 {
1128 if (file_info->uncompressed_size >= UINT32_MAX) // uncompr size
1129 err = mz_stream_write_uint32(stream, UINT32_MAX);
Nathan Moinvaziric565fa82018-10-19 08:48:33 -07001130 else if (mask)
1131 err = mz_stream_write_uint32(stream, 0);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001132 else
1133 err = mz_stream_write_uint32(stream, (uint32_t)file_info->uncompressed_size);
1134 }
1135
Nathan Moinvaziric565fa82018-10-19 08:48:33 -07001136 if (mask)
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -07001137 {
Nathan Moinvazirifa620e42018-10-20 10:37:10 -07001138 snprintf(masked_name, sizeof(masked_name), "%"PRIx32"_%"PRIx64,
1139 file_info->disk_number, file_info->disk_offset);
1140 filename = masked_name;
Nathan Moinvaziri3ad1a922018-05-02 13:04:41 -07001141 }
Nathan Moinvaziric565fa82018-10-19 08:48:33 -07001142 else
1143 {
1144 filename = file_info->filename;
1145
1146 if ((mz_zip_attrib_is_dir(file_info->external_fa, file_info->version_madeby) == MZ_OK) &&
1147 ((filename[filename_length - 1] != '/') && (filename[filename_length - 1] != '\\')))
1148 {
1149 filename_size += 1;
1150 write_end_slash = 1;
1151 }
1152 }
1153
1154 filename_length = (uint16_t)strlen(filename);
1155 filename_size += filename_length;
1156
1157 if (err == MZ_OK)
1158 err = mz_stream_write_uint16(stream, filename_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001159 if (err == MZ_OK)
1160 err = mz_stream_write_uint16(stream, extrafield_size);
1161
1162 if (!local)
1163 {
1164 if (file_info->comment != NULL)
1165 comment_size = (uint16_t)strlen(file_info->comment);
1166 if (err == MZ_OK)
1167 err = mz_stream_write_uint16(stream, comment_size);
1168 if (err == MZ_OK)
Nathan Moinvazirifd039e32017-10-22 14:40:39 -07001169 err = mz_stream_write_uint16(stream, (uint16_t)file_info->disk_number);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001170 if (err == MZ_OK)
1171 err = mz_stream_write_uint16(stream, file_info->internal_fa);
1172 if (err == MZ_OK)
1173 err = mz_stream_write_uint32(stream, file_info->external_fa);
1174 if (err == MZ_OK)
1175 {
1176 if (file_info->disk_offset >= UINT32_MAX)
1177 err = mz_stream_write_uint32(stream, UINT32_MAX);
1178 else
1179 err = mz_stream_write_uint32(stream, (uint32_t)file_info->disk_offset);
1180 }
1181 }
1182
1183 if (err == MZ_OK)
1184 {
Nathan Moinvaziric565fa82018-10-19 08:48:33 -07001185 if (mz_stream_write(stream, filename, filename_length) != filename_length)
Nathan Moinvaziri05e03ca2018-10-28 16:15:13 -07001186 err = MZ_WRITE_ERROR;
Nathan Moinvaziric565fa82018-10-19 08:48:33 -07001187
1188 // Ensure that directories have a slash appended to them for compatibility
1189 if (err == MZ_OK && write_end_slash)
1190 err = mz_stream_write_uint8(stream, '/');
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001191 }
Nathan Moinvaziri298126a2018-07-31 10:19:48 -07001192
1193 if (file_info->extrafield_size > 0)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001194 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07001195 err_mem = mz_stream_mem_seek(file_extra_stream, 0, SEEK_SET);
Nathan Moinvaziri298126a2018-07-31 10:19:48 -07001196 while (err == MZ_OK && err_mem == MZ_OK)
1197 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07001198 err_mem = mz_stream_read_uint16(file_extra_stream, &field_type);
Nathan Moinvaziri298126a2018-07-31 10:19:48 -07001199 if (err_mem == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07001200 err_mem = mz_stream_read_uint16(file_extra_stream, &field_length);
Nathan Moinvaziri298126a2018-07-31 10:19:48 -07001201 if (err_mem != MZ_OK)
1202 break;
1203
1204 // Prefer our zip 64, ntfs extensions over incoming
1205 if (field_type == MZ_ZIP_EXTENSION_ZIP64 || field_type == MZ_ZIP_EXTENSION_NTFS)
1206 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07001207 err_mem = mz_stream_seek(file_extra_stream, field_length, SEEK_CUR);
Nathan Moinvaziri298126a2018-07-31 10:19:48 -07001208 continue;
1209 }
1210
1211 err = mz_stream_write_uint16(stream, field_type);
1212 if (err == MZ_OK)
1213 err = mz_stream_write_uint16(stream, field_length);
1214 if (err == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07001215 err = mz_stream_copy(stream, file_extra_stream, field_length);
Nathan Moinvaziri298126a2018-07-31 10:19:48 -07001216 }
1217
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07001218 mz_stream_mem_delete(&file_extra_stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001219 }
Nathan Moinvaziri298126a2018-07-31 10:19:48 -07001220
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07001221 // Write ZIP64 extra field
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001222 if ((err == MZ_OK) && (zip64))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001223 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07001224 err = mz_zip_extrafield_write(stream, MZ_ZIP_EXTENSION_ZIP64, field_length_zip64);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001225 if ((err == MZ_OK) && (file_info->uncompressed_size >= UINT32_MAX))
Nathan Moinvaziric565fa82018-10-19 08:48:33 -07001226 {
1227 if (mask)
1228 err = mz_stream_write_int64(stream, 0);
1229 else
1230 err = mz_stream_write_int64(stream, file_info->uncompressed_size);
1231 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001232 if ((err == MZ_OK) && (file_info->compressed_size >= UINT32_MAX))
Nathan Moinvaziri904c4082018-10-08 23:31:21 -07001233 err = mz_stream_write_int64(stream, file_info->compressed_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001234 if ((err == MZ_OK) && (file_info->disk_offset >= UINT32_MAX))
Nathan Moinvaziri904c4082018-10-08 23:31:21 -07001235 err = mz_stream_write_int64(stream, file_info->disk_offset);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001236 }
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07001237 // Write NTFS extra field
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07001238 if ((err == MZ_OK) && (field_length_ntfs > 0))
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001239 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07001240 err = mz_zip_extrafield_write(stream, MZ_ZIP_EXTENSION_NTFS, field_length_ntfs);
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001241 if (err == MZ_OK)
1242 err = mz_stream_write_uint32(stream, reserved);
1243 if (err == MZ_OK)
1244 err = mz_stream_write_uint16(stream, 0x01);
1245 if (err == MZ_OK)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07001246 err = mz_stream_write_uint16(stream, field_length_ntfs - 8);
juanii3679a3d2018-02-11 13:55:38 -03001247 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001248 {
1249 mz_zip_unix_to_ntfs_time(file_info->modified_date, &ntfs_time);
1250 err = mz_stream_write_uint64(stream, ntfs_time);
1251 }
juanii3679a3d2018-02-11 13:55:38 -03001252 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001253 {
1254 mz_zip_unix_to_ntfs_time(file_info->accessed_date, &ntfs_time);
1255 err = mz_stream_write_uint64(stream, ntfs_time);
1256 }
juanii3679a3d2018-02-11 13:55:38 -03001257 if (err == MZ_OK)
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001258 {
1259 mz_zip_unix_to_ntfs_time(file_info->creation_date, &ntfs_time);
1260 err = mz_stream_write_uint64(stream, ntfs_time);
1261 }
1262 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001263#ifdef HAVE_AES
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07001264 // Write AES extra field
Nathan Moinvaziri298126a2018-07-31 10:19:48 -07001265 if ((err == MZ_OK) && (!skip_aes) && (file_info->flag & MZ_ZIP_FLAG_ENCRYPTED) && (file_info->aes_version))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001266 {
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07001267 err = mz_zip_extrafield_write(stream, MZ_ZIP_EXTENSION_AES, field_length_aes);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001268 if (err == MZ_OK)
1269 err = mz_stream_write_uint16(stream, file_info->aes_version);
1270 if (err == MZ_OK)
1271 err = mz_stream_write_uint8(stream, 'A');
1272 if (err == MZ_OK)
1273 err = mz_stream_write_uint8(stream, 'E');
1274 if (err == MZ_OK)
1275 err = mz_stream_write_uint8(stream, file_info->aes_encryption_mode);
1276 if (err == MZ_OK)
1277 err = mz_stream_write_uint16(stream, file_info->compression_method);
1278 }
1279#endif
Nathan Moinvaziri298126a2018-07-31 10:19:48 -07001280 if ((err == MZ_OK) && (!local) && (file_info->comment != NULL))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001281 {
Nathan Moinvazirica014e62018-08-15 07:31:12 -07001282 if (mz_stream_write(stream, file_info->comment, file_info->comment_size) != file_info->comment_size)
Nathan Moinvaziri05e03ca2018-10-28 16:15:13 -07001283 err = MZ_WRITE_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001284 }
1285
1286 return err;
1287}
1288
Nathan Moinvaziri26308b22018-08-08 09:40:15 -07001289static int32_t mz_zip_entry_open_int(void *handle, uint8_t raw, int16_t compress_level, const char *password)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001290{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001291 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001292 int64_t max_total_in = 0;
Nathan Moinvazirifa8105b2018-07-08 19:05:00 -07001293 int64_t header_size = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001294 int64_t footer_size = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001295 int32_t err = MZ_OK;
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001296 uint8_t use_crypt = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001297
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001298 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001299 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001300
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001301 switch (zip->file_info.compression_method)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001302 {
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001303 case MZ_COMPRESS_METHOD_STORE:
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001304 case MZ_COMPRESS_METHOD_DEFLATE:
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001305#ifdef HAVE_BZIP2
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001306 case MZ_COMPRESS_METHOD_BZIP2:
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001307#endif
Nathan Moinvaziri566dfe02018-10-26 00:36:30 -07001308#ifdef HAVE_LZMA
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001309 case MZ_COMPRESS_METHOD_LZMA:
1310#endif
1311 err = MZ_OK;
1312 break;
1313 default:
Nathan Moinvaziri829ffb52018-08-14 14:00:16 -07001314 return MZ_SUPPORT_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001315 }
1316
Nathan Moinvaziria64c9192018-08-08 17:47:12 -07001317 zip->entry_raw = raw;
1318
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001319 if ((zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED) && (password != NULL))
1320 {
1321 if (zip->open_mode & MZ_OPEN_MODE_WRITE)
1322 {
1323 // Encrypt only when we are not trying to write raw and password is supplied.
Nathan Moinvaziria64c9192018-08-08 17:47:12 -07001324 if (!zip->entry_raw)
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001325 use_crypt = 1;
1326 }
1327 else if (zip->open_mode & MZ_OPEN_MODE_READ)
1328 {
1329 // Decrypt only when password is supplied. Don't error when password
1330 // is not supplied as we may want to read the raw encrypted data.
1331 use_crypt = 1;
1332 }
1333 }
1334
1335 if ((err == MZ_OK) && (use_crypt))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001336 {
1337#ifdef HAVE_AES
1338 if (zip->file_info.aes_version)
1339 {
Nathan Moinvaziri21a31022018-10-24 09:50:16 -07001340 mz_stream_wzaes_create(&zip->crypt_stream);
1341 mz_stream_wzaes_set_password(zip->crypt_stream, password);
1342 mz_stream_wzaes_set_encryption_mode(zip->crypt_stream, zip->file_info.aes_encryption_mode);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001343 }
1344 else
1345#endif
1346 {
Nathan Moinvaziri0e5c9dc2018-05-23 20:21:48 -07001347#ifdef HAVE_PKCRYPT
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001348 uint8_t verify1 = 0;
1349 uint8_t verify2 = 0;
1350
1351 // Info-ZIP modification to ZipCrypto format:
1352 // If bit 3 of the general purpose bit flag is set, it uses high byte of 16-bit File Time.
1353
Nathan Moinvaziri18a30652017-12-07 06:59:53 -08001354 if (zip->file_info.flag & MZ_ZIP_FLAG_DATA_DESCRIPTOR)
1355 {
1356 uint32_t dos_date = 0;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001357
Nathan Moinvaziri18a30652017-12-07 06:59:53 -08001358 dos_date = mz_zip_time_t_to_dos_date(zip->file_info.modified_date);
1359
1360 verify1 = (uint8_t)((dos_date >> 16) & 0xff);
1361 verify2 = (uint8_t)((dos_date >> 8) & 0xff);
1362 }
1363 else
1364 {
1365 verify1 = (uint8_t)((zip->file_info.crc >> 16) & 0xff);
1366 verify2 = (uint8_t)((zip->file_info.crc >> 24) & 0xff);
1367 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001368
Nathan Moinvaziri0e5c9dc2018-05-23 20:21:48 -07001369 mz_stream_pkcrypt_create(&zip->crypt_stream);
1370 mz_stream_pkcrypt_set_password(zip->crypt_stream, password);
1371 mz_stream_pkcrypt_set_verify(zip->crypt_stream, verify1, verify2);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001372#endif
1373 }
1374 }
1375
1376 if (err == MZ_OK)
1377 {
1378 if (zip->crypt_stream == NULL)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001379 mz_stream_raw_create(&zip->crypt_stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001380
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001381 mz_stream_set_base(zip->crypt_stream, zip->stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001382
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001383 err = mz_stream_open(zip->crypt_stream, NULL, zip->open_mode);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001384 }
1385
1386 if (err == MZ_OK)
1387 {
Nathan Moinvaziria64c9192018-08-08 17:47:12 -07001388 if (zip->entry_raw || zip->file_info.compression_method == MZ_COMPRESS_METHOD_STORE)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001389 mz_stream_raw_create(&zip->compress_stream);
Andy Maloney3909c232018-05-22 09:02:09 -04001390#ifdef HAVE_ZLIB
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001391 else if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_DEFLATE)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001392 mz_stream_zlib_create(&zip->compress_stream);
Andy Maloney3909c232018-05-22 09:02:09 -04001393#endif
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001394#ifdef HAVE_BZIP2
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001395 else if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_BZIP2)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001396 mz_stream_bzip_create(&zip->compress_stream);
1397#endif
1398#ifdef HAVE_LZMA
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001399 else if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_LZMA)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001400 mz_stream_lzma_create(&zip->compress_stream);
1401#endif
1402 else
1403 err = MZ_PARAM_ERROR;
1404 }
1405
1406 if (err == MZ_OK)
1407 {
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001408 if (zip->open_mode & MZ_OPEN_MODE_WRITE)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001409 {
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001410 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_COMPRESS_LEVEL, compress_level);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001411 }
1412 else
1413 {
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07001414 if (zip->entry_raw || zip->file_info.compression_method == MZ_COMPRESS_METHOD_STORE || zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001415 {
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001416 max_total_in = zip->file_info.compressed_size;
Nathan Moinvazirifa8105b2018-07-08 19:05:00 -07001417 mz_stream_set_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_TOTAL_IN_MAX, max_total_in);
1418
1419 if (mz_stream_get_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_HEADER_SIZE, &header_size) == MZ_OK)
1420 max_total_in -= header_size;
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001421 if (mz_stream_get_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_FOOTER_SIZE, &footer_size) == MZ_OK)
1422 max_total_in -= footer_size;
Nathan Moinvazirifa8105b2018-07-08 19:05:00 -07001423
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001424 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_IN_MAX, max_total_in);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001425 }
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001426 if ((zip->file_info.compression_method == MZ_COMPRESS_METHOD_LZMA) && (zip->file_info.flag & MZ_ZIP_FLAG_LZMA_EOS_MARKER) == 0)
juanii55bcdaf2018-02-11 20:55:57 -03001427 {
1428 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_IN_MAX, zip->file_info.compressed_size);
1429 mz_stream_set_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_OUT_MAX, zip->file_info.uncompressed_size);
1430 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001431 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001432
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001433 mz_stream_set_base(zip->compress_stream, zip->crypt_stream);
1434
1435 err = mz_stream_open(zip->compress_stream, NULL, zip->open_mode);
1436 }
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07001437
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001438 if (err == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001439 {
1440 mz_stream_crc32_create(&zip->crc32_stream);
1441 mz_stream_set_base(zip->crc32_stream, zip->compress_stream);
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07001442
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001443 err = mz_stream_open(zip->crc32_stream, NULL, zip->open_mode);
1444 }
1445
1446 if (err == MZ_OK)
1447 {
1448 zip->entry_opened = 1;
1449 }
1450
1451 return err;
1452}
1453
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001454int32_t mz_zip_entry_is_open(void *handle)
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001455{
1456 mz_zip *zip = (mz_zip *)handle;
1457 if (zip == NULL)
1458 return MZ_PARAM_ERROR;
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07001459 if (zip->entry_opened == 0)
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001460 return MZ_EXIST_ERROR;
1461 return MZ_OK;
1462}
1463
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001464int32_t mz_zip_entry_is_dir(void *handle)
Nathan Moinvaziri829ffb52018-08-14 14:00:16 -07001465{
1466 mz_zip *zip = (mz_zip *)handle;
1467 int32_t filename_length = 0;
1468
1469 if (zip == NULL)
1470 return MZ_PARAM_ERROR;
1471 if (zip->entry_scanned == 0)
1472 return MZ_PARAM_ERROR;
1473 if (mz_zip_attrib_is_dir(zip->file_info.external_fa, zip->file_info.version_madeby) == MZ_OK)
1474 return MZ_OK;
1475
1476 filename_length = (int32_t )strlen(zip->file_info.filename);
1477 if (filename_length > 0)
1478 {
1479 if ((zip->file_info.filename[filename_length - 1] == '/') ||
1480 (zip->file_info.filename[filename_length - 1] == '\\'))
1481 return MZ_OK;
1482 }
1483 return MZ_EXIST_ERROR;
1484}
1485
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001486int32_t mz_zip_entry_read_open(void *handle, uint8_t raw, const char *password)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001487{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001488 mz_zip *zip = (mz_zip *)handle;
1489 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001490
Nathan Moinvaziri40427632018-07-22 11:12:40 -07001491#if defined(MZ_ZIP_NO_ENCRYPTION)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001492 if (password != NULL)
1493 return MZ_PARAM_ERROR;
1494#endif
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001495 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001496 return MZ_PARAM_ERROR;
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001497 if ((zip->open_mode & MZ_OPEN_MODE_READ) == 0)
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001498 return MZ_PARAM_ERROR;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001499 if (zip->entry_scanned == 0)
1500 return MZ_PARAM_ERROR;
Nathan Moinvaziri0f09a002018-04-23 18:48:30 -07001501 if ((zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED) && (password == NULL) && (!raw))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001502 return MZ_PARAM_ERROR;
1503
Nathan Moinvazirifd039e32017-10-22 14:40:39 -07001504 if (zip->file_info.disk_number == zip->disk_number_with_cd)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001505 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
1506 else
Nathan Moinvazirifd039e32017-10-22 14:40:39 -07001507 mz_stream_set_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, zip->file_info.disk_number);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001508
Nathan Moinvaziriee614ff2018-07-12 12:36:33 -07001509 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 -07001510 if (err == MZ_OK)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001511 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 -07001512
Nathan Moinvaziria6d1f662018-07-22 10:35:49 -07001513#ifdef MZ_ZIP_NO_DECOMPRESSION
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001514 if (zip->file_info.compression_method != MZ_COMPRESS_METHOD_STORE)
Nathan Moinvaziri0a9282d2018-06-19 11:59:07 -07001515 err = MZ_SUPPORT_ERROR;
Viktor Szakatse7215072018-09-04 15:06:53 +00001516#endif
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001517 if (err == MZ_OK)
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001518 err = mz_zip_entry_open_int(handle, raw, 0, password);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001519
1520 return err;
1521}
1522
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001523int32_t mz_zip_entry_write_open(void *handle, const mz_zip_file *file_info, int16_t compress_level, uint8_t raw, const char *password)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001524{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001525 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001526 int64_t disk_number = 0;
Nathan Moinvazirifa620e42018-10-20 10:37:10 -07001527 uint8_t is_dir = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001528 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001529
Nathan Moinvaziri40427632018-07-22 11:12:40 -07001530#if defined(MZ_ZIP_NO_ENCRYPTION)
tz-lomb1b25802017-11-10 15:03:02 +03001531 if (password != NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001532 return MZ_PARAM_ERROR;
1533#endif
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001534 if (zip == NULL || file_info == NULL || file_info->filename == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001535 return MZ_PARAM_ERROR;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001536
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001537 if (mz_zip_entry_is_open(handle) == MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001538 {
1539 err = mz_zip_entry_close(handle);
1540 if (err != MZ_OK)
1541 return err;
1542 }
1543
1544 memcpy(&zip->file_info, file_info, sizeof(mz_zip_file));
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001545
Nathan Moinvazirif5cc4c02018-08-14 17:46:36 -07001546 mz_stream_seek(zip->file_info_stream, 0, SEEK_SET);
1547 mz_stream_write(zip->file_info_stream, file_info, sizeof(mz_zip_file));
1548
1549 // Copy filename, extrafield, and comment internally
1550 if (file_info->filename != NULL)
1551 {
Nathan Moinvaziric714ef62018-10-25 22:01:02 -07001552 mz_stream_mem_get_buffer_at_current(zip->file_info_stream, (const void **)&zip->file_info.filename);
Nathan Moinvazirica014e62018-08-15 07:31:12 -07001553 mz_stream_write_chars(zip->file_info_stream, file_info->filename, 1);
Nathan Moinvazirif5cc4c02018-08-14 17:46:36 -07001554 }
1555 if (file_info->extrafield != NULL)
1556 {
Nathan Moinvaziri6bde0342018-10-25 22:05:13 -07001557 mz_stream_mem_get_buffer_at_current(zip->file_info_stream, (const void **)&zip->file_info.extrafield);
Nathan Moinvazirica014e62018-08-15 07:31:12 -07001558 mz_stream_write(zip->file_info_stream, file_info->extrafield, file_info->extrafield_size);
Nathan Moinvazirif5cc4c02018-08-14 17:46:36 -07001559 }
1560 if (file_info->comment != NULL)
1561 {
Nathan Moinvaziric714ef62018-10-25 22:01:02 -07001562 mz_stream_mem_get_buffer_at_current(zip->file_info_stream, (const void **)&zip->file_info.comment);
Nathan Moinvazirica014e62018-08-15 07:31:12 -07001563 mz_stream_write_chars(zip->file_info_stream, file_info->comment, 1);
Nathan Moinvazirif5cc4c02018-08-14 17:46:36 -07001564 }
1565
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001566 if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_DEFLATE)
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001567 {
1568 if ((compress_level == 8) || (compress_level == 9))
1569 zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_MAX;
1570 if (compress_level == 2)
1571 zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_FAST;
1572 if (compress_level == 1)
1573 zip->file_info.flag |= MZ_ZIP_FLAG_DEFLATE_SUPER_FAST;
1574 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001575#ifdef HAVE_LZMA
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001576 else if (zip->file_info.compression_method == MZ_COMPRESS_METHOD_LZMA)
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001577 zip->file_info.flag |= MZ_ZIP_FLAG_LZMA_EOS_MARKER;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001578#endif
Nathan Moinvaziri91a76f62017-11-10 07:39:06 -08001579
Nathan Moinvazirifa620e42018-10-20 10:37:10 -07001580 if (mz_zip_attrib_is_dir(zip->file_info.external_fa, zip->file_info.version_madeby) == MZ_OK)
1581 is_dir = 1;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001582
Nathan Moinvazirifa620e42018-10-20 10:37:10 -07001583 if (!is_dir)
1584 {
1585 zip->file_info.flag |= MZ_ZIP_FLAG_DATA_DESCRIPTOR;
1586 if (password != NULL)
1587 zip->file_info.flag |= MZ_ZIP_FLAG_ENCRYPTED;
1588 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001589
juaniib8887e92018-02-14 00:51:05 -03001590 mz_stream_get_prop_int64(zip->stream, MZ_STREAM_PROP_DISK_NUMBER, &disk_number);
1591 zip->file_info.disk_number = (uint32_t)disk_number;
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001592
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001593 zip->file_info.disk_offset = mz_stream_tell(zip->stream);
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001594 zip->file_info.crc = 0;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001595 zip->file_info.compressed_size = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001596
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001597#ifdef HAVE_AES
1598 if (zip->file_info.aes_version && zip->file_info.aes_encryption_mode == 0)
1599 zip->file_info.aes_encryption_mode = MZ_AES_ENCRYPTION_MODE_256;
1600#endif
1601
Nathan Moinvazirifa620e42018-10-20 10:37:10 -07001602 if ((compress_level == 0) || (is_dir))
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001603 zip->file_info.compression_method = MZ_COMPRESS_METHOD_STORE;
Viktor Szakats2884e672018-04-30 08:12:13 +00001604
Nathan Moinvaziria6d1f662018-07-22 10:35:49 -07001605#ifdef MZ_ZIP_NO_COMPRESSION
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001606 if (zip->file_info.compression_method != MZ_COMPRESS_METHOD_STORE)
Nathan Moinvaziri0a9282d2018-06-19 11:59:07 -07001607 err = MZ_SUPPORT_ERROR;
1608#endif
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001609 if (err == MZ_OK)
Nathan Moinvazirifa620e42018-10-20 10:37:10 -07001610 err = mz_zip_entry_write_header(zip->stream, 1, &zip->file_info);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001611 if (err == MZ_OK)
Nathan Moinvazirifa146ad2018-08-07 23:50:31 -07001612 err = mz_zip_entry_open_int(handle, raw, compress_level, password);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001613
1614 return err;
1615}
1616
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001617int32_t mz_zip_entry_read(void *handle, void *buf, int32_t len)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001618{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001619 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001620 int32_t read = 0;
1621
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001622 if (zip == NULL || mz_zip_entry_is_open(handle) != MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001623 return MZ_PARAM_ERROR;
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07001624 if (UINT_MAX == UINT16_MAX && len > UINT16_MAX) // zlib limitation
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001625 return MZ_PARAM_ERROR;
Nathan Moinvazirid7814e92018-07-11 14:54:14 -07001626 if (len == 0)
1627 return MZ_PARAM_ERROR;
1628
Nathan Moinvazirieb80cd92018-07-11 16:45:09 -07001629 if (zip->file_info.compressed_size == 0)
1630 return 0;
1631
Nathan Moinvazirid7814e92018-07-11 14:54:14 -07001632 // Read entire entry even if uncompressed_size = 0, otherwise
1633 // aes encryption validation will fail if compressed_size > 0
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07001634 read = mz_stream_read(zip->crc32_stream, buf, len);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001635 return read;
1636}
1637
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001638int32_t mz_zip_entry_write(void *handle, const void *buf, int32_t len)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001639{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001640 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri0a9282d2018-06-19 11:59:07 -07001641 int32_t written = 0;
1642
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001643 if (zip == NULL || mz_zip_entry_is_open(handle) != MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001644 return MZ_PARAM_ERROR;
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07001645 written = mz_stream_write(zip->crc32_stream, buf, len);
Nathan Moinvaziri0a9282d2018-06-19 11:59:07 -07001646 return written;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001647}
1648
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001649int32_t mz_zip_entry_get_info(void *handle, mz_zip_file **file_info)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001650{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001651 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07001652
1653 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001654 return MZ_PARAM_ERROR;
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07001655
1656 if ((zip->open_mode & MZ_OPEN_MODE_WRITE) == 0)
1657 {
1658 if (!zip->entry_scanned)
1659 return MZ_PARAM_ERROR;
1660 }
1661
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001662 *file_info = &zip->file_info;
1663 return MZ_OK;
1664}
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001665
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001666int32_t mz_zip_entry_get_local_info(void *handle, mz_zip_file **local_file_info)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001667{
1668 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001669 if (zip == NULL || mz_zip_entry_is_open(handle) != MZ_OK)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001670 return MZ_PARAM_ERROR;
1671 *local_file_info = &zip->local_file_info;
1672 return MZ_OK;
1673}
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001674
Nathan Moinvaziri915c5132018-10-26 20:00:52 -07001675int32_t mz_zip_entry_set_extrafield(void *handle, const uint8_t *extrafield, uint16_t extrafield_size)
1676{
1677 mz_zip *zip = (mz_zip *)handle;
1678
1679 if (zip == NULL || mz_zip_entry_is_open(handle) != MZ_OK)
1680 return MZ_PARAM_ERROR;
1681
1682 zip->file_info.extrafield = extrafield;
1683 zip->file_info.extrafield_size = extrafield_size;
1684 return MZ_OK;
1685}
1686
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001687int32_t mz_zip_entry_close(void *handle)
Nathan Moinvaziri9eb113d2018-08-08 17:38:38 -07001688{
1689 return mz_zip_entry_close_raw(handle, 0, 0);
1690}
1691
Nathan Moinvaziri904c4082018-10-08 23:31:21 -07001692int32_t mz_zip_entry_close_raw(void *handle, int64_t uncompressed_size, uint32_t crc32)
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001693{
1694 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri904c4082018-10-08 23:31:21 -07001695 int64_t compressed_size = 0;
Nathan Moinvaziri8871f6c2018-07-08 19:23:56 -07001696 int64_t total_in = 0;
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001697 int32_t err = MZ_OK;
1698
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001699 if (zip == NULL || mz_zip_entry_is_open(handle) != MZ_OK)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001700 return MZ_PARAM_ERROR;
Nathan Moinvaziri915c5132018-10-26 20:00:52 -07001701
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001702 mz_stream_close(zip->compress_stream);
Nathan Moinvaziri6ac2ff42018-07-21 14:29:29 -07001703
Nathan Moinvaziri9eb113d2018-08-08 17:38:38 -07001704 if (!zip->entry_raw)
Nathan Moinvaziri915c5132018-10-26 20:00:52 -07001705 crc32 = mz_stream_crc32_get_value(zip->crc32_stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001706
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001707 if ((zip->open_mode & MZ_OPEN_MODE_WRITE) == 0)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001708 {
Nathan Moinvaziri413822a2018-10-20 09:45:07 -07001709 mz_stream_get_prop_int64(zip->crc32_stream, MZ_STREAM_PROP_TOTAL_IN, &total_in);
1710
1711 // If entire entry was not read verification will fail
1712 if ((total_in > 0) && (!zip->entry_raw))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001713 {
Nathan Moinvaziri413822a2018-10-20 09:45:07 -07001714#ifdef HAVE_AES
Nathan Moinvaziri413822a2018-10-20 09:45:07 -07001715 // AES zip version AE-1 will expect a valid crc as well
1716 if (zip->file_info.aes_version <= 0x0001)
1717#endif
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001718 {
1719 if (crc32 != zip->file_info.crc)
1720 err = MZ_CRC_ERROR;
1721 }
1722 }
1723 }
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001724
Nathan Moinvaziri904c4082018-10-08 23:31:21 -07001725 mz_stream_get_prop_int64(zip->compress_stream, MZ_STREAM_PROP_TOTAL_OUT, &compressed_size);
Nathan Moinvaziri9eb113d2018-08-08 17:38:38 -07001726 if (!zip->entry_raw)
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07001727 mz_stream_get_prop_int64(zip->crc32_stream, MZ_STREAM_PROP_TOTAL_OUT, &uncompressed_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001728
1729 if (zip->file_info.flag & MZ_ZIP_FLAG_ENCRYPTED)
1730 {
1731 mz_stream_set_base(zip->crypt_stream, zip->stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001732 err = mz_stream_close(zip->crypt_stream);
1733
Nathan Moinvaziria66cc312017-10-18 16:51:10 -07001734 mz_stream_get_prop_int64(zip->crypt_stream, MZ_STREAM_PROP_TOTAL_OUT, (int64_t *)&compressed_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001735 }
1736
1737 mz_stream_delete(&zip->crypt_stream);
1738
1739 mz_stream_delete(&zip->compress_stream);
Nathan Moinvaziri915c5132018-10-26 20:00:52 -07001740 mz_stream_close(zip->crc32_stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001741 mz_stream_crc32_delete(&zip->crc32_stream);
1742
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001743 if (zip->open_mode & MZ_OPEN_MODE_WRITE)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001744 {
Nathan Moinvaziric565fa82018-10-19 08:48:33 -07001745 if ((err == MZ_OK) && (zip->file_info.flag & MZ_ZIP_FLAG_DATA_DESCRIPTOR))
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001746 {
1747 err = mz_stream_write_uint32(zip->stream, MZ_ZIP_MAGIC_DATADESCRIPTOR);
1748 if (err == MZ_OK)
Nathan Moinvaziric565fa82018-10-19 08:48:33 -07001749 {
1750 if (zip->file_info.flag & MZ_ZIP_FLAG_MASK_LOCAL_INFO)
1751 err = mz_stream_write_uint32(zip->stream, 0);
1752 else
1753 err = mz_stream_write_uint32(zip->stream, crc32);
1754 }
Nathan Moinvaziri298126a2018-07-31 10:19:48 -07001755 // Store data descriptor as 8 bytes if zip 64 extension enabled
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001756 if (err == MZ_OK)
1757 {
Nathan Moinvaziric565fa82018-10-19 08:48:33 -07001758 // Zip 64 extension is enabled when uncompressed size is > UINT32_MAX
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001759 if (zip->file_info.uncompressed_size <= UINT32_MAX)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001760 err = mz_stream_write_uint32(zip->stream, (uint32_t)compressed_size);
Andrew Gunnerson3c52d212018-01-05 22:28:12 -05001761 else
Nathan Moinvaziri904c4082018-10-08 23:31:21 -07001762 err = mz_stream_write_int64(zip->stream, compressed_size);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001763 }
1764 if (err == MZ_OK)
1765 {
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001766 if (zip->file_info.uncompressed_size <= UINT32_MAX)
Nathan Moinvaziric565fa82018-10-19 08:48:33 -07001767 {
1768 if (zip->file_info.flag & MZ_ZIP_FLAG_MASK_LOCAL_INFO)
1769 err = mz_stream_write_uint32(zip->stream, 0);
1770 else
1771 err = mz_stream_write_uint32(zip->stream, (uint32_t)uncompressed_size);
1772 }
Andrew Gunnerson3c52d212018-01-05 22:28:12 -05001773 else
Nathan Moinvaziric565fa82018-10-19 08:48:33 -07001774 {
1775 if (zip->file_info.flag & MZ_ZIP_FLAG_MASK_LOCAL_INFO)
1776 err = mz_stream_write_int64(zip->stream, 0);
1777 else
1778 err = mz_stream_write_int64(zip->stream, uncompressed_size);
1779 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001780 }
1781 }
1782
1783 zip->file_info.crc = crc32;
1784 zip->file_info.compressed_size = compressed_size;
1785 zip->file_info.uncompressed_size = uncompressed_size;
1786
1787 if (err == MZ_OK)
Nathan Moinvazirifa620e42018-10-20 10:37:10 -07001788 err = mz_zip_entry_write_header(zip->cd_mem_stream, 0, &zip->file_info);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001789
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07001790 zip->number_entry += 1;
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07001791 }
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001792
1793 zip->entry_opened = 0;
1794
1795 return err;
1796}
1797
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001798static int32_t mz_zip_goto_next_entry_int(void *handle)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001799{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001800 mz_zip *zip = (mz_zip *)handle;
1801 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001802
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001803 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001804 return MZ_PARAM_ERROR;
1805
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001806 zip->entry_scanned = 0;
1807
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001808 mz_stream_set_prop_int64(zip->cd_stream, MZ_STREAM_PROP_DISK_NUMBER, -1);
Viktor Szakats915b82e2018-04-24 10:02:39 +00001809
Nathan Moinvaziri79ac3382017-10-23 17:22:36 -07001810 err = mz_stream_seek(zip->cd_stream, zip->cd_current_pos, MZ_SEEK_SET);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001811 if (err == MZ_OK)
Nathan Moinvaziricda36002017-10-21 09:37:18 -07001812 err = mz_zip_entry_read_header(zip->cd_stream, 0, &zip->file_info, zip->file_info_stream);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001813 if (err == MZ_OK)
1814 zip->entry_scanned = 1;
1815 return err;
1816}
1817
Nathan Moinvaziric565fa82018-10-19 08:48:33 -07001818int32_t mz_zip_set_number_entry(void *handle, uint64_t number_entry)
1819{
1820 mz_zip *zip = (mz_zip *)handle;
1821 if (zip == NULL)
1822 return MZ_PARAM_ERROR;
1823 zip->number_entry = number_entry;
1824 return MZ_OK;
1825}
1826
Nathan Moinvaziri904c4082018-10-08 23:31:21 -07001827int32_t mz_zip_get_number_entry(void *handle, uint64_t *number_entry)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07001828{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001829 mz_zip *zip = (mz_zip *)handle;
1830 if (zip == NULL || number_entry == NULL)
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07001831 return MZ_PARAM_ERROR;
Nathan Moinvaziri6e21e862017-10-19 09:57:54 -07001832 *number_entry = zip->number_entry;
1833 return MZ_OK;
1834}
1835
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001836int32_t mz_zip_get_disk_number_with_cd(void *handle, uint32_t *disk_number_with_cd)
juanii566b9782018-02-10 15:07:54 -03001837{
1838 mz_zip *zip = (mz_zip *)handle;
1839 if (zip == NULL || disk_number_with_cd == NULL)
1840 return MZ_PARAM_ERROR;
1841 *disk_number_with_cd = zip->disk_number_with_cd;
1842 return MZ_OK;
1843}
1844
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001845int64_t mz_zip_get_entry(void *handle)
Viktor Szakatsce26dba2018-04-22 19:30:34 +00001846{
1847 mz_zip *zip = (mz_zip *)handle;
1848
1849 if (zip == NULL)
1850 return MZ_PARAM_ERROR;
1851
1852 return zip->cd_current_pos;
1853}
1854
Nathan Moinvaziri904c4082018-10-08 23:31:21 -07001855int32_t mz_zip_goto_entry(void *handle, int64_t cd_pos)
Viktor Szakatsce26dba2018-04-22 19:30:34 +00001856{
1857 mz_zip *zip = (mz_zip *)handle;
1858
1859 if (zip == NULL)
1860 return MZ_PARAM_ERROR;
1861
1862 if (cd_pos < zip->cd_start_pos || cd_pos > zip->cd_start_pos + zip->cd_size)
1863 return MZ_PARAM_ERROR;
1864
1865 zip->cd_current_pos = cd_pos;
1866
1867 return mz_zip_goto_next_entry_int(handle);
1868}
1869
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001870int32_t mz_zip_goto_first_entry(void *handle)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001871{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001872 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001873
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001874 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001875 return MZ_PARAM_ERROR;
1876
Nathan Moinvaziricda36002017-10-21 09:37:18 -07001877 zip->cd_current_pos = zip->cd_start_pos;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001878
1879 return mz_zip_goto_next_entry_int(handle);
1880}
1881
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001882int32_t mz_zip_goto_next_entry(void *handle)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001883{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001884 mz_zip *zip = (mz_zip *)handle;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001885
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001886 if (zip == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001887 return MZ_PARAM_ERROR;
1888
Nathan Moinvaziri904c4082018-10-08 23:31:21 -07001889 zip->cd_current_pos += (int64_t)MZ_ZIP_SIZE_CD_ITEM + zip->file_info.filename_size +
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001890 zip->file_info.extrafield_size + zip->file_info.comment_size;
1891
1892 return mz_zip_goto_next_entry_int(handle);
1893}
1894
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001895int32_t mz_zip_locate_entry(void *handle, const char *filename, uint8_t ignore_case)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001896{
Nathan Moinvaziri5f9e5d32017-10-20 07:59:39 -07001897 mz_zip *zip = (mz_zip *)handle;
1898 int32_t err = MZ_OK;
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001899 int32_t result = 0;
1900
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001901 if (zip == NULL || filename == NULL)
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001902 return MZ_PARAM_ERROR;
1903
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07001904 // If we are already on the current entry, no need to search
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001905 if ((zip->entry_scanned) && (zip->file_info.filename != NULL))
1906 {
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07001907 result = mz_zip_path_compare(zip->file_info.filename, filename, ignore_case);
Nathan Moinvaziri77687f32018-08-09 16:29:26 -07001908 if (result == 0)
1909 return MZ_OK;
1910 }
1911
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07001912 // Search all entries starting at the first
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001913 err = mz_zip_goto_first_entry(handle);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001914 while (err == MZ_OK)
1915 {
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07001916 result = mz_zip_path_compare(zip->file_info.filename, filename, ignore_case);
1917 if (result == 0)
1918 return MZ_OK;
1919
1920 err = mz_zip_goto_next_entry(handle);
1921 }
1922
1923 return err;
1924}
1925
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001926int32_t mz_zip_locate_first_entry(void *handle, void *userdata, mz_zip_locate_entry_cb cb)
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07001927{
1928 mz_zip *zip = (mz_zip *)handle;
1929 int32_t err = MZ_OK;
1930 int32_t result = 0;
1931
1932 // Search first entry looking for match
1933 err = mz_zip_goto_first_entry(handle);
1934 if (err != MZ_OK)
1935 return err;
1936
1937 result = cb(handle, userdata, &zip->file_info);
1938 if (result == 0)
1939 return MZ_OK;
1940
1941 return mz_zip_locate_next_entry(handle, userdata, cb);
1942}
1943
Nathan Moinvaziri590e5902018-08-17 11:10:35 -07001944int32_t mz_zip_locate_next_entry(void *handle, void *userdata, mz_zip_locate_entry_cb cb)
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07001945{
1946 mz_zip *zip = (mz_zip *)handle;
1947 int32_t err = MZ_OK;
1948 int32_t result = 0;
1949
1950 // Search next entries looking for match
1951 err = mz_zip_goto_next_entry(handle);
1952 while (err == MZ_OK)
1953 {
1954 result = cb(handle, userdata, &zip->file_info);
Nathan Moinvaziri8f67ff92017-10-17 23:22:29 -07001955 if (result == 0)
1956 return MZ_OK;
1957
1958 err = mz_zip_goto_next_entry(handle);
1959 }
1960
1961 return err;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07001962}
1963
1964/***************************************************************************/
1965
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07001966int32_t mz_zip_attrib_is_dir(uint32_t attrib, int32_t version_madeby)
Nathan Moinvaziribb3b75b2018-05-02 10:48:51 -07001967{
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07001968 uint32_t posix_attrib = 0;
Nathan Moinvaziri904c4082018-10-08 23:31:21 -07001969 uint8_t system = MZ_HOST_SYSTEM(version_madeby);
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07001970 int32_t err = MZ_OK;
Nathan Moinvaziribb3b75b2018-05-02 10:48:51 -07001971
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07001972 err = mz_zip_attrib_convert(system, attrib, MZ_HOST_SYSTEM_UNIX, &posix_attrib);
1973 if (err == MZ_OK)
Nathan Moinvaziribb3b75b2018-05-02 10:48:51 -07001974 {
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07001975 if ((posix_attrib & 0170000) == 0040000) // S_ISDIR
Nathan Moinvaziribb3b75b2018-05-02 10:48:51 -07001976 return MZ_OK;
1977 }
1978
1979 return MZ_EXIST_ERROR;
1980}
1981
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07001982int32_t mz_zip_attrib_convert(uint8_t src_sys, uint32_t src_attrib, uint8_t target_sys, uint32_t *target_attrib)
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07001983{
1984 if (target_attrib == NULL)
1985 return MZ_PARAM_ERROR;
1986
1987 *target_attrib = 0;
1988
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07001989 if ((src_sys == MZ_HOST_SYSTEM_MSDOS) || (src_sys == MZ_HOST_SYSTEM_WINDOWS_NTFS))
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07001990 {
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07001991 if ((target_sys == MZ_HOST_SYSTEM_MSDOS) || (target_sys == MZ_HOST_SYSTEM_WINDOWS_NTFS))
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07001992 {
1993 *target_attrib = src_attrib;
1994 return MZ_OK;
1995 }
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07001996 if ((target_sys == MZ_HOST_SYSTEM_UNIX) || (target_sys == MZ_HOST_SYSTEM_OSX_DARWIN))
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07001997 return mz_zip_attrib_win32_to_posix(src_attrib, target_attrib);
1998 }
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07001999 else if ((src_sys == MZ_HOST_SYSTEM_UNIX) || (src_sys == MZ_HOST_SYSTEM_OSX_DARWIN))
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002000 {
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002001 if ((target_sys == MZ_HOST_SYSTEM_UNIX) || (target_sys == MZ_HOST_SYSTEM_OSX_DARWIN))
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002002 {
Nathan Moinvaziri5e6bac52018-10-08 23:47:02 -07002003 // If high bytes are set, it contains unix specific attributes
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002004 if ((src_attrib >> 16) != 0)
2005 src_attrib >>= 16;
2006
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002007 *target_attrib = src_attrib;
2008 return MZ_OK;
2009 }
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002010 if ((target_sys == MZ_HOST_SYSTEM_MSDOS) || (target_sys == MZ_HOST_SYSTEM_WINDOWS_NTFS))
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002011 return mz_zip_attrib_posix_to_win32(src_attrib, target_attrib);
2012 }
2013
2014 return MZ_SUPPORT_ERROR;
2015}
2016
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002017int32_t mz_zip_attrib_posix_to_win32(uint32_t posix_attrib, uint32_t *win32_attrib)
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002018{
2019 if (win32_attrib == NULL)
2020 return MZ_PARAM_ERROR;
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002021
2022 *win32_attrib = 0;
2023
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002024 // S_IWUSR | S_IWGRP | S_IWOTH | S_IXUSR | S_IXGRP | S_IXOTH
2025 if ((posix_attrib & 0000333) == 0 && (posix_attrib & 0000444) != 0)
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002026 *win32_attrib |= 0x01; // FILE_ATTRIBUTE_READONLY
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002027 // S_IFDIR
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002028 if ((posix_attrib & 0170000) == 0040000)
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002029 *win32_attrib |= 0x10; // FILE_ATTRIBUTE_DIRECTORY
Nathan Moinvazirida4b58f2018-08-20 18:21:26 -07002030 // S_IFREG
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002031 else
2032 *win32_attrib |= 0x80; // FILE_ATTRIBUTE_NORMAL
2033
2034 return MZ_OK;
2035}
2036
Nathan Moinvaziri2bb21d72018-10-08 21:47:15 -07002037int32_t mz_zip_attrib_win32_to_posix(uint32_t win32_attrib, uint32_t *posix_attrib)
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002038{
2039 if (posix_attrib == NULL)
2040 return MZ_PARAM_ERROR;
2041
2042 *posix_attrib = 0000444; // S_IRUSR | S_IRGRP | S_IROTH
2043 // FILE_ATTRIBUTE_READONLY
2044 if ((win32_attrib & 0x01) == 0)
2045 *posix_attrib |= 0000222; // S_IWUSR | S_IWGRP | S_IWOTH
2046 // FILE_ATTRIBUTE_DIRECTORY
2047 if ((win32_attrib & 0x10) == 0x10)
2048 *posix_attrib |= 0040111; // S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002049 else
2050 *posix_attrib |= 0100000; // S_IFREG
2051
2052 return MZ_OK;
2053}
2054
Nathan Moinvaziribb3b75b2018-05-02 10:48:51 -07002055/***************************************************************************/
2056
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07002057int32_t mz_zip_extrafield_find(void *stream, uint16_t type, uint16_t *length)
2058{
2059 int32_t err = MZ_OK;
Nathan Moinvazirie1f68fc2018-10-23 09:04:04 -07002060 uint16_t field_type = 0;
2061 uint16_t field_length = 0;
2062
2063 do
2064 {
2065 err = mz_stream_read_uint16(stream, &field_type);
2066 if (err == MZ_OK)
2067 err = mz_stream_read_uint16(stream, &field_length);
2068 if (err != MZ_OK)
2069 break;
2070
2071 if (type == field_type)
2072 {
2073 if (length != NULL)
2074 *length = field_length;
2075 return MZ_OK;
2076 }
2077
2078 err = mz_stream_seek(stream, field_length, SEEK_CUR);
2079 }
2080 while (err == MZ_OK);
2081
2082 return MZ_EXIST_ERROR;
2083}
2084
2085int32_t mz_zip_extrafield_read(void *stream, uint16_t *type, uint16_t *length)
2086{
2087 int32_t err = MZ_OK;
2088 if (type == NULL || length == NULL)
2089 return MZ_PARAM_ERROR;
2090 err = mz_stream_read_uint16(stream, type);
2091 if (err == MZ_OK)
2092 err = mz_stream_read_uint16(stream, length);
2093 return err;
2094}
2095
2096int32_t mz_zip_extrafield_write(void *stream, uint32_t type, uint16_t length)
2097{
2098 int32_t err = MZ_OK;
2099 err = mz_stream_write_uint16(stream, type);
2100 if (err == MZ_OK)
2101 err = mz_stream_write_uint16(stream, length);
2102 return err;
2103}
2104
2105/***************************************************************************/
2106
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002107static int32_t mz_zip_invalid_date(const struct tm *ptm)
2108{
2109#define datevalue_in_range(min, max, value) ((min) <= (value) && (value) <= (max))
Viktor Szakatsce26dba2018-04-22 19:30:34 +00002110 return (!datevalue_in_range(0, 127 + 80, ptm->tm_year) || // 1980-based year, allow 80 extra
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002111 !datevalue_in_range(0, 11, ptm->tm_mon) ||
2112 !datevalue_in_range(1, 31, ptm->tm_mday) ||
2113 !datevalue_in_range(0, 23, ptm->tm_hour) ||
2114 !datevalue_in_range(0, 59, ptm->tm_min) ||
2115 !datevalue_in_range(0, 59, ptm->tm_sec));
2116#undef datevalue_in_range
2117}
2118
2119static void mz_zip_dosdate_to_raw_tm(uint64_t dos_date, struct tm *ptm)
2120{
2121 uint64_t date = (uint64_t)(dos_date >> 16);
2122
2123 ptm->tm_mday = (uint16_t)(date & 0x1f);
2124 ptm->tm_mon = (uint16_t)(((date & 0x1E0) / 0x20) - 1);
2125 ptm->tm_year = (uint16_t)(((date & 0x0FE00) / 0x0200) + 80);
2126 ptm->tm_hour = (uint16_t)((dos_date & 0xF800) / 0x800);
2127 ptm->tm_min = (uint16_t)((dos_date & 0x7E0) / 0x20);
2128 ptm->tm_sec = (uint16_t)(2 * (dos_date & 0x1f));
2129 ptm->tm_isdst = -1;
2130}
2131
2132int32_t mz_zip_dosdate_to_tm(uint64_t dos_date, struct tm *ptm)
2133{
2134 if (ptm == NULL)
2135 return MZ_PARAM_ERROR;
2136
2137 mz_zip_dosdate_to_raw_tm(dos_date, ptm);
2138
2139 if (mz_zip_invalid_date(ptm))
2140 {
2141 // Invalid date stored, so don't return it
2142 memset(ptm, 0, sizeof(struct tm));
2143 return MZ_FORMAT_ERROR;
2144 }
2145 return MZ_OK;
2146}
2147
2148time_t mz_zip_dosdate_to_time_t(uint64_t dos_date)
2149{
2150 struct tm ptm;
2151 mz_zip_dosdate_to_raw_tm(dos_date, &ptm);
2152 return mktime(&ptm);
2153}
2154
2155int32_t mz_zip_time_t_to_tm(time_t unix_time, struct tm *ptm)
2156{
Nathan Moinvaziri17fcdcd2017-10-26 08:13:13 -07002157 struct tm *ltm = NULL;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002158 if (ptm == NULL)
2159 return MZ_PARAM_ERROR;
Viktor Szakatsce26dba2018-04-22 19:30:34 +00002160 ltm = localtime(&unix_time); // Returns a 1900-based year
Nathan Moinvaziri17fcdcd2017-10-26 08:13:13 -07002161 if (ltm == NULL)
Viktor Szakatsce26dba2018-04-22 19:30:34 +00002162 {
2163 // Invalid date stored, so don't return it
2164 memset(ptm, 0, sizeof(struct tm));
Nathan Moinvaziri17fcdcd2017-10-26 08:13:13 -07002165 return MZ_INTERNAL_ERROR;
Viktor Szakatsce26dba2018-04-22 19:30:34 +00002166 }
Nathan Moinvaziri17fcdcd2017-10-26 08:13:13 -07002167 memcpy(ptm, ltm, sizeof(struct tm));
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002168 return MZ_OK;
2169}
2170
2171uint32_t mz_zip_time_t_to_dos_date(time_t unix_time)
2172{
2173 struct tm ptm;
2174 mz_zip_time_t_to_tm(unix_time, &ptm);
2175 return mz_zip_tm_to_dosdate((const struct tm *)&ptm);
2176}
2177
2178uint32_t mz_zip_tm_to_dosdate(const struct tm *ptm)
2179{
Nathan Moinvazirie33916b2018-05-01 13:45:08 -07002180 struct tm fixed_tm;
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002181
2182 // Years supported:
2183
2184 // [00, 79] (assumed to be between 2000 and 2079)
2185 // [80, 207] (assumed to be between 1980 and 2107, typical output of old
2186 // software that does 'year-1900' to get a double digit year)
2187 // [1980, 2107] (due to format limitations, only years 1980-2107 can be stored.)
2188
2189 memcpy(&fixed_tm, ptm, sizeof(struct tm));
2190 if (fixed_tm.tm_year >= 1980) // range [1980, 2107]
2191 fixed_tm.tm_year -= 1980;
Viktor Szakatsce26dba2018-04-22 19:30:34 +00002192 else if (fixed_tm.tm_year >= 80) // range [80, 207]
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002193 fixed_tm.tm_year -= 80;
2194 else // range [00, 79]
2195 fixed_tm.tm_year += 20;
2196
Viktor Szakatsce26dba2018-04-22 19:30:34 +00002197 if (mz_zip_invalid_date(&fixed_tm))
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002198 return 0;
2199
2200 return (uint32_t)(((fixed_tm.tm_mday) + (32 * (fixed_tm.tm_mon + 1)) + (512 * fixed_tm.tm_year)) << 16) |
Nathan Moinvaziri904c4082018-10-08 23:31:21 -07002201 (((uint32_t)fixed_tm.tm_sec / 2) + (32 * (uint32_t)fixed_tm.tm_min) + (2048 * (uint32_t)fixed_tm.tm_hour));
Nathan Moinvaziri9eab88e2017-10-22 14:32:37 -07002202}
2203
2204int32_t mz_zip_ntfs_to_unix_time(uint64_t ntfs_time, time_t *unix_time)
2205{
2206 *unix_time = (time_t)((ntfs_time - 116444736000000000LL) / 10000000);
2207 return MZ_OK;
2208}
2209
2210int32_t mz_zip_unix_to_ntfs_time(time_t unix_time, uint64_t *ntfs_time)
2211{
2212 *ntfs_time = ((uint64_t)unix_time * 10000000) + 116444736000000000LL;
2213 return MZ_OK;
2214}
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07002215
Nathan Moinvaziri4b8e4b32018-08-20 09:06:23 -07002216/***************************************************************************/
2217
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07002218int32_t mz_zip_path_compare(const char *path1, const char *path2, uint8_t ignore_case)
2219{
2220 do
2221 {
2222 if ((*path1 == '\\' && *path2 == '/') ||
2223 (*path2 == '\\' && *path1 == '/'))
2224 {
2225 // Ignore comparison of path slashes
2226 }
2227 else if (ignore_case)
2228 {
2229 if (tolower(*path1) != tolower(*path2))
2230 break;
2231 }
2232 else if (*path1 != *path2)
2233 {
2234 break;
2235 }
2236
2237 path1 += 1;
2238 path2 += 1;
2239 }
2240 while (*path1 != 0 && *path2 != 0);
2241
2242 if (ignore_case)
2243 return (int32_t)(tolower(*path1) - tolower(*path2));
2244
2245 return (int32_t)(*path1 - *path2);
2246}
2247
Nathan Moinvaziri8cb7df72018-10-25 08:39:48 -07002248static uint32_t mz_zip_encoding_codepage437_to_utf8[256] = {
2249 0x000000, 0xba98e2, 0xbb98e2, 0xa599e2, 0xa699e2, 0xa399e2, 0xa099e2, 0xa280e2,
2250 0x9897e2, 0x8b97e2, 0x9997e2, 0x8299e2, 0x8099e2, 0xaa99e2, 0xab99e2, 0xbc98e2,
2251 0xba96e2, 0x8497e2, 0x9586e2, 0xbc80e2, 0x00b6c2, 0x00a7c2, 0xac96e2, 0xa886e2,
2252 0x9186e2, 0x9386e2, 0x9286e2, 0x9086e2, 0x9f88e2, 0x9486e2, 0xb296e2, 0xbc96e2,
2253 0x000020, 0x000021, 0x000022, 0x000023, 0x000024, 0x000025, 0x000026, 0x000027,
2254 0x000028, 0x000029, 0x00002a, 0x00002b, 0x00002c, 0x00002d, 0x00002e, 0x00002f,
2255 0x000030, 0x000031, 0x000032, 0x000033, 0x000034, 0x000035, 0x000036, 0x000037,
2256 0x000038, 0x000039, 0x00003a, 0x00003b, 0x00003c, 0x00003d, 0x00003e, 0x00003f,
2257 0x000040, 0x000041, 0x000042, 0x000043, 0x000044, 0x000045, 0x000046, 0x000047,
2258 0x000048, 0x000049, 0x00004a, 0x00004b, 0x00004c, 0x00004d, 0x00004e, 0x00004f,
2259 0x000050, 0x000051, 0x000052, 0x000053, 0x000054, 0x000055, 0x000056, 0x000057,
2260 0x000058, 0x000059, 0x00005a, 0x00005b, 0x00005c, 0x00005d, 0x00005e, 0x00005f,
2261 0x000060, 0x000061, 0x000062, 0x000063, 0x000064, 0x000065, 0x000066, 0x000067,
2262 0x000068, 0x000069, 0x00006a, 0x00006b, 0x00006c, 0x00006d, 0x00006e, 0x00006f,
2263 0x000070, 0x000071, 0x000072, 0x000073, 0x000074, 0x000075, 0x000076, 0x000077,
2264 0x000078, 0x000079, 0x00007a, 0x00007b, 0x00007c, 0x00007d, 0x00007e, 0x828ce2,
2265 0x0087c3, 0x00bcc3, 0x00a9c3, 0x00a2c3, 0x00a4c3, 0x00a0c3, 0x00a5c3, 0x00a7c3,
2266 0x00aac3, 0x00abc3, 0x00a8c3, 0x00afc3, 0x00aec3, 0x00acc3, 0x0084c3, 0x0085c3,
2267 0x0089c3, 0x00a6c3, 0x0086c3, 0x00b4c3, 0x00b6c3, 0x00b2c3, 0x00bbc3, 0x00b9c3,
2268 0x00bfc3, 0x0096c3, 0x009cc3, 0x00a2c2, 0x00a3c2, 0x00a5c2, 0xa782e2, 0x0092c6,
2269 0x00a1c3, 0x00adc3, 0x00b3c3, 0x00bac3, 0x00b1c3, 0x0091c3, 0x00aac2, 0x00bac2,
2270 0x00bfc2, 0x908ce2, 0x00acc2, 0x00bdc2, 0x00bcc2, 0x00a1c2, 0x00abc2, 0x00bbc2,
2271 0x9196e2, 0x9296e2, 0x9396e2, 0x8294e2, 0xa494e2, 0xa195e2, 0xa295e2, 0x9695e2,
2272 0x9595e2, 0xa395e2, 0x9195e2, 0x9795e2, 0x9d95e2, 0x9c95e2, 0x9b95e2, 0x9094e2,
2273 0x9494e2, 0xb494e2, 0xac94e2, 0x9c94e2, 0x8094e2, 0xbc94e2, 0x9e95e2, 0x9f95e2,
2274 0x9a95e2, 0x9495e2, 0xa995e2, 0xa695e2, 0xa095e2, 0x9095e2, 0xac95e2, 0xa795e2,
2275 0xa895e2, 0xa495e2, 0xa595e2, 0x9995e2, 0x9895e2, 0x9295e2, 0x9395e2, 0xab95e2,
2276 0xaa95e2, 0x9894e2, 0x8c94e2, 0x8896e2, 0x8496e2, 0x8c96e2, 0x9096e2, 0x8096e2,
2277 0x00b1ce, 0x009fc3, 0x0093ce, 0x0080cf, 0x00a3ce, 0x0083cf, 0x00b5c2, 0x0084cf,
2278 0x00a6ce, 0x0098ce, 0x00a9ce, 0x00b4ce, 0x9e88e2, 0x0086cf, 0x00b5ce, 0xa988e2,
2279 0xa189e2, 0x00b1c2, 0xa589e2, 0xa489e2, 0xa08ce2, 0xa18ce2, 0x00b7c3, 0x8889e2,
2280 0x00b0c2, 0x9988e2, 0x00b7c2, 0x9a88e2, 0xbf81e2, 0x00b2c2, 0xa096e2, 0x00a0c2
2281};
2282
2283int32_t mz_zip_encoding_cp437_to_utf8(const char *source, char *target, int32_t max_target)
2284{
2285 uint32_t utf8_char = 0;
2286 uint8_t utf8_byte = 0;
2287 uint8_t cp437_char = 0;
2288 int32_t x = 0;
2289 int32_t written = 0;
2290
2291 // Convert ibm codepage 437 encoding to utf-8
2292 while (*source != 0)
2293 {
2294 cp437_char = (uint8_t)*source;
2295 source += 1;
2296 utf8_char = mz_zip_encoding_codepage437_to_utf8[cp437_char];
2297 for (x = 0; x < 32; x += 8)
2298 {
2299 utf8_byte = (uint8_t)(utf8_char >> x);
2300 if (x > 0 && utf8_byte == 0)
2301 continue;
2302 if (max_target <= 1)
2303 break;
2304 target[written] = (char)utf8_byte;
2305 written += 1;
2306 max_target -= 1;
2307 }
2308 }
2309
2310 if (max_target > 0)
2311 {
2312 target[written] = 0;
2313 written += 1;
2314 }
2315
2316 return written;
2317}
2318
Nathan Moinvaziri9a170d42018-08-13 23:07:42 -07002319/***************************************************************************/