]> git.cworth.org Git - vogl/blob - src/voglcore/vogl_miniz_zip.cpp
Initial vogl checkin
[vogl] / src / voglcore / vogl_miniz_zip.cpp
1 /**************************************************************************
2  *
3  * Copyright 2013-2014 RAD Game Tools and Valve Software
4  * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
5  * All Rights Reserved.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  *
25  **************************************************************************/
26
27 // File: vogl_miniz_zip.cpp
28 #include "vogl_miniz_zip.h"
29
30 // ------------------- .ZIP archive reading
31
32 #ifdef __cplusplus
33 extern "C" {
34 #endif
35
36 #ifndef MINIZ_NO_ARCHIVE_APIS
37
38 #ifdef MINIZ_NO_STDIO
39 #define MZ_FILE void *
40 #else
41 #include <sys/stat.h>
42
43 #if defined(_MSC_VER) || defined(__MINGW64__)
44 static FILE *mz_fopen(const char *pFilename, const char *pMode)
45 {
46     FILE *pFile = NULL;
47     fopen_s(&pFile, pFilename, pMode);
48     return pFile;
49 }
50 static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream)
51 {
52     FILE *pFile = NULL;
53     if (freopen_s(&pFile, pPath, pMode, pStream))
54         return NULL;
55     return pFile;
56 }
57 #ifndef MINIZ_NO_TIME
58 #include <sys/utime.h>
59 #endif
60 #define MZ_FOPEN mz_fopen
61 #define MZ_FCLOSE fclose
62 #define MZ_FREAD fread
63 #define MZ_FWRITE fwrite
64 #define MZ_FTELL64 _ftelli64
65 #define MZ_FSEEK64 _fseeki64
66 #define MZ_FILE_STAT_STRUCT _stat
67 #define MZ_FILE_STAT _stat
68 #define MZ_FFLUSH fflush
69 #define MZ_FREOPEN mz_freopen
70 #define MZ_DELETE_FILE remove
71 #elif defined(__MINGW32__)
72 #ifndef MINIZ_NO_TIME
73 #include <sys/utime.h>
74 #endif
75 #define MZ_FOPEN(f, m) fopen(f, m)
76 #define MZ_FCLOSE fclose
77 #define MZ_FREAD fread
78 #define MZ_FWRITE fwrite
79 #define MZ_FTELL64 ftello64
80 #define MZ_FSEEK64 fseeko64
81 #define MZ_FILE_STAT_STRUCT _stat
82 #define MZ_FILE_STAT _stat
83 #define MZ_FFLUSH fflush
84 #define MZ_FREOPEN(f, m, s) freopen(f, m, s)
85 #define MZ_DELETE_FILE remove
86 #elif defined(__TINYC__)
87 #ifndef MINIZ_NO_TIME
88 #include <sys/utime.h>
89 #endif
90 #define MZ_FOPEN(f, m) fopen(f, m)
91 #define MZ_FCLOSE fclose
92 #define MZ_FREAD fread
93 #define MZ_FWRITE fwrite
94 #define MZ_FTELL64 ftell
95 #define MZ_FSEEK64 fseek
96 #define MZ_FILE_STAT_STRUCT stat
97 #define MZ_FILE_STAT stat
98 #define MZ_FFLUSH fflush
99 #define MZ_FREOPEN(f, m, s) freopen(f, m, s)
100 #define MZ_DELETE_FILE remove
101 #elif defined(__GNUC__) && _LARGEFILE64_SOURCE
102 #ifndef MINIZ_NO_TIME
103 #include <utime.h>
104 #endif
105 #define MZ_FOPEN(f, m) fopen64(f, m)
106 #define MZ_FCLOSE fclose
107 #define MZ_FREAD fread
108 #define MZ_FWRITE fwrite
109 #define MZ_FTELL64 ftello64
110 #define MZ_FSEEK64 fseeko64
111 #define MZ_FILE_STAT_STRUCT stat64
112 #define MZ_FILE_STAT stat64
113 #define MZ_FFLUSH fflush
114 #define MZ_FREOPEN(p, m, s) freopen64(p, m, s)
115 #define MZ_DELETE_FILE remove
116 #else
117 #warning Using fopen, ftello, fseeko, stat() etc. path for file I/O - this path may not support large files
118 #ifndef MINIZ_NO_TIME
119 #include <utime.h>
120 #endif
121 #define MZ_FOPEN(f, m) fopen(f, m)
122 #define MZ_FCLOSE fclose
123 #define MZ_FREAD fread
124 #define MZ_FWRITE fwrite
125 #define MZ_FTELL64 ftello
126 #define MZ_FSEEK64 fseeko
127 #define MZ_FILE_STAT_STRUCT stat
128 #define MZ_FILE_STAT stat
129 #define MZ_FFLUSH fflush
130 #define MZ_FREOPEN(f, m, s) freopen(f, m, s)
131 #define MZ_DELETE_FILE remove
132 #endif // #ifdef _MSC_VER
133 #endif // #ifdef MINIZ_NO_STDIO
134
135 #define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c))
136
137 // Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff.
138 enum
139 {
140     // ZIP archive identifiers and record sizes
141     MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50,
142     MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50,
143     MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50,
144     MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30,
145     MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46,
146     MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22,
147
148     // ZIP64 archive identifier and record sizes
149     MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06064b50,
150     MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50,
151     MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE = 56,
152     MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE = 20,
153     MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID = 0x0001,
154     MZ_ZIP_DATA_DESCRIPTOR_ID = 0x08074b50,
155
156     // Central directory header record offsets
157     MZ_ZIP_CDH_SIG_OFS = 0,
158     MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4,
159     MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6,
160     MZ_ZIP_CDH_BIT_FLAG_OFS = 8,
161     MZ_ZIP_CDH_METHOD_OFS = 10,
162     MZ_ZIP_CDH_FILE_TIME_OFS = 12,
163     MZ_ZIP_CDH_FILE_DATE_OFS = 14,
164     MZ_ZIP_CDH_CRC32_OFS = 16,
165     MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20,
166     MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24,
167     MZ_ZIP_CDH_FILENAME_LEN_OFS = 28,
168     MZ_ZIP_CDH_EXTRA_LEN_OFS = 30,
169     MZ_ZIP_CDH_COMMENT_LEN_OFS = 32,
170     MZ_ZIP_CDH_DISK_START_OFS = 34,
171     MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36,
172     MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38,
173     MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42,
174
175     // Local directory header offsets
176     MZ_ZIP_LDH_SIG_OFS = 0,
177     MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4,
178     MZ_ZIP_LDH_BIT_FLAG_OFS = 6,
179     MZ_ZIP_LDH_METHOD_OFS = 8,
180     MZ_ZIP_LDH_FILE_TIME_OFS = 10,
181     MZ_ZIP_LDH_FILE_DATE_OFS = 12,
182     MZ_ZIP_LDH_CRC32_OFS = 14,
183     MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18,
184     MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22,
185     MZ_ZIP_LDH_FILENAME_LEN_OFS = 26,
186     MZ_ZIP_LDH_EXTRA_LEN_OFS = 28,
187
188     // End of central directory offsets
189     MZ_ZIP_ECDH_SIG_OFS = 0,
190     MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4,
191     MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6,
192     MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8,
193     MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10,
194     MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12,
195     MZ_ZIP_ECDH_CDIR_OFS_OFS = 16,
196     MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20,
197
198     // ZIP64 End of central directory locator offsets
199     MZ_ZIP64_ECDL_SIG_OFS = 0,                    // 4 bytes
200     MZ_ZIP64_ECDL_NUM_DISK_CDIR_OFS = 4,          // 4 bytes
201     MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS = 8,  // 8 bytes
202     MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS = 16, // 4 bytes
203
204     // ZIP64 End of central directory header offsets
205     MZ_ZIP64_ECDH_SIG_OFS = 0,                       // 4 bytes
206     MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS = 4,            // 8 bytes
207     MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS = 12,          // 2 bytes
208     MZ_ZIP64_ECDH_VERSION_NEEDED_OFS = 14,           // 2 bytes
209     MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS = 16,            // 4 bytes
210     MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS = 20,            // 4 bytes
211     MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 24, // 8 bytes
212     MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS = 32,       // 8 bytes
213     MZ_ZIP64_ECDH_CDIR_SIZE_OFS = 40,                // 8 bytes
214     MZ_ZIP64_ECDH_CDIR_OFS_OFS = 48,                 // 8 bytes
215     MZ_ZIP_VERSION_MADE_BY_DOS_FILESYSTEM_ID = 0,
216     MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG = 0x10,
217     MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED = 1,
218     MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG = 32,
219     MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION = 64,
220     MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED = 8192
221 };
222
223 typedef struct
224 {
225     void *m_p;
226     size_t m_size, m_capacity;
227     mz_uint m_element_size;
228 } mz_zip_array;
229
230 struct mz_zip_internal_state_tag
231 {
232     mz_zip_array m_central_dir;
233     mz_zip_array m_central_dir_offsets;
234     mz_zip_array m_sorted_central_dir_offsets;
235
236     // The flags passed in when the archive is initially opened.
237     uint m_init_flags;
238
239     // MZ_TRUE if the archive has a zip64 end of central directory headers, etc.
240     mz_bool m_zip64;
241
242     // MZ_TRUE if we found zip64 extended info in the central directory (m_zip64 will also be slammed to true too, even if we didn't find a zip64 end of central dir header, etc.)
243     mz_bool m_zip64_has_extended_info_fields;
244
245     // These fields are used by the file, FILE, memory, and memory/heap read/write helpers.
246     MZ_FILE *m_pFile;
247     mz_uint64 m_file_archive_start_ofs;
248
249     void *m_pMem;
250     size_t m_mem_size;
251     size_t m_mem_capacity;
252 };
253
254 #define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size
255
256 #if defined(DEBUG) || defined(_DEBUG) || defined(VOGL_BUILD_DEBUG)
257 static MZ_FORCEINLINE mz_uint mz_zip_array_range_check(const mz_zip_array *pArray, mz_uint index)
258 {
259     MZ_ASSERT(index < pArray->m_size);
260     return index;
261 }
262 #define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[mz_zip_array_range_check(array_ptr, index)]
263 #else
264 #define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index]
265 #endif
266
267 static MZ_FORCEINLINE void mz_zip_array_init(mz_zip_array *pArray, mz_uint32 element_size)
268 {
269     memset(pArray, 0, sizeof(mz_zip_array));
270     pArray->m_element_size = element_size;
271 }
272
273 static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray)
274 {
275     pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p);
276     memset(pArray, 0, sizeof(mz_zip_array));
277 }
278
279 static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing)
280 {
281     void *pNew_p;
282     size_t new_capacity = min_new_capacity;
283     MZ_ASSERT(pArray->m_element_size);
284     if (pArray->m_capacity >= min_new_capacity)
285         return MZ_TRUE;
286     if (growing)
287     {
288         new_capacity = MZ_MAX(1, pArray->m_capacity);
289         while (new_capacity < min_new_capacity)
290             new_capacity *= 2;
291     }
292     if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity)))
293         return MZ_FALSE;
294     pArray->m_p = pNew_p;
295     pArray->m_capacity = new_capacity;
296     return MZ_TRUE;
297 }
298
299 static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing)
300 {
301     if (new_capacity > pArray->m_capacity)
302     {
303         if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing))
304             return MZ_FALSE;
305     }
306     return MZ_TRUE;
307 }
308
309 static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing)
310 {
311     if (new_size > pArray->m_capacity)
312     {
313         if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing))
314             return MZ_FALSE;
315     }
316     pArray->m_size = new_size;
317     return MZ_TRUE;
318 }
319
320 static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n)
321 {
322     return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE);
323 }
324
325 static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n)
326 {
327     size_t orig_size = pArray->m_size;
328     if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE))
329         return MZ_FALSE;
330     memcpy((mz_uint8 *)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size);
331     return MZ_TRUE;
332 }
333
334 #ifndef MINIZ_NO_TIME
335 static MZ_TIME_T mz_zip_dos_to_time_t(int dos_time, int dos_date)
336 {
337     struct tm tm;
338     memset(&tm, 0, sizeof(tm));
339     tm.tm_isdst = -1;
340     tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900;
341     tm.tm_mon = ((dos_date >> 5) & 15) - 1;
342     tm.tm_mday = dos_date & 31;
343     tm.tm_hour = (dos_time >> 11) & 31;
344     tm.tm_min = (dos_time >> 5) & 63;
345     tm.tm_sec = (dos_time << 1) & 62;
346     return mktime(&tm);
347 }
348
349 static void mz_zip_time_t_to_dos_time(MZ_TIME_T time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date)
350 {
351 #ifdef _MSC_VER
352     struct tm tm_struct;
353     struct tm *tm = &tm_struct;
354     errno_t err = localtime_s(tm, &time);
355     if (err)
356     {
357         *pDOS_date = 0;
358         *pDOS_time = 0;
359         return;
360     }
361 #else
362     struct tm *tm = localtime(&time);
363 #endif // #ifdef_MSC_VER
364
365     *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1));
366     *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday);
367 }
368
369 #ifndef MINIZ_NO_STDIO
370 static mz_bool mz_zip_get_file_modified_time(const char *pFilename, MZ_TIME_T *pTime)
371 {
372     struct MZ_FILE_STAT_STRUCT file_stat;
373
374     // On Linux with x86 glibc, this call will fail on large files (I think >= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh.
375     if (MZ_FILE_STAT(pFilename, &file_stat) != 0)
376         return MZ_FALSE;
377
378     *pTime = file_stat.st_mtime;
379
380     return MZ_TRUE;
381 }
382
383 static mz_bool mz_zip_set_file_times(const char *pFilename, MZ_TIME_T access_time, MZ_TIME_T modified_time)
384 {
385     struct utimbuf t;
386
387     memset(&t, 0, sizeof(t));
388     t.actime = access_time;
389     t.modtime = modified_time;
390
391     return !utime(pFilename, &t);
392 }
393 #endif // #ifndef MINIZ_NO_STDIO
394
395 #endif // #ifndef MINIZ_NO_TIME
396
397 static MZ_FORCEINLINE mz_bool mz_zip_set_error(mz_zip_archive *pZip, mz_zip_error err_num)
398 {
399     if (pZip)
400         pZip->m_last_error = err_num;
401     return MZ_FALSE;
402 }
403
404 static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint flags)
405 {
406     (void)flags;
407     if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID))
408         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
409
410     if (!pZip->m_pAlloc)
411         pZip->m_pAlloc = miniz_def_alloc_func;
412     if (!pZip->m_pFree)
413         pZip->m_pFree = miniz_def_free_func;
414     if (!pZip->m_pRealloc)
415         pZip->m_pRealloc = miniz_def_realloc_func;
416
417     pZip->m_archive_size = 0;
418     pZip->m_central_directory_file_ofs = 0;
419     pZip->m_total_files = 0;
420     pZip->m_last_error = MZ_ZIP_NO_ERROR;
421
422     if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state))))
423         return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
424
425     memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state));
426     MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8));
427     MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32));
428     MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32));
429     pZip->m_pState->m_init_flags = flags;
430     pZip->m_pState->m_zip64 = MZ_FALSE;
431     pZip->m_pState->m_zip64_has_extended_info_fields = MZ_FALSE;
432
433     pZip->m_zip_mode = MZ_ZIP_MODE_READING;
434
435     return MZ_TRUE;
436 }
437
438 static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index)
439 {
440     const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE;
441     const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index));
442     mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS);
443     mz_uint8 l = 0, r = 0;
444     pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
445     pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
446     pE = pL + MZ_MIN(l_len, r_len);
447     while (pL < pE)
448     {
449         if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR)))
450             break;
451         pL++;
452         pR++;
453     }
454     return (pL == pE) ? (l_len < r_len) : (l < r);
455 }
456
457 #define MZ_SWAP_UINT32(a, b) \
458     do                       \
459     {                        \
460         mz_uint32 t = a;     \
461         a = b;               \
462         b = t;               \
463     }                        \
464     MZ_MACRO_END
465
466 // Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_locate_file(). (Could also use qsort(), but it could allocate memory.)
467 static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip)
468 {
469     mz_zip_internal_state *pState = pZip->m_pState;
470     const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets;
471     const mz_zip_array *pCentral_dir = &pState->m_central_dir;
472     mz_uint32 *pIndices;
473     mz_uint32 start, end;
474     const mz_uint32 size = pZip->m_total_files;
475
476     if (size <= 1U)
477         return;
478
479     pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0);
480
481     start = (size - 2U) >> 1U;
482     for (;;)
483     {
484         mz_uint64 child, root = start;
485         for (;;)
486         {
487             if ((child = (root << 1U) + 1U) >= size)
488                 break;
489             child += (((child + 1U) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U])));
490             if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child]))
491                 break;
492             MZ_SWAP_UINT32(pIndices[root], pIndices[child]);
493             root = child;
494         }
495         if (!start)
496             break;
497         start--;
498     }
499
500     end = size - 1;
501     while (end > 0)
502     {
503         mz_uint64 child, root = 0;
504         MZ_SWAP_UINT32(pIndices[end], pIndices[0]);
505         for (;;)
506         {
507             if ((child = (root << 1U) + 1U) >= end)
508                 break;
509             child += (((child + 1U) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1U]));
510             if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child]))
511                 break;
512             MZ_SWAP_UINT32(pIndices[root], pIndices[child]);
513             root = child;
514         }
515         end--;
516     }
517 }
518
519 static mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive *pZip, mz_uint32 record_sig, mz_uint32 record_size, mz_int64 *pOfs)
520 {
521     mz_int64 cur_file_ofs;
522     mz_uint32 buf_u32[4096 / sizeof(mz_uint32)];
523     mz_uint8 *pBuf = (mz_uint8 *)buf_u32;
524
525     // Basic sanity checks - reject files which are too small
526     if (pZip->m_archive_size < record_size)
527         return MZ_FALSE;
528
529     // Find the record by scanning the file from the end towards the beginning.
530     cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0);
531     for (;;)
532     {
533         int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs);
534
535         if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n)
536             return MZ_FALSE;
537
538         for (i = n - 4; i >= 0; --i)
539         {
540             mz_uint s = MZ_READ_LE32(pBuf + i);
541             if (s == record_sig)
542             {
543                 if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size)
544                     break;
545             }
546         }
547
548         if (i >= 0)
549         {
550             cur_file_ofs += i;
551             break;
552         }
553
554         // Give up if we've searched the entire file, or we've gone back "too far" (~64kb)
555         if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (MZ_UINT16_MAX + record_size)))
556             return MZ_FALSE;
557
558         cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0);
559     }
560
561     *pOfs = cur_file_ofs;
562     return MZ_TRUE;
563 }
564
565 static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint flags)
566 {
567     mz_uint cdir_size = 0, cdir_entries_on_this_disk = 0, num_this_disk = 0, cdir_disk_index = 0;
568     mz_uint64 cdir_ofs = 0;
569     mz_int64 cur_file_ofs = 0;
570     const mz_uint8 *p;
571
572     mz_uint32 buf_u32[4096 / sizeof(mz_uint32)];
573     mz_uint8 *pBuf = (mz_uint8 *)buf_u32;
574     mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0);
575     mz_uint32 zip64_end_of_central_dir_locator_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)];
576     mz_uint8 *pZip64_locator = (mz_uint8 *)zip64_end_of_central_dir_locator_u32;
577
578     mz_uint32 zip64_end_of_central_dir_header_u32[(MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)];
579     mz_uint8 *pZip64_end_of_central_dir = (mz_uint8 *)zip64_end_of_central_dir_header_u32;
580
581     mz_uint64 zip64_end_of_central_dir_ofs = 0;
582
583     // Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there.
584     if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
585         return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE);
586
587     if (!mz_zip_reader_locate_header_sig(pZip, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE, &cur_file_ofs))
588         return mz_zip_set_error(pZip, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR);
589
590     // Read and verify the end of central directory record.
591     if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
592         return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
593
594     if (MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG)
595         return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE);
596
597     if (cur_file_ofs >= (MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE))
598     {
599         if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs - MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE, pZip64_locator, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE)
600         {
601             if (MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG)
602             {
603                 zip64_end_of_central_dir_ofs = MZ_READ_LE64(pZip64_locator + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS);
604                 if (zip64_end_of_central_dir_ofs > (pZip->m_archive_size - MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE))
605                     return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE);
606
607                 if (pZip->m_pRead(pZip->m_pIO_opaque, zip64_end_of_central_dir_ofs, pZip64_end_of_central_dir, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)
608                 {
609                     if (MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG)
610                     {
611                         pZip->m_pState->m_zip64 = MZ_TRUE;
612                     }
613                 }
614             }
615         }
616     }
617
618     pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS);
619     cdir_entries_on_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS);
620     num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS);
621     cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS);
622     cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS);
623     cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS);
624
625     if (pZip->m_pState->m_zip64)
626     {
627         mz_uint32 zip64_total_num_of_disks = MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS);
628         mz_uint64 zip64_cdir_total_entries = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS);
629         mz_uint64 zip64_cdir_total_entries_on_this_disk = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS);
630         mz_uint64 zip64_size_of_end_of_central_dir_record = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS);
631         mz_uint64 zip64_size_of_central_directory = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_SIZE_OFS);
632
633         if (zip64_size_of_end_of_central_dir_record < (MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - 12))
634             return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
635
636         if (zip64_total_num_of_disks != 1U)
637             return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK);
638
639         // Check for miniz's practical limits
640         if (zip64_cdir_total_entries > MZ_UINT32_MAX)
641             return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
642
643         pZip->m_total_files = (mz_uint32)zip64_cdir_total_entries;
644
645         if (zip64_cdir_total_entries_on_this_disk > MZ_UINT32_MAX)
646             return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
647
648         cdir_entries_on_this_disk = (mz_uint32)zip64_cdir_total_entries_on_this_disk;
649
650         // Check for miniz's current practical limits (sorry, this should be enough for millions of files)
651         if (zip64_size_of_central_directory > MZ_UINT32_MAX)
652             return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE);
653
654         cdir_size = (mz_uint32)zip64_size_of_central_directory;
655
656         num_this_disk = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS);
657
658         cdir_disk_index = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS);
659
660         cdir_ofs = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_OFS_OFS);
661     }
662
663     if (pZip->m_total_files != cdir_entries_on_this_disk)
664         return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK);
665
666     if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1)))
667         return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK);
668
669     if (cdir_size < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)
670         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
671
672     if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size)
673         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
674
675     pZip->m_central_directory_file_ofs = cdir_ofs;
676
677     if (pZip->m_total_files)
678     {
679         mz_uint i, n;
680         // Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and possibly another to hold the sorted indices.
681         if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) ||
682             (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE)))
683             return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
684
685         if (sort_central_dir)
686         {
687             if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE))
688                 return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
689         }
690
691         if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size)
692             return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
693
694         // Now create an index into the central directory file records, do some basic sanity checking on each record
695         p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p;
696         for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i)
697         {
698             mz_uint total_header_size, disk_index, bit_flags, filename_size, ext_data_size;
699             mz_uint64 comp_size, decomp_size, local_header_ofs;
700
701             if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG))
702                 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
703
704             MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p);
705
706             if (sort_central_dir)
707                 MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i;
708
709             comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
710             decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);
711             local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS);
712             filename_size = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
713             ext_data_size = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS);
714
715             if ((!pZip->m_pState->m_zip64_has_extended_info_fields) &&
716                 (ext_data_size) &&
717                 (MZ_MAX(MZ_MAX(comp_size, decomp_size), local_header_ofs) == MZ_UINT32_MAX))
718             {
719                 // Attempt to find zip64 extended information field in the entry's extra data
720                 mz_uint32 extra_size_remaining = ext_data_size;
721
722                 if (extra_size_remaining)
723                 {
724                     const mz_uint8 *pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size;
725
726                     do
727                     {
728                         if (extra_size_remaining < (sizeof(mz_uint16) * 2))
729                             return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
730
731                         mz_uint32 field_id = MZ_READ_LE16(pExtra_data);
732                         mz_uint32 field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16));
733
734                         if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining)
735                             return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
736
737                         if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID)
738                         {
739                             // Ok, the archive didn't have any zip64 headers but it uses a zip64 extended information field so mark it as zip64 anyway (this can occur with infozip's zip util when it reads compresses files from stdin).
740                             pZip->m_pState->m_zip64 = MZ_TRUE;
741                             pZip->m_pState->m_zip64_has_extended_info_fields = MZ_TRUE;
742                             break;
743                         }
744
745                         pExtra_data += sizeof(mz_uint16) * 2 + field_data_size;
746                         extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size;
747                     } while (extra_size_remaining);
748                 }
749             }
750
751             // I've seen archives that aren't marked as zip64 that uses zip64 ext data, argh
752             if ((comp_size != MZ_UINT32_MAX) && (decomp_size != MZ_UINT32_MAX))
753             {
754                 if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size))
755                     return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
756             }
757
758             disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS);
759             if ((disk_index == MZ_UINT16_MAX) || ((disk_index != num_this_disk) && (disk_index != 1)))
760                 return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK);
761
762             if (comp_size != MZ_UINT32_MAX)
763             {
764                 if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size)
765                     return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
766             }
767
768             bit_flags = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS);
769             if (bit_flags & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED)
770                 return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION);
771
772             if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n)
773                 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
774
775             n -= total_header_size;
776             p += total_header_size;
777         }
778     }
779
780     if (sort_central_dir)
781         mz_zip_reader_sort_central_dir_offsets_by_filename(pZip);
782
783     return MZ_TRUE;
784 }
785
786 void mz_zip_zero_struct(mz_zip_archive *pZip)
787 {
788     if (pZip)
789         MZ_CLEAR_OBJ(*pZip);
790 }
791
792 static mz_bool mz_zip_reader_end_internal(mz_zip_archive *pZip, mz_bool set_last_error)
793 {
794     mz_bool status = MZ_TRUE;
795
796     if (!pZip)
797         return MZ_FALSE;
798
799     if ((!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))
800     {
801         if (set_last_error)
802             pZip->m_last_error = MZ_ZIP_INVALID_PARAMETER;
803
804         return MZ_FALSE;
805     }
806
807     if (pZip->m_pState)
808     {
809         mz_zip_internal_state *pState = pZip->m_pState;
810         pZip->m_pState = NULL;
811
812         mz_zip_array_clear(pZip, &pState->m_central_dir);
813         mz_zip_array_clear(pZip, &pState->m_central_dir_offsets);
814         mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets);
815
816 #ifndef MINIZ_NO_STDIO
817         if (pState->m_pFile)
818         {
819             if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE)
820             {
821                 if (MZ_FCLOSE(pState->m_pFile) == EOF)
822                 {
823                     if (set_last_error)
824                         pZip->m_last_error = MZ_ZIP_FILE_CLOSE_FAILED;
825                     status = MZ_FALSE;
826                 }
827             }
828             pState->m_pFile = NULL;
829         }
830 #endif // #ifndef MINIZ_NO_STDIO
831
832         pZip->m_pFree(pZip->m_pAlloc_opaque, pState);
833     }
834     pZip->m_zip_mode = MZ_ZIP_MODE_INVALID;
835
836     return status;
837 }
838
839 mz_bool mz_zip_reader_end(mz_zip_archive *pZip)
840 {
841     return mz_zip_reader_end_internal(pZip, MZ_TRUE);
842 }
843
844 static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n)
845 {
846     mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
847     size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n);
848     memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s);
849     return s;
850 }
851
852 mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags)
853 {
854     if ((!pZip) || (!pZip->m_pRead))
855         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
856
857     if (!mz_zip_reader_init_internal(pZip, flags))
858         return MZ_FALSE;
859
860     pZip->m_zip_type = MZ_ZIP_TYPE_USER;
861     pZip->m_archive_size = size;
862
863     if (!mz_zip_reader_read_central_dir(pZip, flags))
864     {
865         mz_zip_reader_end_internal(pZip, MZ_FALSE);
866         return MZ_FALSE;
867     }
868
869     return MZ_TRUE;
870 }
871
872 mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags)
873 {
874     if (!pMem)
875         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
876
877     if (size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
878         return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE);
879
880     if (!mz_zip_reader_init_internal(pZip, flags))
881         return MZ_FALSE;
882
883     pZip->m_zip_type = MZ_ZIP_TYPE_MEMORY;
884     pZip->m_archive_size = size;
885     pZip->m_pRead = mz_zip_mem_read_func;
886     pZip->m_pIO_opaque = pZip;
887
888 #ifdef __cplusplus
889     pZip->m_pState->m_pMem = const_cast<void *>(pMem);
890 #else
891     pZip->m_pState->m_pMem = (void *)pMem;
892 #endif
893
894     pZip->m_pState->m_mem_size = size;
895
896     if (!mz_zip_reader_read_central_dir(pZip, flags))
897     {
898         mz_zip_reader_end_internal(pZip, MZ_FALSE);
899         return MZ_FALSE;
900     }
901
902     return MZ_TRUE;
903 }
904
905 #ifndef MINIZ_NO_STDIO
906 static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n)
907 {
908     mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
909     mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile);
910
911     file_ofs += pZip->m_pState->m_file_archive_start_ofs;
912
913     if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET))))
914         return 0;
915
916     return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile);
917 }
918
919 mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint flags, mz_uint64 file_start_ofs, mz_uint64 archive_size)
920 {
921     if ((!pZip) || (!pFilename) || ((archive_size) && (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)))
922         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
923
924     mz_uint64 file_size;
925     MZ_FILE *pFile = MZ_FOPEN(pFilename, "rb");
926     if (!pFile)
927         return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED);
928
929     file_size = archive_size;
930     if (!file_size)
931     {
932         if (MZ_FSEEK64(pFile, 0, SEEK_END))
933         {
934             MZ_FCLOSE(pFile);
935             return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED);
936         }
937
938         file_size = MZ_FTELL64(pFile);
939     }
940
941     // TODO: Better sanity check archive_size and the # of actual remaining bytes
942
943     if (file_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
944         return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE);
945
946     if (!mz_zip_reader_init_internal(pZip, flags))
947     {
948         MZ_FCLOSE(pFile);
949         return MZ_FALSE;
950     }
951
952     pZip->m_zip_type = MZ_ZIP_TYPE_FILE;
953     pZip->m_pRead = mz_zip_file_read_func;
954     pZip->m_pIO_opaque = pZip;
955     pZip->m_pState->m_pFile = pFile;
956     pZip->m_archive_size = file_size;
957     pZip->m_pState->m_file_archive_start_ofs = file_start_ofs;
958
959     if (!mz_zip_reader_read_central_dir(pZip, flags))
960     {
961         mz_zip_reader_end_internal(pZip, MZ_FALSE);
962         return MZ_FALSE;
963     }
964
965     return MZ_TRUE;
966 }
967
968 mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags)
969 {
970     mz_uint64 cur_file_ofs;
971
972     if ((!pZip) || (!pFile))
973         return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED);
974
975     cur_file_ofs = MZ_FTELL64(pFile);
976
977     if (!archive_size)
978     {
979         if (MZ_FSEEK64(pFile, 0, SEEK_END))
980             return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED);
981
982         archive_size = MZ_FTELL64(pFile) - cur_file_ofs;
983
984         if (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
985             return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE);
986     }
987
988     if (!mz_zip_reader_init_internal(pZip, flags))
989         return MZ_FALSE;
990
991     pZip->m_zip_type = MZ_ZIP_TYPE_CFILE;
992     pZip->m_pRead = mz_zip_file_read_func;
993
994     pZip->m_pIO_opaque = pZip;
995     pZip->m_pState->m_pFile = pFile;
996     pZip->m_archive_size = archive_size;
997     pZip->m_pState->m_file_archive_start_ofs = cur_file_ofs;
998
999     if (!mz_zip_reader_read_central_dir(pZip, flags))
1000     {
1001         mz_zip_reader_end_internal(pZip, MZ_FALSE);
1002         return MZ_FALSE;
1003     }
1004
1005     return MZ_TRUE;
1006 }
1007
1008 #endif // #ifndef MINIZ_NO_STDIO
1009
1010 static MZ_FORCEINLINE const mz_uint8 *mz_zip_get_cdh(mz_zip_archive *pZip, mz_uint file_index)
1011 {
1012     if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files))
1013         return NULL;
1014     return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index));
1015 }
1016
1017 mz_bool mz_zip_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index)
1018 {
1019     mz_uint m_bit_flag;
1020     const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index);
1021     if (!p)
1022     {
1023         mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
1024         return MZ_FALSE;
1025     }
1026
1027     m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS);
1028     return (m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION)) != 0;
1029 }
1030
1031 mz_bool mz_zip_is_file_supported(mz_zip_archive *pZip, mz_uint file_index)
1032 {
1033     mz_uint bit_flag;
1034     mz_uint method;
1035
1036     const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index);
1037     if (!p)
1038     {
1039         mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
1040         return MZ_FALSE;
1041     }
1042
1043     method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS);
1044     bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS);
1045
1046     if ((method != 0) && (method != MZ_DEFLATED))
1047     {
1048         mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD);
1049         return MZ_FALSE;
1050     }
1051
1052     if (bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION))
1053     {
1054         mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION);
1055         return MZ_FALSE;
1056     }
1057
1058     if (bit_flag & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)
1059     {
1060         mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE);
1061         return MZ_FALSE;
1062     }
1063
1064     return MZ_TRUE;
1065 }
1066
1067 mz_bool mz_zip_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index)
1068 {
1069     mz_uint filename_len, attribute_mapping_id, external_attr;
1070     const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index);
1071     if (!p)
1072     {
1073         mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
1074         return MZ_FALSE;
1075     }
1076
1077     filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
1078     if (filename_len)
1079     {
1080         if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/')
1081             return MZ_TRUE;
1082     }
1083
1084     // Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct.
1085     // Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field.
1086     // FIXME: Remove this check? Is it necessary - we already check the filename.
1087     attribute_mapping_id = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS) >> 8;
1088     (void)attribute_mapping_id;
1089
1090     external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS);
1091     if ((external_attr & MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG) != 0)
1092     {
1093         return MZ_TRUE;
1094     }
1095
1096     return MZ_FALSE;
1097 }
1098
1099 static mz_bool mz_zip_file_stat_internal(mz_zip_archive *pZip, mz_uint file_index, const mz_uint8 *pCentral_dir_header, mz_zip_archive_file_stat *pStat, mz_bool *pFound_zip64_extra_data)
1100 {
1101     mz_uint n;
1102     const mz_uint8 *p = pCentral_dir_header;
1103
1104     if (pFound_zip64_extra_data)
1105         *pFound_zip64_extra_data = MZ_FALSE;
1106
1107     if ((!p) || (!pStat))
1108         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
1109
1110     // Extract fields from the central directory record.
1111     pStat->m_file_index = file_index;
1112     pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index);
1113     pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS);
1114     pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS);
1115     pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS);
1116     pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS);
1117 #ifndef MINIZ_NO_TIME
1118     pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS));
1119 #endif
1120     pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS);
1121     pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
1122     pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);
1123     pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS);
1124     pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS);
1125     pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS);
1126
1127     // Copy as much of the filename and comment as possible.
1128     n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
1129     n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1);
1130     memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n);
1131     pStat->m_filename[n] = '\0';
1132
1133     n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS);
1134     n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1);
1135     pStat->m_comment_size = n;
1136     memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n);
1137     pStat->m_comment[n] = '\0';
1138
1139     // Set some flags for convienance
1140     pStat->m_is_directory = mz_zip_is_file_a_directory(pZip, file_index);
1141     pStat->m_is_encrypted = mz_zip_is_file_encrypted(pZip, file_index);
1142     pStat->m_is_supported = mz_zip_is_file_supported(pZip, file_index);
1143
1144     // See if we need to read any zip64 extended information fields.
1145     // Confusingly, these zip64 fields can be present even on non-zip64 archives (Debian zip on a huge files from stdin piped to stdout creates them).
1146     if (MZ_MAX(MZ_MAX(pStat->m_comp_size, pStat->m_uncomp_size), pStat->m_local_header_ofs) == MZ_UINT32_MAX)
1147     {
1148         // Attempt to find zip64 extended information field in the entry's extra data
1149         mz_uint32 extra_size_remaining = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS);
1150
1151         if (extra_size_remaining)
1152         {
1153             const mz_uint8 *pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
1154
1155             do
1156             {
1157                 if (extra_size_remaining < (sizeof(mz_uint16) * 2))
1158                     return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
1159
1160                 mz_uint32 field_id = MZ_READ_LE16(pExtra_data);
1161                 mz_uint32 field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16));
1162
1163                 if ((field_data_size + sizeof(mz_uint16) * 2) > extra_size_remaining)
1164                     return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
1165
1166                 if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID)
1167                 {
1168                     const mz_uint8 *pField_data = pExtra_data + sizeof(mz_uint16) * 2;
1169                     mz_uint32 field_data_remaining = field_data_size;
1170
1171                     if (pFound_zip64_extra_data)
1172                         *pFound_zip64_extra_data = MZ_TRUE;
1173
1174                     if (pStat->m_uncomp_size == MZ_UINT32_MAX)
1175                     {
1176                         if (field_data_remaining < sizeof(mz_uint64))
1177                             return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
1178
1179                         pStat->m_uncomp_size = MZ_READ_LE64(pField_data);
1180                         pField_data += sizeof(mz_uint64);
1181                         field_data_remaining -= sizeof(mz_uint64);
1182                     }
1183
1184                     if (pStat->m_comp_size == MZ_UINT32_MAX)
1185                     {
1186                         if (field_data_remaining < sizeof(mz_uint64))
1187                             return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
1188
1189                         pStat->m_comp_size = MZ_READ_LE64(pField_data);
1190                         pField_data += sizeof(mz_uint64);
1191                         field_data_remaining -= sizeof(mz_uint64);
1192                     }
1193
1194                     if (pStat->m_local_header_ofs == MZ_UINT32_MAX)
1195                     {
1196                         if (field_data_remaining < sizeof(mz_uint64))
1197                             return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
1198
1199                         pStat->m_local_header_ofs = MZ_READ_LE64(pField_data);
1200                         pField_data += sizeof(mz_uint64);
1201                         field_data_remaining -= sizeof(mz_uint64);
1202                     }
1203
1204                     break;
1205                 }
1206
1207                 pExtra_data += sizeof(mz_uint16) * 2 + field_data_size;
1208                 extra_size_remaining = extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size;
1209             } while (extra_size_remaining);
1210         }
1211     }
1212
1213     return MZ_TRUE;
1214 }
1215
1216 static MZ_FORCEINLINE mz_bool mz_zip_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags)
1217 {
1218     mz_uint i;
1219     if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE)
1220         return 0 == memcmp(pA, pB, len);
1221     for (i = 0; i < len; ++i)
1222         if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i]))
1223             return MZ_FALSE;
1224     return MZ_TRUE;
1225 }
1226
1227 static MZ_FORCEINLINE int mz_zip_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len)
1228 {
1229     const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE;
1230     mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS);
1231     mz_uint8 l = 0, r = 0;
1232     pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
1233     pE = pL + MZ_MIN(l_len, r_len);
1234     while (pL < pE)
1235     {
1236         if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR)))
1237             break;
1238         pL++;
1239         pR++;
1240     }
1241     return (pL == pE) ? (int)(l_len - r_len) : (l - r);
1242 }
1243
1244 static mz_bool mz_zip_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename, mz_uint32 *pIndex)
1245 {
1246     mz_zip_internal_state *pState = pZip->m_pState;
1247     const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets;
1248     const mz_zip_array *pCentral_dir = &pState->m_central_dir;
1249     mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0);
1250     const uint size = pZip->m_total_files;
1251     const mz_uint filename_len = (mz_uint)strlen(pFilename);
1252
1253     if (pIndex)
1254         *pIndex = 0;
1255
1256     if (size)
1257     {
1258         // yes I could use uint's, but then we would have to add some special case checks in the loop, argh, and
1259         // honestly the major expense here on 32-bit CPU's will still be the filename compare
1260         mz_int64 l = 0, h = (mz_int64)size - 1;
1261
1262         while (l <= h)
1263         {
1264             mz_int64 m = l + ((h - l) >> 1);
1265             uint file_index = pIndices[(uint)m];
1266
1267             int comp = mz_zip_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len);
1268             if (!comp)
1269             {
1270                 if (pIndex)
1271                     *pIndex = file_index;
1272                 return MZ_TRUE;
1273             }
1274             else if (comp < 0)
1275                 l = m + 1;
1276             else
1277                 h = m - 1;
1278         }
1279     }
1280
1281     return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND);
1282 }
1283
1284 mz_bool mz_zip_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *pIndex)
1285 {
1286     mz_uint file_index;
1287     size_t name_len, comment_len;
1288
1289     if (pIndex)
1290         *pIndex = 0;
1291
1292     if ((!pZip) || (!pZip->m_pState) || (!pName))
1293         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
1294
1295     // See if we can use a binary search
1296     if (((pZip->m_pState->m_init_flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0) &&
1297         (pZip->m_zip_mode == MZ_ZIP_MODE_READING) &&
1298         ((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size))
1299     {
1300         return mz_zip_locate_file_binary_search(pZip, pName, pIndex);
1301     }
1302
1303     // Locate the entry by scanning the entire central directory
1304     name_len = strlen(pName);
1305     if (name_len > MZ_UINT16_MAX)
1306         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
1307
1308     comment_len = pComment ? strlen(pComment) : 0;
1309     if (comment_len > MZ_UINT16_MAX)
1310         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
1311
1312     for (file_index = 0; file_index < pZip->m_total_files; file_index++)
1313     {
1314         const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index));
1315         mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS);
1316         const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
1317         if (filename_len < name_len)
1318             continue;
1319         if (comment_len)
1320         {
1321             mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS);
1322             const char *pFile_comment = pFilename + filename_len + file_extra_len;
1323             if ((file_comment_len != comment_len) || (!mz_zip_string_equal(pComment, pFile_comment, file_comment_len, flags)))
1324                 continue;
1325         }
1326         if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len))
1327         {
1328             int ofs = filename_len - 1;
1329             do
1330             {
1331                 if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':'))
1332                     break;
1333             } while (--ofs >= 0);
1334             ofs++;
1335             pFilename += ofs;
1336             filename_len -= ofs;
1337         }
1338         if ((filename_len == name_len) && (mz_zip_string_equal(pName, pFilename, filename_len, flags)))
1339         {
1340             *pIndex = file_index;
1341             return MZ_TRUE;
1342         }
1343     }
1344
1345     return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND);
1346 }
1347
1348 mz_bool mz_zip_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size)
1349 {
1350     int status = TINFL_STATUS_DONE;
1351     mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail;
1352     mz_zip_archive_file_stat file_stat;
1353     void *pRead_buf;
1354     mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)];
1355     mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;
1356     tinfl_decompressor inflator;
1357
1358     if ((!pZip) || (!pZip->m_pState) || ((buf_size) && (!pBuf)) || ((user_read_buf_size) && (!pUser_read_buf)) || (!pZip->m_pRead))
1359         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
1360
1361     if (!mz_zip_file_stat(pZip, file_index, &file_stat))
1362         return MZ_FALSE;
1363
1364     // A directory or zero length file
1365     if ((file_stat.m_is_directory) || (!file_stat.m_comp_size))
1366         return MZ_TRUE;
1367
1368     // Encryption and patch files are not supported.
1369     if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG))
1370         return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION);
1371
1372     // This function only supports decompressing stored and deflate.
1373     if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED))
1374         return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD);
1375
1376     // Ensure supplied output buffer is large enough.
1377     needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size;
1378     if (buf_size < needed_size)
1379         return mz_zip_set_error(pZip, MZ_ZIP_BUF_TOO_SMALL);
1380
1381     // Read and parse the local directory entry.
1382     cur_file_ofs = file_stat.m_local_header_ofs;
1383     if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
1384         return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
1385
1386     if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)
1387         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
1388
1389     cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);
1390     if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size)
1391         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
1392
1393     if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method))
1394     {
1395         // The file is stored or the caller has requested the compressed data.
1396         if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size)
1397             return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
1398
1399 #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS
1400         if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) == 0)
1401         {
1402             if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32)
1403                 return mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED);
1404         }
1405 #endif
1406
1407         return MZ_TRUE;
1408     }
1409
1410     // Decompress the file either directly from memory or from a file input buffer.
1411     tinfl_init(&inflator);
1412
1413     if (pZip->m_pState->m_pMem)
1414     {
1415         // Read directly from the archive in memory.
1416         pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs;
1417         read_buf_size = read_buf_avail = file_stat.m_comp_size;
1418         comp_remaining = 0;
1419     }
1420     else if (pUser_read_buf)
1421     {
1422         // Use a user provided read buffer.
1423         if (!user_read_buf_size)
1424             return MZ_FALSE;
1425         pRead_buf = (mz_uint8 *)pUser_read_buf;
1426         read_buf_size = user_read_buf_size;
1427         read_buf_avail = 0;
1428         comp_remaining = file_stat.m_comp_size;
1429     }
1430     else
1431     {
1432         // Temporarily allocate a read buffer.
1433         read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE);
1434         if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF))
1435             return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);
1436
1437         if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size)))
1438             return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
1439
1440         read_buf_avail = 0;
1441         comp_remaining = file_stat.m_comp_size;
1442     }
1443
1444     do
1445     {
1446         // The size_t cast here should be OK because we've verified that the output buffer is >= file_stat.m_uncomp_size above
1447         size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs);
1448         if ((!read_buf_avail) && (!pZip->m_pState->m_pMem))
1449         {
1450             read_buf_avail = MZ_MIN(read_buf_size, comp_remaining);
1451             if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
1452             {
1453                 status = TINFL_STATUS_FAILED;
1454                 mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED);
1455                 break;
1456             }
1457             cur_file_ofs += read_buf_avail;
1458             comp_remaining -= read_buf_avail;
1459             read_buf_ofs = 0;
1460         }
1461         in_buf_size = (size_t)read_buf_avail;
1462         status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0));
1463         read_buf_avail -= in_buf_size;
1464         read_buf_ofs += in_buf_size;
1465         out_buf_ofs += out_buf_size;
1466     } while (status == TINFL_STATUS_NEEDS_MORE_INPUT);
1467
1468     if (status == TINFL_STATUS_DONE)
1469     {
1470         // Make sure the entire file was decompressed, and check its CRC.
1471         if (out_buf_ofs != file_stat.m_uncomp_size)
1472         {
1473             mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE);
1474             status = TINFL_STATUS_FAILED;
1475         }
1476 #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS
1477         else if (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32)
1478         {
1479             mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED);
1480             status = TINFL_STATUS_FAILED;
1481         }
1482 #endif
1483     }
1484
1485     if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf))
1486         pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
1487
1488     return status == TINFL_STATUS_DONE;
1489 }
1490
1491 mz_bool mz_zip_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size)
1492 {
1493     mz_uint32 file_index;
1494     if (!mz_zip_locate_file(pZip, pFilename, NULL, flags, &file_index))
1495         return MZ_FALSE;
1496     return mz_zip_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size);
1497 }
1498
1499 mz_bool mz_zip_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags)
1500 {
1501     return mz_zip_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0);
1502 }
1503
1504 mz_bool mz_zip_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags)
1505 {
1506     return mz_zip_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0);
1507 }
1508
1509 void *mz_zip_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags)
1510 {
1511     mz_uint64 comp_size, uncomp_size, alloc_size;
1512     const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index);
1513     void *pBuf;
1514
1515     if (pSize)
1516         *pSize = 0;
1517
1518     if (!p)
1519     {
1520         mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
1521         return NULL;
1522     }
1523
1524     comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
1525     uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);
1526
1527     alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size;
1528     if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF))
1529     {
1530         mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);
1531         return NULL;
1532     }
1533
1534     if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size)))
1535     {
1536         mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
1537         return NULL;
1538     }
1539
1540     if (!mz_zip_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags))
1541     {
1542         pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
1543         return NULL;
1544     }
1545
1546     if (pSize)
1547         *pSize = (size_t)alloc_size;
1548     return pBuf;
1549 }
1550
1551 void *mz_zip_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags)
1552 {
1553     mz_uint32 file_index;
1554     if (!mz_zip_locate_file(pZip, pFilename, NULL, flags, &file_index))
1555     {
1556         if (pSize)
1557             *pSize = 0;
1558
1559         return MZ_FALSE;
1560     }
1561
1562     return mz_zip_extract_to_heap(pZip, file_index, pSize, flags);
1563 }
1564
1565 mz_bool mz_zip_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags)
1566 {
1567     int status = TINFL_STATUS_DONE;
1568     mz_uint file_crc32 = MZ_CRC32_INIT;
1569     mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs;
1570     mz_zip_archive_file_stat file_stat;
1571     void *pRead_buf = NULL;
1572     void *pWrite_buf = NULL;
1573     mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)];
1574     mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;
1575
1576     if ((!pZip) || (!pZip->m_pState) || (!pCallback) || (!pZip->m_pRead))
1577         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
1578
1579     if (!mz_zip_file_stat(pZip, file_index, &file_stat))
1580         return MZ_FALSE;
1581
1582     // A directory or zero length file
1583     if ((file_stat.m_is_directory) || (!file_stat.m_comp_size))
1584         return MZ_TRUE;
1585
1586     // Encryption and patch files are not supported.
1587     if (file_stat.m_bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG))
1588         return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION);
1589
1590     // This function only supports decompressing stored and deflate.
1591     if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED))
1592         return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD);
1593
1594     // Read and do some minimal validation of the local directory entry (this doesn't crack the zip64 stuff, which we already have from the central dir)
1595     cur_file_ofs = file_stat.m_local_header_ofs;
1596     if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
1597         return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
1598
1599     if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)
1600         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
1601
1602     cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);
1603     if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size)
1604         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
1605
1606     // Decompress the file either directly from memory or from a file input buffer.
1607     if (pZip->m_pState->m_pMem)
1608     {
1609         pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs;
1610         read_buf_size = read_buf_avail = file_stat.m_comp_size;
1611         comp_remaining = 0;
1612     }
1613     else
1614     {
1615         read_buf_size = MZ_MIN(file_stat.m_comp_size, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE);
1616         if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size)))
1617             return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
1618
1619         read_buf_avail = 0;
1620         comp_remaining = file_stat.m_comp_size;
1621     }
1622
1623     if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method))
1624     {
1625         // The file is stored or the caller has requested the compressed data.
1626         if (pZip->m_pState->m_pMem)
1627         {
1628             if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > MZ_UINT32_MAX))
1629                 return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);
1630
1631             if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size)
1632             {
1633                 mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED);
1634                 status = TINFL_STATUS_FAILED;
1635             }
1636             else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))
1637             {
1638 #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS
1639                 file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size);
1640 #endif
1641             }
1642
1643             cur_file_ofs += file_stat.m_comp_size;
1644             out_buf_ofs += file_stat.m_comp_size;
1645             comp_remaining = 0;
1646         }
1647         else
1648         {
1649             while (comp_remaining)
1650             {
1651                 read_buf_avail = MZ_MIN(read_buf_size, comp_remaining);
1652                 if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
1653                 {
1654                     mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
1655                     status = TINFL_STATUS_FAILED;
1656                     break;
1657                 }
1658
1659 #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS
1660                 if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))
1661                 {
1662                     file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail);
1663                 }
1664 #endif
1665
1666                 if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
1667                 {
1668                     mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED);
1669                     status = TINFL_STATUS_FAILED;
1670                     break;
1671                 }
1672
1673                 cur_file_ofs += read_buf_avail;
1674                 out_buf_ofs += read_buf_avail;
1675                 comp_remaining -= read_buf_avail;
1676             }
1677         }
1678     }
1679     else
1680     {
1681         tinfl_decompressor inflator;
1682         tinfl_init(&inflator);
1683
1684         if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE)))
1685         {
1686             mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
1687             status = TINFL_STATUS_FAILED;
1688         }
1689         else
1690         {
1691             do
1692             {
1693                 mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1));
1694                 size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1));
1695                 if ((!read_buf_avail) && (!pZip->m_pState->m_pMem))
1696                 {
1697                     read_buf_avail = MZ_MIN(read_buf_size, comp_remaining);
1698                     if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
1699                     {
1700                         mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
1701                         status = TINFL_STATUS_FAILED;
1702                         break;
1703                     }
1704                     cur_file_ofs += read_buf_avail;
1705                     comp_remaining -= read_buf_avail;
1706                     read_buf_ofs = 0;
1707                 }
1708
1709                 in_buf_size = (size_t)read_buf_avail;
1710                 status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0);
1711                 read_buf_avail -= in_buf_size;
1712                 read_buf_ofs += in_buf_size;
1713
1714                 if (out_buf_size)
1715                 {
1716                     if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size)
1717                     {
1718                         mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED);
1719                         status = TINFL_STATUS_FAILED;
1720                         break;
1721                     }
1722
1723 #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS
1724                     file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size);
1725 #endif
1726                     if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size)
1727                     {
1728                         mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED);
1729                         status = TINFL_STATUS_FAILED;
1730                         break;
1731                     }
1732                 }
1733             } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT));
1734         }
1735     }
1736
1737     if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)))
1738     {
1739         // Make sure the entire file was decompressed, and check its CRC.
1740         if (out_buf_ofs != file_stat.m_uncomp_size)
1741         {
1742             mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE);
1743             status = TINFL_STATUS_FAILED;
1744         }
1745 #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS
1746         else if (file_crc32 != file_stat.m_crc32)
1747         {
1748             mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED);
1749             status = TINFL_STATUS_FAILED;
1750         }
1751 #endif
1752     }
1753
1754     if (!pZip->m_pState->m_pMem)
1755         pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
1756
1757     if (pWrite_buf)
1758         pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf);
1759
1760     return status == TINFL_STATUS_DONE;
1761 }
1762
1763 mz_bool mz_zip_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags)
1764 {
1765     mz_uint32 file_index;
1766     if (!mz_zip_locate_file(pZip, pFilename, NULL, flags, &file_index))
1767         return MZ_FALSE;
1768
1769     return mz_zip_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags);
1770 }
1771
1772 #ifndef MINIZ_NO_STDIO
1773 static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n)
1774 {
1775     (void)ofs;
1776
1777     return MZ_FWRITE(pBuf, 1, n, (MZ_FILE *)pOpaque);
1778 }
1779
1780 mz_bool mz_zip_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags)
1781 {
1782     mz_bool status;
1783     mz_zip_archive_file_stat file_stat;
1784     MZ_FILE *pFile;
1785
1786     if (!mz_zip_file_stat(pZip, file_index, &file_stat))
1787         return MZ_FALSE;
1788
1789     if ((file_stat.m_is_directory) || (!file_stat.m_is_supported))
1790         return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE);
1791
1792     pFile = MZ_FOPEN(pDst_filename, "wb");
1793     if (!pFile)
1794         return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED);
1795
1796     status = mz_zip_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags);
1797
1798     if (MZ_FCLOSE(pFile) == EOF)
1799     {
1800         if (status)
1801             mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED);
1802
1803         status = MZ_FALSE;
1804     }
1805
1806 #if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO)
1807     if (status)
1808         mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time);
1809 #endif
1810
1811     return status;
1812 }
1813
1814 mz_bool mz_zip_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags)
1815 {
1816     mz_uint32 file_index;
1817     if (!mz_zip_locate_file(pZip, pArchive_filename, NULL, flags, &file_index))
1818         return MZ_FALSE;
1819
1820     return mz_zip_extract_to_file(pZip, file_index, pDst_filename, flags);
1821 }
1822
1823 mz_bool mz_zip_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *pFile, mz_uint flags)
1824 {
1825     mz_zip_archive_file_stat file_stat;
1826
1827     if (!mz_zip_file_stat(pZip, file_index, &file_stat))
1828         return MZ_FALSE;
1829
1830     if ((file_stat.m_is_directory) || (!file_stat.m_is_supported))
1831         return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE);
1832
1833     return mz_zip_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags);
1834 }
1835
1836 mz_bool mz_zip_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags)
1837 {
1838     mz_uint32 file_index;
1839     if (!mz_zip_locate_file(pZip, pArchive_filename, NULL, flags, &file_index))
1840         return MZ_FALSE;
1841
1842     return mz_zip_extract_to_cfile(pZip, file_index, pFile, flags);
1843 }
1844 #endif // #ifndef MINIZ_NO_STDIO
1845
1846 static size_t mz_zip_compute_crc32_callback(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n)
1847 {
1848     mz_uint32 *p = (mz_uint32 *)pOpaque;
1849     (void)file_ofs;
1850     *p = (mz_uint32)mz_crc32(*p, (const mz_uint8 *)pBuf, n);
1851     return n;
1852 }
1853
1854 mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags)
1855 {
1856     mz_zip_archive_file_stat file_stat;
1857     mz_zip_internal_state *pState;
1858     const mz_uint8 *pCentral_dir_header;
1859     mz_bool found_zip64_ext_data_in_cdir = MZ_FALSE;
1860     mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE;
1861     mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)];
1862     mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;
1863     mz_uint64 local_header_ofs = 0;
1864     mz_uint32 local_header_filename_len, local_header_extra_len, local_header_crc32;
1865     mz_uint64 local_header_comp_size, local_header_uncomp_size;
1866     mz_uint32 uncomp_crc32 = MZ_CRC32_INIT;
1867     mz_bool has_data_descriptor;
1868     mz_uint32 local_header_bit_flags;
1869
1870     mz_zip_array file_data_array;
1871     mz_zip_array_init(&file_data_array, 1);
1872
1873     if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead))
1874         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
1875
1876     if (file_index > pZip->m_total_files)
1877         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
1878
1879     pState = pZip->m_pState;
1880
1881     pCentral_dir_header = mz_zip_get_cdh(pZip, file_index);
1882
1883     if (!mz_zip_file_stat_internal(pZip, file_index, pCentral_dir_header, &file_stat, &found_zip64_ext_data_in_cdir))
1884         return MZ_FALSE;
1885
1886     // A directory or zero length file
1887     if ((file_stat.m_is_directory) || (!file_stat.m_uncomp_size))
1888         return MZ_TRUE;
1889
1890     // Encryption and patch files are not supported.
1891     if (file_stat.m_is_encrypted)
1892         return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION);
1893
1894     // This function only supports stored and deflate.
1895     if ((file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED))
1896         return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD);
1897
1898     if (!file_stat.m_is_supported)
1899         return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE);
1900
1901     // Read and parse the local directory entry.
1902     local_header_ofs = file_stat.m_local_header_ofs;
1903     if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
1904         return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
1905
1906     if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)
1907         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
1908
1909     local_header_filename_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS);
1910     local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);
1911     local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS);
1912     local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS);
1913     local_header_crc32 = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_CRC32_OFS);
1914     local_header_bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS);
1915     has_data_descriptor = (local_header_bit_flags & 8) != 0;
1916
1917     if (local_header_filename_len != strlen(file_stat.m_filename))
1918         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
1919
1920     if ((local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size) > pZip->m_archive_size)
1921         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
1922
1923     if (!mz_zip_array_resize(pZip, &file_data_array, MZ_MAX(local_header_filename_len, local_header_extra_len), MZ_FALSE))
1924         return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
1925
1926     if (local_header_filename_len)
1927     {
1928         if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE, file_data_array.m_p, local_header_filename_len) != local_header_filename_len)
1929         {
1930             mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
1931             goto handle_failure;
1932         }
1933
1934         // I've seen 1 archive that had the same pathname, but used backslashes in the local dir and forward slashes in the central dir. Do we care about this? For now, this case will fail validation.
1935         if (memcmp(file_stat.m_filename, file_data_array.m_p, local_header_filename_len) != 0)
1936         {
1937             mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED);
1938             goto handle_failure;
1939         }
1940     }
1941
1942     if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX)))
1943     {
1944         if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len, file_data_array.m_p, local_header_extra_len) != local_header_extra_len)
1945         {
1946             mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
1947             goto handle_failure;
1948         }
1949
1950         mz_uint32 extra_size_remaining = local_header_extra_len;
1951         const mz_uint8 *pExtra_data = (const mz_uint8 *)file_data_array.m_p;
1952
1953         do
1954         {
1955             mz_uint32 field_id, field_data_size, field_total_size;
1956
1957             if (extra_size_remaining < (sizeof(mz_uint16) * 2))
1958                 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
1959
1960             field_id = MZ_READ_LE16(pExtra_data);
1961             field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16));
1962             field_total_size = field_data_size + sizeof(mz_uint16) * 2;
1963
1964             if (field_total_size > extra_size_remaining)
1965                 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
1966
1967             if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID)
1968             {
1969                 const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32);
1970
1971                 if (field_data_size < sizeof(mz_uint64) * 2)
1972                 {
1973                     mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
1974                     goto handle_failure;
1975                 }
1976
1977                 local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data);
1978                 local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64));
1979
1980                 found_zip64_ext_data_in_ldir = MZ_TRUE;
1981                 break;
1982             }
1983
1984             pExtra_data += field_total_size;
1985             extra_size_remaining -= field_total_size;
1986         } while (extra_size_remaining);
1987     }
1988
1989     // TODO: parse local header extra data when local_header_comp_size is 0xFFFFFFFF! (big_descriptor.zip)
1990     // I've seen zips in the wild with the data descriptor bit set, but proper local header values and bogus data descriptors
1991     if ((has_data_descriptor) && (!local_header_comp_size) && (!local_header_crc32))
1992     {
1993         mz_uint8 descriptor_buf[32];
1994
1995         mz_uint32 num_descriptor_uint32s = ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) ? 6 : 4;
1996
1997         if (pZip->m_pRead(pZip->m_pIO_opaque, local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_len + local_header_extra_len + file_stat.m_comp_size, descriptor_buf, sizeof(mz_uint32) * num_descriptor_uint32s) != (sizeof(mz_uint32) * num_descriptor_uint32s))
1998         {
1999             mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
2000             goto handle_failure;
2001         }
2002
2003         mz_bool has_id = (MZ_READ_LE32(descriptor_buf) == MZ_ZIP_DATA_DESCRIPTOR_ID);
2004         const mz_uint8 *pSrc = has_id ? (descriptor_buf + sizeof(mz_uint32)) : descriptor_buf;
2005
2006         mz_uint32 file_crc32 = MZ_READ_LE32(pSrc);
2007         mz_uint64 comp_size = 0, uncomp_size = 0;
2008
2009         if ((pState->m_zip64) || (found_zip64_ext_data_in_ldir))
2010         {
2011             comp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32));
2012             uncomp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32) + sizeof(mz_uint64));
2013         }
2014         else
2015         {
2016             comp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32));
2017             uncomp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32) + sizeof(mz_uint32));
2018         }
2019
2020         if ((file_crc32 != file_stat.m_crc32) || (comp_size != file_stat.m_comp_size) || (uncomp_size != file_stat.m_uncomp_size))
2021         {
2022             mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED);
2023             goto handle_failure;
2024         }
2025     }
2026     else
2027     {
2028         if ((local_header_crc32 != file_stat.m_crc32) || (local_header_comp_size != file_stat.m_comp_size) || (local_header_uncomp_size != file_stat.m_uncomp_size))
2029         {
2030             mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED);
2031             goto handle_failure;
2032         }
2033     }
2034
2035     mz_zip_array_clear(pZip, &file_data_array);
2036
2037     if ((flags & MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY) == 0)
2038     {
2039         if (!mz_zip_extract_to_callback(pZip, file_index, mz_zip_compute_crc32_callback, &uncomp_crc32, 0))
2040             return MZ_FALSE;
2041
2042         // 1 more check to be sure, although the extract checks too.
2043         if (uncomp_crc32 != file_stat.m_crc32)
2044         {
2045             mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED);
2046             return MZ_FALSE;
2047         }
2048     }
2049
2050     return MZ_TRUE;
2051
2052 handle_failure:
2053     mz_zip_array_clear(pZip, &file_data_array);
2054     return MZ_FALSE;
2055 }
2056
2057 mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags)
2058 {
2059     mz_zip_internal_state *pState;
2060     if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (!pZip->m_pRead))
2061         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
2062
2063     pState = pZip->m_pState;
2064
2065     // Basic sanity checks
2066     if (!pState->m_zip64)
2067     {
2068         if (pZip->m_total_files > MZ_UINT16_MAX)
2069             return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
2070
2071         if (pZip->m_archive_size > MZ_UINT32_MAX)
2072             return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
2073     }
2074     else
2075     {
2076         if (pZip->m_total_files >= MZ_UINT32_MAX)
2077             return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
2078
2079         if (pState->m_central_dir.m_size >= MZ_UINT32_MAX)
2080             return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
2081     }
2082
2083     for (uint i = 0; i < pZip->m_total_files; i++)
2084     {
2085         if (MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG & flags)
2086         {
2087             mz_uint32 found_index;
2088             mz_zip_archive_file_stat stat;
2089
2090             if (!mz_zip_file_stat(pZip, i, &stat))
2091                 return MZ_FALSE;
2092
2093             if (!mz_zip_locate_file(pZip, stat.m_filename, NULL, 0, &found_index))
2094                 return MZ_FALSE;
2095
2096             // This check can fail if there are duplicate filenames in the archive (which we don't check for when writing - that's up to the user)
2097             if (found_index != i)
2098                 return mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED);
2099         }
2100
2101         if (!mz_zip_validate_file(pZip, i, flags))
2102             return MZ_FALSE;
2103     }
2104
2105     return MZ_TRUE;
2106 }
2107
2108 mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr)
2109 {
2110     mz_bool success = MZ_TRUE;
2111     mz_zip_archive zip;
2112     mz_zip_error actual_err = MZ_ZIP_NO_ERROR;
2113
2114     if ((!pMem) || (!size))
2115     {
2116         if (pErr)
2117             *pErr = MZ_ZIP_INVALID_PARAMETER;
2118         return MZ_FALSE;
2119     }
2120
2121     mz_zip_zero_struct(&zip);
2122
2123     if (!mz_zip_reader_init_mem(&zip, pMem, size, flags))
2124     {
2125         if (pErr)
2126             *pErr = zip.m_last_error;
2127         return MZ_FALSE;
2128     }
2129
2130     if (!mz_zip_validate_archive(&zip, flags))
2131     {
2132         actual_err = zip.m_last_error;
2133         success = MZ_FALSE;
2134     }
2135
2136     if (!mz_zip_reader_end_internal(&zip, success))
2137     {
2138         if (!actual_err)
2139             actual_err = zip.m_last_error;
2140         success = MZ_FALSE;
2141     }
2142
2143     if (pErr)
2144         *pErr = actual_err;
2145
2146     return success;
2147 }
2148
2149 #ifndef MINIZ_NO_STDIO
2150 mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr)
2151 {
2152     mz_bool success = MZ_TRUE;
2153     mz_zip_archive zip;
2154     mz_zip_error actual_err = MZ_ZIP_NO_ERROR;
2155
2156     if (!pFilename)
2157     {
2158         if (pErr)
2159             *pErr = MZ_ZIP_INVALID_PARAMETER;
2160         return MZ_FALSE;
2161     }
2162
2163     mz_zip_zero_struct(&zip);
2164
2165     if (!mz_zip_reader_init_file(&zip, pFilename, flags, 0, 0))
2166     {
2167         if (pErr)
2168             *pErr = zip.m_last_error;
2169         return MZ_FALSE;
2170     }
2171
2172     if (!mz_zip_validate_archive(&zip, flags))
2173     {
2174         actual_err = zip.m_last_error;
2175         success = MZ_FALSE;
2176     }
2177
2178     if (!mz_zip_reader_end_internal(&zip, success))
2179     {
2180         if (!actual_err)
2181             actual_err = zip.m_last_error;
2182         success = MZ_FALSE;
2183     }
2184
2185     if (pErr)
2186         *pErr = actual_err;
2187
2188     return success;
2189 }
2190 #endif // #ifndef MINIZ_NO_STDIO
2191
2192 // ------------------- .ZIP archive writing
2193
2194 #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS
2195
2196 static MZ_FORCEINLINE void mz_write_le16(mz_uint8 *p, mz_uint16 v)
2197 {
2198     p[0] = (mz_uint8)v;
2199     p[1] = (mz_uint8)(v >> 8);
2200 }
2201 static MZ_FORCEINLINE void mz_write_le32(mz_uint8 *p, mz_uint32 v)
2202 {
2203     p[0] = (mz_uint8)v;
2204     p[1] = (mz_uint8)(v >> 8);
2205     p[2] = (mz_uint8)(v >> 16);
2206     p[3] = (mz_uint8)(v >> 24);
2207 }
2208 static MZ_FORCEINLINE void mz_write_le64(mz_uint8 *p, mz_uint64 v)
2209 {
2210     mz_write_le32(p, (mz_uint32)v);
2211     mz_write_le32(p + sizeof(mz_uint32), (mz_uint32)(v >> 32));
2212 }
2213
2214 #define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v))
2215 #define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v))
2216 #define MZ_WRITE_LE64(p, v) mz_write_le64((mz_uint8 *)(p), (mz_uint64)(v))
2217
2218 static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n)
2219 {
2220     mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
2221     mz_zip_internal_state *pState = pZip->m_pState;
2222     mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size);
2223
2224     if (!n)
2225         return 0;
2226
2227     // An allocation this big is likely to just fail on 32-bit systems, so don't even go there.
2228     if ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))
2229     {
2230         mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE);
2231         return 0;
2232     }
2233
2234     if (new_size > pState->m_mem_capacity)
2235     {
2236         void *pNew_block;
2237         size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity);
2238
2239         while (new_capacity < new_size)
2240             new_capacity *= 2;
2241
2242         if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity)))
2243         {
2244             mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
2245             return 0;
2246         }
2247
2248         pState->m_pMem = pNew_block;
2249         pState->m_mem_capacity = new_capacity;
2250     }
2251     memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n);
2252     pState->m_mem_size = (size_t)new_size;
2253     return n;
2254 }
2255
2256 static mz_bool mz_zip_writer_end_internal(mz_zip_archive *pZip, mz_bool set_last_error)
2257 {
2258     mz_zip_internal_state *pState;
2259     mz_bool status = MZ_TRUE;
2260
2261     if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED)))
2262     {
2263         if (set_last_error)
2264             mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
2265         return MZ_FALSE;
2266     }
2267
2268     pState = pZip->m_pState;
2269     pZip->m_pState = NULL;
2270     mz_zip_array_clear(pZip, &pState->m_central_dir);
2271     mz_zip_array_clear(pZip, &pState->m_central_dir_offsets);
2272     mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets);
2273
2274 #ifndef MINIZ_NO_STDIO
2275     if (pState->m_pFile)
2276     {
2277         if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE)
2278         {
2279             if (MZ_FCLOSE(pState->m_pFile) == EOF)
2280             {
2281                 if (set_last_error)
2282                     mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED);
2283                 status = MZ_FALSE;
2284             }
2285         }
2286
2287         pState->m_pFile = NULL;
2288     }
2289 #endif // #ifndef MINIZ_NO_STDIO
2290
2291     if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem))
2292     {
2293         pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem);
2294         pState->m_pMem = NULL;
2295     }
2296
2297     pZip->m_pFree(pZip->m_pAlloc_opaque, pState);
2298     pZip->m_zip_mode = MZ_ZIP_MODE_INVALID;
2299     return status;
2300 }
2301
2302 mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags)
2303 {
2304     mz_bool zip64 = (flags & MZ_ZIP_FLAG_WRITE_ZIP64) != 0;
2305
2306     if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID))
2307         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
2308
2309     if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING)
2310     {
2311         if (!pZip->m_pRead)
2312             return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
2313     }
2314
2315     if (pZip->m_file_offset_alignment)
2316     {
2317         // Ensure user specified file offset alignment is a power of 2.
2318         if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1))
2319             return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
2320     }
2321
2322     if (!pZip->m_pAlloc)
2323         pZip->m_pAlloc = miniz_def_alloc_func;
2324     if (!pZip->m_pFree)
2325         pZip->m_pFree = miniz_def_free_func;
2326     if (!pZip->m_pRealloc)
2327         pZip->m_pRealloc = miniz_def_realloc_func;
2328
2329     pZip->m_archive_size = existing_size;
2330     pZip->m_central_directory_file_ofs = 0;
2331     pZip->m_total_files = 0;
2332
2333     if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state))))
2334         return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
2335
2336     memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state));
2337
2338     MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8));
2339     MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32));
2340     MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32));
2341
2342     pZip->m_pState->m_zip64 = zip64;
2343     pZip->m_pState->m_zip64_has_extended_info_fields = zip64;
2344
2345     pZip->m_zip_type = MZ_ZIP_TYPE_USER;
2346     pZip->m_zip_mode = MZ_ZIP_MODE_WRITING;
2347
2348     return MZ_TRUE;
2349 }
2350
2351 mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size, mz_uint flags)
2352 {
2353     pZip->m_pWrite = mz_zip_heap_write_func;
2354
2355     if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING)
2356         pZip->m_pRead = mz_zip_mem_read_func;
2357
2358     pZip->m_pIO_opaque = pZip;
2359
2360     if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning, flags))
2361         return MZ_FALSE;
2362
2363     pZip->m_zip_type = MZ_ZIP_TYPE_HEAP;
2364
2365     if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning)))
2366     {
2367         if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size)))
2368         {
2369             mz_zip_writer_end_internal(pZip, MZ_FALSE);
2370             return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
2371         }
2372         pZip->m_pState->m_mem_capacity = initial_allocation_size;
2373     }
2374
2375     return MZ_TRUE;
2376 }
2377
2378 #ifndef MINIZ_NO_STDIO
2379 static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n)
2380 {
2381     mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
2382     mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile);
2383
2384     file_ofs += pZip->m_pState->m_file_archive_start_ofs;
2385
2386     if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET))))
2387     {
2388         mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED);
2389         return 0;
2390     }
2391
2392     return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile);
2393 }
2394
2395 mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning, mz_uint flags)
2396 {
2397     MZ_FILE *pFile;
2398
2399     pZip->m_pWrite = mz_zip_file_write_func;
2400
2401     if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING)
2402         pZip->m_pRead = mz_zip_file_read_func;
2403
2404     pZip->m_pIO_opaque = pZip;
2405
2406     if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning, flags))
2407         return MZ_FALSE;
2408
2409     if (NULL == (pFile = MZ_FOPEN(pFilename, (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) ? "w+b" : "wb")))
2410     {
2411         mz_zip_writer_end(pZip);
2412         return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED);
2413     }
2414
2415     pZip->m_pState->m_pFile = pFile;
2416     pZip->m_zip_type = MZ_ZIP_TYPE_FILE;
2417
2418     if (size_to_reserve_at_beginning)
2419     {
2420         mz_uint64 cur_ofs = 0;
2421         char buf[4096];
2422
2423         MZ_CLEAR_OBJ(buf);
2424
2425         do
2426         {
2427             size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning);
2428             if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n)
2429             {
2430                 mz_zip_writer_end(pZip);
2431                 return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
2432             }
2433             cur_ofs += n;
2434             size_to_reserve_at_beginning -= n;
2435         } while (size_to_reserve_at_beginning);
2436     }
2437
2438     return MZ_TRUE;
2439 }
2440
2441 mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags)
2442 {
2443     pZip->m_pWrite = mz_zip_file_write_func;
2444
2445     if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING)
2446         pZip->m_pRead = mz_zip_file_read_func;
2447
2448     pZip->m_pIO_opaque = pZip;
2449
2450     if (!mz_zip_writer_init(pZip, 0, flags))
2451         return MZ_FALSE;
2452
2453     pZip->m_pState->m_pFile = pFile;
2454     pZip->m_pState->m_file_archive_start_ofs = MZ_FTELL64(pZip->m_pState->m_pFile);
2455     pZip->m_zip_type = MZ_ZIP_TYPE_CFILE;
2456
2457     return MZ_TRUE;
2458 }
2459 #endif // #ifndef MINIZ_NO_STDIO
2460
2461 mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename, mz_uint flags)
2462 {
2463     mz_zip_internal_state *pState;
2464
2465     if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))
2466         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
2467
2468     if (flags & MZ_ZIP_FLAG_WRITE_ZIP64)
2469     {
2470         // We don't support converting a non-zip64 file to zip64 - this seems like more trouble than it's worth. (What about the existing 32-bit data descriptors that could follow the compressed data?)
2471         if (!pZip->m_pState->m_zip64)
2472             return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
2473     }
2474
2475     // No sense in trying to write to an archive that's already at the support max size
2476     if (pZip->m_pState->m_zip64)
2477     {
2478         if (pZip->m_total_files == MZ_UINT32_MAX)
2479             return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
2480     }
2481     else
2482     {
2483         if (pZip->m_total_files == MZ_UINT16_MAX)
2484             return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
2485
2486         if ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX)
2487             return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE);
2488     }
2489
2490     pState = pZip->m_pState;
2491
2492     if (pState->m_pFile)
2493     {
2494 #ifdef MINIZ_NO_STDIO
2495         (void)pFilename;
2496         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
2497 #else
2498         if (pZip->m_pIO_opaque != pZip)
2499             return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
2500
2501         if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE)
2502         {
2503             if (!pFilename)
2504                 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
2505
2506             // Archive is being read from stdio and was originally opened only for reading. Try to reopen as writable.
2507             if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile)))
2508             {
2509                 // The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it.
2510                 mz_zip_reader_end_internal(pZip, MZ_FALSE);
2511                 return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED);
2512             }
2513         }
2514
2515         pZip->m_pWrite = mz_zip_file_write_func;
2516 #endif // #ifdef MINIZ_NO_STDIO
2517     }
2518     else if (pState->m_pMem)
2519     {
2520         // Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback.
2521         if (pZip->m_pIO_opaque != pZip)
2522             return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
2523
2524         pState->m_mem_capacity = pState->m_mem_size;
2525         pZip->m_pWrite = mz_zip_heap_write_func;
2526     }
2527     // Archive is being read via a user provided read function - make sure the user has specified a write function too.
2528     else if (!pZip->m_pWrite)
2529         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
2530
2531     // Start writing new files at the archive's current central directory location.
2532     // TODO: We could add a flag that lets the user start writing immediately AFTER the existing central dir - this would be safer.
2533     pZip->m_archive_size = pZip->m_central_directory_file_ofs;
2534     pZip->m_central_directory_file_ofs = 0;
2535
2536     // Clear the sorted central dir offsets, they aren't useful or maintained now.
2537     // Even though we're now in write mode, files can still be extracted and verified, but file locates will be slow.
2538     // TODO: We could easily maintain the sorted central directory offsets.
2539     mz_zip_array_clear(pZip, &pZip->m_pState->m_sorted_central_dir_offsets);
2540
2541     pZip->m_zip_mode = MZ_ZIP_MODE_WRITING;
2542
2543     return MZ_TRUE;
2544 }
2545
2546 // TODO: pArchive_name is a terrible name here!
2547 mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags)
2548 {
2549     return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0);
2550 }
2551
2552 typedef struct
2553 {
2554     mz_zip_archive *m_pZip;
2555     mz_uint64 m_cur_archive_file_ofs;
2556     mz_uint64 m_comp_size;
2557 } mz_zip_writer_add_state;
2558
2559 static mz_bool mz_zip_writer_add_put_buf_callback(const void *pBuf, int len, void *pUser)
2560 {
2561     mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser;
2562     if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len)
2563         return MZ_FALSE;
2564
2565     pState->m_cur_archive_file_ofs += len;
2566     pState->m_comp_size += len;
2567     return MZ_TRUE;
2568 }
2569
2570 #define MZ_ZIP64_MAX_LOCAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 2)
2571 #define MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE (sizeof(mz_uint16) * 2 + sizeof(mz_uint64) * 3)
2572 static mz_uint32 mz_zip_writer_create_zip64_extra_data(mz_uint8 *pBuf, mz_uint64 *pUncomp_size, mz_uint64 *pComp_size, mz_uint64 *pLocal_header_ofs)
2573 {
2574     mz_uint8 *pDst = pBuf;
2575
2576     MZ_WRITE_LE16(pDst + 0, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID);
2577     MZ_WRITE_LE16(pDst + 2, 0);
2578     pDst += sizeof(mz_uint16) * 2;
2579
2580     mz_uint32 field_size = 0;
2581
2582     if (pUncomp_size)
2583     {
2584         MZ_WRITE_LE64(pDst, *pUncomp_size);
2585         pDst += sizeof(mz_uint64);
2586         field_size += sizeof(mz_uint64);
2587     }
2588
2589     if (pComp_size)
2590     {
2591         MZ_WRITE_LE64(pDst, *pComp_size);
2592         pDst += sizeof(mz_uint64);
2593         field_size += sizeof(mz_uint64);
2594     }
2595
2596     if (pLocal_header_ofs)
2597     {
2598         MZ_WRITE_LE64(pDst, *pLocal_header_ofs);
2599         pDst += sizeof(mz_uint64);
2600         field_size += sizeof(mz_uint64);
2601     }
2602
2603     MZ_WRITE_LE16(pBuf + 2, field_size);
2604
2605     return (mz_uint32)(pDst - pBuf);
2606 }
2607
2608 static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date)
2609 {
2610     (void)pZip;
2611     memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE);
2612     MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG);
2613     MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0);
2614     MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags);
2615     MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method);
2616     MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time);
2617     MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date);
2618     MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32);
2619     MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX));
2620     MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX));
2621     MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size);
2622     MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size);
2623     return MZ_TRUE;
2624 }
2625
2626 static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst,
2627                                                        mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size,
2628                                                        mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32,
2629                                                        mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date,
2630                                                        mz_uint64 local_header_ofs, mz_uint32 ext_attributes)
2631 {
2632     (void)pZip;
2633     memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE);
2634     MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG);
2635     MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0);
2636     MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags);
2637     MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method);
2638     MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time);
2639     MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date);
2640     MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32);
2641     MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_MIN(comp_size, MZ_UINT32_MAX));
2642     MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_MIN(uncomp_size, MZ_UINT32_MAX));
2643     MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size);
2644     MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size);
2645     MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size);
2646     MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes);
2647     MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_MIN(local_header_ofs, MZ_UINT32_MAX));
2648     return MZ_TRUE;
2649 }
2650
2651 static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size,
2652                                                 const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size,
2653                                                 mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32,
2654                                                 mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date,
2655                                                 mz_uint64 local_header_ofs, mz_uint32 ext_attributes)
2656 {
2657     mz_zip_internal_state *pState = pZip->m_pState;
2658     mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size;
2659     size_t orig_central_dir_size = pState->m_central_dir.m_size;
2660     mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE];
2661
2662     if (!pZip->m_pState->m_zip64)
2663     {
2664         if (local_header_ofs > 0xFFFFFFFF)
2665             return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE);
2666     }
2667
2668     // miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet
2669     if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + comment_size) >= MZ_UINT32_MAX)
2670         return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE);
2671
2672     if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, extra_size, comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes))
2673         return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);
2674
2675     if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) ||
2676         (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) ||
2677         (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) ||
2678         (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) ||
2679         (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &central_dir_ofs, 1)))
2680     {
2681         // Try to resize the central directory array back into its original state.
2682         mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
2683         return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
2684     }
2685
2686     return MZ_TRUE;
2687 }
2688
2689 static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name)
2690 {
2691     // Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes.
2692     if (*pArchive_name == '/')
2693         return MZ_FALSE;
2694
2695     while (*pArchive_name)
2696     {
2697         if ((*pArchive_name == '\\') || (*pArchive_name == ':'))
2698             return MZ_FALSE;
2699
2700         pArchive_name++;
2701     }
2702
2703     return MZ_TRUE;
2704 }
2705
2706 static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip)
2707 {
2708     mz_uint32 n;
2709     if (!pZip->m_file_offset_alignment)
2710         return 0;
2711     n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1));
2712     return (mz_uint)((pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1));
2713 }
2714
2715 static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n)
2716 {
2717     char buf[4096];
2718     memset(buf, 0, MZ_MIN(sizeof(buf), n));
2719     while (n)
2720     {
2721         mz_uint32 s = MZ_MIN(sizeof(buf), n);
2722         if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s)
2723             return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
2724
2725         cur_file_ofs += s;
2726         n -= s;
2727     }
2728     return MZ_TRUE;
2729 }
2730
2731 mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32)
2732 {
2733     mz_uint16 method = 0, dos_time = 0, dos_date = 0;
2734     mz_uint level, ext_attributes = 0, num_alignment_padding_bytes;
2735     mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, extra_data_file_ofs = 0, comp_size = 0;
2736     size_t archive_name_size;
2737     mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE];
2738     tdefl_compressor *pComp = NULL;
2739     mz_bool store_data_uncompressed;
2740     mz_zip_internal_state *pState;
2741     mz_uint8 *pExtra_data = NULL;
2742     mz_uint32 extra_size = 0;
2743     mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE];
2744
2745     if ((int)level_and_flags < 0)
2746         level_and_flags = MZ_DEFAULT_LEVEL;
2747     level = level_and_flags & 0xF;
2748     store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA));
2749
2750     if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION))
2751         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
2752
2753     pState = pZip->m_pState;
2754
2755     if (pState->m_zip64)
2756     {
2757         if (pZip->m_total_files == MZ_UINT32_MAX)
2758             return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
2759     }
2760     else
2761     {
2762         if (pZip->m_total_files == MZ_UINT16_MAX)
2763             return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
2764         if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF))
2765             return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
2766     }
2767
2768     if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size))
2769         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
2770
2771     if (!mz_zip_writer_validate_archive_name(pArchive_name))
2772         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME);
2773
2774 #ifndef MINIZ_NO_TIME
2775     {
2776         MZ_TIME_T cur_time;
2777         time(&cur_time);
2778         mz_zip_time_t_to_dos_time(cur_time, &dos_time, &dos_date);
2779     }
2780 #endif // #ifndef MINIZ_NO_TIME
2781
2782     archive_name_size = strlen(pArchive_name);
2783     if (archive_name_size > MZ_UINT16_MAX)
2784         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME);
2785
2786     num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip);
2787
2788     // miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet
2789     if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX)
2790         return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE);
2791
2792     if (!pState->m_zip64)
2793     {
2794         // Bail early if the archive would obviously become too large
2795         if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)
2796             return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
2797     }
2798
2799     if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/'))
2800     {
2801         // Set DOS Subdirectory attribute bit.
2802         ext_attributes |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG;
2803
2804         // Subdirectories cannot contain data.
2805         if ((buf_size) || (uncomp_size))
2806             return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
2807     }
2808
2809     // Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.)
2810     if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + (pState->m_zip64 ? MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE : 0))) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1)))
2811         return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
2812
2813     if ((!store_data_uncompressed) && (buf_size))
2814     {
2815         if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor))))
2816             return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
2817     }
2818
2819     if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header)))
2820     {
2821         pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
2822         return MZ_FALSE;
2823     }
2824
2825     local_dir_header_ofs += num_alignment_padding_bytes;
2826     if (pZip->m_file_offset_alignment)
2827     {
2828         MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0);
2829     }
2830     cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header);
2831
2832     MZ_CLEAR_OBJ(local_dir_header);
2833
2834     if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size)
2835     {
2836         pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
2837         return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
2838     }
2839     cur_archive_file_ofs += archive_name_size;
2840
2841     extra_data_file_ofs = cur_archive_file_ofs;
2842     if (pState->m_zip64)
2843     {
2844         // Reserve space for an extra data field for zip64
2845         cur_archive_file_ofs += MZ_ZIP64_MAX_LOCAL_EXTRA_FIELD_SIZE;
2846     }
2847
2848     if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))
2849     {
2850         uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, buf_size);
2851         uncomp_size = buf_size;
2852         if (uncomp_size <= 3)
2853         {
2854             level = 0;
2855             store_data_uncompressed = MZ_TRUE;
2856         }
2857     }
2858
2859     if (store_data_uncompressed)
2860     {
2861         if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size)
2862         {
2863             pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
2864             return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
2865         }
2866
2867         cur_archive_file_ofs += buf_size;
2868         comp_size = buf_size;
2869
2870         if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)
2871             method = MZ_DEFLATED;
2872     }
2873     else if (buf_size)
2874     {
2875         mz_zip_writer_add_state state;
2876
2877         state.m_pZip = pZip;
2878         state.m_cur_archive_file_ofs = cur_archive_file_ofs;
2879         state.m_comp_size = 0;
2880
2881         if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) ||
2882             (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE))
2883         {
2884             pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
2885             return mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED);
2886         }
2887
2888         comp_size = state.m_comp_size;
2889         cur_archive_file_ofs = state.m_cur_archive_file_ofs;
2890
2891         method = MZ_DEFLATED;
2892     }
2893
2894     pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
2895     pComp = NULL;
2896
2897     if (pState->m_zip64)
2898     {
2899         // Write the local header+zip64 extra data
2900         mz_uint32 local_dir_entry_extra_data_size = mz_zip_writer_create_zip64_extra_data(extra_data, &uncomp_size, &comp_size, NULL);
2901         MZ_ASSERT(local_dir_entry_extra_data_size <= sizeof(extra_data));
2902
2903         if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, local_dir_entry_extra_data_size, MZ_UINT32_MAX, MZ_UINT32_MAX, uncomp_crc32, method, 0, dos_time, dos_date))
2904             return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);
2905
2906         if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header))
2907             return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
2908
2909         if (pZip->m_pWrite(pZip->m_pIO_opaque, extra_data_file_ofs, extra_data, local_dir_entry_extra_data_size) != local_dir_entry_extra_data_size)
2910             return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
2911
2912         // See if we need to create zip64 extra data
2913         if (MZ_MAX(local_dir_header_ofs, MZ_MAX(comp_size, uncomp_size)) >= MZ_UINT32_MAX)
2914         {
2915             pExtra_data = extra_data;
2916             extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, (comp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL);
2917         }
2918     }
2919     else
2920     {
2921         if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX))
2922             return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
2923         if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date))
2924             return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);
2925
2926         if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header))
2927             return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
2928     }
2929
2930     if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, extra_size, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes))
2931         return MZ_FALSE;
2932
2933     pZip->m_total_files++;
2934     pZip->m_archive_size = cur_archive_file_ofs;
2935
2936     return MZ_TRUE;
2937 }
2938
2939 #ifndef MINIZ_NO_STDIO
2940 mz_bool mz_zip_writer_add_cfile(mz_zip_archive *pZip, const char *pArchive_name, MZ_FILE *pSrc_file, mz_uint64 size_to_add, const MZ_TIME_T *pFile_time, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags)
2941 {
2942     mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes;
2943     mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0;
2944     mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, extra_data_file_ofs = 0, uncomp_size = size_to_add, comp_size = 0;
2945     size_t archive_name_size;
2946     mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE];
2947     mz_uint8 *pExtra_data = NULL;
2948     mz_uint32 extra_size = 0;
2949     mz_uint8 extra_data[MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE];
2950     mz_zip_internal_state *pState;
2951
2952     if ((int)level_and_flags < 0)
2953         level_and_flags = MZ_DEFAULT_LEVEL;
2954     level = level_and_flags & 0xF;
2955
2956     // Sanity checks
2957     if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION))
2958         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
2959
2960     pState = pZip->m_pState;
2961
2962     if ((!pState->m_zip64) && (uncomp_size > MZ_UINT32_MAX))
2963     {
2964         // Source file is too large for non-zip64
2965         return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
2966     }
2967
2968     // We could support this, but why?
2969     if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)
2970         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
2971
2972     if (!mz_zip_writer_validate_archive_name(pArchive_name))
2973         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME);
2974
2975     if (pState->m_zip64)
2976     {
2977         if (pZip->m_total_files == MZ_UINT32_MAX)
2978             return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
2979     }
2980     else
2981     {
2982         if (pZip->m_total_files == MZ_UINT16_MAX)
2983             return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
2984     }
2985
2986     archive_name_size = strlen(pArchive_name);
2987     if (archive_name_size > MZ_UINT16_MAX)
2988         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME);
2989
2990     num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip);
2991
2992     // miniz doesn't support central dirs >= MZ_UINT32_MAX bytes yet
2993     if (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP64_MAX_CENTRAL_EXTRA_FIELD_SIZE + comment_size) >= MZ_UINT32_MAX)
2994         return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE);
2995
2996     if (!pState->m_zip64)
2997     {
2998         // Bail early if the archive would obviously become too large
2999         if ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + archive_name_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)
3000             return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
3001     }
3002
3003 #ifndef MINIZ_NO_TIME
3004     if (pFile_time)
3005     {
3006         mz_zip_time_t_to_dos_time(*pFile_time, &dos_time, &dos_date);
3007     }
3008 #endif
3009
3010     if (uncomp_size <= 3)
3011         level = 0;
3012
3013     if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header)))
3014     {
3015         return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
3016     }
3017
3018     local_dir_header_ofs += num_alignment_padding_bytes;
3019     if (pZip->m_file_offset_alignment)
3020     {
3021         MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0);
3022     }
3023     cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header);
3024
3025     MZ_CLEAR_OBJ(local_dir_header);
3026     if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size)
3027     {
3028         return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
3029     }
3030     cur_archive_file_ofs += archive_name_size;
3031
3032     extra_data_file_ofs = cur_archive_file_ofs;
3033     if (pState->m_zip64)
3034     {
3035         // Reserve space for an extra data field for zip64
3036         cur_archive_file_ofs += MZ_ZIP64_MAX_LOCAL_EXTRA_FIELD_SIZE;
3037     }
3038
3039     if (uncomp_size)
3040     {
3041         mz_uint64 uncomp_remaining = uncomp_size;
3042         void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE);
3043         if (!pRead_buf)
3044         {
3045             return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
3046         }
3047
3048         if (!level)
3049         {
3050             while (uncomp_remaining)
3051             {
3052                 mz_uint n = (mz_uint)MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining);
3053                 if ((MZ_FREAD(pRead_buf, 1, n, pSrc_file) != n) || (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n))
3054                 {
3055                     pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
3056                     return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
3057                 }
3058                 uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n);
3059                 uncomp_remaining -= n;
3060                 cur_archive_file_ofs += n;
3061             }
3062             comp_size = uncomp_size;
3063         }
3064         else
3065         {
3066             mz_bool result = MZ_FALSE;
3067             mz_zip_writer_add_state state;
3068             tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor));
3069             if (!pComp)
3070             {
3071                 pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
3072                 return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
3073             }
3074
3075             state.m_pZip = pZip;
3076             state.m_cur_archive_file_ofs = cur_archive_file_ofs;
3077             state.m_comp_size = 0;
3078
3079             if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY)
3080             {
3081                 pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
3082                 pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
3083                 return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);
3084             }
3085
3086             for (;;)
3087             {
3088                 size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE);
3089                 tdefl_status status;
3090
3091                 if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size)
3092                 {
3093                     mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
3094                     break;
3095                 }
3096
3097                 uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size);
3098                 uncomp_remaining -= in_buf_size;
3099
3100                 status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? TDEFL_NO_FLUSH : TDEFL_FINISH);
3101                 if (status == TDEFL_STATUS_DONE)
3102                 {
3103                     result = MZ_TRUE;
3104                     break;
3105                 }
3106                 else if (status != TDEFL_STATUS_OKAY)
3107                 {
3108                     mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED);
3109                     break;
3110                 }
3111             }
3112
3113             pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
3114
3115             if (!result)
3116             {
3117                 pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
3118                 return MZ_FALSE;
3119             }
3120
3121             comp_size = state.m_comp_size;
3122             cur_archive_file_ofs = state.m_cur_archive_file_ofs;
3123
3124             method = MZ_DEFLATED;
3125         }
3126
3127         pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
3128     }
3129
3130     if (pState->m_zip64)
3131     {
3132         // Write the local header+zip64 extra data
3133         mz_uint32 local_dir_entry_extra_data_size = mz_zip_writer_create_zip64_extra_data(extra_data, &uncomp_size, &comp_size, NULL);
3134         MZ_ASSERT(local_dir_entry_extra_data_size <= sizeof(extra_data));
3135
3136         if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, local_dir_entry_extra_data_size, MZ_UINT32_MAX, MZ_UINT32_MAX, uncomp_crc32, method, 0, dos_time, dos_date))
3137             return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);
3138
3139         if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header))
3140             return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
3141
3142         if (pZip->m_pWrite(pZip->m_pIO_opaque, extra_data_file_ofs, extra_data, local_dir_entry_extra_data_size) != local_dir_entry_extra_data_size)
3143             return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
3144
3145         // See if we need to create zip64 extra data
3146         if (MZ_MAX(local_dir_header_ofs, MZ_MAX(comp_size, uncomp_size)) >= MZ_UINT32_MAX)
3147         {
3148             pExtra_data = extra_data;
3149             extra_size = mz_zip_writer_create_zip64_extra_data(extra_data, (uncomp_size >= MZ_UINT32_MAX) ? &uncomp_size : NULL, (comp_size >= MZ_UINT32_MAX) ? &comp_size : NULL, (local_dir_header_ofs >= MZ_UINT32_MAX) ? &local_dir_header_ofs : NULL);
3150         }
3151     }
3152     else
3153     {
3154         if ((comp_size > MZ_UINT32_MAX) || (cur_archive_file_ofs > MZ_UINT32_MAX))
3155             return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
3156         if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date))
3157             return mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);
3158
3159         if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header))
3160             return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
3161     }
3162
3163     if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, pExtra_data, extra_size, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes))
3164         return MZ_FALSE;
3165
3166     pZip->m_total_files++;
3167     pZip->m_archive_size = cur_archive_file_ofs;
3168
3169     return MZ_TRUE;
3170 }
3171
3172 mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags)
3173 {
3174     MZ_FILE *pSrc_file = NULL;
3175     mz_uint64 uncomp_size = 0;
3176     MZ_TIME_T file_modified_time;
3177     MZ_TIME_T *pFile_modified_time = NULL;
3178
3179     memset(&file_modified_time, 0, sizeof(file_modified_time));
3180
3181 #if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO)
3182     pFile_modified_time = &file_modified_time;
3183
3184     if (!mz_zip_get_file_modified_time(pSrc_filename, &file_modified_time))
3185         return mz_zip_set_error(pZip, MZ_ZIP_FILE_STAT_FAILED);
3186 #endif
3187
3188     pSrc_file = MZ_FOPEN(pSrc_filename, "rb");
3189     if (!pSrc_file)
3190         return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED);
3191
3192     MZ_FSEEK64(pSrc_file, 0, SEEK_END);
3193     uncomp_size = MZ_FTELL64(pSrc_file);
3194     MZ_FSEEK64(pSrc_file, 0, SEEK_SET);
3195
3196     mz_bool status = mz_zip_writer_add_cfile(pZip, pArchive_name, pSrc_file, uncomp_size, pFile_modified_time, pComment, comment_size, level_and_flags);
3197
3198     MZ_FCLOSE(pSrc_file);
3199
3200     return status;
3201 }
3202 #endif // #ifndef MINIZ_NO_STDIO
3203
3204 static mz_bool mz_zip_writer_update_zip64_extension_block(mz_zip_array *pNew_ext, mz_zip_archive *pZip, const mz_uint8 *pExt, uint ext_len, mz_uint64 *pComp_size, mz_uint64 *pUncomp_size, mz_uint64 *pLocal_header_ofs, mz_uint32 *pDisk_start)
3205 {
3206     // + 64 should be enough for any new zip64 data
3207     if (!mz_zip_array_reserve(pZip, pNew_ext, ext_len + 64, MZ_FALSE))
3208         return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
3209
3210     mz_zip_array_resize(pZip, pNew_ext, 0, MZ_FALSE);
3211
3212     if ((pUncomp_size) || (pComp_size) || (pLocal_header_ofs) || (pDisk_start))
3213     {
3214         mz_uint8 new_ext_block[64];
3215         mz_uint8 *pDst = new_ext_block;
3216         mz_write_le16(pDst, MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID);
3217         mz_write_le16(pDst + sizeof(mz_uint16), 0);
3218         pDst += sizeof(mz_uint16) * 2;
3219
3220         if (pUncomp_size)
3221         {
3222             mz_write_le64(pDst, *pUncomp_size);
3223             pDst += sizeof(mz_uint64);
3224         }
3225
3226         if (pComp_size)
3227         {
3228             mz_write_le64(pDst, *pComp_size);
3229             pDst += sizeof(mz_uint64);
3230         }
3231
3232         if (pLocal_header_ofs)
3233         {
3234             mz_write_le64(pDst, *pLocal_header_ofs);
3235             pDst += sizeof(mz_uint64);
3236         }
3237
3238         if (pDisk_start)
3239         {
3240             mz_write_le32(pDst, *pDisk_start);
3241             pDst += sizeof(mz_uint32);
3242         }
3243
3244         mz_write_le16(new_ext_block + sizeof(mz_uint16), (pDst - new_ext_block) - sizeof(mz_uint16) * 2);
3245
3246         if (!mz_zip_array_push_back(pZip, pNew_ext, new_ext_block, pDst - new_ext_block))
3247             return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
3248     }
3249
3250     if ((pExt) && (ext_len))
3251     {
3252         mz_uint32 extra_size_remaining = ext_len;
3253         const mz_uint8 *pExtra_data = pExt;
3254
3255         do
3256         {
3257             mz_uint32 field_id, field_data_size, field_total_size;
3258
3259             if (extra_size_remaining < (sizeof(mz_uint16) * 2))
3260                 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
3261
3262             field_id = MZ_READ_LE16(pExtra_data);
3263             field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16));
3264             field_total_size = field_data_size + sizeof(mz_uint16) * 2;
3265
3266             if (field_total_size > extra_size_remaining)
3267                 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
3268
3269             if (field_id != MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID)
3270             {
3271                 if (!mz_zip_array_push_back(pZip, pNew_ext, pExtra_data, field_total_size))
3272                     return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
3273             }
3274
3275             pExtra_data += field_total_size;
3276             extra_size_remaining -= field_total_size;
3277         } while (extra_size_remaining);
3278     }
3279
3280     return MZ_TRUE;
3281 }
3282
3283 // TODO: This func is now pretty freakin complex due to zip64, split it up?
3284 mz_bool mz_zip_writer_add_from_other_zip(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint src_file_index)
3285 {
3286     mz_uint n, bit_flags, num_alignment_padding_bytes, src_central_dir_following_data_size;
3287     mz_uint64 src_archive_bytes_remaining, local_dir_header_ofs;
3288     mz_uint64 cur_src_file_ofs, cur_dst_file_ofs;
3289     mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)];
3290     mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32;
3291     mz_uint8 new_central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE];
3292     size_t orig_central_dir_size;
3293     mz_zip_internal_state *pState;
3294     void *pBuf;
3295     const mz_uint8 *pSrc_central_header;
3296     mz_zip_archive_file_stat src_file_stat;
3297     mz_uint32 src_filename_len, src_comment_len, src_ext_len;
3298     mz_uint32 local_header_filename_size, local_header_extra_len;
3299     mz_uint64 local_header_comp_size, local_header_uncomp_size;
3300     mz_bool found_zip64_ext_data_in_ldir = MZ_FALSE;
3301
3302     // Sanity checks
3303     if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pSource_zip->m_pRead))
3304         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
3305
3306     pState = pZip->m_pState;
3307
3308     // Don't support copying files from zip64 archives to non-zip64, even though in some cases this is possible
3309     if ((pSource_zip->m_pState->m_zip64) && (!pZip->m_pState->m_zip64))
3310         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
3311
3312     // Get pointer to the source central dir header and crack it
3313     if (NULL == (pSrc_central_header = mz_zip_get_cdh(pSource_zip, src_file_index)))
3314         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
3315
3316     if (MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_SIG_OFS) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)
3317         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
3318
3319     src_filename_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS);
3320     src_comment_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS);
3321     src_ext_len = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS);
3322     src_central_dir_following_data_size = src_filename_len + src_ext_len + src_comment_len;
3323
3324     // TODO: We don't support central dir's >= MZ_UINT32_MAX bytes right now (+32 fudge factor in case we need to add more extra data)
3325     if ((pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + 32) >= MZ_UINT32_MAX)
3326         return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE);
3327
3328     num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip);
3329
3330     if (!pState->m_zip64)
3331     {
3332         if (pZip->m_total_files == MZ_UINT16_MAX)
3333             return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
3334     }
3335     else
3336     {
3337         // TODO: Our zip64 support still has some 32-bit limits that may not be worth fixing.
3338         if (pZip->m_total_files == MZ_UINT32_MAX)
3339             return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
3340     }
3341
3342     if (!mz_zip_file_stat_internal(pSource_zip, src_file_index, pSrc_central_header, &src_file_stat, NULL))
3343         return MZ_FALSE;
3344
3345     cur_src_file_ofs = src_file_stat.m_local_header_ofs;
3346     cur_dst_file_ofs = pZip->m_archive_size;
3347
3348     // Read the source archive's local dir header
3349     if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
3350         return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
3351
3352     if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG)
3353         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
3354
3355     cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE;
3356
3357     // Compute the total size we need to copy (filename+extra data+compressed data)
3358     local_header_filename_size = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS);
3359     local_header_extra_len = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS);
3360     local_header_comp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS);
3361     local_header_uncomp_size = MZ_READ_LE32(pLocal_header + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS);
3362     src_archive_bytes_remaining = local_header_filename_size + local_header_extra_len + src_file_stat.m_comp_size;
3363
3364     // Try to find a zip64 extended information field
3365     if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX)))
3366     {
3367         mz_zip_array file_data_array;
3368         mz_zip_array_init(&file_data_array, 1);
3369         if (!mz_zip_array_resize(pZip, &file_data_array, local_header_extra_len, MZ_FALSE))
3370         {
3371             return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
3372         }
3373
3374         if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, src_file_stat.m_local_header_ofs + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + local_header_filename_size, file_data_array.m_p, local_header_extra_len) != local_header_extra_len)
3375         {
3376             mz_zip_array_clear(pZip, &file_data_array);
3377             return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
3378         }
3379
3380         mz_uint32 extra_size_remaining = local_header_extra_len;
3381         const mz_uint8 *pExtra_data = (const mz_uint8 *)file_data_array.m_p;
3382
3383         do
3384         {
3385             mz_uint32 field_id, field_data_size, field_total_size;
3386
3387             if (extra_size_remaining < (sizeof(mz_uint16) * 2))
3388             {
3389                 mz_zip_array_clear(pZip, &file_data_array);
3390                 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
3391             }
3392
3393             field_id = MZ_READ_LE16(pExtra_data);
3394             field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16));
3395             field_total_size = field_data_size + sizeof(mz_uint16) * 2;
3396
3397             if (field_total_size > extra_size_remaining)
3398             {
3399                 mz_zip_array_clear(pZip, &file_data_array);
3400                 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
3401             }
3402
3403             if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID)
3404             {
3405                 const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32);
3406
3407                 if (field_data_size < sizeof(mz_uint64) * 2)
3408                 {
3409                     mz_zip_array_clear(pZip, &file_data_array);
3410                     return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
3411                 }
3412
3413                 local_header_uncomp_size = MZ_READ_LE64(pSrc_field_data);
3414                 local_header_comp_size = MZ_READ_LE64(pSrc_field_data + sizeof(mz_uint64)); // may be 0 if there's a descriptor
3415
3416                 found_zip64_ext_data_in_ldir = MZ_TRUE;
3417                 break;
3418             }
3419
3420             pExtra_data += field_total_size;
3421             extra_size_remaining -= field_total_size;
3422         } while (extra_size_remaining);
3423
3424         mz_zip_array_clear(pZip, &file_data_array);
3425     }
3426
3427     if (!pState->m_zip64)
3428     {
3429         // Try to detect if the new archive will most likely wind up too big and bail early (+(sizeof(mz_uint32) * 4) is for the optional descriptor which could be present, +64 is a fudge factor).
3430         // We also check when the archive is finalized so this doesn't need to be perfect.
3431         mz_uint64 approx_new_archive_size = cur_dst_file_ofs + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + src_archive_bytes_remaining + (sizeof(mz_uint32) * 4) +
3432                                             pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_central_dir_following_data_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE + 64;
3433
3434         if (approx_new_archive_size >= MZ_UINT32_MAX)
3435             return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
3436     }
3437
3438     // Write dest archive padding
3439     if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes))
3440         return MZ_FALSE;
3441
3442     cur_dst_file_ofs += num_alignment_padding_bytes;
3443
3444     local_dir_header_ofs = cur_dst_file_ofs;
3445     if (pZip->m_file_offset_alignment)
3446     {
3447         MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0);
3448     }
3449
3450     // The original zip's local header+ext block doesn't change, even with zip64, so we can just copy it over to the dest zip
3451     if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE)
3452         return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
3453
3454     cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE;
3455
3456     // Copy over the source archive bytes to the dest archive, also ensure we have enough buf space to handle optional data descriptor
3457     if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(32U, MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining)))))
3458         return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
3459
3460     while (src_archive_bytes_remaining)
3461     {
3462         n = (mz_uint)MZ_MIN((mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE, src_archive_bytes_remaining);
3463         if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n)
3464         {
3465             pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
3466             return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
3467         }
3468         cur_src_file_ofs += n;
3469
3470         if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n)
3471         {
3472             pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
3473             return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
3474         }
3475         cur_dst_file_ofs += n;
3476
3477         src_archive_bytes_remaining -= n;
3478     }
3479
3480     // Now deal with the optional data descriptor
3481     bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS);
3482     if (bit_flags & 8)
3483     {
3484         // Copy data descriptor
3485         if ((pSource_zip->m_pState->m_zip64) || (found_zip64_ext_data_in_ldir))
3486         {
3487             // src is zip64, dest must be zip64
3488
3489             // name                     uint32's
3490             // id                               1 (optional in zip64?)
3491             // crc                      1
3492             // comp_size        2
3493             // uncomp_size 2
3494             if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, (sizeof(mz_uint32) * 6)) != (sizeof(mz_uint32) * 6))
3495             {
3496                 pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
3497                 return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
3498             }
3499
3500             n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID) ? 6 : 5);
3501         }
3502         else
3503         {
3504             // src is NOT zip64
3505             mz_bool has_id;
3506
3507             if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4)
3508             {
3509                 pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
3510                 return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
3511             }
3512
3513             has_id = (MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID);
3514
3515             if (pZip->m_pState->m_zip64)
3516             {
3517                 // dest is zip64, so upgrade the data descriptor
3518                 const mz_uint32 *pSrc_descriptor = (const mz_uint32 *)((const mz_uint8 *)pBuf + (has_id ? sizeof(mz_uint32) : 0));
3519                 const mz_uint32 src_crc32 = pSrc_descriptor[0];
3520                 const mz_uint64 src_comp_size = pSrc_descriptor[1];
3521                 const mz_uint64 src_uncomp_size = pSrc_descriptor[2];
3522
3523                 mz_write_le32((mz_uint8 *)pBuf, MZ_ZIP_DATA_DESCRIPTOR_ID);
3524                 mz_write_le32((mz_uint8 *)pBuf + sizeof(mz_uint32) * 1, src_crc32);
3525                 mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 2, src_comp_size);
3526                 mz_write_le64((mz_uint8 *)pBuf + sizeof(mz_uint32) * 4, src_uncomp_size);
3527
3528                 n = sizeof(mz_uint32) * 6;
3529             }
3530             else
3531             {
3532                 // dest is NOT zip64, just copy it as-is
3533                 n = sizeof(mz_uint32) * (has_id ? 4 : 3);
3534             }
3535         }
3536
3537         if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n)
3538         {
3539             pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
3540             return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
3541         }
3542
3543         cur_src_file_ofs += n;
3544         cur_dst_file_ofs += n;
3545     }
3546     pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
3547
3548     // Finally, add the new central dir header
3549     orig_central_dir_size = pState->m_central_dir.m_size;
3550
3551     memcpy(new_central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE);
3552
3553     if (pState->m_zip64)
3554     {
3555         // This is the painful part: We need to write a new central dir header + ext block with updated zip64 fields, and ensure the old fields (if any) are not included.
3556         const mz_uint8 *pSrc_ext = pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len;
3557         mz_zip_array new_ext_block;
3558
3559         mz_zip_array_init(&new_ext_block, sizeof(mz_uint8));
3560
3561         MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, MZ_UINT32_MAX);
3562         MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, MZ_UINT32_MAX);
3563         MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, MZ_UINT32_MAX);
3564
3565         if (!mz_zip_writer_update_zip64_extension_block(&new_ext_block, pZip, pSrc_ext, src_ext_len, &src_file_stat.m_comp_size, &src_file_stat.m_uncomp_size, &local_dir_header_ofs, NULL))
3566         {
3567             mz_zip_array_clear(pZip, &new_ext_block);
3568             return MZ_FALSE;
3569         }
3570
3571         MZ_WRITE_LE16(new_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS, new_ext_block.m_size);
3572
3573         if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE))
3574         {
3575             mz_zip_array_clear(pZip, &new_ext_block);
3576             return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
3577         }
3578
3579         if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_filename_len))
3580         {
3581             mz_zip_array_clear(pZip, &new_ext_block);
3582             mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
3583             return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
3584         }
3585
3586         if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_ext_block.m_p, new_ext_block.m_size))
3587         {
3588             mz_zip_array_clear(pZip, &new_ext_block);
3589             mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
3590             return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
3591         }
3592
3593         if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + src_filename_len + src_ext_len, src_comment_len))
3594         {
3595             mz_zip_array_clear(pZip, &new_ext_block);
3596             mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
3597             return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
3598         }
3599
3600         mz_zip_array_clear(pZip, &new_ext_block);
3601     }
3602     else
3603     {
3604         // sanity checks
3605         if (cur_dst_file_ofs > MZ_UINT32_MAX)
3606             return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
3607
3608         if (local_dir_header_ofs >= MZ_UINT32_MAX)
3609             return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
3610
3611         MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs);
3612
3613         if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE))
3614             return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
3615
3616         if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_central_dir_following_data_size))
3617         {
3618             mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
3619             return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
3620         }
3621     }
3622
3623     // This shouldn't trigger unless we screwed up during the initial sanity checks
3624     if (pState->m_central_dir.m_size >= MZ_UINT32_MAX)
3625     {
3626         // TODO: Support central dirs >= 32-bits in size
3627         mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
3628         return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE);
3629     }
3630
3631     n = (mz_uint32)orig_central_dir_size;
3632     if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1))
3633     {
3634         mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE);
3635         return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
3636     }
3637
3638     pZip->m_total_files++;
3639     pZip->m_archive_size = cur_dst_file_ofs;
3640
3641     return MZ_TRUE;
3642 }
3643
3644 mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip)
3645 {
3646     mz_zip_internal_state *pState;
3647     mz_uint64 central_dir_ofs, central_dir_size;
3648     mz_uint8 hdr[256];
3649
3650     if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING))
3651         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
3652
3653     pState = pZip->m_pState;
3654
3655     if (pState->m_zip64)
3656     {
3657         if ((pZip->m_total_files > MZ_UINT32_MAX) || (pState->m_central_dir.m_size >= MZ_UINT32_MAX))
3658             return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
3659     }
3660     else
3661     {
3662         if ((pZip->m_total_files > MZ_UINT16_MAX) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > MZ_UINT32_MAX))
3663             return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
3664     }
3665
3666     central_dir_ofs = 0;
3667     central_dir_size = 0;
3668     if (pZip->m_total_files)
3669     {
3670         // Write central directory
3671         central_dir_ofs = pZip->m_archive_size;
3672         central_dir_size = pState->m_central_dir.m_size;
3673         pZip->m_central_directory_file_ofs = central_dir_ofs;
3674         if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size)
3675             return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
3676
3677         pZip->m_archive_size += central_dir_size;
3678     }
3679
3680     if (pState->m_zip64)
3681     {
3682         // Write zip64 end of central directory header
3683         mz_uint64 rel_ofs_to_zip64_ecdr = pZip->m_archive_size;
3684
3685         MZ_CLEAR_OBJ(hdr);
3686         MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDH_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG);
3687         MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - sizeof(mz_uint32) - sizeof(mz_uint64));
3688         MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS, 0x031E); // TODO: always Unix
3689         MZ_WRITE_LE16(hdr + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS, 0x002D);
3690         MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files);
3691         MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files);
3692         MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_SIZE_OFS, central_dir_size);
3693         MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDH_CDIR_OFS_OFS, central_dir_ofs);
3694         if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)
3695             return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
3696
3697         pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE;
3698
3699         // Write zip64 end of central directory locator
3700         MZ_CLEAR_OBJ(hdr);
3701         MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_SIG_OFS, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG);
3702         MZ_WRITE_LE64(hdr + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS, rel_ofs_to_zip64_ecdr);
3703         MZ_WRITE_LE32(hdr + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS, 1);
3704         if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) != MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE)
3705             return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
3706
3707         pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE;
3708     }
3709
3710     // Write end of central directory record
3711     MZ_CLEAR_OBJ(hdr);
3712     MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG);
3713     MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files));
3714     MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, MZ_MIN(MZ_UINT16_MAX, pZip->m_total_files));
3715     MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_size));
3716     MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, MZ_MIN(MZ_UINT32_MAX, central_dir_ofs));
3717
3718     if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
3719         return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
3720
3721 #ifndef MINIZ_NO_STDIO
3722     if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF))
3723         return mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED);
3724 #endif // #ifndef MINIZ_NO_STDIO
3725
3726     pZip->m_archive_size += MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE;
3727
3728     pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED;
3729     return MZ_TRUE;
3730 }
3731
3732 mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize)
3733 {
3734     if ((!ppBuf) || (!pSize))
3735         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
3736
3737     *ppBuf = NULL;
3738     *pSize = 0;
3739
3740     if ((!pZip) || (!pZip->m_pState))
3741         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
3742
3743     if (pZip->m_pWrite != mz_zip_heap_write_func)
3744         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
3745
3746     if (!mz_zip_writer_finalize_archive(pZip))
3747         return MZ_FALSE;
3748
3749     *ppBuf = pZip->m_pState->m_pMem;
3750     *pSize = pZip->m_pState->m_mem_size;
3751     pZip->m_pState->m_pMem = NULL;
3752     pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0;
3753
3754     return MZ_TRUE;
3755 }
3756
3757 mz_bool mz_zip_writer_end(mz_zip_archive *pZip)
3758 {
3759     return mz_zip_writer_end_internal(pZip, MZ_TRUE);
3760 }
3761
3762 #ifndef MINIZ_NO_STDIO
3763 mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_zip_error *pErr)
3764 {
3765     mz_bool status, created_new_archive = MZ_FALSE;
3766     mz_zip_archive zip_archive;
3767     struct MZ_FILE_STAT_STRUCT file_stat;
3768     mz_zip_error actual_err = MZ_ZIP_NO_ERROR;
3769
3770     mz_zip_zero_struct(&zip_archive);
3771     if ((int)level_and_flags < 0)
3772         level_and_flags = MZ_DEFAULT_LEVEL;
3773
3774     if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION))
3775     {
3776         if (pErr)
3777             *pErr = MZ_ZIP_INVALID_PARAMETER;
3778         return MZ_FALSE;
3779     }
3780
3781     if (!mz_zip_writer_validate_archive_name(pArchive_name))
3782     {
3783         if (pErr)
3784             *pErr = MZ_ZIP_INVALID_FILENAME;
3785         return MZ_FALSE;
3786     }
3787
3788     // Important: The regular non-64 bit version of stat() can fail here if the file is very large, which could cause the archive to be overwritten.
3789     // So be sure to compile with _LARGEFILE64_SOURCE 1
3790     if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0)
3791     {
3792         // Create a new archive.
3793         if (!mz_zip_writer_init_file(&zip_archive, pZip_filename, 0, level_and_flags))
3794         {
3795             if (pErr)
3796                 *pErr = zip_archive.m_last_error;
3797             return MZ_FALSE;
3798         }
3799
3800         created_new_archive = MZ_TRUE;
3801     }
3802     else
3803     {
3804         // Append to an existing archive.
3805         if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0))
3806         {
3807             if (pErr)
3808                 *pErr = zip_archive.m_last_error;
3809             return MZ_FALSE;
3810         }
3811
3812         if (!mz_zip_writer_init_from_reader(&zip_archive, pZip_filename, level_and_flags))
3813         {
3814             if (pErr)
3815                 *pErr = zip_archive.m_last_error;
3816
3817             mz_zip_reader_end_internal(&zip_archive, MZ_FALSE);
3818
3819             return MZ_FALSE;
3820         }
3821     }
3822
3823     status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0);
3824     actual_err = zip_archive.m_last_error;
3825
3826     // Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.)
3827     if (!mz_zip_writer_finalize_archive(&zip_archive))
3828     {
3829         if (!actual_err)
3830             actual_err = zip_archive.m_last_error;
3831
3832         status = MZ_FALSE;
3833     }
3834
3835     if (!mz_zip_writer_end_internal(&zip_archive, status))
3836     {
3837         if (!actual_err)
3838             actual_err = zip_archive.m_last_error;
3839
3840         status = MZ_FALSE;
3841     }
3842
3843     if ((!status) && (created_new_archive))
3844     {
3845         // It's a new archive and something went wrong, so just delete it.
3846         int ignoredStatus = MZ_DELETE_FILE(pZip_filename);
3847         (void)ignoredStatus;
3848     }
3849
3850     if (pErr)
3851         *pErr = actual_err;
3852
3853     return status;
3854 }
3855
3856 void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, const char *pComment, size_t *pSize, mz_uint flags, mz_zip_error *pErr)
3857 {
3858     mz_uint32 file_index;
3859     mz_zip_archive zip_archive;
3860     void *p = NULL;
3861
3862     if (pSize)
3863         *pSize = 0;
3864
3865     if ((!pZip_filename) || (!pArchive_name))
3866     {
3867         if (pErr)
3868             *pErr = MZ_ZIP_INVALID_PARAMETER;
3869
3870         return NULL;
3871     }
3872
3873     mz_zip_zero_struct(&zip_archive);
3874     if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY, 0, 0))
3875     {
3876         if (pErr)
3877             *pErr = zip_archive.m_last_error;
3878
3879         return NULL;
3880     }
3881
3882     if (mz_zip_locate_file(&zip_archive, pArchive_name, pComment, flags, &file_index))
3883     {
3884         p = mz_zip_extract_to_heap(&zip_archive, file_index, pSize, flags);
3885     }
3886
3887     mz_zip_reader_end_internal(&zip_archive, p != NULL);
3888
3889     if (pErr)
3890         *pErr = zip_archive.m_last_error;
3891
3892     return p;
3893 }
3894
3895 #endif // #ifndef MINIZ_NO_STDIO
3896
3897 #endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS
3898
3899 // ------------------- Misc utils
3900
3901 mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip)
3902 {
3903     return pZip ? pZip->m_zip_mode : MZ_ZIP_MODE_INVALID;
3904 }
3905
3906 mz_zip_type mz_zip_get_type(mz_zip_archive *pZip)
3907 {
3908     return pZip ? pZip->m_zip_type : MZ_ZIP_TYPE_INVALID;
3909 }
3910
3911 mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num)
3912 {
3913     mz_zip_error prev_err;
3914
3915     if (!pZip)
3916         return MZ_ZIP_INVALID_PARAMETER;
3917
3918     prev_err = pZip->m_last_error;
3919
3920     pZip->m_last_error = err_num;
3921     return prev_err;
3922 }
3923
3924 mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip)
3925 {
3926     if (!pZip)
3927         return MZ_ZIP_INVALID_PARAMETER;
3928
3929     return pZip->m_last_error;
3930 }
3931
3932 mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip)
3933 {
3934     return mz_zip_set_last_error(pZip, MZ_ZIP_NO_ERROR);
3935 }
3936
3937 mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip)
3938 {
3939     mz_zip_error prev_err;
3940
3941     if (!pZip)
3942         return MZ_ZIP_INVALID_PARAMETER;
3943
3944     prev_err = pZip->m_last_error;
3945
3946     pZip->m_last_error = MZ_ZIP_NO_ERROR;
3947     return prev_err;
3948 }
3949
3950 const char *mz_zip_get_error_string(mz_zip_error mz_err)
3951 {
3952     switch (mz_err)
3953     {
3954         case MZ_ZIP_NO_ERROR:
3955             return "no error";
3956         case MZ_ZIP_UNDEFINED_ERROR:
3957             return "undefined error";
3958         case MZ_ZIP_TOO_MANY_FILES:
3959             return "too many files";
3960         case MZ_ZIP_FILE_TOO_LARGE:
3961             return "file too large";
3962         case MZ_ZIP_UNSUPPORTED_METHOD:
3963             return "unsupported method";
3964         case MZ_ZIP_UNSUPPORTED_ENCRYPTION:
3965             return "unsupported encryption";
3966         case MZ_ZIP_UNSUPPORTED_FEATURE:
3967             return "unsupported feature";
3968         case MZ_ZIP_FAILED_FINDING_CENTRAL_DIR:
3969             return "failed finding central directory";
3970         case MZ_ZIP_NOT_AN_ARCHIVE:
3971             return "not a ZIP archive";
3972         case MZ_ZIP_INVALID_HEADER_OR_CORRUPTED:
3973             return "invalid header or archive is corrupted";
3974         case MZ_ZIP_UNSUPPORTED_MULTIDISK:
3975             return "unsupported multidisk archive";
3976         case MZ_ZIP_DECOMPRESSION_FAILED:
3977             return "decompression failed or archive is corrupted";
3978         case MZ_ZIP_COMPRESSION_FAILED:
3979             return "compression failed";
3980         case MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE:
3981             return "unexpected decompressed size";
3982         case MZ_ZIP_CRC_CHECK_FAILED:
3983             return "CRC-32 check failed";
3984         case MZ_ZIP_UNSUPPORTED_CDIR_SIZE:
3985             return "unsupported central directory size";
3986         case MZ_ZIP_ALLOC_FAILED:
3987             return "allocation failed";
3988         case MZ_ZIP_FILE_OPEN_FAILED:
3989             return "file open failed";
3990         case MZ_ZIP_FILE_CREATE_FAILED:
3991             return "file create failed";
3992         case MZ_ZIP_FILE_WRITE_FAILED:
3993             return "file write failed";
3994         case MZ_ZIP_FILE_READ_FAILED:
3995             return "file read failed";
3996         case MZ_ZIP_FILE_CLOSE_FAILED:
3997             return "file close failed";
3998         case MZ_ZIP_FILE_SEEK_FAILED:
3999             return "file seek failed";
4000         case MZ_ZIP_FILE_STAT_FAILED:
4001             return "file stat failed";
4002         case MZ_ZIP_INVALID_PARAMETER:
4003             return "invalid parameter";
4004         case MZ_ZIP_INVALID_FILENAME:
4005             return "invalid filename";
4006         case MZ_ZIP_BUF_TOO_SMALL:
4007             return "buffer too small";
4008         case MZ_ZIP_INTERNAL_ERROR:
4009             return "internal error";
4010         case MZ_ZIP_FILE_NOT_FOUND:
4011             return "file not found";
4012         case MZ_ZIP_ARCHIVE_TOO_LARGE:
4013             return "archive is too large";
4014         case MZ_ZIP_VALIDATION_FAILED:
4015             return "validation failed";
4016         case MZ_ZIP_WRITE_CALLBACK_FAILED:
4017             return "write calledback failed";
4018         default:
4019             break;
4020     }
4021
4022     return "unknown error";
4023 }
4024
4025 // Note: Just because the archive is not zip64 doesn't necessarily mean it doesn't have Zip64 extended information extra field, argh.
4026 mz_bool mz_zip_is_zip64(mz_zip_archive *pZip)
4027 {
4028     if ((!pZip) || (!pZip->m_pState))
4029         return MZ_FALSE;
4030
4031     return pZip->m_pState->m_zip64;
4032 }
4033
4034 size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip)
4035 {
4036     if ((!pZip) || (!pZip->m_pState))
4037         return 0;
4038
4039     return pZip->m_pState->m_central_dir.m_size;
4040 }
4041
4042 mz_uint mz_zip_get_num_files(mz_zip_archive *pZip)
4043 {
4044     return pZip ? pZip->m_total_files : 0;
4045 }
4046
4047 mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip)
4048 {
4049     if (!pZip)
4050         return 0;
4051     return pZip->m_archive_size;
4052 }
4053
4054 mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip)
4055 {
4056     if ((!pZip) || (!pZip->m_pState))
4057         return 0;
4058     return pZip->m_pState->m_file_archive_start_ofs;
4059 }
4060
4061 MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip)
4062 {
4063     if ((!pZip) || (!pZip->m_pState))
4064         return 0;
4065     return pZip->m_pState->m_pFile;
4066 }
4067
4068 size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n)
4069 {
4070     if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pZip->m_pRead))
4071         return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
4072
4073     return pZip->m_pRead(pZip->m_pIO_opaque, file_ofs, pBuf, n);
4074 }
4075
4076 mz_uint mz_zip_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size)
4077 {
4078     mz_uint n;
4079     const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index);
4080     if (!p)
4081     {
4082         if (filename_buf_size)
4083             pFilename[0] = '\0';
4084         mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
4085         return 0;
4086     }
4087     n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
4088     if (filename_buf_size)
4089     {
4090         n = MZ_MIN(n, filename_buf_size - 1);
4091         memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n);
4092         pFilename[n] = '\0';
4093     }
4094     return n + 1;
4095 }
4096
4097 mz_bool mz_zip_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat)
4098 {
4099     return mz_zip_file_stat_internal(pZip, file_index, mz_zip_get_cdh(pZip, file_index), pStat, NULL);
4100 }
4101
4102 mz_bool mz_zip_end(mz_zip_archive *pZip)
4103 {
4104     if (!pZip)
4105         return MZ_FALSE;
4106
4107     if (pZip->m_zip_mode == MZ_ZIP_MODE_READING)
4108         return mz_zip_reader_end(pZip);
4109     else if ((pZip->m_zip_mode == MZ_ZIP_MODE_WRITING) || (pZip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))
4110         return mz_zip_writer_end(pZip);
4111
4112     return MZ_FALSE;
4113 }
4114
4115 #endif // #ifndef MINIZ_NO_ARCHIVE_APIS
4116
4117 #ifdef __cplusplus
4118 }
4119 #endif