1 /**************************************************************************
3 * Copyright 2013-2014 RAD Game Tools and Valve Software
4 * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
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:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
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
25 **************************************************************************/
27 // File: vogl_miniz_zip.cpp
28 #include "vogl_miniz_zip.h"
30 // ------------------- .ZIP archive reading
36 #ifndef MINIZ_NO_ARCHIVE_APIS
39 #define MZ_FILE void *
43 #if defined(_MSC_VER) || defined(__MINGW64__)
44 static FILE *mz_fopen(const char *pFilename, const char *pMode)
47 fopen_s(&pFile, pFilename, pMode);
50 static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream)
53 if (freopen_s(&pFile, pPath, pMode, pStream))
58 #include <sys/utime.h>
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__)
73 #include <sys/utime.h>
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__)
88 #include <sys/utime.h>
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
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
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
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
135 #define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c))
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.
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,
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,
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,
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,
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,
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
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
226 size_t m_size, m_capacity;
227 mz_uint m_element_size;
230 struct mz_zip_internal_state_tag
232 mz_zip_array m_central_dir;
233 mz_zip_array m_central_dir_offsets;
234 mz_zip_array m_sorted_central_dir_offsets;
236 // The flags passed in when the archive is initially opened.
239 // MZ_TRUE if the archive has a zip64 end of central directory headers, etc.
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;
245 // These fields are used by the file, FILE, memory, and memory/heap read/write helpers.
247 mz_uint64 m_file_archive_start_ofs;
251 size_t m_mem_capacity;
254 #define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size
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)
259 MZ_ASSERT(index < pArray->m_size);
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)]
264 #define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index]
267 static MZ_FORCEINLINE void mz_zip_array_init(mz_zip_array *pArray, mz_uint32 element_size)
269 memset(pArray, 0, sizeof(mz_zip_array));
270 pArray->m_element_size = element_size;
273 static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray)
275 pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p);
276 memset(pArray, 0, sizeof(mz_zip_array));
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)
282 size_t new_capacity = min_new_capacity;
283 MZ_ASSERT(pArray->m_element_size);
284 if (pArray->m_capacity >= min_new_capacity)
288 new_capacity = MZ_MAX(1, pArray->m_capacity);
289 while (new_capacity < min_new_capacity)
292 if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity)))
294 pArray->m_p = pNew_p;
295 pArray->m_capacity = new_capacity;
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)
301 if (new_capacity > pArray->m_capacity)
303 if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing))
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)
311 if (new_size > pArray->m_capacity)
313 if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing))
316 pArray->m_size = new_size;
320 static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n)
322 return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE);
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)
327 size_t orig_size = pArray->m_size;
328 if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE))
330 memcpy((mz_uint8 *)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size);
334 #ifndef MINIZ_NO_TIME
335 static MZ_TIME_T mz_zip_dos_to_time_t(int dos_time, int dos_date)
338 memset(&tm, 0, sizeof(tm));
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;
349 static void mz_zip_time_t_to_dos_time(MZ_TIME_T time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date)
353 struct tm *tm = &tm_struct;
354 errno_t err = localtime_s(tm, &time);
362 struct tm *tm = localtime(&time);
363 #endif // #ifdef_MSC_VER
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);
369 #ifndef MINIZ_NO_STDIO
370 static mz_bool mz_zip_get_file_modified_time(const char *pFilename, MZ_TIME_T *pTime)
372 struct MZ_FILE_STAT_STRUCT file_stat;
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)
378 *pTime = file_stat.st_mtime;
383 static mz_bool mz_zip_set_file_times(const char *pFilename, MZ_TIME_T access_time, MZ_TIME_T modified_time)
387 memset(&t, 0, sizeof(t));
388 t.actime = access_time;
389 t.modtime = modified_time;
391 return !utime(pFilename, &t);
393 #endif // #ifndef MINIZ_NO_STDIO
395 #endif // #ifndef MINIZ_NO_TIME
397 static MZ_FORCEINLINE mz_bool mz_zip_set_error(mz_zip_archive *pZip, mz_zip_error err_num)
400 pZip->m_last_error = err_num;
404 static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint 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);
411 pZip->m_pAlloc = miniz_def_alloc_func;
413 pZip->m_pFree = miniz_def_free_func;
414 if (!pZip->m_pRealloc)
415 pZip->m_pRealloc = miniz_def_realloc_func;
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;
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);
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;
433 pZip->m_zip_mode = MZ_ZIP_MODE_READING;
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)
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);
449 if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR)))
454 return (pL == pE) ? (l_len < r_len) : (l < r);
457 #define MZ_SWAP_UINT32(a, b) \
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)
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;
473 mz_uint32 start, end;
474 const mz_uint32 size = pZip->m_total_files;
479 pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0);
481 start = (size - 2U) >> 1U;
484 mz_uint64 child, root = start;
487 if ((child = (root << 1U) + 1U) >= size)
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]))
492 MZ_SWAP_UINT32(pIndices[root], pIndices[child]);
503 mz_uint64 child, root = 0;
504 MZ_SWAP_UINT32(pIndices[end], pIndices[0]);
507 if ((child = (root << 1U) + 1U) >= end)
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]))
512 MZ_SWAP_UINT32(pIndices[root], pIndices[child]);
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)
521 mz_int64 cur_file_ofs;
522 mz_uint32 buf_u32[4096 / sizeof(mz_uint32)];
523 mz_uint8 *pBuf = (mz_uint8 *)buf_u32;
525 // Basic sanity checks - reject files which are too small
526 if (pZip->m_archive_size < record_size)
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);
533 int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs);
535 if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n)
538 for (i = n - 4; i >= 0; --i)
540 mz_uint s = MZ_READ_LE32(pBuf + i);
543 if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size)
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)))
558 cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0);
561 *pOfs = cur_file_ofs;
565 static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint flags)
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;
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;
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;
581 mz_uint64 zip64_end_of_central_dir_ofs = 0;
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);
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);
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);
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);
597 if (cur_file_ofs >= (MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE))
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)
601 if (MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG)
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);
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)
609 if (MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIG_OFS) == MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG)
611 pZip->m_pState->m_zip64 = MZ_TRUE;
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);
625 if (pZip->m_pState->m_zip64)
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);
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);
636 if (zip64_total_num_of_disks != 1U)
637 return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK);
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);
643 pZip->m_total_files = (mz_uint32)zip64_cdir_total_entries;
645 if (zip64_cdir_total_entries_on_this_disk > MZ_UINT32_MAX)
646 return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
648 cdir_entries_on_this_disk = (mz_uint32)zip64_cdir_total_entries_on_this_disk;
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);
654 cdir_size = (mz_uint32)zip64_size_of_central_directory;
656 num_this_disk = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS);
658 cdir_disk_index = MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS);
660 cdir_ofs = MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_OFS_OFS);
663 if (pZip->m_total_files != cdir_entries_on_this_disk)
664 return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK);
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);
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);
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);
675 pZip->m_central_directory_file_ofs = cdir_ofs;
677 if (pZip->m_total_files)
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);
685 if (sort_central_dir)
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);
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);
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)
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;
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);
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);
706 if (sort_central_dir)
707 MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i;
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);
715 if ((!pZip->m_pState->m_zip64_has_extended_info_fields) &&
717 (MZ_MAX(MZ_MAX(comp_size, decomp_size), local_header_ofs) == MZ_UINT32_MAX))
719 // Attempt to find zip64 extended information field in the entry's extra data
720 mz_uint32 extra_size_remaining = ext_data_size;
722 if (extra_size_remaining)
724 const mz_uint8 *pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size;
728 if (extra_size_remaining < (sizeof(mz_uint16) * 2))
729 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
731 mz_uint32 field_id = MZ_READ_LE16(pExtra_data);
732 mz_uint32 field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16));
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);
737 if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID)
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;
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);
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))
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);
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);
762 if (comp_size != MZ_UINT32_MAX)
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);
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);
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);
775 n -= total_header_size;
776 p += total_header_size;
780 if (sort_central_dir)
781 mz_zip_reader_sort_central_dir_offsets_by_filename(pZip);
786 void mz_zip_zero_struct(mz_zip_archive *pZip)
792 static mz_bool mz_zip_reader_end_internal(mz_zip_archive *pZip, mz_bool set_last_error)
794 mz_bool status = MZ_TRUE;
799 if ((!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING))
802 pZip->m_last_error = MZ_ZIP_INVALID_PARAMETER;
809 mz_zip_internal_state *pState = pZip->m_pState;
810 pZip->m_pState = NULL;
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);
816 #ifndef MINIZ_NO_STDIO
819 if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE)
821 if (MZ_FCLOSE(pState->m_pFile) == EOF)
824 pZip->m_last_error = MZ_ZIP_FILE_CLOSE_FAILED;
828 pState->m_pFile = NULL;
830 #endif // #ifndef MINIZ_NO_STDIO
832 pZip->m_pFree(pZip->m_pAlloc_opaque, pState);
834 pZip->m_zip_mode = MZ_ZIP_MODE_INVALID;
839 mz_bool mz_zip_reader_end(mz_zip_archive *pZip)
841 return mz_zip_reader_end_internal(pZip, MZ_TRUE);
844 static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n)
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);
852 mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint flags)
854 if ((!pZip) || (!pZip->m_pRead))
855 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
857 if (!mz_zip_reader_init_internal(pZip, flags))
860 pZip->m_zip_type = MZ_ZIP_TYPE_USER;
861 pZip->m_archive_size = size;
863 if (!mz_zip_reader_read_central_dir(pZip, flags))
865 mz_zip_reader_end_internal(pZip, MZ_FALSE);
872 mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint flags)
875 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
877 if (size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
878 return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE);
880 if (!mz_zip_reader_init_internal(pZip, flags))
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;
889 pZip->m_pState->m_pMem = const_cast<void *>(pMem);
891 pZip->m_pState->m_pMem = (void *)pMem;
894 pZip->m_pState->m_mem_size = size;
896 if (!mz_zip_reader_read_central_dir(pZip, flags))
898 mz_zip_reader_end_internal(pZip, MZ_FALSE);
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)
908 mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
909 mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile);
911 file_ofs += pZip->m_pState->m_file_archive_start_ofs;
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))))
916 return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile);
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)
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);
925 MZ_FILE *pFile = MZ_FOPEN(pFilename, "rb");
927 return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED);
929 file_size = archive_size;
932 if (MZ_FSEEK64(pFile, 0, SEEK_END))
935 return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED);
938 file_size = MZ_FTELL64(pFile);
941 // TODO: Better sanity check archive_size and the # of actual remaining bytes
943 if (file_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
944 return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE);
946 if (!mz_zip_reader_init_internal(pZip, flags))
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;
959 if (!mz_zip_reader_read_central_dir(pZip, flags))
961 mz_zip_reader_end_internal(pZip, MZ_FALSE);
968 mz_bool mz_zip_reader_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint64 archive_size, mz_uint flags)
970 mz_uint64 cur_file_ofs;
972 if ((!pZip) || (!pFile))
973 return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED);
975 cur_file_ofs = MZ_FTELL64(pFile);
979 if (MZ_FSEEK64(pFile, 0, SEEK_END))
980 return mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED);
982 archive_size = MZ_FTELL64(pFile) - cur_file_ofs;
984 if (archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
985 return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE);
988 if (!mz_zip_reader_init_internal(pZip, flags))
991 pZip->m_zip_type = MZ_ZIP_TYPE_CFILE;
992 pZip->m_pRead = mz_zip_file_read_func;
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;
999 if (!mz_zip_reader_read_central_dir(pZip, flags))
1001 mz_zip_reader_end_internal(pZip, MZ_FALSE);
1008 #endif // #ifndef MINIZ_NO_STDIO
1010 static MZ_FORCEINLINE const mz_uint8 *mz_zip_get_cdh(mz_zip_archive *pZip, mz_uint file_index)
1012 if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files))
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));
1017 mz_bool mz_zip_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index)
1020 const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index);
1023 mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
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;
1031 mz_bool mz_zip_is_file_supported(mz_zip_archive *pZip, mz_uint file_index)
1036 const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index);
1039 mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
1043 method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS);
1044 bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS);
1046 if ((method != 0) && (method != MZ_DEFLATED))
1048 mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_METHOD);
1052 if (bit_flag & (MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED | MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION))
1054 mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION);
1058 if (bit_flag & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG)
1060 mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE);
1067 mz_bool mz_zip_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index)
1069 mz_uint filename_len, attribute_mapping_id, external_attr;
1070 const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index);
1073 mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
1077 filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
1080 if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/')
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;
1090 external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS);
1091 if ((external_attr & MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG) != 0)
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)
1102 const mz_uint8 *p = pCentral_dir_header;
1104 if (pFound_zip64_extra_data)
1105 *pFound_zip64_extra_data = MZ_FALSE;
1107 if ((!p) || (!pStat))
1108 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
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));
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);
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';
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';
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);
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)
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);
1151 if (extra_size_remaining)
1153 const mz_uint8 *pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
1157 if (extra_size_remaining < (sizeof(mz_uint16) * 2))
1158 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
1160 mz_uint32 field_id = MZ_READ_LE16(pExtra_data);
1161 mz_uint32 field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16));
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);
1166 if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID)
1168 const mz_uint8 *pField_data = pExtra_data + sizeof(mz_uint16) * 2;
1169 mz_uint32 field_data_remaining = field_data_size;
1171 if (pFound_zip64_extra_data)
1172 *pFound_zip64_extra_data = MZ_TRUE;
1174 if (pStat->m_uncomp_size == MZ_UINT32_MAX)
1176 if (field_data_remaining < sizeof(mz_uint64))
1177 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
1179 pStat->m_uncomp_size = MZ_READ_LE64(pField_data);
1180 pField_data += sizeof(mz_uint64);
1181 field_data_remaining -= sizeof(mz_uint64);
1184 if (pStat->m_comp_size == MZ_UINT32_MAX)
1186 if (field_data_remaining < sizeof(mz_uint64))
1187 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
1189 pStat->m_comp_size = MZ_READ_LE64(pField_data);
1190 pField_data += sizeof(mz_uint64);
1191 field_data_remaining -= sizeof(mz_uint64);
1194 if (pStat->m_local_header_ofs == MZ_UINT32_MAX)
1196 if (field_data_remaining < sizeof(mz_uint64))
1197 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
1199 pStat->m_local_header_ofs = MZ_READ_LE64(pField_data);
1200 pField_data += sizeof(mz_uint64);
1201 field_data_remaining -= sizeof(mz_uint64);
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);
1216 static MZ_FORCEINLINE mz_bool mz_zip_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags)
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]))
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)
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);
1236 if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR)))
1241 return (pL == pE) ? (int)(l_len - r_len) : (l - r);
1244 static mz_bool mz_zip_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename, mz_uint32 *pIndex)
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);
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;
1264 mz_int64 m = l + ((h - l) >> 1);
1265 uint file_index = pIndices[(uint)m];
1267 int comp = mz_zip_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len);
1271 *pIndex = file_index;
1281 return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND);
1284 mz_bool mz_zip_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags, mz_uint32 *pIndex)
1287 size_t name_len, comment_len;
1292 if ((!pZip) || (!pZip->m_pState) || (!pName))
1293 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
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))
1300 return mz_zip_locate_file_binary_search(pZip, pName, pIndex);
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);
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);
1312 for (file_index = 0; file_index < pZip->m_total_files; file_index++)
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)
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)))
1326 if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len))
1328 int ofs = filename_len - 1;
1331 if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':'))
1333 } while (--ofs >= 0);
1336 filename_len -= ofs;
1338 if ((filename_len == name_len) && (mz_zip_string_equal(pName, pFilename, filename_len, flags)))
1340 *pIndex = file_index;
1345 return mz_zip_set_error(pZip, MZ_ZIP_FILE_NOT_FOUND);
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)
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;
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;
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);
1361 if (!mz_zip_file_stat(pZip, file_index, &file_stat))
1364 // A directory or zero length file
1365 if ((file_stat.m_is_directory) || (!file_stat.m_comp_size))
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);
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);
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);
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);
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);
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);
1393 if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method))
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);
1399 #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS
1400 if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) == 0)
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);
1410 // Decompress the file either directly from memory or from a file input buffer.
1411 tinfl_init(&inflator);
1413 if (pZip->m_pState->m_pMem)
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;
1420 else if (pUser_read_buf)
1422 // Use a user provided read buffer.
1423 if (!user_read_buf_size)
1425 pRead_buf = (mz_uint8 *)pUser_read_buf;
1426 read_buf_size = user_read_buf_size;
1428 comp_remaining = file_stat.m_comp_size;
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);
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);
1441 comp_remaining = file_stat.m_comp_size;
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))
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)
1453 status = TINFL_STATUS_FAILED;
1454 mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED);
1457 cur_file_ofs += read_buf_avail;
1458 comp_remaining -= read_buf_avail;
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);
1468 if (status == TINFL_STATUS_DONE)
1470 // Make sure the entire file was decompressed, and check its CRC.
1471 if (out_buf_ofs != file_stat.m_uncomp_size)
1473 mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE);
1474 status = TINFL_STATUS_FAILED;
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)
1479 mz_zip_set_error(pZip, MZ_ZIP_CRC_CHECK_FAILED);
1480 status = TINFL_STATUS_FAILED;
1485 if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf))
1486 pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
1488 return status == TINFL_STATUS_DONE;
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)
1493 mz_uint32 file_index;
1494 if (!mz_zip_locate_file(pZip, pFilename, NULL, flags, &file_index))
1496 return mz_zip_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size);
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)
1501 return mz_zip_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0);
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)
1506 return mz_zip_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0);
1509 void *mz_zip_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags)
1511 mz_uint64 comp_size, uncomp_size, alloc_size;
1512 const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index);
1520 mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
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);
1527 alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size;
1528 if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF))
1530 mz_zip_set_error(pZip, MZ_ZIP_INTERNAL_ERROR);
1534 if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size)))
1536 mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
1540 if (!mz_zip_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags))
1542 pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
1547 *pSize = (size_t)alloc_size;
1551 void *mz_zip_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags)
1553 mz_uint32 file_index;
1554 if (!mz_zip_locate_file(pZip, pFilename, NULL, flags, &file_index))
1562 return mz_zip_extract_to_heap(pZip, file_index, pSize, flags);
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)
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;
1576 if ((!pZip) || (!pZip->m_pState) || (!pCallback) || (!pZip->m_pRead))
1577 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
1579 if (!mz_zip_file_stat(pZip, file_index, &file_stat))
1582 // A directory or zero length file
1583 if ((file_stat.m_is_directory) || (!file_stat.m_comp_size))
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);
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);
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);
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);
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);
1606 // Decompress the file either directly from memory or from a file input buffer.
1607 if (pZip->m_pState->m_pMem)
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;
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);
1620 comp_remaining = file_stat.m_comp_size;
1623 if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method))
1625 // The file is stored or the caller has requested the compressed data.
1626 if (pZip->m_pState->m_pMem)
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);
1631 if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size)
1633 mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED);
1634 status = TINFL_STATUS_FAILED;
1636 else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))
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);
1643 cur_file_ofs += file_stat.m_comp_size;
1644 out_buf_ofs += file_stat.m_comp_size;
1649 while (comp_remaining)
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)
1654 mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
1655 status = TINFL_STATUS_FAILED;
1659 #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS
1660 if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))
1662 file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail);
1666 if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail)
1668 mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED);
1669 status = TINFL_STATUS_FAILED;
1673 cur_file_ofs += read_buf_avail;
1674 out_buf_ofs += read_buf_avail;
1675 comp_remaining -= read_buf_avail;
1681 tinfl_decompressor inflator;
1682 tinfl_init(&inflator);
1684 if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE)))
1686 mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
1687 status = TINFL_STATUS_FAILED;
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))
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)
1700 mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
1701 status = TINFL_STATUS_FAILED;
1704 cur_file_ofs += read_buf_avail;
1705 comp_remaining -= read_buf_avail;
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;
1716 if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size)
1718 mz_zip_set_error(pZip, MZ_ZIP_WRITE_CALLBACK_FAILED);
1719 status = TINFL_STATUS_FAILED;
1723 #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS
1724 file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size);
1726 if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size)
1728 mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED);
1729 status = TINFL_STATUS_FAILED;
1733 } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT));
1737 if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)))
1739 // Make sure the entire file was decompressed, and check its CRC.
1740 if (out_buf_ofs != file_stat.m_uncomp_size)
1742 mz_zip_set_error(pZip, MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE);
1743 status = TINFL_STATUS_FAILED;
1745 #ifndef MINIZ_DISABLE_ZIP_READER_CRC32_CHECKS
1746 else if (file_crc32 != file_stat.m_crc32)
1748 mz_zip_set_error(pZip, MZ_ZIP_DECOMPRESSION_FAILED);
1749 status = TINFL_STATUS_FAILED;
1754 if (!pZip->m_pState->m_pMem)
1755 pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
1758 pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf);
1760 return status == TINFL_STATUS_DONE;
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)
1765 mz_uint32 file_index;
1766 if (!mz_zip_locate_file(pZip, pFilename, NULL, flags, &file_index))
1769 return mz_zip_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags);
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)
1777 return MZ_FWRITE(pBuf, 1, n, (MZ_FILE *)pOpaque);
1780 mz_bool mz_zip_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags)
1783 mz_zip_archive_file_stat file_stat;
1786 if (!mz_zip_file_stat(pZip, file_index, &file_stat))
1789 if ((file_stat.m_is_directory) || (!file_stat.m_is_supported))
1790 return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE);
1792 pFile = MZ_FOPEN(pDst_filename, "wb");
1794 return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED);
1796 status = mz_zip_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags);
1798 if (MZ_FCLOSE(pFile) == EOF)
1801 mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED);
1806 #if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO)
1808 mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time);
1814 mz_bool mz_zip_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags)
1816 mz_uint32 file_index;
1817 if (!mz_zip_locate_file(pZip, pArchive_filename, NULL, flags, &file_index))
1820 return mz_zip_extract_to_file(pZip, file_index, pDst_filename, flags);
1823 mz_bool mz_zip_extract_to_cfile(mz_zip_archive *pZip, mz_uint file_index, MZ_FILE *pFile, mz_uint flags)
1825 mz_zip_archive_file_stat file_stat;
1827 if (!mz_zip_file_stat(pZip, file_index, &file_stat))
1830 if ((file_stat.m_is_directory) || (!file_stat.m_is_supported))
1831 return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE);
1833 return mz_zip_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags);
1836 mz_bool mz_zip_extract_file_to_cfile(mz_zip_archive *pZip, const char *pArchive_filename, MZ_FILE *pFile, mz_uint flags)
1838 mz_uint32 file_index;
1839 if (!mz_zip_locate_file(pZip, pArchive_filename, NULL, flags, &file_index))
1842 return mz_zip_extract_to_cfile(pZip, file_index, pFile, flags);
1844 #endif // #ifndef MINIZ_NO_STDIO
1846 static size_t mz_zip_compute_crc32_callback(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n)
1848 mz_uint32 *p = (mz_uint32 *)pOpaque;
1850 *p = (mz_uint32)mz_crc32(*p, (const mz_uint8 *)pBuf, n);
1854 mz_bool mz_zip_validate_file(mz_zip_archive *pZip, mz_uint file_index, mz_uint flags)
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;
1870 mz_zip_array file_data_array;
1871 mz_zip_array_init(&file_data_array, 1);
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);
1876 if (file_index > pZip->m_total_files)
1877 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
1879 pState = pZip->m_pState;
1881 pCentral_dir_header = mz_zip_get_cdh(pZip, file_index);
1883 if (!mz_zip_file_stat_internal(pZip, file_index, pCentral_dir_header, &file_stat, &found_zip64_ext_data_in_cdir))
1886 // A directory or zero length file
1887 if ((file_stat.m_is_directory) || (!file_stat.m_uncomp_size))
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);
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);
1898 if (!file_stat.m_is_supported)
1899 return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_FEATURE);
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);
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);
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;
1917 if (local_header_filename_len != strlen(file_stat.m_filename))
1918 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
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);
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);
1926 if (local_header_filename_len)
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)
1930 mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
1931 goto handle_failure;
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)
1937 mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED);
1938 goto handle_failure;
1942 if ((local_header_extra_len) && ((local_header_comp_size == MZ_UINT32_MAX) || (local_header_uncomp_size == MZ_UINT32_MAX)))
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)
1946 mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
1947 goto handle_failure;
1950 mz_uint32 extra_size_remaining = local_header_extra_len;
1951 const mz_uint8 *pExtra_data = (const mz_uint8 *)file_data_array.m_p;
1955 mz_uint32 field_id, field_data_size, field_total_size;
1957 if (extra_size_remaining < (sizeof(mz_uint16) * 2))
1958 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
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;
1964 if (field_total_size > extra_size_remaining)
1965 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
1967 if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID)
1969 const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32);
1971 if (field_data_size < sizeof(mz_uint64) * 2)
1973 mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
1974 goto handle_failure;
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));
1980 found_zip64_ext_data_in_ldir = MZ_TRUE;
1984 pExtra_data += field_total_size;
1985 extra_size_remaining -= field_total_size;
1986 } while (extra_size_remaining);
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))
1993 mz_uint8 descriptor_buf[32];
1995 mz_uint32 num_descriptor_uint32s = ((pState->m_zip64) || (found_zip64_ext_data_in_ldir)) ? 6 : 4;
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))
1999 mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
2000 goto handle_failure;
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;
2006 mz_uint32 file_crc32 = MZ_READ_LE32(pSrc);
2007 mz_uint64 comp_size = 0, uncomp_size = 0;
2009 if ((pState->m_zip64) || (found_zip64_ext_data_in_ldir))
2011 comp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32));
2012 uncomp_size = MZ_READ_LE64(pSrc + sizeof(mz_uint32) + sizeof(mz_uint64));
2016 comp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32));
2017 uncomp_size = MZ_READ_LE32(pSrc + sizeof(mz_uint32) + sizeof(mz_uint32));
2020 if ((file_crc32 != file_stat.m_crc32) || (comp_size != file_stat.m_comp_size) || (uncomp_size != file_stat.m_uncomp_size))
2022 mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED);
2023 goto handle_failure;
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))
2030 mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED);
2031 goto handle_failure;
2035 mz_zip_array_clear(pZip, &file_data_array);
2037 if ((flags & MZ_ZIP_FLAG_VALIDATE_HEADERS_ONLY) == 0)
2039 if (!mz_zip_extract_to_callback(pZip, file_index, mz_zip_compute_crc32_callback, &uncomp_crc32, 0))
2042 // 1 more check to be sure, although the extract checks too.
2043 if (uncomp_crc32 != file_stat.m_crc32)
2045 mz_zip_set_error(pZip, MZ_ZIP_VALIDATION_FAILED);
2053 mz_zip_array_clear(pZip, &file_data_array);
2057 mz_bool mz_zip_validate_archive(mz_zip_archive *pZip, mz_uint flags)
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);
2063 pState = pZip->m_pState;
2065 // Basic sanity checks
2066 if (!pState->m_zip64)
2068 if (pZip->m_total_files > MZ_UINT16_MAX)
2069 return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
2071 if (pZip->m_archive_size > MZ_UINT32_MAX)
2072 return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
2076 if (pZip->m_total_files >= MZ_UINT32_MAX)
2077 return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
2079 if (pState->m_central_dir.m_size >= MZ_UINT32_MAX)
2080 return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
2083 for (uint i = 0; i < pZip->m_total_files; i++)
2085 if (MZ_ZIP_FLAG_VALIDATE_LOCATE_FILE_FLAG & flags)
2087 mz_uint32 found_index;
2088 mz_zip_archive_file_stat stat;
2090 if (!mz_zip_file_stat(pZip, i, &stat))
2093 if (!mz_zip_locate_file(pZip, stat.m_filename, NULL, 0, &found_index))
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);
2101 if (!mz_zip_validate_file(pZip, i, flags))
2108 mz_bool mz_zip_validate_mem_archive(const void *pMem, size_t size, mz_uint flags, mz_zip_error *pErr)
2110 mz_bool success = MZ_TRUE;
2112 mz_zip_error actual_err = MZ_ZIP_NO_ERROR;
2114 if ((!pMem) || (!size))
2117 *pErr = MZ_ZIP_INVALID_PARAMETER;
2121 mz_zip_zero_struct(&zip);
2123 if (!mz_zip_reader_init_mem(&zip, pMem, size, flags))
2126 *pErr = zip.m_last_error;
2130 if (!mz_zip_validate_archive(&zip, flags))
2132 actual_err = zip.m_last_error;
2136 if (!mz_zip_reader_end_internal(&zip, success))
2139 actual_err = zip.m_last_error;
2149 #ifndef MINIZ_NO_STDIO
2150 mz_bool mz_zip_validate_file_archive(const char *pFilename, mz_uint flags, mz_zip_error *pErr)
2152 mz_bool success = MZ_TRUE;
2154 mz_zip_error actual_err = MZ_ZIP_NO_ERROR;
2159 *pErr = MZ_ZIP_INVALID_PARAMETER;
2163 mz_zip_zero_struct(&zip);
2165 if (!mz_zip_reader_init_file(&zip, pFilename, flags, 0, 0))
2168 *pErr = zip.m_last_error;
2172 if (!mz_zip_validate_archive(&zip, flags))
2174 actual_err = zip.m_last_error;
2178 if (!mz_zip_reader_end_internal(&zip, success))
2181 actual_err = zip.m_last_error;
2190 #endif // #ifndef MINIZ_NO_STDIO
2192 // ------------------- .ZIP archive writing
2194 #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS
2196 static MZ_FORCEINLINE void mz_write_le16(mz_uint8 *p, mz_uint16 v)
2199 p[1] = (mz_uint8)(v >> 8);
2201 static MZ_FORCEINLINE void mz_write_le32(mz_uint8 *p, mz_uint32 v)
2204 p[1] = (mz_uint8)(v >> 8);
2205 p[2] = (mz_uint8)(v >> 16);
2206 p[3] = (mz_uint8)(v >> 24);
2208 static MZ_FORCEINLINE void mz_write_le64(mz_uint8 *p, mz_uint64 v)
2210 mz_write_le32(p, (mz_uint32)v);
2211 mz_write_le32(p + sizeof(mz_uint32), (mz_uint32)(v >> 32));
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))
2218 static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n)
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);
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))
2230 mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE);
2234 if (new_size > pState->m_mem_capacity)
2237 size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity);
2239 while (new_capacity < new_size)
2242 if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity)))
2244 mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
2248 pState->m_pMem = pNew_block;
2249 pState->m_mem_capacity = new_capacity;
2251 memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n);
2252 pState->m_mem_size = (size_t)new_size;
2256 static mz_bool mz_zip_writer_end_internal(mz_zip_archive *pZip, mz_bool set_last_error)
2258 mz_zip_internal_state *pState;
2259 mz_bool status = MZ_TRUE;
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)))
2264 mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
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);
2274 #ifndef MINIZ_NO_STDIO
2275 if (pState->m_pFile)
2277 if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE)
2279 if (MZ_FCLOSE(pState->m_pFile) == EOF)
2282 mz_zip_set_error(pZip, MZ_ZIP_FILE_CLOSE_FAILED);
2287 pState->m_pFile = NULL;
2289 #endif // #ifndef MINIZ_NO_STDIO
2291 if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem))
2293 pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem);
2294 pState->m_pMem = NULL;
2297 pZip->m_pFree(pZip->m_pAlloc_opaque, pState);
2298 pZip->m_zip_mode = MZ_ZIP_MODE_INVALID;
2302 mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size, mz_uint flags)
2304 mz_bool zip64 = (flags & MZ_ZIP_FLAG_WRITE_ZIP64) != 0;
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);
2309 if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING)
2312 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
2315 if (pZip->m_file_offset_alignment)
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);
2322 if (!pZip->m_pAlloc)
2323 pZip->m_pAlloc = miniz_def_alloc_func;
2325 pZip->m_pFree = miniz_def_free_func;
2326 if (!pZip->m_pRealloc)
2327 pZip->m_pRealloc = miniz_def_realloc_func;
2329 pZip->m_archive_size = existing_size;
2330 pZip->m_central_directory_file_ofs = 0;
2331 pZip->m_total_files = 0;
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);
2336 memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state));
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));
2342 pZip->m_pState->m_zip64 = zip64;
2343 pZip->m_pState->m_zip64_has_extended_info_fields = zip64;
2345 pZip->m_zip_type = MZ_ZIP_TYPE_USER;
2346 pZip->m_zip_mode = MZ_ZIP_MODE_WRITING;
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)
2353 pZip->m_pWrite = mz_zip_heap_write_func;
2355 if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING)
2356 pZip->m_pRead = mz_zip_mem_read_func;
2358 pZip->m_pIO_opaque = pZip;
2360 if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning, flags))
2363 pZip->m_zip_type = MZ_ZIP_TYPE_HEAP;
2365 if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning)))
2367 if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size)))
2369 mz_zip_writer_end_internal(pZip, MZ_FALSE);
2370 return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
2372 pZip->m_pState->m_mem_capacity = initial_allocation_size;
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)
2381 mz_zip_archive *pZip = (mz_zip_archive *)pOpaque;
2382 mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile);
2384 file_ofs += pZip->m_pState->m_file_archive_start_ofs;
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))))
2388 mz_zip_set_error(pZip, MZ_ZIP_FILE_SEEK_FAILED);
2392 return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile);
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)
2399 pZip->m_pWrite = mz_zip_file_write_func;
2401 if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING)
2402 pZip->m_pRead = mz_zip_file_read_func;
2404 pZip->m_pIO_opaque = pZip;
2406 if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning, flags))
2409 if (NULL == (pFile = MZ_FOPEN(pFilename, (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING) ? "w+b" : "wb")))
2411 mz_zip_writer_end(pZip);
2412 return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED);
2415 pZip->m_pState->m_pFile = pFile;
2416 pZip->m_zip_type = MZ_ZIP_TYPE_FILE;
2418 if (size_to_reserve_at_beginning)
2420 mz_uint64 cur_ofs = 0;
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)
2430 mz_zip_writer_end(pZip);
2431 return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
2434 size_to_reserve_at_beginning -= n;
2435 } while (size_to_reserve_at_beginning);
2441 mz_bool mz_zip_writer_init_cfile(mz_zip_archive *pZip, MZ_FILE *pFile, mz_uint flags)
2443 pZip->m_pWrite = mz_zip_file_write_func;
2445 if (flags & MZ_ZIP_FLAG_WRITE_ALLOW_READING)
2446 pZip->m_pRead = mz_zip_file_read_func;
2448 pZip->m_pIO_opaque = pZip;
2450 if (!mz_zip_writer_init(pZip, 0, flags))
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;
2459 #endif // #ifndef MINIZ_NO_STDIO
2461 mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename, mz_uint flags)
2463 mz_zip_internal_state *pState;
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);
2468 if (flags & MZ_ZIP_FLAG_WRITE_ZIP64)
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);
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)
2478 if (pZip->m_total_files == MZ_UINT32_MAX)
2479 return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
2483 if (pZip->m_total_files == MZ_UINT16_MAX)
2484 return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
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);
2490 pState = pZip->m_pState;
2492 if (pState->m_pFile)
2494 #ifdef MINIZ_NO_STDIO
2496 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
2498 if (pZip->m_pIO_opaque != pZip)
2499 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
2501 if (pZip->m_zip_type == MZ_ZIP_TYPE_FILE)
2504 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
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)))
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);
2515 pZip->m_pWrite = mz_zip_file_write_func;
2516 #endif // #ifdef MINIZ_NO_STDIO
2518 else if (pState->m_pMem)
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);
2524 pState->m_mem_capacity = pState->m_mem_size;
2525 pZip->m_pWrite = mz_zip_heap_write_func;
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);
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;
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);
2541 pZip->m_zip_mode = MZ_ZIP_MODE_WRITING;
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)
2549 return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0);
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;
2559 static mz_bool mz_zip_writer_add_put_buf_callback(const void *pBuf, int len, void *pUser)
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)
2565 pState->m_cur_archive_file_ofs += len;
2566 pState->m_comp_size += len;
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)
2574 mz_uint8 *pDst = pBuf;
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;
2580 mz_uint32 field_size = 0;
2584 MZ_WRITE_LE64(pDst, *pUncomp_size);
2585 pDst += sizeof(mz_uint64);
2586 field_size += sizeof(mz_uint64);
2591 MZ_WRITE_LE64(pDst, *pComp_size);
2592 pDst += sizeof(mz_uint64);
2593 field_size += sizeof(mz_uint64);
2596 if (pLocal_header_ofs)
2598 MZ_WRITE_LE64(pDst, *pLocal_header_ofs);
2599 pDst += sizeof(mz_uint64);
2600 field_size += sizeof(mz_uint64);
2603 MZ_WRITE_LE16(pBuf + 2, field_size);
2605 return (mz_uint32)(pDst - pBuf);
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)
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);
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)
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));
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)
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];
2662 if (!pZip->m_pState->m_zip64)
2664 if (local_header_ofs > 0xFFFFFFFF)
2665 return mz_zip_set_error(pZip, MZ_ZIP_FILE_TOO_LARGE);
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);
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);
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, ¢ral_dir_ofs, 1)))
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);
2689 static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name)
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 == '/')
2695 while (*pArchive_name)
2697 if ((*pArchive_name == '\\') || (*pArchive_name == ':'))
2706 static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip)
2709 if (!pZip->m_file_offset_alignment)
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));
2715 static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n)
2718 memset(buf, 0, MZ_MIN(sizeof(buf), n));
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);
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)
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];
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));
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);
2753 pState = pZip->m_pState;
2755 if (pState->m_zip64)
2757 if (pZip->m_total_files == MZ_UINT32_MAX)
2758 return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
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);
2768 if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size))
2769 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
2771 if (!mz_zip_writer_validate_archive_name(pArchive_name))
2772 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME);
2774 #ifndef MINIZ_NO_TIME
2778 mz_zip_time_t_to_dos_time(cur_time, &dos_time, &dos_date);
2780 #endif // #ifndef MINIZ_NO_TIME
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);
2786 num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip);
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);
2792 if (!pState->m_zip64)
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);
2799 if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/'))
2801 // Set DOS Subdirectory attribute bit.
2802 ext_attributes |= MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG;
2804 // Subdirectories cannot contain data.
2805 if ((buf_size) || (uncomp_size))
2806 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
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);
2813 if ((!store_data_uncompressed) && (buf_size))
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);
2819 if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header)))
2821 pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
2825 local_dir_header_ofs += num_alignment_padding_bytes;
2826 if (pZip->m_file_offset_alignment)
2828 MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0);
2830 cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header);
2832 MZ_CLEAR_OBJ(local_dir_header);
2834 if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size)
2836 pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
2837 return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
2839 cur_archive_file_ofs += archive_name_size;
2841 extra_data_file_ofs = cur_archive_file_ofs;
2842 if (pState->m_zip64)
2844 // Reserve space for an extra data field for zip64
2845 cur_archive_file_ofs += MZ_ZIP64_MAX_LOCAL_EXTRA_FIELD_SIZE;
2848 if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA))
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)
2855 store_data_uncompressed = MZ_TRUE;
2859 if (store_data_uncompressed)
2861 if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size)
2863 pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
2864 return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
2867 cur_archive_file_ofs += buf_size;
2868 comp_size = buf_size;
2870 if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)
2871 method = MZ_DEFLATED;
2875 mz_zip_writer_add_state state;
2877 state.m_pZip = pZip;
2878 state.m_cur_archive_file_ofs = cur_archive_file_ofs;
2879 state.m_comp_size = 0;
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))
2884 pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
2885 return mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED);
2888 comp_size = state.m_comp_size;
2889 cur_archive_file_ofs = state.m_cur_archive_file_ofs;
2891 method = MZ_DEFLATED;
2894 pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
2897 if (pState->m_zip64)
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));
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);
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);
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);
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)
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);
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);
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);
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))
2933 pZip->m_total_files++;
2934 pZip->m_archive_size = cur_archive_file_ofs;
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)
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;
2952 if ((int)level_and_flags < 0)
2953 level_and_flags = MZ_DEFAULT_LEVEL;
2954 level = level_and_flags & 0xF;
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);
2960 pState = pZip->m_pState;
2962 if ((!pState->m_zip64) && (uncomp_size > MZ_UINT32_MAX))
2964 // Source file is too large for non-zip64
2965 return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
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);
2972 if (!mz_zip_writer_validate_archive_name(pArchive_name))
2973 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_FILENAME);
2975 if (pState->m_zip64)
2977 if (pZip->m_total_files == MZ_UINT32_MAX)
2978 return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
2982 if (pZip->m_total_files == MZ_UINT16_MAX)
2983 return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
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);
2990 num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip);
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);
2996 if (!pState->m_zip64)
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);
3003 #ifndef MINIZ_NO_TIME
3006 mz_zip_time_t_to_dos_time(*pFile_time, &dos_time, &dos_date);
3010 if (uncomp_size <= 3)
3013 if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header)))
3015 return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
3018 local_dir_header_ofs += num_alignment_padding_bytes;
3019 if (pZip->m_file_offset_alignment)
3021 MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0);
3023 cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header);
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)
3028 return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
3030 cur_archive_file_ofs += archive_name_size;
3032 extra_data_file_ofs = cur_archive_file_ofs;
3033 if (pState->m_zip64)
3035 // Reserve space for an extra data field for zip64
3036 cur_archive_file_ofs += MZ_ZIP64_MAX_LOCAL_EXTRA_FIELD_SIZE;
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);
3045 return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
3050 while (uncomp_remaining)
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))
3055 pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
3056 return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
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;
3062 comp_size = uncomp_size;
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));
3071 pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
3072 return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
3075 state.m_pZip = pZip;
3076 state.m_cur_archive_file_ofs = cur_archive_file_ofs;
3077 state.m_comp_size = 0;
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)
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);
3088 size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, (mz_uint64)MZ_ZIP_MAX_IO_BUF_SIZE);
3089 tdefl_status status;
3091 if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size)
3093 mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
3097 uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size);
3098 uncomp_remaining -= in_buf_size;
3100 status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? TDEFL_NO_FLUSH : TDEFL_FINISH);
3101 if (status == TDEFL_STATUS_DONE)
3106 else if (status != TDEFL_STATUS_OKAY)
3108 mz_zip_set_error(pZip, MZ_ZIP_COMPRESSION_FAILED);
3113 pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
3117 pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
3121 comp_size = state.m_comp_size;
3122 cur_archive_file_ofs = state.m_cur_archive_file_ofs;
3124 method = MZ_DEFLATED;
3127 pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
3130 if (pState->m_zip64)
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));
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);
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);
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);
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)
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);
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);
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);
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))
3166 pZip->m_total_files++;
3167 pZip->m_archive_size = cur_archive_file_ofs;
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)
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;
3179 memset(&file_modified_time, 0, sizeof(file_modified_time));
3181 #if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_STDIO)
3182 pFile_modified_time = &file_modified_time;
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);
3188 pSrc_file = MZ_FOPEN(pSrc_filename, "rb");
3190 return mz_zip_set_error(pZip, MZ_ZIP_FILE_OPEN_FAILED);
3192 MZ_FSEEK64(pSrc_file, 0, SEEK_END);
3193 uncomp_size = MZ_FTELL64(pSrc_file);
3194 MZ_FSEEK64(pSrc_file, 0, SEEK_SET);
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);
3198 MZ_FCLOSE(pSrc_file);
3202 #endif // #ifndef MINIZ_NO_STDIO
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)
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);
3210 mz_zip_array_resize(pZip, pNew_ext, 0, MZ_FALSE);
3212 if ((pUncomp_size) || (pComp_size) || (pLocal_header_ofs) || (pDisk_start))
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;
3222 mz_write_le64(pDst, *pUncomp_size);
3223 pDst += sizeof(mz_uint64);
3228 mz_write_le64(pDst, *pComp_size);
3229 pDst += sizeof(mz_uint64);
3232 if (pLocal_header_ofs)
3234 mz_write_le64(pDst, *pLocal_header_ofs);
3235 pDst += sizeof(mz_uint64);
3240 mz_write_le32(pDst, *pDisk_start);
3241 pDst += sizeof(mz_uint32);
3244 mz_write_le16(new_ext_block + sizeof(mz_uint16), (pDst - new_ext_block) - sizeof(mz_uint16) * 2);
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);
3250 if ((pExt) && (ext_len))
3252 mz_uint32 extra_size_remaining = ext_len;
3253 const mz_uint8 *pExtra_data = pExt;
3257 mz_uint32 field_id, field_data_size, field_total_size;
3259 if (extra_size_remaining < (sizeof(mz_uint16) * 2))
3260 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
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;
3266 if (field_total_size > extra_size_remaining)
3267 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
3269 if (field_id != MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID)
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);
3275 pExtra_data += field_total_size;
3276 extra_size_remaining -= field_total_size;
3277 } while (extra_size_remaining);
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)
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;
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;
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);
3306 pState = pZip->m_pState;
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);
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);
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);
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;
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);
3328 num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip);
3330 if (!pState->m_zip64)
3332 if (pZip->m_total_files == MZ_UINT16_MAX)
3333 return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
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);
3342 if (!mz_zip_file_stat_internal(pSource_zip, src_file_index, pSrc_central_header, &src_file_stat, NULL))
3345 cur_src_file_ofs = src_file_stat.m_local_header_ofs;
3346 cur_dst_file_ofs = pZip->m_archive_size;
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);
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);
3355 cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE;
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;
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)))
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))
3371 return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
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)
3376 mz_zip_array_clear(pZip, &file_data_array);
3377 return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
3380 mz_uint32 extra_size_remaining = local_header_extra_len;
3381 const mz_uint8 *pExtra_data = (const mz_uint8 *)file_data_array.m_p;
3385 mz_uint32 field_id, field_data_size, field_total_size;
3387 if (extra_size_remaining < (sizeof(mz_uint16) * 2))
3389 mz_zip_array_clear(pZip, &file_data_array);
3390 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
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;
3397 if (field_total_size > extra_size_remaining)
3399 mz_zip_array_clear(pZip, &file_data_array);
3400 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
3403 if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID)
3405 const mz_uint8 *pSrc_field_data = pExtra_data + sizeof(mz_uint32);
3407 if (field_data_size < sizeof(mz_uint64) * 2)
3409 mz_zip_array_clear(pZip, &file_data_array);
3410 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
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
3416 found_zip64_ext_data_in_ldir = MZ_TRUE;
3420 pExtra_data += field_total_size;
3421 extra_size_remaining -= field_total_size;
3422 } while (extra_size_remaining);
3424 mz_zip_array_clear(pZip, &file_data_array);
3427 if (!pState->m_zip64)
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;
3434 if (approx_new_archive_size >= MZ_UINT32_MAX)
3435 return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
3438 // Write dest archive padding
3439 if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes))
3442 cur_dst_file_ofs += num_alignment_padding_bytes;
3444 local_dir_header_ofs = cur_dst_file_ofs;
3445 if (pZip->m_file_offset_alignment)
3447 MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0);
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);
3454 cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE;
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);
3460 while (src_archive_bytes_remaining)
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)
3465 pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
3466 return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
3468 cur_src_file_ofs += n;
3470 if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n)
3472 pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
3473 return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
3475 cur_dst_file_ofs += n;
3477 src_archive_bytes_remaining -= n;
3480 // Now deal with the optional data descriptor
3481 bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS);
3484 // Copy data descriptor
3485 if ((pSource_zip->m_pState->m_zip64) || (found_zip64_ext_data_in_ldir))
3487 // src is zip64, dest must be zip64
3490 // id 1 (optional in zip64?)
3494 if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, (sizeof(mz_uint32) * 6)) != (sizeof(mz_uint32) * 6))
3496 pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
3497 return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
3500 n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID) ? 6 : 5);
3507 if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4)
3509 pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
3510 return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
3513 has_id = (MZ_READ_LE32(pBuf) == MZ_ZIP_DATA_DESCRIPTOR_ID);
3515 if (pZip->m_pState->m_zip64)
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];
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);
3528 n = sizeof(mz_uint32) * 6;
3532 // dest is NOT zip64, just copy it as-is
3533 n = sizeof(mz_uint32) * (has_id ? 4 : 3);
3537 if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n)
3539 pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
3540 return mz_zip_set_error(pZip, MZ_ZIP_FILE_WRITE_FAILED);
3543 cur_src_file_ofs += n;
3544 cur_dst_file_ofs += n;
3546 pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf);
3548 // Finally, add the new central dir header
3549 orig_central_dir_size = pState->m_central_dir.m_size;
3551 memcpy(new_central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE);
3553 if (pState->m_zip64)
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;
3559 mz_zip_array_init(&new_ext_block, sizeof(mz_uint8));
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);
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))
3567 mz_zip_array_clear(pZip, &new_ext_block);
3571 MZ_WRITE_LE16(new_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS, new_ext_block.m_size);
3573 if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE))
3575 mz_zip_array_clear(pZip, &new_ext_block);
3576 return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
3579 if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, src_filename_len))
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);
3586 if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, new_ext_block.m_p, new_ext_block.m_size))
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);
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))
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);
3600 mz_zip_array_clear(pZip, &new_ext_block);
3605 if (cur_dst_file_ofs > MZ_UINT32_MAX)
3606 return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
3608 if (local_dir_header_ofs >= MZ_UINT32_MAX)
3609 return mz_zip_set_error(pZip, MZ_ZIP_ARCHIVE_TOO_LARGE);
3611 MZ_WRITE_LE32(new_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs);
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);
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))
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);
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)
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);
3631 n = (mz_uint32)orig_central_dir_size;
3632 if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1))
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);
3638 pZip->m_total_files++;
3639 pZip->m_archive_size = cur_dst_file_ofs;
3644 mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip)
3646 mz_zip_internal_state *pState;
3647 mz_uint64 central_dir_ofs, central_dir_size;
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);
3653 pState = pZip->m_pState;
3655 if (pState->m_zip64)
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);
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);
3666 central_dir_ofs = 0;
3667 central_dir_size = 0;
3668 if (pZip->m_total_files)
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);
3677 pZip->m_archive_size += central_dir_size;
3680 if (pState->m_zip64)
3682 // Write zip64 end of central directory header
3683 mz_uint64 rel_ofs_to_zip64_ecdr = pZip->m_archive_size;
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);
3697 pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE;
3699 // Write zip64 end of central directory locator
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);
3707 pZip->m_archive_size += MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE;
3710 // Write end of central directory record
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));
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);
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
3726 pZip->m_archive_size += MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE;
3728 pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED;
3732 mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **ppBuf, size_t *pSize)
3734 if ((!ppBuf) || (!pSize))
3735 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
3740 if ((!pZip) || (!pZip->m_pState))
3741 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
3743 if (pZip->m_pWrite != mz_zip_heap_write_func)
3744 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
3746 if (!mz_zip_writer_finalize_archive(pZip))
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;
3757 mz_bool mz_zip_writer_end(mz_zip_archive *pZip)
3759 return mz_zip_writer_end_internal(pZip, MZ_TRUE);
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)
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;
3770 mz_zip_zero_struct(&zip_archive);
3771 if ((int)level_and_flags < 0)
3772 level_and_flags = MZ_DEFAULT_LEVEL;
3774 if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION))
3777 *pErr = MZ_ZIP_INVALID_PARAMETER;
3781 if (!mz_zip_writer_validate_archive_name(pArchive_name))
3784 *pErr = MZ_ZIP_INVALID_FILENAME;
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)
3792 // Create a new archive.
3793 if (!mz_zip_writer_init_file(&zip_archive, pZip_filename, 0, level_and_flags))
3796 *pErr = zip_archive.m_last_error;
3800 created_new_archive = MZ_TRUE;
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))
3808 *pErr = zip_archive.m_last_error;
3812 if (!mz_zip_writer_init_from_reader(&zip_archive, pZip_filename, level_and_flags))
3815 *pErr = zip_archive.m_last_error;
3817 mz_zip_reader_end_internal(&zip_archive, MZ_FALSE);
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;
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))
3830 actual_err = zip_archive.m_last_error;
3835 if (!mz_zip_writer_end_internal(&zip_archive, status))
3838 actual_err = zip_archive.m_last_error;
3843 if ((!status) && (created_new_archive))
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;
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)
3858 mz_uint32 file_index;
3859 mz_zip_archive zip_archive;
3865 if ((!pZip_filename) || (!pArchive_name))
3868 *pErr = MZ_ZIP_INVALID_PARAMETER;
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))
3877 *pErr = zip_archive.m_last_error;
3882 if (mz_zip_locate_file(&zip_archive, pArchive_name, pComment, flags, &file_index))
3884 p = mz_zip_extract_to_heap(&zip_archive, file_index, pSize, flags);
3887 mz_zip_reader_end_internal(&zip_archive, p != NULL);
3890 *pErr = zip_archive.m_last_error;
3895 #endif // #ifndef MINIZ_NO_STDIO
3897 #endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS
3899 // ------------------- Misc utils
3901 mz_zip_mode mz_zip_get_mode(mz_zip_archive *pZip)
3903 return pZip ? pZip->m_zip_mode : MZ_ZIP_MODE_INVALID;
3906 mz_zip_type mz_zip_get_type(mz_zip_archive *pZip)
3908 return pZip ? pZip->m_zip_type : MZ_ZIP_TYPE_INVALID;
3911 mz_zip_error mz_zip_set_last_error(mz_zip_archive *pZip, mz_zip_error err_num)
3913 mz_zip_error prev_err;
3916 return MZ_ZIP_INVALID_PARAMETER;
3918 prev_err = pZip->m_last_error;
3920 pZip->m_last_error = err_num;
3924 mz_zip_error mz_zip_peek_last_error(mz_zip_archive *pZip)
3927 return MZ_ZIP_INVALID_PARAMETER;
3929 return pZip->m_last_error;
3932 mz_zip_error mz_zip_clear_last_error(mz_zip_archive *pZip)
3934 return mz_zip_set_last_error(pZip, MZ_ZIP_NO_ERROR);
3937 mz_zip_error mz_zip_get_last_error(mz_zip_archive *pZip)
3939 mz_zip_error prev_err;
3942 return MZ_ZIP_INVALID_PARAMETER;
3944 prev_err = pZip->m_last_error;
3946 pZip->m_last_error = MZ_ZIP_NO_ERROR;
3950 const char *mz_zip_get_error_string(mz_zip_error mz_err)
3954 case MZ_ZIP_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";
4022 return "unknown error";
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)
4028 if ((!pZip) || (!pZip->m_pState))
4031 return pZip->m_pState->m_zip64;
4034 size_t mz_zip_get_central_dir_size(mz_zip_archive *pZip)
4036 if ((!pZip) || (!pZip->m_pState))
4039 return pZip->m_pState->m_central_dir.m_size;
4042 mz_uint mz_zip_get_num_files(mz_zip_archive *pZip)
4044 return pZip ? pZip->m_total_files : 0;
4047 mz_uint64 mz_zip_get_archive_size(mz_zip_archive *pZip)
4051 return pZip->m_archive_size;
4054 mz_uint64 mz_zip_get_archive_file_start_offset(mz_zip_archive *pZip)
4056 if ((!pZip) || (!pZip->m_pState))
4058 return pZip->m_pState->m_file_archive_start_ofs;
4061 MZ_FILE *mz_zip_get_cfile(mz_zip_archive *pZip)
4063 if ((!pZip) || (!pZip->m_pState))
4065 return pZip->m_pState->m_pFile;
4068 size_t mz_zip_read_archive_data(mz_zip_archive *pZip, mz_uint64 file_ofs, void *pBuf, size_t n)
4070 if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pZip->m_pRead))
4071 return mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
4073 return pZip->m_pRead(pZip->m_pIO_opaque, file_ofs, pBuf, n);
4076 mz_uint mz_zip_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size)
4079 const mz_uint8 *p = mz_zip_get_cdh(pZip, file_index);
4082 if (filename_buf_size)
4083 pFilename[0] = '\0';
4084 mz_zip_set_error(pZip, MZ_ZIP_INVALID_PARAMETER);
4087 n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
4088 if (filename_buf_size)
4090 n = MZ_MIN(n, filename_buf_size - 1);
4091 memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n);
4092 pFilename[n] = '\0';
4097 mz_bool mz_zip_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat)
4099 return mz_zip_file_stat_internal(pZip, file_index, mz_zip_get_cdh(pZip, file_index), pStat, NULL);
4102 mz_bool mz_zip_end(mz_zip_archive *pZip)
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);
4115 #endif // #ifndef MINIZ_NO_ARCHIVE_APIS