package/assimp: fix build on musl
authorFabrice Fontaine <fontaine.fabrice@gmail.com>
Thu, 28 Jan 2021 20:04:35 +0000 (21:04 +0100)
committerArnout Vandecappelle (Essensium/Mind) <arnout@mind.be>
Thu, 20 May 2021 19:45:23 +0000 (21:45 +0200)
Fixes:
 - http://autobuild.buildroot.net/results/7c2db184ee200d1719308f38f42382bb39d8d5c6

Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
Signed-off-by: Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be>
package/assimp/0002-closes-2733-update-of-zlip-to-fix-gcc-build-for-v9-2-0-32-bit.patch [new file with mode: 0644]
package/assimp/0003-closes-2954-upgrade-to-latest-greatest.patch [new file with mode: 0644]

diff --git a/package/assimp/0002-closes-2733-update-of-zlip-to-fix-gcc-build-for-v9-2-0-32-bit.patch b/package/assimp/0002-closes-2733-update-of-zlip-to-fix-gcc-build-for-v9-2-0-32-bit.patch
new file mode 100644 (file)
index 0000000..4b86cc5
--- /dev/null
@@ -0,0 +1,1638 @@
+From f78446b14aff46db2ef27d062a275b6a01fd68b1 Mon Sep 17 00:00:00 2001
+From: Kim Kulling <kim.kulling@googlemail.com>
+Date: Tue, 19 Nov 2019 20:30:40 +0100
+Subject: [PATCH] closes https://github.com/assimp/assimp/issues/2733: update
+ of zlip to fix gcc build for v9.2.0 32 bit
+
+[Retrieved (and updated to remove .gitignore and appveyor.yml) from:
+https://github.com/assimp/assimp/commit/f78446b14aff46db2ef27d062a275b6a01fd68b1]
+Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
+---
+ contrib/zip/.gitignore          |   2 +
+ contrib/zip/CMakeLists.txt      |  83 +++++-
+ contrib/zip/README.md           |  12 +-
+ contrib/zip/appveyor.yml        |   2 +-
+ contrib/zip/src/miniz.h         | 457 ++++++++++++++++++++++++++++----
+ contrib/zip/src/zip.c           |  62 +++--
+ contrib/zip/src/zip.h           | 457 ++++++++++++++++----------------
+ contrib/zip/test/CMakeLists.txt |  27 +-
+ contrib/zip/test/test.c         |  38 ++-
+ contrib/zip/test/test_miniz.c   |  25 +-
+ 10 files changed, 821 insertions(+), 344 deletions(-)
+
+diff --git a/contrib/zip/CMakeLists.txt b/contrib/zip/CMakeLists.txt
+index b46dbb1db0..77916d2e14 100644
+--- a/contrib/zip/CMakeLists.txt
++++ b/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.15")
+ 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")
+@@ -12,28 +16,80 @@ elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR
+         "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR
+         "${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang")
+   set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Wall -Wextra -Werror -pedantic")
++  if(ENABLE_COVERAGE)
++    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
++    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage")
++  endif()
+ endif (MSVC)
+ # 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} ${test_miniz_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)
+@@ -45,3 +101,12 @@ if(NOT TARGET uninstall)
+     add_custom_target(uninstall
+         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 --git a/contrib/zip/README.md b/contrib/zip/README.md
+index d5fb8cd203..14eb9a34c8 100644
+--- a/contrib/zip/README.md
++++ b/contrib/zip/README.md
+@@ -71,7 +71,7 @@ int arg = 2;
+ 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 +89,7 @@ zip_close(zip);
+ 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 +110,7 @@ zip_close(zip);
+ 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 +144,7 @@ free(buf.data);
+ ```
+-*   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 +157,7 @@ struct zip_t *zip = zip_open("foo.zip", 0, 'r');
+ 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 +174,7 @@ for (i = 0; i < n; ++i) {
+ zip_close(zip);
+ ```
+-## Bindings
++# Bindings
+ Compile zip library as a dynamic library.
+ ```shell
+ $ mkdir build
+diff --git a/contrib/zip/src/miniz.h b/contrib/zip/src/miniz.h
+index 2c27a94d8d..c4fcfb83e6 100644
+--- a/contrib/zip/src/miniz.h
++++ b/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 --git a/contrib/zip/src/zip.c b/contrib/zip/src/zip.c
+index ff3a8fe1e6..1abcfd8fd1 100644
+--- a/contrib/zip/src/zip.c
++++ b/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 @@ int symlink(const char *target, const char *linkpath); // needed on Linux
+ #endif
+ #ifndef ISSLASH
+-#define ISSLASH(C) ((C) == '/')
++#define ISSLASH(C) ((C) == '/' || (C) == '\\')
+ #endif
+ #define CLEANUP(ptr)                                                           \
+@@ -78,26 +77,34 @@ static const char *base_name(const char *name) {
+   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;
+   }
+@@ -279,7 +286,14 @@ int zip_entry_open(struct zip_t *zip, const char *entryname) {
+   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 +674,7 @@ ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, size_t bufsize) {
+   }
+   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 +684,7 @@ ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, size_t bufsize) {
+ 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) {
+@@ -875,12 +886,19 @@ int zip_extract(const char *zipname, const char *dir,
+       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 --git a/contrib/zip/src/zip.h b/contrib/zip/src/zip.h
+index 5f39df50ad..a48d64d6de 100644
+--- a/contrib/zip/src/zip.h
++++ b/contrib/zip/src/zip.h
+@@ -20,241 +20,240 @@ extern "C" {
+ #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_)
++
+ // 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_
++
+ #endif
+ #ifndef MAX_PATH
+ #define MAX_PATH 32767 /* # chars in a path name including NULL */
+ #endif
++/**
++ * @mainpage
++ *
++ * Documenation for @ref zip.
++ */
++
++/**
++ * @addtogroup zip
++ * @{
++ */
++
++/**
++ * Default zip compression level.
++ */
++
+ #define ZIP_DEFAULT_COMPRESSION_LEVEL 6
+-/*
+-  This data structure is used throughout the library to represent zip archive
+-  - forward declaration.
+-*/
++/**
++ * @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.
+-
+-  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.
+-
+-  Returns:
+-    The zip archive handler or NULL on error
+-*/
++/**
++ * 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.
+-
+-  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.
+-
+-  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 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);
++
++/**
++ * 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,53 +261,49 @@ zip_entry_extract(struct zip_t *zip,
+                                        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
+ }
+ #endif
+diff --git a/contrib/zip/test/CMakeLists.txt b/contrib/zip/test/CMakeLists.txt
+index 9b2a8db106..cc060b00fe 100644
+--- a/contrib/zip/test/CMakeLists.txt
++++ b/contrib/zip/test/CMakeLists.txt
+@@ -1,19 +1,16 @@
+ 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)
++set(test_miniz_out test_miniz.out)
++
++add_executable(${test_out} test.c)
++target_link_libraries(${test_out} zip)
++add_executable(${test_miniz_out} test_miniz.c)
++target_link_libraries(${test_miniz_out} zip)
++
++add_test(NAME ${test_out} COMMAND ${test_out})
++add_test(NAME ${test_miniz_out} COMMAND ${test_miniz_out})
+-add_test(NAME test COMMAND test.exe)
+-add_test(NAME test_miniz COMMAND test_miniz.exe)
++set(test_out ${test_out} PARENT_SCOPE)
++set(test_miniz_out ${test_miniz_out} PARENT_SCOPE)
+diff --git a/contrib/zip/test/test.c b/contrib/zip/test/test.c
+index 454430533a..a9b2ddab1e 100644
+--- a/contrib/zip/test/test.c
++++ b/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;
+@@ -102,7 +104,8 @@ static void test_read(void) {
+   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 +134,8 @@ static void test_read(void) {
+   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"));
+@@ -433,6 +437,35 @@ static void test_mtime(void) {
+   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 +486,7 @@ int main(int argc, char *argv[]) {
+   test_write_permissions();
+   test_exe_permissions();
+   test_mtime();
++  test_unix_permissions();
+   remove(ZIPNAME);
+   return 0;
+diff --git a/contrib/zip/test/test_miniz.c b/contrib/zip/test/test_miniz.c
+index ebc0564dc3..babcaecdb6 100644
+--- a/contrib/zip/test/test_miniz.c
++++ b/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)) {
diff --git a/package/assimp/0003-closes-2954-upgrade-to-latest-greatest.patch b/package/assimp/0003-closes-2954-upgrade-to-latest-greatest.patch
new file mode 100644 (file)
index 0000000..9bd2463
--- /dev/null
@@ -0,0 +1,243 @@
+From bb3db0ebaffc6b76de256e597ec1d1e4d2a6663f Mon Sep 17 00:00:00 2001
+From: kimkulling <kim.kulling@googlemail.com>
+Date: Mon, 9 Mar 2020 10:51:26 +0100
+Subject: [PATCH] closes https://github.com/assimp/assimp/issues/2954: upgrade
+ to latest greatest.
+
+[Retrieved from:
+https://github.com/assimp/assimp/commit/bb3db0ebaffc6b76de256e597ec1d1e4d2a6663f]
+Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
+---
+ contrib/zip/CMakeLists.txt      |  8 ++----
+ contrib/zip/README.md           | 51 +++++++++++++++++++++++++++++++--
+ contrib/zip/src/zip.c           | 17 ++++++++++-
+ contrib/zip/src/zip.h           | 13 ++++++++-
+ contrib/zip/test/CMakeLists.txt |  5 ----
+ contrib/zip/test/test.c         |  4 ++-
+ 6 files changed, 81 insertions(+), 17 deletions(-)
+
+diff --git a/contrib/zip/CMakeLists.txt b/contrib/zip/CMakeLists.txt
+index 77916d2e14..f194649ede 100644
+--- a/contrib/zip/CMakeLists.txt
++++ b/contrib/zip/CMakeLists.txt
+@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.0)
+ project(zip
+   LANGUAGES C
+-  VERSION "0.1.15")
++  VERSION "0.1.18")
+ set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
+ option(CMAKE_DISABLE_TESTING "Disable test creation" OFF)
+@@ -16,10 +16,6 @@ elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR
+         "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR
+         "${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang")
+   set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Wall -Wextra -Werror -pedantic")
+-  if(ENABLE_COVERAGE)
+-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage")
+-    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage")
+-  endif()
+ endif (MSVC)
+ # zip
+@@ -35,7 +31,7 @@ if (NOT CMAKE_DISABLE_TESTING)
+   enable_testing()
+   add_subdirectory(test)
+   find_package(Sanitizers)
+-  add_sanitizers(${PROJECT_NAME} ${test_out} ${test_miniz_out})
++  add_sanitizers(${PROJECT_NAME} ${test_out})
+ endif()
+ ####
+diff --git a/contrib/zip/README.md b/contrib/zip/README.md
+index 14eb9a34c8..bdd0822b67 100644
+--- a/contrib/zip/README.md
++++ b/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
+@@ -213,6 +211,53 @@ func main() {
+ }
+ ```
++### 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);
++    }
++}
++```
++
+ ### Ruby (ffi)
+ Install _ffi_ gem.
+ ```shell
+diff --git a/contrib/zip/src/zip.c b/contrib/zip/src/zip.c
+index 1abcfd8fd1..3b2821e6a3 100644
+--- a/contrib/zip/src/zip.c
++++ b/contrib/zip/src/zip.c
+@@ -222,6 +222,20 @@ void zip_close(struct zip_t *zip) {
+   }
+ }
++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;
+@@ -794,7 +808,8 @@ int zip_create(const char *zipname, const char *filenames[], size_t len) {
+     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) {
+diff --git a/contrib/zip/src/zip.h b/contrib/zip/src/zip.h
+index a48d64d6de..cd3ab5cd00 100644
+--- a/contrib/zip/src/zip.h
++++ b/contrib/zip/src/zip.h
+@@ -21,7 +21,7 @@ extern "C" {
+ #if !defined(_SSIZE_T_DEFINED) && !defined(_SSIZE_T_DEFINED_) &&               \
+     !defined(__DEFINED_ssize_t) && !defined(__ssize_t_defined) &&              \
+-    !defined(_SSIZE_T) && !defined(_SSIZE_T_)
++    !defined(_SSIZE_T) && !defined(_SSIZE_T_) && !defined(_SSIZE_T_DECLARED)
+ // 64-bit Windows is the only mainstream platform
+ // where sizeof(long) != sizeof(void*)
+@@ -37,6 +37,7 @@ typedef long ssize_t; /* byte count or error */
+ #define __ssize_t_defined
+ #define _SSIZE_T
+ #define _SSIZE_T_
++#define _SSIZE_T_DECLARED
+ #endif
+@@ -90,6 +91,16 @@ extern struct zip_t *zip_open(const char *zipname, int level, char mode);
+  */
+ extern void zip_close(struct zip_t *zip);
++/**
++ * 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);
++
+ /**
+  * Opens an entry by name in the zip archive.
+  *
+diff --git a/contrib/zip/test/CMakeLists.txt b/contrib/zip/test/CMakeLists.txt
+index cc060b00fe..1224115858 100644
+--- a/contrib/zip/test/CMakeLists.txt
++++ b/contrib/zip/test/CMakeLists.txt
+@@ -2,15 +2,10 @@ cmake_minimum_required(VERSION 2.8)
+ # test
+ set(test_out test.out)
+-set(test_miniz_out test_miniz.out)
+ add_executable(${test_out} test.c)
+ target_link_libraries(${test_out} zip)
+-add_executable(${test_miniz_out} test_miniz.c)
+-target_link_libraries(${test_miniz_out} zip)
+ add_test(NAME ${test_out} COMMAND ${test_out})
+-add_test(NAME ${test_miniz_out} COMMAND ${test_miniz_out})
+ set(test_out ${test_out} PARENT_SCOPE)
+-set(test_miniz_out ${test_miniz_out} PARENT_SCOPE)
+diff --git a/contrib/zip/test/test.c b/contrib/zip/test/test.c
+index a9b2ddab1e..9cc2248ac0 100644
+--- a/contrib/zip/test/test.c
++++ b/contrib/zip/test/test.c
+@@ -47,7 +47,7 @@ static void test_write(void) {
+   assert(CRC32DATA1 == zip_entry_crc32(zip));
+   ++total_entries;
+   assert(0 == zip_entry_close(zip));
+-
++  assert(0 == zip_is64(zip));
+   zip_close(zip);
+ }
+@@ -92,6 +92,7 @@ static void test_read(void) {
+   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));
+@@ -310,6 +311,7 @@ static void test_fwrite(void) {
+   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);