Update contrib/zip to 0.1.18 to fix a build failure on 32 bit platforms.
This patch is a combination of two upstream patches [1][2] slightly
edited to fit FreeBSD conventions and to skip files not shipped in the
distribution.  See upstream issue 2954 [3] for details.

[1]: https://github.com/assimp/assimp/commit/f78446b14aff46db2ef27d062a275b6a01fd68b1.diff
[2]: https://github.com/assimp/assimp/commit/f78446b14aff46db2ef27d062a275b6a01fd68b1.diff
[3]: https://github.com/assimp/assimp/issues/2954

diff -u contrib/zip/CMakeLists.txt contrib/zip/CMakeLists.txt
--- contrib/zip/CMakeLists.txt
+++ contrib/zip/CMakeLists.txt
@@ -1,10 +1,14 @@
-cmake_minimum_required(VERSION 2.8)
-project(zip)
-enable_language(C)
+cmake_minimum_required(VERSION 3.0)
+
+project(zip
+  LANGUAGES C
+  VERSION "0.1.18")
 set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
 
+option(CMAKE_DISABLE_TESTING "Disable test creation" OFF)
+
 if (MSVC)
-  # Use secure functions by defaualt and suppress warnings about "deprecated" functions
+  # Use secure functions by default and suppress warnings about "deprecated" functions
   set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1")
   set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT=1")
   set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_NONSTDC_NO_WARNINGS=1 /D _CRT_SECURE_NO_WARNINGS=1")
@@ -17,23 +21,71 @@
 # zip
 set(SRC src/miniz.h src/zip.h src/zip.c)
 add_library(${PROJECT_NAME} ${SRC})
-target_include_directories(${PROJECT_NAME} INTERFACE src)
+target_include_directories(${PROJECT_NAME} PUBLIC
+  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
+  $<INSTALL_INTERFACE:include>
+)
 
 # test
 if (NOT CMAKE_DISABLE_TESTING)
   enable_testing()
   add_subdirectory(test)
   find_package(Sanitizers)
-  add_sanitizers(${PROJECT_NAME} test.exe)
-  add_sanitizers(${PROJECT_NAME} test_miniz.exe)
+  add_sanitizers(${PROJECT_NAME} ${test_out})
 endif()
 
+####
+# Installation (https://github.com/forexample/package-example) {
+
+set(CONFIG_INSTALL_DIR "lib/cmake/${PROJECT_NAME}")
+set(INCLUDE_INSTALL_DIR "include")
+
+set(GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated")
+
+# Configuration
+set(VERSION_CONFIG "${GENERATED_DIR}/${PROJECT_NAME}ConfigVersion.cmake")
+set(PROJECT_CONFIG "${GENERATED_DIR}/${PROJECT_NAME}Config.cmake")
+set(TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets")
+set(NAMESPACE "${PROJECT_NAME}::")
+
+# Include module with fuction 'write_basic_package_version_file'
+include(CMakePackageConfigHelpers)
+
+# Note: PROJECT_VERSION is used as a VERSION
+write_basic_package_version_file(
+    "${VERSION_CONFIG}" COMPATIBILITY SameMajorVersion
+)
+
+# Use variables:
+#   * TARGETS_EXPORT_NAME
+#   * PROJECT_NAME
+configure_package_config_file(
+    "cmake/Config.cmake.in"
+    "${PROJECT_CONFIG}"
+    INSTALL_DESTINATION "${CONFIG_INSTALL_DIR}"
+)
+
+install(
+    FILES "${PROJECT_CONFIG}" "${VERSION_CONFIG}"
+    DESTINATION "${CONFIG_INSTALL_DIR}"
+)
+
+install(
+    EXPORT "${TARGETS_EXPORT_NAME}"
+    NAMESPACE "${NAMESPACE}"
+    DESTINATION "${CONFIG_INSTALL_DIR}"
+)
+
+# }
+
 install(TARGETS ${PROJECT_NAME}
+        EXPORT ${TARGETS_EXPORT_NAME}
         RUNTIME DESTINATION bin
         ARCHIVE DESTINATION lib
         LIBRARY DESTINATION lib
-        COMPONENT library)
-install(FILES ${PROJECT_SOURCE_DIR}/src/zip.h DESTINATION include)
+        INCLUDES DESTINATION ${INCLUDE_INSTALL_DIR}
+)
+install(FILES ${PROJECT_SOURCE_DIR}/src/zip.h DESTINATION ${INCLUDE_INSTALL_DIR}/zip)
 
 # uninstall target (https://gitlab.kitware.com/cmake/community/wikis/FAQ#can-i-do-make-uninstall-with-cmake)
 if(NOT TARGET uninstall)
@@ -46,2 +98,11 @@
         COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake/cmake_uninstall.cmake)
+endif()
+
+find_package(Doxygen)
+if(DOXYGEN_FOUND)
+    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile @ONLY)
+    add_custom_target(doc
+        ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile
+        WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+        COMMENT "Generating API documentation with Doxygen" VERBATIM)
 endif()
diff -u contrib/zip/README.md contrib/zip/README.md
--- contrib/zip/README.md
+++ contrib/zip/README.md
@@ -1,10 +1,8 @@
 ### A portable (OSX/Linux/Windows), simple zip library written in C
 This is done by hacking awesome [miniz](https://code.google.com/p/miniz) library and layering functions on top of the miniz v1.15 API.
 
-[![Windows](https://ci.appveyor.com/api/projects/status/bph8dr3jacgmjv32/branch/master?svg=true&label=windows)](https://ci.appveyor.com/project/kuba--/zip)
-[![Linux](https://travis-ci.org/kuba--/zip.svg?branch=master&label=linux%2fosx)](https://travis-ci.org/kuba--/zip)
+[![Build](https://github.com/kuba--/zip/workflows/build/badge.svg)](https://github.com/kuba--/zip/actions?query=workflow%3Abuild)
 [![Version](https://badge.fury.io/gh/kuba--%2Fzip.svg)](https://github.com/kuba--/zip/releases)
-[![Codecov](https://codecov.io/gh/kuba--/zip/branch/master/graph/badge.svg)](https://codecov.io/gh/kuba--/zip)
 
 
 # The Idea
@@ -71,7 +69,7 @@
 zip_extract("foo.zip", "/tmp", on_extract_entry, &arg);
 ```
 
-*   Extract a zip entry into memory.
+* Extract a zip entry into memory.
 ```c
 void *buf = NULL;
 size_t bufsize;
@@ -89,7 +87,7 @@
 free(buf);
 ```
 
-*   Extract a zip entry into memory (no internal allocation).
+* Extract a zip entry into memory (no internal allocation).
 ```c
 unsigned char *buf;
 size_t bufsize;
@@ -110,7 +108,7 @@
 free(buf);
 ```
 
-*   Extract a zip entry into memory using callback.
+* Extract a zip entry into memory using callback.
 ```c
 struct buffer_t {
     char *data;
@@ -144,7 +142,7 @@
 ```
 
 
-*   Extract a zip entry into a file.
+* Extract a zip entry into a file.
 ```c
 struct zip_t *zip = zip_open("foo.zip", 0, 'r');
 {
@@ -157,7 +155,7 @@
 zip_close(zip);
 ```
 
-*   List of all zip entries
+* List of all zip entries
 ```c
 struct zip_t *zip = zip_open("foo.zip", 0, 'r');
 int i, n = zip_total_entries(zip);
@@ -174,7 +172,7 @@
 zip_close(zip);
 ```
 
-## Bindings
+# Bindings
 Compile zip library as a dynamic library.
 ```shell
 $ mkdir build
@@ -213,3 +211,50 @@
+}
+```
+
+### Rust (ffi)
+```rust
+extern crate libc;
+use std::ffi::CString;
+
+#[repr(C)]
+pub struct Zip {
+    _private: [u8; 0],
+}
+
+#[link(name = "zip")]
+extern "C" {
+    fn zip_open(path: *const libc::c_char, level: libc::c_int, mode: libc::c_char) -> *mut Zip;
+    fn zip_close(zip: *mut Zip) -> libc::c_void;
+
+    fn zip_entry_open(zip: *mut Zip, entryname: *const libc::c_char) -> libc::c_int;
+    fn zip_entry_close(zip: *mut Zip) -> libc::c_int;
+    fn zip_entry_write(
+        zip: *mut Zip,
+        buf: *const libc::c_void,
+        bufsize: libc::size_t,
+    ) -> libc::c_int;
+}
+
+fn main() {
+    let path = CString::new("/tmp/test.zip").unwrap();
+    let mode: libc::c_char = 'w' as libc::c_char;
+
+    let entryname = CString::new("test.txt").unwrap();
+    let content = "test content\0";
+
+    unsafe {
+        let zip: *mut Zip = zip_open(path.as_ptr(), 5, mode);
+        {
+            zip_entry_open(zip, entryname.as_ptr());
+            {
+                let buf = content.as_ptr() as *const libc::c_void;
+                let bufsize = content.len() as libc::size_t;
+                zip_entry_write(zip, buf, bufsize);
+            }
+            zip_entry_close(zip);
+        }
+        zip_close(zip);
+    }
 }
 ```
 
unchanged:
--- contrib/zip/src/miniz.h
+++ contrib/zip/src/miniz.h
@@ -221,6 +221,7 @@
 #ifndef MINIZ_HEADER_INCLUDED
 #define MINIZ_HEADER_INCLUDED
 
+#include <stdint.h>
 #include <stdlib.h>
 
 // Defines to completely disable specific portions of miniz.c:
@@ -284,7 +285,8 @@
 /* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES only if not set */
 #if !defined(MINIZ_USE_UNALIGNED_LOADS_AND_STORES)
 #if MINIZ_X86_OR_X64_CPU
-/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. */
+/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient
+ * integer loads and stores from unaligned addresses. */
 #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1
 #define MINIZ_UNALIGNED_USE_MEMCPY
 #else
@@ -354,6 +356,44 @@ enum {
   MZ_FIXED = 4
 };
 
+/* miniz error codes. Be sure to update mz_zip_get_error_string() if you add or
+ * modify this enum. */
+typedef enum {
+  MZ_ZIP_NO_ERROR = 0,
+  MZ_ZIP_UNDEFINED_ERROR,
+  MZ_ZIP_TOO_MANY_FILES,
+  MZ_ZIP_FILE_TOO_LARGE,
+  MZ_ZIP_UNSUPPORTED_METHOD,
+  MZ_ZIP_UNSUPPORTED_ENCRYPTION,
+  MZ_ZIP_UNSUPPORTED_FEATURE,
+  MZ_ZIP_FAILED_FINDING_CENTRAL_DIR,
+  MZ_ZIP_NOT_AN_ARCHIVE,
+  MZ_ZIP_INVALID_HEADER_OR_CORRUPTED,
+  MZ_ZIP_UNSUPPORTED_MULTIDISK,
+  MZ_ZIP_DECOMPRESSION_FAILED,
+  MZ_ZIP_COMPRESSION_FAILED,
+  MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE,
+  MZ_ZIP_CRC_CHECK_FAILED,
+  MZ_ZIP_UNSUPPORTED_CDIR_SIZE,
+  MZ_ZIP_ALLOC_FAILED,
+  MZ_ZIP_FILE_OPEN_FAILED,
+  MZ_ZIP_FILE_CREATE_FAILED,
+  MZ_ZIP_FILE_WRITE_FAILED,
+  MZ_ZIP_FILE_READ_FAILED,
+  MZ_ZIP_FILE_CLOSE_FAILED,
+  MZ_ZIP_FILE_SEEK_FAILED,
+  MZ_ZIP_FILE_STAT_FAILED,
+  MZ_ZIP_INVALID_PARAMETER,
+  MZ_ZIP_INVALID_FILENAME,
+  MZ_ZIP_BUF_TOO_SMALL,
+  MZ_ZIP_INTERNAL_ERROR,
+  MZ_ZIP_FILE_NOT_FOUND,
+  MZ_ZIP_ARCHIVE_TOO_LARGE,
+  MZ_ZIP_VALIDATION_FAILED,
+  MZ_ZIP_WRITE_CALLBACK_FAILED,
+  MZ_ZIP_TOTAL_ERRORS
+} mz_zip_error;
+
 // Method
 #define MZ_DEFLATED 8
 
@@ -696,6 +736,7 @@ typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs,
                                     void *pBuf, size_t n);
 typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs,
                                      const void *pBuf, size_t n);
+typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque);
 
 struct mz_zip_internal_state_tag;
 typedef struct mz_zip_internal_state_tag mz_zip_internal_state;
@@ -707,13 +748,27 @@ typedef enum {
   MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3
 } mz_zip_mode;
 
-typedef struct mz_zip_archive_tag {
+typedef enum {
+  MZ_ZIP_TYPE_INVALID = 0,
+  MZ_ZIP_TYPE_USER,
+  MZ_ZIP_TYPE_MEMORY,
+  MZ_ZIP_TYPE_HEAP,
+  MZ_ZIP_TYPE_FILE,
+  MZ_ZIP_TYPE_CFILE,
+  MZ_ZIP_TOTAL_TYPES
+} mz_zip_type;
+
+typedef struct {
   mz_uint64 m_archive_size;
   mz_uint64 m_central_directory_file_ofs;
-  mz_uint m_total_files;
+
+  /* We only support up to UINT32_MAX files in zip64 mode. */
+  mz_uint32 m_total_files;
   mz_zip_mode m_zip_mode;
+  mz_zip_type m_zip_type;
+  mz_zip_error m_last_error;
 
-  mz_uint m_file_offset_alignment;
+  mz_uint64 m_file_offset_alignment;
 
   mz_alloc_func m_pAlloc;
   mz_free_func m_pFree;
@@ -722,6 +777,7 @@ typedef struct mz_zip_archive_tag {
 
   mz_file_read_func m_pRead;
   mz_file_write_func m_pWrite;
+  mz_file_needs_keepalive m_pNeeds_keepalive;
   void *m_pIO_opaque;
 
   mz_zip_internal_state *m_pState;
@@ -1263,6 +1319,9 @@ mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits,
                                                 int strategy);
 #endif // #ifndef MINIZ_NO_ZLIB_APIS
 
+#define MZ_UINT16_MAX (0xFFFFU)
+#define MZ_UINT32_MAX (0xFFFFFFFFU)
+
 #ifdef __cplusplus
 }
 #endif
@@ -1311,6 +1370,11 @@ typedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1];
    ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U))
 #endif
 
+#define MZ_READ_LE64(p)                                                        \
+  (((mz_uint64)MZ_READ_LE32(p)) |                                              \
+   (((mz_uint64)MZ_READ_LE32((const mz_uint8 *)(p) + sizeof(mz_uint32)))       \
+    << 32U))
+
 #ifdef _MSC_VER
 #define MZ_FORCEINLINE __forceinline
 #elif defined(__GNUC__)
@@ -4160,6 +4224,17 @@ enum {
   MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30,
   MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46,
   MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22,
+
+  /* ZIP64 archive identifier and record sizes */
+  MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06064b50,
+  MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50,
+  MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE = 56,
+  MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE = 20,
+  MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID = 0x0001,
+  MZ_ZIP_DATA_DESCRIPTOR_ID = 0x08074b50,
+  MZ_ZIP_DATA_DESCRIPTER_SIZE64 = 24,
+  MZ_ZIP_DATA_DESCRIPTER_SIZE32 = 16,
+
   // Central directory header record offsets
   MZ_ZIP_CDH_SIG_OFS = 0,
   MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4,
@@ -4199,6 +4274,31 @@ enum {
   MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12,
   MZ_ZIP_ECDH_CDIR_OFS_OFS = 16,
   MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20,
+
+  /* ZIP64 End of central directory locator offsets */
+  MZ_ZIP64_ECDL_SIG_OFS = 0,                    /* 4 bytes */
+  MZ_ZIP64_ECDL_NUM_DISK_CDIR_OFS = 4,          /* 4 bytes */
+  MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS = 8,  /* 8 bytes */
+  MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS = 16, /* 4 bytes */
+
+  /* ZIP64 End of central directory header offsets */
+  MZ_ZIP64_ECDH_SIG_OFS = 0,                       /* 4 bytes */
+  MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS = 4,            /* 8 bytes */
+  MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS = 12,          /* 2 bytes */
+  MZ_ZIP64_ECDH_VERSION_NEEDED_OFS = 14,           /* 2 bytes */
+  MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS = 16,            /* 4 bytes */
+  MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS = 20,            /* 4 bytes */
+  MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 24, /* 8 bytes */
+  MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS = 32,       /* 8 bytes */
+  MZ_ZIP64_ECDH_CDIR_SIZE_OFS = 40,                /* 8 bytes */
+  MZ_ZIP64_ECDH_CDIR_OFS_OFS = 48,                 /* 8 bytes */
+  MZ_ZIP_VERSION_MADE_BY_DOS_FILESYSTEM_ID = 0,
+  MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG = 0x10,
+  MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED = 1,
+  MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG = 32,
+  MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION = 64,
+  MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED = 8192,
+  MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 = 1 << 11
 };
 
 typedef struct {
@@ -4211,7 +4311,24 @@ struct mz_zip_internal_state_tag {
   mz_zip_array m_central_dir;
   mz_zip_array m_central_dir_offsets;
   mz_zip_array m_sorted_central_dir_offsets;
+
+  /* The flags passed in when the archive is initially opened. */
+  uint32_t m_init_flags;
+
+  /* MZ_TRUE if the archive has a zip64 end of central directory headers, etc.
+   */
+  mz_bool m_zip64;
+
+  /* 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.) */
+  mz_bool m_zip64_has_extended_info_fields;
+
+  /* These fields are used by the file, FILE, memory, and memory/heap read/write
+   * helpers. */
   MZ_FILE *m_pFile;
+  mz_uint64 m_file_archive_start_ofs;
+
   void *m_pMem;
   size_t m_mem_size;
   size_t m_mem_capacity;
@@ -4363,6 +4480,13 @@ static mz_bool mz_zip_set_file_times(const char *pFilename, time_t access_time,
 #endif /* #ifndef MINIZ_NO_STDIO */
 #endif /* #ifndef MINIZ_NO_TIME */
 
+static MZ_FORCEINLINE mz_bool mz_zip_set_error(mz_zip_archive *pZip,
+                                               mz_zip_error err_num) {
+  if (pZip)
+    pZip->m_last_error = err_num;
+  return MZ_FALSE;
+}
+
 static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip,
                                            mz_uint32 flags) {
   (void)flags;
@@ -4480,127 +4604,346 @@ mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) {
   }
 }
 
-static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip,
-                                              mz_uint32 flags) {
-  mz_uint cdir_size, num_this_disk, cdir_disk_index;
-  mz_uint64 cdir_ofs;
+static mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive *pZip,
+                                               mz_uint32 record_sig,
+                                               mz_uint32 record_size,
+                                               mz_int64 *pOfs) {
   mz_int64 cur_file_ofs;
-  const mz_uint8 *p;
   mz_uint32 buf_u32[4096 / sizeof(mz_uint32)];
   mz_uint8 *pBuf = (mz_uint8 *)buf_u32;
-  mz_bool sort_central_dir =
-      ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0);
-  // 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.
-  if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
+
+  /* Basic sanity checks - reject files which are too small */
+  if (pZip->m_archive_size < record_size)
     return MZ_FALSE;
-  // Find the end of central directory record by scanning the file from the end
-  // towards the beginning.
+
+  /* Find the record by scanning the file from the end towards the beginning. */
   cur_file_ofs =
       MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0);
   for (;;) {
     int i,
         n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs);
+
     if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n)
       return MZ_FALSE;
-    for (i = n - 4; i >= 0; --i)
-      if (MZ_READ_LE32(pBuf + i) == MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG)
-        break;
+
+    for (i = n - 4; i >= 0; --i) {
+      mz_uint s = MZ_READ_LE32(pBuf + i);
+      if (s == record_sig) {
+        if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size)
+          break;
+      }
+    }
+
     if (i >= 0) {
       cur_file_ofs += i;
       break;
     }
+
+    /* Give up if we've searched the entire file, or we've gone back "too far"
+     * (~64kb) */
     if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >=
-                            (0xFFFF + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)))
+                            (MZ_UINT16_MAX + record_size)))
       return MZ_FALSE;
+
     cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0);
   }
-  // Read and verify the end of central directory record.
+
+  *pOfs = cur_file_ofs;
+  return MZ_TRUE;
+}
+
+static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip,
+                                              mz_uint flags) {
+  mz_uint cdir_size = 0, cdir_entries_on_this_disk = 0, num_this_disk = 0,
+          cdir_disk_index = 0;
+  mz_uint64 cdir_ofs = 0;
+  mz_int64 cur_file_ofs = 0;
+  const mz_uint8 *p;
+
+  mz_uint32 buf_u32[4096 / sizeof(mz_uint32)];
+  mz_uint8 *pBuf = (mz_uint8 *)buf_u32;
+  mz_bool sort_central_dir =
+      ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0);
+  mz_uint32 zip64_end_of_central_dir_locator_u32
+      [(MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + sizeof(mz_uint32) - 1) /
+       sizeof(mz_uint32)];
+  mz_uint8 *pZip64_locator = (mz_uint8 *)zip64_end_of_central_dir_locator_u32;
+
+  mz_uint32 zip64_end_of_central_dir_header_u32
+      [(MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) /
+       sizeof(mz_uint32)];
+  mz_uint8 *pZip64_end_of_central_dir =
+      (mz_uint8 *)zip64_end_of_central_dir_header_u32;
+
+  mz_uint64 zip64_end_of_central_dir_ofs = 0;
+
+  /* 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. */
+  if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE)
+    return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE);
+
+  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))
+    return mz_zip_set_error(pZip, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR);
+
+  /* Read and verify the end of central directory record. */
   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)
-    return MZ_FALSE;
-  if ((MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) !=
-       MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) ||
-      ((pZip->m_total_files =
-            MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS)) !=
-       MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS)))
-    return MZ_FALSE;
+    return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
+
+  if (MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) !=
+      MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG)
+    return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE);
+
+  if (cur_file_ofs >= (MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE +
+                       MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) {
+    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) {
+      if (MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_SIG_OFS) ==
+          MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG) {
+        zip64_end_of_central_dir_ofs = MZ_READ_LE64(
+            pZip64_locator + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS);
+        if (zip64_end_of_central_dir_ofs >
+            (pZip->m_archive_size - MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE))
+          return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE);
+
+        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) {
+          if (MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIG_OFS) ==
+              MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG) {
+            pZip->m_pState->m_zip64 = MZ_TRUE;
+          }
+        }
+      }
+    }
+  }
 
+  pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS);
+  cdir_entries_on_this_disk =
+      MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS);
   num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS);
   cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS);
+  cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS);
+  cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS);
+
+  if (pZip->m_pState->m_zip64) {
+    mz_uint32 zip64_total_num_of_disks =
+        MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS);
+    mz_uint64 zip64_cdir_total_entries = MZ_READ_LE64(
+        pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS);
+    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);
+    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);
+    mz_uint64 zip64_size_of_central_directory =
+        MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_SIZE_OFS);
+
+    if (zip64_size_of_end_of_central_dir_record <
+        (MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - 12))
+      return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+
+    if (zip64_total_num_of_disks != 1U)
+      return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK);
+
+    /* Check for miniz's practical limits */
+    if (zip64_cdir_total_entries > MZ_UINT32_MAX)
+      return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
+
+    pZip->m_total_files = (mz_uint32)zip64_cdir_total_entries;
+
+    if (zip64_cdir_total_entries_on_this_disk > MZ_UINT32_MAX)
+      return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES);
+
+    cdir_entries_on_this_disk =
+        (mz_uint32)zip64_cdir_total_entries_on_this_disk;
+
+    /* Check for miniz's current practical limits (sorry, this should be enough
+     * for millions of files) */
+    if (zip64_size_of_central_directory > MZ_UINT32_MAX)
+      return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE);
+
+    cdir_size = (mz_uint32)zip64_size_of_central_directory;
+
+    num_this_disk = MZ_READ_LE32(pZip64_end_of_central_dir +
+                                 MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS);
+
+    cdir_disk_index = MZ_READ_LE32(pZip64_end_of_central_dir +
+                                   MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS);
+
+    cdir_ofs =
+        MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_OFS_OFS);
+  }
+
+  if (pZip->m_total_files != cdir_entries_on_this_disk)
+    return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK);
+
   if (((num_this_disk | cdir_disk_index) != 0) &&
       ((num_this_disk != 1) || (cdir_disk_index != 1)))
-    return MZ_FALSE;
+    return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK);
 
-  if ((cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS)) <
-      pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)
-    return MZ_FALSE;
+  if (cdir_size < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)
+    return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
 
-  cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS);
   if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size)
-    return MZ_FALSE;
+    return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
 
   pZip->m_central_directory_file_ofs = cdir_ofs;
 
   if (pZip->m_total_files) {
     mz_uint i, n;
-
-    // Read the entire central directory into a heap block, and allocate another
-    // heap block to hold the unsorted central dir file record offsets, and
-    // another to hold the sorted indices.
+    /* 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. */
     if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size,
                               MZ_FALSE)) ||
         (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets,
                               pZip->m_total_files, MZ_FALSE)))
-      return MZ_FALSE;
+      return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
 
     if (sort_central_dir) {
       if (!mz_zip_array_resize(pZip,
                                &pZip->m_pState->m_sorted_central_dir_offsets,
                                pZip->m_total_files, MZ_FALSE))
-        return MZ_FALSE;
+        return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
     }
 
     if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs,
                       pZip->m_pState->m_central_dir.m_p,
                       cdir_size) != cdir_size)
-      return MZ_FALSE;
+      return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
 
-    // Now create an index into the central directory file records, do some
-    // basic sanity checking on each record, and check for zip64 entries (which
-    // are not yet supported).
+    /* Now create an index into the central directory file records, do some
+     * basic sanity checking on each record */
     p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p;
     for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) {
-      mz_uint total_header_size, comp_size, decomp_size, disk_index;
+      mz_uint total_header_size, disk_index, bit_flags, filename_size,
+          ext_data_size;
+      mz_uint64 comp_size, decomp_size, local_header_ofs;
+
       if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) ||
           (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG))
-        return MZ_FALSE;
+        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+
       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);
+
       if (sort_central_dir)
         MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets,
                              mz_uint32, i) = i;
+
       comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS);
       decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS);
-      if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) &&
-           (decomp_size != comp_size)) ||
-          (decomp_size && !comp_size) || (decomp_size == 0xFFFFFFFF) ||
-          (comp_size == 0xFFFFFFFF))
-        return MZ_FALSE;
+      local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS);
+      filename_size = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS);
+      ext_data_size = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS);
+
+      if ((!pZip->m_pState->m_zip64_has_extended_info_fields) &&
+          (ext_data_size) &&
+          (MZ_MAX(MZ_MAX(comp_size, decomp_size), local_header_ofs) ==
+           MZ_UINT32_MAX)) {
+        /* Attempt to find zip64 extended information field in the entry's extra
+         * data */
+        mz_uint32 extra_size_remaining = ext_data_size;
+
+        if (extra_size_remaining) {
+          const mz_uint8 *pExtra_data;
+          void *buf = NULL;
+
+          if (MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + ext_data_size >
+              n) {
+            buf = MZ_MALLOC(ext_data_size);
+            if (buf == NULL)
+              return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED);
+
+            if (pZip->m_pRead(pZip->m_pIO_opaque,
+                              cdir_ofs + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE +
+                                  filename_size,
+                              buf, ext_data_size) != ext_data_size) {
+              MZ_FREE(buf);
+              return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED);
+            }
+
+            pExtra_data = (mz_uint8 *)buf;
+          } else {
+            pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size;
+          }
+
+          do {
+            mz_uint32 field_id;
+            mz_uint32 field_data_size;
+
+            if (extra_size_remaining < (sizeof(mz_uint16) * 2)) {
+              MZ_FREE(buf);
+              return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+            }
+
+            field_id = MZ_READ_LE16(pExtra_data);
+            field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16));
+
+            if ((field_data_size + sizeof(mz_uint16) * 2) >
+                extra_size_remaining) {
+              MZ_FREE(buf);
+              return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+            }
+
+            if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) {
+              /* 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). */
+              pZip->m_pState->m_zip64 = MZ_TRUE;
+              pZip->m_pState->m_zip64_has_extended_info_fields = MZ_TRUE;
+              break;
+            }
+
+            pExtra_data += sizeof(mz_uint16) * 2 + field_data_size;
+            extra_size_remaining =
+                extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size;
+          } while (extra_size_remaining);
+
+          MZ_FREE(buf);
+        }
+      }
+
+      /* I've seen archives that aren't marked as zip64 that uses zip64 ext
+       * data, argh */
+      if ((comp_size != MZ_UINT32_MAX) && (decomp_size != MZ_UINT32_MAX)) {
+        if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) &&
+             (decomp_size != comp_size)) ||
+            (decomp_size && !comp_size))
+          return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+      }
+
       disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS);
-      if ((disk_index != num_this_disk) && (disk_index != 1))
-        return MZ_FALSE;
-      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)
-        return MZ_FALSE;
+      if ((disk_index == MZ_UINT16_MAX) ||
+          ((disk_index != num_this_disk) && (disk_index != 1)))
+        return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK);
+
+      if (comp_size != MZ_UINT32_MAX) {
+        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)
+          return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+      }
+
+      bit_flags = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS);
+      if (bit_flags & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED)
+        return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION);
+
       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)
-        return MZ_FALSE;
+        return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED);
+
       n -= total_header_size;
       p += total_header_size;
     }
diff -u contrib/zip/src/zip.c contrib/zip/src/zip.c
--- contrib/zip/src/zip.c
+++ contrib/zip/src/zip.c
@@ -24,7 +24,6 @@
   ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) &&   \
    (P)[1] == ':')
 #define FILESYSTEM_PREFIX_LEN(P) (HAS_DEVICE(P) ? 2 : 0)
-#define ISSLASH(C) ((C) == '/' || (C) == '\\')
 
 #else
 
@@ -48,7 +47,7 @@
 #endif
 
 #ifndef ISSLASH
-#define ISSLASH(C) ((C) == '/')
+#define ISSLASH(C) ((C) == '/' || (C) == '\\')
 #endif
 
 #define CLEANUP(ptr)                                                           \
@@ -78,26 +77,34 @@
   return base;
 }
 
-static int mkpath(const char *path) {
-  char const *p;
+static int mkpath(char *path) {
+  char *p;
   char npath[MAX_PATH + 1];
   int len = 0;
   int has_device = HAS_DEVICE(path);
 
   memset(npath, 0, MAX_PATH + 1);
-
-#ifdef _WIN32
-  // only on windows fix the path
-  npath[0] = path[0];
-  npath[1] = path[1];
-  len = 2;
-#endif // _WIN32
-    
+  if (has_device) {
+    // only on windows
+    npath[0] = path[0];
+    npath[1] = path[1];
+    len = 2;
+  }
   for (p = path + len; *p && len < MAX_PATH; p++) {
     if (ISSLASH(*p) && ((!has_device && len > 0) || (has_device && len > 2))) {
-      if (MKDIR(npath) == -1)
-        if (errno != EEXIST)
+#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) ||              \
+    defined(__MINGW32__)
+#else
+      if ('\\' == *p) {
+        *p = '/';
+      }
+#endif
+
+      if (MKDIR(npath) == -1) {
+        if (errno != EEXIST) {
           return -1;
+        }
+      }
     }
     npath[len++] = *p;
   }
@@ -215,6 +222,20 @@
   }
 }
 
+int zip_is64(struct zip_t *zip) {
+  if (!zip) {
+    // zip_t handler is not initialized
+    return -1;
+  }
+
+  if (!zip->archive.m_pState) {
+    // zip state is not initialized
+    return -1;
+  }
+
+  return (int)zip->archive.m_pState->m_zip64;
+}
+
 int zip_entry_open(struct zip_t *zip, const char *entryname) {
   size_t entrylen = 0;
   mz_zip_archive *pzip = NULL;
@@ -279,7 +300,14 @@
   zip->entry.header_offset = zip->archive.m_archive_size;
   memset(zip->entry.header, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE * sizeof(mz_uint8));
   zip->entry.method = 0;
+
+  // UNIX or APPLE
+#if MZ_PLATFORM == 3 || MZ_PLATFORM == 19
+  // regular file with rw-r--r-- persmissions
+  zip->entry.external_attr = (mz_uint32)(0100644) << 16;
+#else
   zip->entry.external_attr = 0;
+#endif
 
   num_alignment_padding_bytes =
       mz_zip_writer_compute_padding_needed_for_file_alignment(pzip);
@@ -660,7 +688,7 @@
   }
 
   if (!mz_zip_reader_extract_to_mem_no_alloc(pzip, (mz_uint)zip->entry.index,
-  buf, bufsize, 0, NULL,  0)) {
+                                             buf, bufsize, 0, NULL, 0)) {
     return -1;
   }
 
@@ -670,10 +698,7 @@
 int zip_entry_fread(struct zip_t *zip, const char *filename) {
   mz_zip_archive *pzip = NULL;
   mz_uint idx;
-#if defined(_MSC_VER)
-#else
   mz_uint32 xattr = 0;
-#endif
   mz_zip_archive_file_stat info;
 
   if (!zip) {
@@ -783,7 +808,8 @@
 
     if (MZ_FILE_STAT(name, &file_stat) != 0) {
       // problem getting information - check errno
-      return -1;
+      status = -1;
+      break;
     }
 
     if ((file_stat.st_mode & 0200) == 0) {
@@ -875,12 +901,19 @@
       goto out;
     }
 
-    if ((((info.m_version_made_by >> 8) == 3) || ((info.m_version_made_by >> 8) == 19)) // if zip is produced on Unix or macOS (3 and 19 from section 4.4.2.2 of zip standard)
-        && info.m_external_attr & (0x20 << 24)) { // and has sym link attribute (0x80 is file, 0x40 is directory)
+    if ((((info.m_version_made_by >> 8) == 3) ||
+         ((info.m_version_made_by >> 8) ==
+          19)) // if zip is produced on Unix or macOS (3 and 19 from
+               // section 4.4.2.2 of zip standard)
+        && info.m_external_attr &
+               (0x20 << 24)) { // and has sym link attribute (0x80 is file, 0x40
+                               // is directory)
 #if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) ||              \
     defined(__MINGW32__)
-#else      
-      if (info.m_uncomp_size > MAX_PATH || !mz_zip_reader_extract_to_mem_no_alloc(&zip_archive, i, symlink_to, MAX_PATH, 0, NULL, 0)) {
+#else
+      if (info.m_uncomp_size > MAX_PATH ||
+          !mz_zip_reader_extract_to_mem_no_alloc(&zip_archive, i, symlink_to,
+                                                 MAX_PATH, 0, NULL, 0)) {
         goto out;
       }
       symlink_to[info.m_uncomp_size] = '\0';
diff -u contrib/zip/src/zip.h contrib/zip/src/zip.h
--- contrib/zip/src/zip.h
+++ contrib/zip/src/zip.h
@@ -20,241 +20,251 @@
 #endif
 
 #if !defined(_SSIZE_T_DEFINED) && !defined(_SSIZE_T_DEFINED_) &&               \
-    !defined(_SSIZE_T) && !defined(_SSIZE_T_) && !defined(__ssize_t_defined)
-#define _SSIZE_T
+    !defined(__DEFINED_ssize_t) && !defined(__ssize_t_defined) &&              \
+    !defined(_SSIZE_T) && !defined(_SSIZE_T_) && !defined(_SSIZE_T_DECLARED)
+
 // 64-bit Windows is the only mainstream platform
 // where sizeof(long) != sizeof(void*)
 #ifdef _WIN64
-typedef long long  ssize_t;  /* byte count or error */
+typedef long long ssize_t; /* byte count or error */
 #else
-typedef long  ssize_t;  /* byte count or error */
+typedef long ssize_t; /* byte count or error */
 #endif
+
+#define _SSIZE_T_DEFINED
+#define _SSIZE_T_DEFINED_
+#define __DEFINED_ssize_t
+#define __ssize_t_defined
+#define _SSIZE_T
+#define _SSIZE_T_
+#define _SSIZE_T_DECLARED
+
 #endif
 
 #ifndef MAX_PATH
 #define MAX_PATH 32767 /* # chars in a path name including NULL */
 #endif
 
-#define ZIP_DEFAULT_COMPRESSION_LEVEL 6
+/**
+ * @mainpage
+ *
+ * Documenation for @ref zip.
+ */
 
-/*
-  This data structure is used throughout the library to represent zip archive
-  - forward declaration.
-*/
-struct zip_t;
+/**
+ * @addtogroup zip
+ * @{
+ */
 
-/*
-  Opens zip archive with compression level using the given mode.
+/**
+ * Default zip compression level.
+ */
 
-  Args:
-    zipname: zip archive file name.
-    level: compression level (0-9 are the standard zlib-style levels).
-    mode: file access mode.
-        'r': opens a file for reading/extracting (the file must exists).
-        'w': creates an empty file for writing.
-        'a': appends to an existing archive.
+#define ZIP_DEFAULT_COMPRESSION_LEVEL 6
 
-  Returns:
-    The zip archive handler or NULL on error
-*/
+/**
+ * @struct zip_t
+ *
+ * This data structure is used throughout the library to represent zip archive -
+ * forward declaration.
+ */
+struct zip_t;
+
+/**
+ * Opens zip archive with compression level using the given mode.
+ *
+ * @param zipname zip archive file name.
+ * @param level compression level (0-9 are the standard zlib-style levels).
+ * @param mode file access mode.
+ *        - 'r': opens a file for reading/extracting (the file must exists).
+ *        - 'w': creates an empty file for writing.
+ *        - 'a': appends to an existing archive.
+ *
+ * @return the zip archive handler or NULL on error
+ */
 extern struct zip_t *zip_open(const char *zipname, int level, char mode);
 
-/*
-  Closes the zip archive, releases resources - always finalize.
-
-  Args:
-    zip: zip archive handler.
-*/
+/**
+ * Closes the zip archive, releases resources - always finalize.
+ *
+ * @param zip zip archive handler.
+ */
 extern void zip_close(struct zip_t *zip);
 
-/*
-  Opens an entry by name in the zip archive.
-  For zip archive opened in 'w' or 'a' mode the function will append
-  a new entry. In readonly mode the function tries to locate the entry
-  in global dictionary.
+/**
+ * Determines if the archive has a zip64 end of central directory headers.
+ *
+ * @param zip zip archive handler.
+ *
+ * @return the return code - 1 (true), 0 (false), negative number (< 0) on
+ *         error.
+ */
+extern int zip_is64(struct zip_t *zip);
 
-  Args:
-    zip: zip archive handler.
-    entryname: an entry name in local dictionary.
-
-  Returns:
-    The return code - 0 on success, negative number (< 0) on error.
-*/
+/**
+ * Opens an entry by name in the zip archive.
+ *
+ * For zip archive opened in 'w' or 'a' mode the function will append
+ * a new entry. In readonly mode the function tries to locate the entry
+ * in global dictionary.
+ *
+ * @param zip zip archive handler.
+ * @param entryname an entry name in local dictionary.
+ *
+ * @return the return code - 0 on success, negative number (< 0) on error.
+ */
 extern int zip_entry_open(struct zip_t *zip, const char *entryname);
 
-/*
-  Opens a new entry by index in the zip archive.
-  This function is only valid if zip archive was opened in 'r' (readonly) mode.
-
-  Args:
-    zip: zip archive handler.
-    index: index in local dictionary.
-
-  Returns:
-    The return code - 0 on success, negative number (< 0) on error.
-*/
+/**
+ * Opens a new entry by index in the zip archive.
+ *
+ * This function is only valid if zip archive was opened in 'r' (readonly) mode.
+ *
+ * @param zip zip archive handler.
+ * @param index index in local dictionary.
+ *
+ * @return the return code - 0 on success, negative number (< 0) on error.
+ */
 extern int zip_entry_openbyindex(struct zip_t *zip, int index);
 
-/*
-  Closes a zip entry, flushes buffer and releases resources.
-
-  Args:
-    zip: zip archive handler.
-
-  Returns:
-    The return code - 0 on success, negative number (< 0) on error.
-*/
+/**
+ * Closes a zip entry, flushes buffer and releases resources.
+ *
+ * @param zip zip archive handler.
+ *
+ * @return the return code - 0 on success, negative number (< 0) on error.
+ */
 extern int zip_entry_close(struct zip_t *zip);
 
-/*
-  Returns a local name of the current zip entry.
-  The main difference between user's entry name and local entry name
-  is optional relative path.
-  Following .ZIP File Format Specification - the path stored MUST not contain
-  a drive or device letter, or a leading slash.
-  All slashes MUST be forward slashes '/' as opposed to backwards slashes '\'
-  for compatibility with Amiga and UNIX file systems etc.
-
-  Args:
-    zip: zip archive handler.
-
-  Returns:
-    The pointer to the current zip entry name, or NULL on error.
-*/
+/**
+ * Returns a local name of the current zip entry.
+ *
+ * The main difference between user's entry name and local entry name
+ * is optional relative path.
+ * Following .ZIP File Format Specification - the path stored MUST not contain
+ * a drive or device letter, or a leading slash.
+ * All slashes MUST be forward slashes '/' as opposed to backwards slashes '\'
+ * for compatibility with Amiga and UNIX file systems etc.
+ *
+ * @param zip: zip archive handler.
+ *
+ * @return the pointer to the current zip entry name, or NULL on error.
+ */
 extern const char *zip_entry_name(struct zip_t *zip);
 
-/*
-  Returns an index of the current zip entry.
-
-  Args:
-    zip: zip archive handler.
-
-  Returns:
-    The index on success, negative number (< 0) on error.
-*/
+/**
+ * Returns an index of the current zip entry.
+ *
+ * @param zip zip archive handler.
+ *
+ * @return the index on success, negative number (< 0) on error.
+ */
 extern int zip_entry_index(struct zip_t *zip);
 
-/*
-  Determines if the current zip entry is a directory entry.
-
-  Args:
-    zip: zip archive handler.
-
-  Returns:
-    The return code - 1 (true), 0 (false), negative number (< 0) on error.
-*/
+/**
+ * Determines if the current zip entry is a directory entry.
+ *
+ * @param zip zip archive handler.
+ *
+ * @return the return code - 1 (true), 0 (false), negative number (< 0) on
+ *         error.
+ */
 extern int zip_entry_isdir(struct zip_t *zip);
 
-/*
-  Returns an uncompressed size of the current zip entry.
-
-  Args:
-    zip: zip archive handler.
-
-  Returns:
-    The uncompressed size in bytes.
-*/
+/**
+ * Returns an uncompressed size of the current zip entry.
+ *
+ * @param zip zip archive handler.
+ *
+ * @return the uncompressed size in bytes.
+ */
 extern unsigned long long zip_entry_size(struct zip_t *zip);
 
-/*
-  Returns CRC-32 checksum of the current zip entry.
-
-  Args:
-    zip: zip archive handler.
-
-  Returns:
-    The CRC-32 checksum.
-*/
+/**
+ * Returns CRC-32 checksum of the current zip entry.
+ *
+ * @param zip zip archive handler.
+ *
+ * @return the CRC-32 checksum.
+ */
 extern unsigned int zip_entry_crc32(struct zip_t *zip);
 
-/*
-  Compresses an input buffer for the current zip entry.
-
-  Args:
-    zip: zip archive handler.
-    buf: input buffer.
-    bufsize: input buffer size (in bytes).
-
-  Returns:
-    The return code - 0 on success, negative number (< 0) on error.
-*/
+/**
+ * Compresses an input buffer for the current zip entry.
+ *
+ * @param zip zip archive handler.
+ * @param buf input buffer.
+ * @param bufsize input buffer size (in bytes).
+ *
+ * @return the return code - 0 on success, negative number (< 0) on error.
+ */
 extern int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize);
 
-/*
-  Compresses a file for the current zip entry.
-
-  Args:
-    zip: zip archive handler.
-    filename: input file.
-
-  Returns:
-    The return code - 0 on success, negative number (< 0) on error.
-*/
+/**
+ * Compresses a file for the current zip entry.
+ *
+ * @param zip zip archive handler.
+ * @param filename input file.
+ *
+ * @return the return code - 0 on success, negative number (< 0) on error.
+ */
 extern int zip_entry_fwrite(struct zip_t *zip, const char *filename);
 
-/*
-  Extracts the current zip entry into output buffer.
-  The function allocates sufficient memory for a output buffer.
-
-  Args:
-    zip: zip archive handler.
-    buf: output buffer.
-    bufsize: output buffer size (in bytes).
-
-  Note:
-    - remember to release memory allocated for a output buffer.
-    - for large entries, please take a look at zip_entry_extract function.
-
-  Returns:
-    The return code - the number of bytes actually read on success.
-    Otherwise a -1 on error.
-*/
+/**
+ * Extracts the current zip entry into output buffer.
+ *
+ * The function allocates sufficient memory for a output buffer.
+ *
+ * @param zip zip archive handler.
+ * @param buf output buffer.
+ * @param bufsize output buffer size (in bytes).
+ *
+ * @note remember to release memory allocated for a output buffer.
+ *       for large entries, please take a look at zip_entry_extract function.
+ *
+ * @return the return code - the number of bytes actually read on success.
+ *         Otherwise a -1 on error.
+ */
 extern ssize_t zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize);
 
-/*
-  Extracts the current zip entry into a memory buffer using no memory
-  allocation.
+/**
+ * Extracts the current zip entry into a memory buffer using no memory
+ * allocation.
+ *
+ * @param zip zip archive handler.
+ * @param buf preallocated output buffer.
+ * @param bufsize output buffer size (in bytes).
+ *
+ * @note ensure supplied output buffer is large enough.
+ *       zip_entry_size function (returns uncompressed size for the current
+ *       entry) can be handy to estimate how big buffer is needed. for large
+ * entries, please take a look at zip_entry_extract function.
+ *
+ * @return the return code - the number of bytes actually read on success.
+ *         Otherwise a -1 on error (e.g. bufsize is not large enough).
+ */
+extern ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf,
+                                     size_t bufsize);
 
-  Args:
-    zip: zip archive handler.
-    buf: preallocated output buffer.
-    bufsize: output buffer size (in bytes).
-
-  Note:
-    - ensure supplied output buffer is large enough.
-    - zip_entry_size function (returns uncompressed size for the current entry)
-      can be handy to estimate how big buffer is needed.
-    - for large entries, please take a look at zip_entry_extract function.
-
-  Returns:
-    The return code - the number of bytes actually read on success.
-    Otherwise a -1 on error (e.g. bufsize is not large enough).
-*/
-extern ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, size_t bufsize);
-
-/*
-  Extracts the current zip entry into output file.
-
-  Args:
-    zip: zip archive handler.
-    filename: output file.
-
-  Returns:
-    The return code - 0 on success, negative number (< 0) on error.
-*/
+/**
+ * Extracts the current zip entry into output file.
+ *
+ * @param zip zip archive handler.
+ * @param filename output file.
+ *
+ * @return the return code - 0 on success, negative number (< 0) on error.
+ */
 extern int zip_entry_fread(struct zip_t *zip, const char *filename);
 
-/*
-  Extracts the current zip entry using a callback function (on_extract).
-
-  Args:
-    zip: zip archive handler.
-    on_extract: callback function.
-    arg: opaque pointer (optional argument,
-                         which you can pass to the on_extract callback)
-
-   Returns:
-    The return code - 0 on success, negative number (< 0) on error.
+/**
+ * Extracts the current zip entry using a callback function (on_extract).
+ *
+ * @param zip zip archive handler.
+ * @param on_extract callback function.
+ * @param arg opaque pointer (optional argument, which you can pass to the
+ *        on_extract callback)
+ *
+ * @return the return code - 0 on success, negative number (< 0) on error.
  */
 extern int
 zip_entry_extract(struct zip_t *zip,
@@ -262,52 +272,48 @@
                                        const void *data, size_t size),
                   void *arg);
 
-/*
-  Returns the number of all entries (files and directories) in the zip archive.
-
-  Args:
-    zip: zip archive handler.
-
-  Returns:
-    The return code - the number of entries on success,
-    negative number (< 0) on error.
-*/
+/**
+ * Returns the number of all entries (files and directories) in the zip archive.
+ *
+ * @param zip zip archive handler.
+ *
+ * @return the return code - the number of entries on success, negative number
+ *         (< 0) on error.
+ */
 extern int zip_total_entries(struct zip_t *zip);
 
-/*
-  Creates a new archive and puts files into a single zip archive.
-
-  Args:
-    zipname: zip archive file.
-    filenames: input files.
-    len: number of input files.
-
-  Returns:
-    The return code - 0 on success, negative number (< 0) on error.
-*/
+/**
+ * Creates a new archive and puts files into a single zip archive.
+ *
+ * @param zipname zip archive file.
+ * @param filenames input files.
+ * @param len: number of input files.
+ *
+ * @return the return code - 0 on success, negative number (< 0) on error.
+ */
 extern int zip_create(const char *zipname, const char *filenames[], size_t len);
 
-/*
-  Extracts a zip archive file into directory.
-
-  If on_extract_entry is not NULL, the callback will be called after
-  successfully extracted each zip entry.
-  Returning a negative value from the callback will cause abort and return an
-  error. The last argument (void *arg) is optional, which you can use to pass
-  data to the on_extract_entry callback.
-
-  Args:
-    zipname: zip archive file.
-    dir: output directory.
-    on_extract_entry: on extract callback.
-    arg: opaque pointer.
-
-  Returns:
-    The return code - 0 on success, negative number (< 0) on error.
-*/
+/**
+ * Extracts a zip archive file into directory.
+ *
+ * If on_extract_entry is not NULL, the callback will be called after
+ * successfully extracted each zip entry.
+ * Returning a negative value from the callback will cause abort and return an
+ * error. The last argument (void *arg) is optional, which you can use to pass
+ * data to the on_extract_entry callback.
+ *
+ * @param zipname zip archive file.
+ * @param dir output directory.
+ * @param on_extract_entry on extract callback.
+ * @param arg opaque pointer.
+ *
+ * @return the return code - 0 on success, negative number (< 0) on error.
+ */
 extern int zip_extract(const char *zipname, const char *dir,
                        int (*on_extract_entry)(const char *filename, void *arg),
                        void *arg);
+
+/** @} */
 
 #ifdef __cplusplus
 }
diff -u contrib/zip/test/CMakeLists.txt contrib/zip/test/CMakeLists.txt
--- contrib/zip/test/CMakeLists.txt
+++ contrib/zip/test/CMakeLists.txt
@@ -1,19 +1,11 @@
 cmake_minimum_required(VERSION 2.8)
 
-if ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang")
-  if(ENABLE_COVERAGE)
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g ")
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0")
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs")
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ftest-coverage")
-    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage")
-  endif()
-endif ()
-
 # test
-include_directories(../src)
-add_executable(test.exe test.c ../src/zip.c)
-add_executable(test_miniz.exe test_miniz.c)
+set(test_out test.out)
 
-add_test(NAME test COMMAND test.exe)
-add_test(NAME test_miniz COMMAND test_miniz.exe)
+add_executable(${test_out} test.c)
+target_link_libraries(${test_out} zip)
+
+add_test(NAME ${test_out} COMMAND ${test_out})
+
+set(test_out ${test_out} PARENT_SCOPE)
diff -u contrib/zip/test/test.c contrib/zip/test/test.c
--- contrib/zip/test/test.c
+++ contrib/zip/test/test.c
@@ -29,6 +29,8 @@
 #define XFILE "7.txt\0"
 #define XMODE 0100777
 
+#define UNIXMODE 0100644
+
 #define UNUSED(x) (void)x
 
 static int total_entries = 0;
@@ -45,7 +47,7 @@
   assert(CRC32DATA1 == zip_entry_crc32(zip));
   ++total_entries;
   assert(0 == zip_entry_close(zip));
-
+  assert(0 == zip_is64(zip));
   zip_close(zip);
 }
 
@@ -90,6 +92,7 @@
   size_t buftmp;
   struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
   assert(zip != NULL);
+  assert(0 == zip_is64(zip));
 
   assert(0 == zip_entry_open(zip, "test\\test-1.txt"));
   assert(strlen(TESTDATA1) == zip_entry_size(zip));
@@ -102,7 +105,8 @@
   assert(0 == zip_entry_close(zip));
   free(buf);
   buf = NULL;
-  
+  bufsize = 0;
+
   assert(0 == zip_entry_open(zip, "test/test-2.txt"));
   assert(strlen(TESTDATA2) == zip_entry_size(zip));
   assert(CRC32DATA2 == zip_entry_crc32(zip));
@@ -131,7 +135,8 @@
   assert(0 == zip_entry_close(zip));
   free(buf);
   buf = NULL;
-  
+  bufsize = 0;
+
   buftmp = strlen(TESTDATA1);
   buf = calloc(buftmp, sizeof(char));
   assert(0 == zip_entry_open(zip, "test/test-1.txt"));
@@ -306,6 +311,7 @@
   assert(0 == zip_entry_open(zip, WFILE));
   assert(0 == zip_entry_fwrite(zip, WFILE));
   assert(0 == zip_entry_close(zip));
+  assert(0 == zip_is64(zip));
 
   zip_close(zip);
   remove(WFILE);
@@ -433,6 +439,35 @@
   remove(ZIPNAME);
 }
 
+static void test_unix_permissions(void) {
+#if defined(_WIN64) || defined(_WIN32) || defined(__WIN32__)
+#else
+  // UNIX or APPLE
+  struct MZ_FILE_STAT_STRUCT file_stats;
+
+  remove(ZIPNAME);
+
+  struct zip_t *zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
+  assert(zip != NULL);
+
+  assert(0 == zip_entry_open(zip, RFILE));
+  assert(0 == zip_entry_write(zip, TESTDATA1, strlen(TESTDATA1)));
+  assert(0 == zip_entry_close(zip));
+
+  zip_close(zip);
+
+  remove(RFILE);
+
+  assert(0 == zip_extract(ZIPNAME, ".", NULL, NULL));
+
+  assert(0 == MZ_FILE_STAT(RFILE, &file_stats));
+  assert(UNIXMODE == file_stats.st_mode);
+
+  remove(RFILE);
+  remove(ZIPNAME);
+#endif
+}
+
 int main(int argc, char *argv[]) {
   UNUSED(argc);
   UNUSED(argv);
@@ -453,6 +488,7 @@
   test_write_permissions();
   test_exe_permissions();
   test_mtime();
+  test_unix_permissions();
 
   remove(ZIPNAME);
   return 0;
unchanged:
--- contrib/zip/test/test_miniz.c
+++ contrib/zip/test/test_miniz.c
@@ -23,16 +23,39 @@ int main(int argc, char *argv[]) {
   uint step = 0;
   int cmp_status;
   uLong src_len = (uLong)strlen(s_pStr);
-  uLong cmp_len = compressBound(src_len);
   uLong uncomp_len = src_len;
+  uLong cmp_len;
   uint8 *pCmp, *pUncomp;
+  size_t sz;
   uint total_succeeded = 0;
   (void)argc, (void)argv;
 
   printf("miniz.c version: %s\n", MZ_VERSION);
 
   do {
+    pCmp = (uint8 *)tdefl_compress_mem_to_heap(s_pStr, src_len, &cmp_len, 0);
+    if (!pCmp) {
+      printf("tdefl_compress_mem_to_heap failed\n");
+      return EXIT_FAILURE;
+    }
+    if (src_len <= cmp_len) {
+      printf("tdefl_compress_mem_to_heap failed: from %u to %u bytes\n",
+             (mz_uint32)uncomp_len, (mz_uint32)cmp_len);
+      free(pCmp);
+      return EXIT_FAILURE;
+    }
+
+    sz = tdefl_compress_mem_to_mem(pCmp, cmp_len, s_pStr, src_len, 0);
+    if (sz != cmp_len) {
+      printf("tdefl_compress_mem_to_mem failed: expected %u, got %u\n",
+             (mz_uint32)cmp_len, (mz_uint32)sz);
+      free(pCmp);
+      return EXIT_FAILURE;
+    }
+
     // Allocate buffers to hold compressed and uncompressed data.
+    free(pCmp);
+    cmp_len = compressBound(src_len);
     pCmp = (mz_uint8 *)malloc((size_t)cmp_len);
     pUncomp = (mz_uint8 *)malloc((size_t)src_len);
     if ((!pCmp) || (!pUncomp)) {
