Browse Source

Merge https://github.com/assimp/assimp

Marco Di Benedetto 6 years ago
parent
commit
faf12c6f5f
45 changed files with 2928 additions and 1203 deletions
  1. 10 1
      CMakeLists.txt
  2. 7 4
      INSTALL
  3. 1 0
      Readme.md
  4. 4 4
      appveyor.yml
  5. 1 1
      code/AssbinLoader.cpp
  6. 108 25
      code/CMakeLists.txt
  7. 1 1
      code/FBXConverter.cpp
  8. 10 10
      code/FindInstancesProcess.cpp
  9. 3 1
      code/ImproveCacheLocality.cpp
  10. 1 4
      code/LWOMaterial.cpp
  11. 5 5
      code/MD3FileData.h
  12. 27 25
      code/MD3Loader.cpp
  13. 3 0
      code/ObjFileData.h
  14. 1 1
      code/ObjFileImporter.cpp
  15. 4 2
      code/ObjFileParser.cpp
  16. 1 1
      code/ObjFileParser.h
  17. 3 3
      code/PostStepRegistry.cpp
  18. 2 2
      code/SMDLoader.cpp
  19. 19 18
      code/SortByPTypeProcess.cpp
  20. 20 20
      code/StandardShapes.cpp
  21. 46 32
      code/ValidateDataStructure.cpp
  22. 18 0
      contrib/zip/.gitignore
  23. 18 0
      contrib/zip/.travis.sh
  24. 15 3
      contrib/zip/.travis.yml
  25. 36 7
      contrib/zip/CMakeLists.txt
  26. 249 79
      contrib/zip/README.md
  27. 1 1
      contrib/zip/appveyor.yml
  28. 55 0
      contrib/zip/cmake/asan-wrapper
  29. 23 0
      contrib/zip/cmake/cmake_uninstall.cmake.in
  30. 517 317
      contrib/zip/src/miniz.h
  31. 782 496
      contrib/zip/src/zip.c
  32. 135 12
      contrib/zip/src/zip.h
  33. 12 0
      contrib/zip/test/CMakeLists.txt
  34. 415 61
      contrib/zip/test/test.c
  35. 104 0
      contrib/zip/test/test_miniz.c
  36. 12 0
      doc/dox.h
  37. 9 6
      include/assimp/material.h
  38. 4 4
      scripts/StepImporter/CppGenerator.py
  39. 2 2
      scripts/StepImporter/StepReaderGen.cpp.template
  40. 18 19
      scripts/StepImporter/StepReaderGen.h.template
  41. 2 2
      test/unit/utSTLImportExport.cpp
  42. 199 0
      test/unit/utValidateDataStructure.cpp
  43. 18 26
      tools/assimp_cmd/Info.cpp
  44. 6 7
      tools/assimp_cmd/WriteDumb.cpp
  45. 1 1
      tools/assimp_view/SceneAnimator.cpp

+ 10 - 1
CMakeLists.txt

@@ -157,7 +157,7 @@ SET (PROJECT_VERSION "${ASSIMP_VERSION}")
 
 SET( ASSIMP_PACKAGE_VERSION "0" CACHE STRING "the package-specific version used for uploading the sources" )
 
-# Enable C++1 globally
+# Enable C++11 support globally
 set_property( GLOBAL PROPERTY CXX_STANDARD 11 )
 
 IF(NOT IGNORE_GIT_HASH)
@@ -351,6 +351,15 @@ IF( NOT ZLIB_FOUND )
   INCLUDE(CheckIncludeFile)
   INCLUDE(CheckTypeSize)
   INCLUDE(CheckFunctionExists)
+
+  # Explicitly turn off ASM686 and AMD64 cmake options.
+  # The AMD64 option causes a build failure on MSVC and the ASM builds seem to have problems:
+  #		https://github.com/madler/zlib/issues/41#issuecomment-125848075
+  # Also prevents these options from "polluting" the cmake options if assimp is being
+  # included as a submodule.
+  set( ASM686 FALSE CACHE INTERNAL "Override ZLIB flag to turn off assembly" FORCE )
+  set( AMD64 FALSE CACHE INTERNAL "Override ZLIB flag to turn off assembly" FORCE )
+
   # compile from sources
   ADD_SUBDIRECTORY(contrib/zlib)
   SET(ZLIB_FOUND 1)

+ 7 - 4
INSTALL

@@ -35,13 +35,16 @@ http://www.cmake.org/.
 
 For Unix:
 
-1. cmake CMakeLists.txt -G 'Unix Makefiles'
-2. make
+1. mkdir build && cd build
+2. cmake .. -G 'Unix Makefiles'
+3. make -j4
 
 For Windows:
 1. Open a command prompt
-2. cmake CMakeLists.txt
-2. Open your default IDE and build it
+2. mkdir build
+3. cd build
+4. cmake ..
+5. cmake --build .
 
 For iOS:
 Just check the following project, which deploys a compiler toolchain for different iOS-versions: https://github.com/assimp/assimp/tree/master/port/iOS

+ 1 - 0
Readme.md

@@ -128,6 +128,7 @@ Take a look into the https://github.com/assimp/assimp/blob/master/Build.md file.
 * [Javascript (Alpha)](https://github.com/makc/assimp2json)
 * [Unity 3d Plugin](https://www.assetstore.unity3d.com/en/#!/content/91777)
 * [JVM](https://github.com/kotlin-graphics/assimp) Full jvm port (current [status](https://github.com/kotlin-graphics/assimp/wiki/Status))
+* [HAXE-Port](https://github.com/longde123/assimp-haxe) The Assimp-HAXE-port.
 
 ### Other tools ###
 [open3mod](https://github.com/acgessler/open3mod) is a powerful 3D model viewer based on Assimp's import and export abilities.

+ 4 - 4
appveyor.yml

@@ -15,8 +15,8 @@ matrix:
     
 image:
   - Visual Studio 2013
-#  - Visual Studio 2015
-#  - Visual Studio 2017
+  - Previous Visual Studio 2015
+  - Previous Visual Studio 2017
     
 platform:
   - Win32
@@ -28,8 +28,8 @@ install:
   - set PATH=C:\Ruby24-x64\bin;%PATH%
   - set CMAKE_DEFINES -DASSIMP_WERROR=ON
   - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2013" set CMAKE_GENERATOR_NAME=Visual Studio 12 2013
-  - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" set CMAKE_GENERATOR_NAME=Visual Studio 14 2015
-  - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" set CMAKE_GENERATOR_NAME=Visual Studio 15 2017
+  - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Previous Visual Studio 2015" set CMAKE_GENERATOR_NAME=Visual Studio 14 2015
+  - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Previous Visual Studio 2017" set CMAKE_GENERATOR_NAME=Visual Studio 15 2017
   - if "%platform%"=="x64" set CMAKE_GENERATOR_NAME=%CMAKE_GENERATOR_NAME% Win64
   - cmake %CMAKE_DEFINES% -G "%CMAKE_GENERATOR_NAME%"
   - set PATH=%PATH%;"C:\\Program Files (x86)\\Inno Setup 5"

+ 1 - 1
code/AssbinLoader.cpp

@@ -708,7 +708,7 @@ void AssbinImporter::InternReadFile( const std::string& pFile, aiScene* pScene,
 
         unsigned char * uncompressedData = new unsigned char[ uncompressedSize ];
 
-        int res = uncompress( uncompressedData, &uncompressedSize, compressedData, len );
+        int res = uncompress( uncompressedData, &uncompressedSize, compressedData, (uLong) len );
         if(res != Z_OK)
         {
             delete [] uncompressedData;

+ 108 - 25
code/CMakeLists.txt

@@ -216,7 +216,7 @@ ENDIF ( ASSIMP_BUILD_NONFREE_C4D_IMPORTER )
 # ASSIMP_BUILD_XXX_IMPORTER to FALSE for each importer
 # if this variable is set to FALSE, the user can manually enable importers by setting
 # ASSIMP_BUILD_XXX_IMPORTER to TRUE for each importer
-OPTION(ASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT "default value of all ASSIMP_BUILD_XXX_IMPORTER value" TRUE)
+OPTION(ASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT "default value of all ASSIMP_BUILD_XXX_IMPORTER values" TRUE)
 
 # macro to add the CMake Option ADD_ASSIMP_IMPORTER_<name> which enables compile of loader
 # this way selective loaders can be compiled (reduces filesize + compile time)
@@ -232,19 +232,51 @@ MACRO(ADD_ASSIMP_IMPORTER name)
   IF (ASSIMP_IMPORTER_ENABLED)
     LIST(APPEND ASSIMP_LOADER_SRCS ${ARGN})
     SET(ASSIMP_IMPORTERS_ENABLED "${ASSIMP_IMPORTERS_ENABLED} ${name}")
-    SET(${name}_SRCS ${ARGN})
     SOURCE_GROUP(${name} FILES ${ARGN})
   ELSE()
     SET(${name}_SRC "")
     SET(ASSIMP_IMPORTERS_DISABLED "${ASSIMP_IMPORTERS_DISABLED} ${name}")
     add_definitions(-DASSIMP_BUILD_NO_${name}_IMPORTER)
+  ENDIF()
+ENDMACRO()
+
+# if this variable is set to TRUE, the user can manually disable exporters by setting
+# ASSIMP_BUILD_XXX_EXPORTER to FALSE for each exporter
+# if this variable is set to FALSE, the user can manually enable exporters by setting
+# ASSIMP_BUILD_XXX_EXPORTER to TRUE for each exporter
+OPTION(ASSIMP_BUILD_ALL_EXPORTERS_BY_DEFAULT "default value of all ASSIMP_BUILD_XXX_EXPORTER values" TRUE)
+
+# macro to add the CMake Option ADD_ASSIMP_IMPORTER_<name> which enables compile of loader
+# this way selective loaders can be compiled (reduces filesize + compile time)
+MACRO(ADD_ASSIMP_EXPORTER name)
+  IF (ASSIMP_NO_EXPORT)
+	set(ASSIMP_EXPORTER_ENABLED FALSE)
+  ELSEIF (ASSIMP_BUILD_ALL_EXPORTERS_BY_DEFAULT)
+    set(ASSIMP_EXPORTER_ENABLED TRUE)
+    IF (DEFINED ASSIMP_BUILD_${name}_EXPORTER AND NOT ASSIMP_BUILD_${name}_EXPORTER)
+      set(ASSIMP_EXPORTER_ENABLED FALSE)
+    ENDIF ()
+  ELSE ()
+    set(ASSIMP_EXPORTER_ENABLED ${ASSIMP_BUILD_${name}_EXPORTER})
+  ENDIF ()
+  
+  IF (ASSIMP_EXPORTER_ENABLED)
+    SET(ASSIMP_EXPORTERS_ENABLED "${ASSIMP_EXPORTERS_ENABLED} ${name}")
+	LIST(APPEND ASSIMP_EXPORTER_SRCS ${ARGN})
+    SOURCE_GROUP(${name}_EXPORTER FILES ${ARGN})
+  ELSE()
+    SET(ASSIMP_EXPORTERS_DISABLED "${ASSIMP_EXPORTERS_DISABLED} ${name}")
     add_definitions(-DASSIMP_BUILD_NO_${name}_EXPORTER)
   ENDIF()
 ENDMACRO()
 
 SET(ASSIMP_LOADER_SRCS "")
 SET(ASSIMP_IMPORTERS_ENABLED "") # list of enabled importers
-SET(ASSIMP_IMPORTERS_DISABLED "") # disabled list (used to print)
+SET(ASSIMP_IMPORTERS_DISABLED "") # disabled importers list (used to print)
+
+SET(ASSIMP_EXPORTER_SRCS "")
+SET(ASSIMP_EXPORTERS_ENABLED "") # list of enabled exporters
+SET(ASSIMP_EXPORTERS_DISABLED "") # disabled exporters list (used to print)
 
 ADD_ASSIMP_IMPORTER( AMF
   AMFImporter.hpp
@@ -261,6 +293,9 @@ ADD_ASSIMP_IMPORTER( 3DS
   3DSHelper.h
   3DSLoader.cpp
   3DSLoader.h
+)
+
+ADD_ASSIMP_EXPORTER( 3DS
   3DSExporter.h
   3DSExporter.cpp
 )
@@ -278,12 +313,15 @@ ADD_ASSIMP_IMPORTER( ASE
 )
 
 ADD_ASSIMP_IMPORTER( ASSBIN
-  AssbinExporter.h
-  AssbinExporter.cpp
   AssbinLoader.h
   AssbinLoader.cpp
 )
 
+ADD_ASSIMP_EXPORTER( ASSBIN
+  AssbinExporter.h
+  AssbinExporter.cpp
+)
+
 ADD_ASSIMP_IMPORTER( ASSXML
   AssxmlExporter.h
   AssxmlExporter.cpp
@@ -305,6 +343,9 @@ ADD_ASSIMP_IMPORTER( COLLADA
   ColladaLoader.h
   ColladaParser.cpp
   ColladaParser.h
+)
+
+ADD_ASSIMP_EXPORTER( COLLADA
   ColladaExporter.h
   ColladaExporter.cpp
 )
@@ -421,6 +462,9 @@ ADD_ASSIMP_IMPORTER( OBJ
   ObjFileParser.cpp
   ObjFileParser.h
   ObjTools.h
+)
+
+ADD_ASSIMP_EXPORTER( OBJ
   ObjExporter.h
   ObjExporter.cpp
 )
@@ -439,18 +483,24 @@ ADD_ASSIMP_IMPORTER( OGRE
 )
 
 ADD_ASSIMP_IMPORTER( OPENGEX
-  OpenGEXExporter.cpp
-  OpenGEXExporter.h
   OpenGEXImporter.cpp
   OpenGEXImporter.h
   OpenGEXStructs.h
 )
 
+ADD_ASSIMP_EXPORTER( OPENGEX
+  OpenGEXExporter.cpp
+  OpenGEXExporter.h
+)
+
 ADD_ASSIMP_IMPORTER( PLY
   PlyLoader.cpp
   PlyLoader.h
   PlyParser.cpp
   PlyParser.h
+)
+
+ADD_ASSIMP_EXPORTER( PLY
   PlyExporter.cpp
   PlyExporter.h
 )
@@ -541,13 +591,16 @@ ADD_ASSIMP_IMPORTER( FBX
   FBXDeformer.cpp
   FBXBinaryTokenizer.cpp
   FBXDocumentUtil.cpp
+  FBXCommon.h
+)
+
+ADD_ASSIMP_EXPORTER( FBX
   FBXExporter.h
   FBXExporter.cpp
   FBXExportNode.h
   FBXExportNode.cpp
   FBXExportProperty.h
   FBXExportProperty.cpp
-  FBXCommon.h
 )
 
 SET( PostProcessing_SRCS
@@ -647,6 +700,9 @@ ADD_ASSIMP_IMPORTER( SMD
 ADD_ASSIMP_IMPORTER( STL
   STLLoader.cpp
   STLLoader.h
+)
+
+ADD_ASSIMP_EXPORTER( STL
   STLExporter.h
   STLExporter.cpp
 )
@@ -667,13 +723,14 @@ ADD_ASSIMP_IMPORTER( X
   XFileImporter.h
   XFileParser.cpp
   XFileParser.h
+)
+
+ADD_ASSIMP_EXPORTER( X
   XFileExporter.h
   XFileExporter.cpp
 )
 
 ADD_ASSIMP_IMPORTER( X3D
-  X3DExporter.cpp
-  X3DExporter.hpp
   X3DImporter.cpp
   X3DImporter.hpp
   X3DImporter_Geometry2D.cpp
@@ -693,6 +750,11 @@ ADD_ASSIMP_IMPORTER( X3D
   X3DVocabulary.cpp
 )
 
+ADD_ASSIMP_EXPORTER( X3D
+  X3DExporter.cpp
+  X3DExporter.hpp
+)
+
 ADD_ASSIMP_IMPORTER( GLTF
   glTFAsset.h
   glTFAsset.inl
@@ -700,28 +762,34 @@ ADD_ASSIMP_IMPORTER( GLTF
   glTFAssetWriter.inl
   glTFImporter.cpp
   glTFImporter.h
-  glTFExporter.h
-  glTFExporter.cpp
   glTF2Asset.h
   glTF2Asset.inl
   glTF2AssetWriter.h
   glTF2AssetWriter.inl
   glTF2Importer.cpp
   glTF2Importer.h
+)
+
+ADD_ASSIMP_EXPORTER( GLTF
+  glTFExporter.h
+  glTFExporter.cpp
   glTF2Exporter.h
   glTF2Exporter.cpp
 )
 
 ADD_ASSIMP_IMPORTER( 3MF
-    D3MFImporter.h
+	D3MFImporter.h
     D3MFImporter.cpp
-    D3MFExporter.h
-    D3MFExporter.cpp
-    D3MFOpcPackage.h
+	D3MFOpcPackage.h
     D3MFOpcPackage.cpp
     3MFXmlTags.h
 )
 
+ADD_ASSIMP_EXPORTER( 3MF
+    D3MFExporter.h
+    D3MFExporter.cpp
+)
+
 ADD_ASSIMP_IMPORTER( MMD
   MMDCpp14.h
   MMDImporter.cpp
@@ -740,16 +808,21 @@ ADD_ASSIMP_IMPORTER( STEP
     Importer/StepFile/StepFileGen2.cpp
     Importer/StepFile/StepFileGen3.cpp
     Importer/StepFile/StepReaderGen.h
+)
+
+ADD_ASSIMP_EXPORTER( STEP
     StepExporter.h
     StepExporter.cpp
 )
 
-SET( Exporter_SRCS
-  Exporter.cpp
-  AssimpCExport.cpp
-  ${HEADER_PATH}/BlobIOSystem.h
-)
-SOURCE_GROUP( Exporter FILES ${Exporter_SRCS})
+if ((NOT ASSIMP_NO_EXPORT) OR (NOT ASSIMP_EXPORTERS_ENABLED STREQUAL ""))
+	SET( Exporter_SRCS
+	  Exporter.cpp
+	  AssimpCExport.cpp
+	  ${HEADER_PATH}/BlobIOSystem.h
+	)
+	SOURCE_GROUP( Exporter FILES ${Exporter_SRCS})
+endif()
 
 SET( Extra_SRCS
   MD4FileData.h
@@ -793,6 +866,13 @@ SET( ziplib_SRCS
   ../contrib/zip/src/zip.h
 )
 
+# TODO if cmake required version has been updated to >3.12.0, collapse this to the second case only
+if(${CMAKE_VERSION} VERSION_LESS "3.12.0")
+	add_definitions(-DMINIZ_USE_UNALIGNED_LOADS_AND_STORES=0)
+else()
+	add_compile_definitions(MINIZ_USE_UNALIGNED_LOADS_AND_STORES=0)
+endif()
+
 SOURCE_GROUP( ziplib FILES ${ziplib_SRCS} )
 
 SET ( openddl_parser_SRCS
@@ -883,8 +963,11 @@ else (UNZIP_FOUND)
   INCLUDE_DIRECTORIES( "../contrib/unzip/" )
 endif (UNZIP_FOUND)
 
-MESSAGE(STATUS "Enabled formats:${ASSIMP_IMPORTERS_ENABLED}")
-MESSAGE(STATUS "Disabled formats:${ASSIMP_IMPORTERS_DISABLED}")
+MESSAGE(STATUS "Enabled importer formats:${ASSIMP_IMPORTERS_ENABLED}")
+MESSAGE(STATUS "Disabled importer formats:${ASSIMP_IMPORTERS_DISABLED}")
+
+MESSAGE(STATUS "Enabled exporter formats:${ASSIMP_EXPORTERS_ENABLED}")
+MESSAGE(STATUS "Disabled exporter formats:${ASSIMP_EXPORTERS_DISABLED}")
 
 SET( assimp_src
   # Assimp Files
@@ -899,6 +982,7 @@ SET( assimp_src
 
   # Model Support
   ${ASSIMP_LOADER_SRCS}
+  ${ASSIMP_EXPORTER_SRCS}
 
   # Third-party libraries
   ${IrrXML_SRCS}
@@ -912,7 +996,6 @@ SET( assimp_src
 
   ${PUBLIC_HEADERS}
   ${COMPILER_HEADERS}
-
 )
 ADD_DEFINITIONS( -DOPENDDLPARSER_BUILD )
 

+ 1 - 1
code/FBXConverter.cpp

@@ -964,7 +964,7 @@ namespace Assimp {
             {
                 if (indices[i] < 0) epcount++;
             }
-            unsigned int pcount = indices.size();
+            unsigned int pcount = static_cast<unsigned int>( indices.size() );
             unsigned int scount = out_mesh->mNumFaces = pcount - epcount;
 
             aiFace* fac = out_mesh->mFaces = new aiFace[scount]();

+ 10 - 10
code/FindInstancesProcess.cpp

@@ -178,30 +178,30 @@ void FindInstancesProcess::Execute( aiScene* pScene)
                     // use a constant epsilon for colors and UV coordinates
                     static const float uvEpsilon = 10e-4f;
                     {
-                        unsigned int i, end = orig->GetNumUVChannels();
-                        for(i = 0; i < end; ++i) {
-                            if (!orig->mTextureCoords[i]) {
+                        unsigned int j, end = orig->GetNumUVChannels();
+                        for(j = 0; j < end; ++j) {
+                            if (!orig->mTextureCoords[j]) {
                                 continue;
                             }
-                            if(!CompareArrays(orig->mTextureCoords[i],inst->mTextureCoords[i],orig->mNumVertices,uvEpsilon)) {
+                            if(!CompareArrays(orig->mTextureCoords[j],inst->mTextureCoords[j],orig->mNumVertices,uvEpsilon)) {
                                 break;
                             }
                         }
-                        if (i != end) {
+                        if (j != end) {
                             continue;
                         }
                     }
                     {
-                        unsigned int i, end = orig->GetNumColorChannels();
-                        for(i = 0; i < end; ++i) {
-                            if (!orig->mColors[i]) {
+                        unsigned int j, end = orig->GetNumColorChannels();
+                        for(j = 0; j < end; ++j) {
+                            if (!orig->mColors[j]) {
                                 continue;
                             }
-                            if(!CompareArrays(orig->mColors[i],inst->mColors[i],orig->mNumVertices,uvEpsilon)) {
+                            if(!CompareArrays(orig->mColors[j],inst->mColors[j],orig->mNumVertices,uvEpsilon)) {
                                 break;
                             }
                         }
-                        if (i != end) {
+                        if (j != end) {
                             continue;
                         }
                     }

+ 3 - 1
code/ImproveCacheLocality.cpp

@@ -112,7 +112,9 @@ void ImproveCacheLocalityProcess::Execute( aiScene* pScene)
         }
     }
     if (!DefaultLogger::isNullLogger()) {
-        ASSIMP_LOG_INFO_F("Cache relevant are ", numm, " meshes (", numf," faces). Average output ACMR is ", out / numf );
+        if (numf > 0) {
+            ASSIMP_LOG_INFO_F("Cache relevant are ", numm, " meshes (", numf, " faces). Average output ACMR is ", out / numf);
+        }
         ASSIMP_LOG_DEBUG("ImproveCacheLocalityProcess finished. ");
     }
 }

+ 1 - 4
code/LWOMaterial.cpp

@@ -320,13 +320,10 @@ void LWOImporter::ConvertMaterial(const LWO::Surface& surf,aiMaterial* pcMat)
 
     // opacity ... either additive or default-blended, please
     if (0.0 != surf.mAdditiveTransparency)  {
-
         const int add = aiBlendMode_Additive;
         pcMat->AddProperty(&surf.mAdditiveTransparency,1,AI_MATKEY_OPACITY);
         pcMat->AddProperty(&add,1,AI_MATKEY_BLEND_FUNC);
-    }
-
-    else if (10e10f != surf.mTransparency)  {
+    } else if (10e10f != surf.mTransparency)  {
         const int def = aiBlendMode_Default;
         const float f = 1.0f-surf.mTransparency;
         pcMat->AddProperty(&f,1,AI_MATKEY_OPACITY);

+ 5 - 5
code/MD3FileData.h

@@ -59,7 +59,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <assimp/Compiler/pushpack1.h>
 
-namespace Assimp    {
+namespace Assimp {
 namespace MD3   {
 
 // to make it easier for us, we test the magic word against both "endianesses"
@@ -303,12 +303,12 @@ inline void Vec3NormalToLatLng( const aiVector3D& p_vIn, uint16_t& p_iOut )
         b = int(57.2957795f * ( std::acos( p_vIn[2] ) ) * ( 255.0f / 360.0f ));
         b &= 0xff;
 
-        ((unsigned char*)&p_iOut)[0] = b;   // longitude
-        ((unsigned char*)&p_iOut)[1] = a;   // latitude
+        ((unsigned char*)&p_iOut)[0] = (unsigned char) b;   // longitude
+        ((unsigned char*)&p_iOut)[1] = (unsigned char) a;   // latitude
     }
 }
 
-}
-}
+} // Namespace MD3
+} // Namespace Assimp
 
 #endif // !! AI_MD3FILEHELPER_H_INC

+ 27 - 25
code/MD3Loader.cpp

@@ -258,10 +258,10 @@ bool Q3Shader::LoadSkin(SkinData& fill, const std::string& pFile,IOSystem* io)
             continue;
 
         fill.textures.push_back(SkinData::TextureEntry());
-        SkinData::TextureEntry& s = fill.textures.back();
+        SkinData::TextureEntry &entry = fill.textures.back();
 
-        s.first  = ss;
-        s.second = GetNextToken(buff);
+        entry.first  = ss;
+        entry.second = GetNextToken(buff);
     }
     return true;
 }
@@ -718,9 +718,7 @@ void MD3Importer::ConvertPath(const char* texture_name, const char* header_name,
 
 // ------------------------------------------------------------------------------------------------
 // Imports the given file into the given scene structure.
-void MD3Importer::InternReadFile( const std::string& pFile,
-    aiScene* pScene, IOSystem* pIOHandler)
-{
+void MD3Importer::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) {
     mFile = pFile;
     mScene = pScene;
     mIOHandler = pIOHandler;
@@ -730,11 +728,13 @@ void MD3Importer::InternReadFile( const std::string& pFile,
     std::string::size_type s = mFile.find_last_of("/\\");
     if (s == std::string::npos) {
         s = 0;
+    } else {
+        ++s;
+    }
+    filename = mFile.substr(s), path = mFile.substr(0, s);
+    for (std::string::iterator it = filename.begin(); it != filename.end(); ++it) {
+        *it = static_cast<char>( tolower(*it) );
     }
-    else ++s;
-    filename = mFile.substr(s), path = mFile.substr(0,s);
-    for( std::string::iterator it = filename .begin(); it != filename.end(); ++it)
-        *it = tolower( *it);
 
     // Load multi-part model file, if necessary
     if (configHandleMP) {
@@ -905,15 +905,15 @@ void MD3Importer::InternReadFile( const std::string& pFile,
         // Now search the current shader for a record with this name (
         // excluding texture file extension)
         if (!shaders.blocks.empty()) {
+            std::string::size_type sh = convertedPath.find_last_of('.');
+            if (sh == std::string::npos) {
+                sh = convertedPath.length();
+            }
 
-            std::string::size_type s = convertedPath.find_last_of('.');
-            if (s == std::string::npos)
-                s = convertedPath.length();
-
-            const std::string without_ext = convertedPath.substr(0,s);
+            const std::string without_ext = convertedPath.substr(0,sh);
             std::list< Q3Shader::ShaderDataBlock >::const_iterator dit = std::find(shaders.blocks.begin(),shaders.blocks.end(),without_ext);
             if (dit != shaders.blocks.end()) {
-                // Hurra, wir haben einen. Tolle Sache.
+                // We made it!
                 shader = &*dit;
                 ASSIMP_LOG_INFO("Found shader record for " +without_ext );
             } else {
@@ -945,8 +945,7 @@ void MD3Importer::InternReadFile( const std::string& pFile,
             aiString szString;
             if (convertedPath.length()) {
                 szString.Set(convertedPath);
-            }
-            else    {
+            } else    {
                 ASSIMP_LOG_WARN("Texture file name has zero length. Using default name");
                 szString.Set("dummy_texture.bmp");
             }
@@ -955,8 +954,7 @@ void MD3Importer::InternReadFile( const std::string& pFile,
             // prevent transparency by default
             int no_alpha = aiTextureFlags_IgnoreAlpha;
             pcHelper->AddProperty(&no_alpha,1,AI_MATKEY_TEXFLAGS_DIFFUSE(0));
-        }
-        else {
+        } else {
             Q3Shader::ConvertShaderToMaterial(pcHelper,*shader);
         }
 
@@ -1026,7 +1024,7 @@ void MD3Importer::InternReadFile( const std::string& pFile,
             if (!shader || shader->cull == Q3Shader::CULL_CW) {
                 std::swap(pcMesh->mFaces[i].mIndices[2],pcMesh->mFaces[i].mIndices[1]);
             }
-            pcTriangles++;
+            ++pcTriangles;
         }
 
         // Go to the next surface
@@ -1042,8 +1040,9 @@ void MD3Importer::InternReadFile( const std::string& pFile,
         }
     }
 
-    if (!pScene->mNumMeshes)
+    if (!pScene->mNumMeshes) {
         throw DeadlyImportError( "MD3: File contains no valid mesh");
+    }
     pScene->mNumMaterials = iNumMaterials;
 
     // Now we need to generate an empty node graph
@@ -1057,7 +1056,6 @@ void MD3Importer::InternReadFile( const std::string& pFile,
         pScene->mRootNode->mChildren = new aiNode*[pcHeader->NUM_TAGS];
 
         for (unsigned int i = 0; i < pcHeader->NUM_TAGS; ++i, ++pcTags) {
-
             aiNode* nd = pScene->mRootNode->mChildren[i] = new aiNode();
             nd->mName.Set((const char*)pcTags->NAME);
             nd->mParent = pScene->mRootNode;
@@ -1085,8 +1083,12 @@ void MD3Importer::InternReadFile( const std::string& pFile,
         pScene->mRootNode->mMeshes[i] = i;
 
     // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system
-    pScene->mRootNode->mTransformation = aiMatrix4x4(1.f,0.f,0.f,0.f,
-        0.f,0.f,1.f,0.f,0.f,-1.f,0.f,0.f,0.f,0.f,0.f,1.f);
+    pScene->mRootNode->mTransformation = aiMatrix4x4(
+        1.f,0.f,0.f,0.f,
+        0.f,0.f,1.f,0.f,
+        0.f,-1.f,0.f,0.f,
+        0.f,0.f,0.f,1.f
+    );
 }
 
 #endif // !! ASSIMP_BUILD_NO_MD3_IMPORTER

+ 3 - 0
code/ObjFileData.h

@@ -281,6 +281,8 @@ struct Model {
     std::string m_strActiveGroup;
     //! Vector with generated texture coordinates
     std::vector<aiVector3D> m_TextureCoord;
+    //! Maximum dimension of texture coordinates
+    unsigned int m_TextureCoordDim;
     //! Current mesh instance
     Mesh *m_pCurrentMesh;
     //! Vector with stored meshes
@@ -296,6 +298,7 @@ struct Model {
         m_pDefaultMaterial(NULL),
         m_pGroupFaceIDs(NULL),
         m_strActiveGroup(""),
+        m_TextureCoordDim(0),
         m_pCurrentMesh(NULL)
     {
         // empty

+ 1 - 1
code/ObjFileImporter.cpp

@@ -442,7 +442,7 @@ void ObjFileImporter::createVertexArray(const ObjFile::Model* pModel,
     // Allocate buffer for texture coordinates
     if ( !pModel->m_TextureCoord.empty() && pObjMesh->m_uiUVCoordinates[0] )
     {
-        pMesh->mNumUVComponents[ 0 ] = 2;
+        pMesh->mNumUVComponents[ 0 ] = pModel->m_TextureCoordDim;
         pMesh->mTextureCoords[ 0 ] = new aiVector3D[ pMesh->mNumVertices ];
     }
 

+ 4 - 2
code/ObjFileParser.cpp

@@ -151,7 +151,8 @@ void ObjFileParser::parseFile( IOStreamBuffer<char> &streamBuffer ) {
                 } else if (*m_DataIt == 't') {
                     // read in texture coordinate ( 2D or 3D )
                     ++m_DataIt;
-                    getVector( m_pModel->m_TextureCoord );
+                    size_t dim = getVector(m_pModel->m_TextureCoord);
+                    m_pModel->m_TextureCoordDim = std::max(m_pModel->m_TextureCoordDim, (unsigned int)dim);
                 } else if (*m_DataIt == 'n') {
                     // Read in normal vector definition
                     ++m_DataIt;
@@ -296,7 +297,7 @@ size_t ObjFileParser::getNumComponentsInDataDefinition() {
     return numComponents;
 }
 
-void ObjFileParser::getVector( std::vector<aiVector3D> &point3d_array ) {
+size_t ObjFileParser::getVector( std::vector<aiVector3D> &point3d_array ) {
     size_t numComponents = getNumComponentsInDataDefinition();
     ai_real x, y, z;
     if( 2 == numComponents ) {
@@ -320,6 +321,7 @@ void ObjFileParser::getVector( std::vector<aiVector3D> &point3d_array ) {
     }
     point3d_array.push_back( aiVector3D( x, y, z ) );
     m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+    return numComponents;
 }
 
 void ObjFileParser::getVector3( std::vector<aiVector3D> &point3d_array ) {

+ 1 - 1
code/ObjFileParser.h

@@ -96,7 +96,7 @@ protected:
     /// Get the number of components in a line.
     size_t getNumComponentsInDataDefinition();
     /// Stores the vector
-    void getVector( std::vector<aiVector3D> &point3d_array );
+    size_t getVector( std::vector<aiVector3D> &point3d_array );
     /// Stores the following 3d vector.
     void getVector3( std::vector<aiVector3D> &point3d_array );
     /// Stores the following homogeneous vector as a 3D vector

+ 3 - 3
code/PostStepRegistry.cpp

@@ -173,6 +173,9 @@ void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out)
 #ifndef ASSIMP_BUILD_NO_TRANSFORMTEXCOORDS_PROCESS
     out.push_back( new TextureTransformStep());
 #endif
+#if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS)
+    out.push_back( new ScaleProcess());
+#endif
 #if (!defined ASSIMP_BUILD_NO_PRETRANSFORMVERTICES_PROCESS)
     out.push_back( new PretransformVertices());
 #endif
@@ -208,9 +211,6 @@ void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out)
 #endif
 #if (!defined ASSIMP_BUILD_NO_GENFACENORMALS_PROCESS)
     out.push_back( new GenFaceNormalsProcess());
-#endif
-#if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS)
-    out.push_back( new ScaleProcess());
 #endif
     // .........................................................................
     // DON'T change the order of these five ..

+ 2 - 2
code/SMDLoader.cpp

@@ -486,7 +486,7 @@ void SMDImporter::CreateOutputAnimations(const std::string &pFile, IOSystem* pIO
     if (bLoadAnimationList) {
         GetAnimationFileList(pFile, pIOHandler, animFileList);
     }
-    int animCount = animFileList.size() + 1;
+    int animCount = static_cast<int>( animFileList.size() + 1u );
     pScene->mNumAnimations = 1;
     pScene->mAnimations = new aiAnimation*[animCount];
     memset(pScene->mAnimations, 0, sizeof(aiAnimation*)*animCount);
@@ -510,7 +510,7 @@ void SMDImporter::CreateOutputAnimation(int index, const std::string &name) {
         anim->mName.Set(name.c_str());
     }
     anim->mDuration = dLengthOfAnim;
-    anim->mNumChannels = asBones.size();
+    anim->mNumChannels = static_cast<unsigned int>( asBones.size() );
     anim->mTicksPerSecond = 25.0; // FIXME: is this correct?
 
     aiNodeAnim** pp = anim->mChannels = new aiNodeAnim*[anim->mNumChannels];

+ 19 - 18
code/SortByPTypeProcess.cpp

@@ -228,36 +228,37 @@ void SortByPTypeProcess::Execute( aiScene* pScene) {
 
             out->mNumVertices = (3 == real ? numPolyVerts : out->mNumFaces * (real+1));
 
-            aiVector3D *vert(NULL), *nor(NULL), *tan(NULL), *bit(NULL);
+            aiVector3D *vert(nullptr), *nor(nullptr), *tan(nullptr), *bit(nullptr);
             aiVector3D *uv   [AI_MAX_NUMBER_OF_TEXTURECOORDS];
             aiColor4D  *cols [AI_MAX_NUMBER_OF_COLOR_SETS];
 
-            if (mesh->mVertices)
+            if (mesh->mVertices) {
                 vert = out->mVertices = new aiVector3D[out->mNumVertices];
+            }
 
-            if (mesh->mNormals)
-                nor  = out->mNormals  = new aiVector3D[out->mNumVertices];
+            if (mesh->mNormals) {
+                nor = out->mNormals = new aiVector3D[out->mNumVertices];
+            }
 
-            if (mesh->mTangents)
-            {
+            if (mesh->mTangents) {
                 tan = out->mTangents   = new aiVector3D[out->mNumVertices];
                 bit = out->mBitangents = new aiVector3D[out->mNumVertices];
             }
 
-            for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i)
-            {
-                if (mesh->mTextureCoords[i])
-                    uv[i] = out->mTextureCoords[i] = new aiVector3D[out->mNumVertices];
-                else uv[i] = NULL;
+            for (unsigned int j = 0; j < AI_MAX_NUMBER_OF_TEXTURECOORDS;++j) {
+                uv[j] = nullptr;
+                if (mesh->mTextureCoords[j]) {
+                    uv[j] = out->mTextureCoords[j] = new aiVector3D[out->mNumVertices];
+                }
 
-                out->mNumUVComponents[i] = mesh->mNumUVComponents[i];
+                out->mNumUVComponents[j] = mesh->mNumUVComponents[j];
             }
 
-            for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_COLOR_SETS;++i)
-            {
-                if (mesh->mColors[i])
-                    cols[i] = out->mColors[i] = new aiColor4D[out->mNumVertices];
-                else cols[i] = NULL;
+            for (unsigned int j = 0; j < AI_MAX_NUMBER_OF_COLOR_SETS;++j) {
+                cols[j] = nullptr;
+                if (mesh->mColors[j]) {
+                    cols[j] = out->mColors[j] = new aiColor4D[out->mNumVertices];
+                }
             }
 
             typedef std::vector< aiVertexWeight > TempBoneInfo;
@@ -323,7 +324,7 @@ void SortByPTypeProcess::Execute( aiScene* pScene) {
                     in.mIndices[q] = outIdx++;
                 }
 
-                in.mIndices = NULL;
+                in.mIndices = nullptr;
                 ++outFaces;
             }
             ai_assert(outFaces == out->mFaces + out->mNumFaces);

+ 20 - 20
code/StandardShapes.cpp

@@ -127,35 +127,35 @@ aiMesh* StandardShapes::MakeMesh(const std::vector<aiVector3D>& positions,
 
     // Determine which kinds of primitives the mesh consists of
     aiMesh* out = new aiMesh();
-    switch (numIndices)
-    {
-    case 1:
-        out->mPrimitiveTypes = aiPrimitiveType_POINT;
-        break;
-    case 2:
-        out->mPrimitiveTypes = aiPrimitiveType_LINE;
-        break;
-    case 3:
-        out->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
-        break;
-    default:
-        out->mPrimitiveTypes = aiPrimitiveType_POLYGON;
-        break;
+    switch (numIndices) {
+        case 1:
+            out->mPrimitiveTypes = aiPrimitiveType_POINT;
+            break;
+        case 2:
+            out->mPrimitiveTypes = aiPrimitiveType_LINE;
+            break;
+        case 3:
+            out->mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
+            break;
+        default:
+            out->mPrimitiveTypes = aiPrimitiveType_POLYGON;
+            break;
     };
 
     out->mNumFaces = (unsigned int)positions.size() / numIndices;
     out->mFaces = new aiFace[out->mNumFaces];
-    for (unsigned int i = 0, a = 0; i < out->mNumFaces;++i)
-    {
+    for (unsigned int i = 0, a = 0; i < out->mNumFaces;++i) {
         aiFace& f = out->mFaces[i];
         f.mNumIndices = numIndices;
         f.mIndices = new unsigned int[numIndices];
-        for (unsigned int i = 0; i < numIndices;++i,++a)
-            f.mIndices[i] = a;
+        for (unsigned int j = 0; i < numIndices; ++i, ++a) {
+            f.mIndices[j] = a;
+        }
     }
     out->mNumVertices = (unsigned int)positions.size();
     out->mVertices = new aiVector3D[out->mNumVertices];
     ::memcpy(out->mVertices,&positions[0],out->mNumVertices*sizeof(aiVector3D));
+
     return out;
 }
 
@@ -466,8 +466,8 @@ void StandardShapes::MakeCone(ai_real height,ai_real radius1,
 
     // Need to flip face order?
     if ( SIZE_MAX != old )  {
-        for (size_t s = old; s < positions.size();s += 3) {
-            std::swap(positions[s],positions[s+1]);
+        for (size_t p = old; p < positions.size();p += 3) {
+            std::swap(positions[p],positions[p+1]);
         }
     }
 }

+ 46 - 32
code/ValidateDataStructure.cpp

@@ -160,7 +160,7 @@ inline void ValidateDSProcess::DoValidationEx(T** parray, unsigned int size,
         {
             if (!parray[i])
             {
-                ReportError("aiScene::%s[%i] is NULL (aiScene::%s is %i)",
+                ReportError("aiScene::%s[%u] is NULL (aiScene::%s is %u)",
                     firstName,i,secondName,size);
             }
             Validate(parray[i]);
@@ -170,8 +170,8 @@ inline void ValidateDSProcess::DoValidationEx(T** parray, unsigned int size,
             {
                 if (parray[i]->mName == parray[a]->mName)
                 {
-                    this->ReportError("aiScene::%s[%i] has the same name as "
-                        "aiScene::%s[%i]",firstName, i,secondName, a);
+                	ReportError("aiScene::%s[%u] has the same name as "
+                        "aiScene::%s[%u]",firstName, i,secondName, a);
                 }
             }
         }
@@ -330,6 +330,7 @@ void ValidateDSProcess::Validate( const aiMesh* pMesh)
             {
             case 0:
                 ReportError("aiMesh::mFaces[%i].mNumIndices is 0",i);
+                break;
             case 1:
                 if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_POINT))
                 {
@@ -422,7 +423,9 @@ void ValidateDSProcess::Validate( const aiMesh* pMesh)
         if (!abRefList[i])b = true;
     }
     abRefList.clear();
-    if (b)ReportWarning("There are unreferenced vertices");
+    if (b) {
+    	ReportWarning("There are unreferenced vertices");
+    }
 
     // texture channel 2 may not be set if channel 1 is zero ...
     {
@@ -557,7 +560,9 @@ void ValidateDSProcess::Validate( const aiAnimation* pAnimation)
             Validate(pAnimation, pAnimation->mChannels[i]);
         }
     }
-    else ReportError("aiAnimation::mNumChannels is 0. At least one node animation channel must be there.");
+    else {
+    	ReportError("aiAnimation::mNumChannels is 0. At least one node animation channel must be there.");
+    }
 
     // Animation duration is allowed to be zero in cases where the anim contains only a single key frame.
     // if (!pAnimation->mDuration)this->ReportError("aiAnimation::mDuration is zero");
@@ -577,15 +582,16 @@ void ValidateDSProcess::SearchForInvalidTextures(const aiMaterial* pMaterial,
 
     int iNumIndices = 0;
     int iIndex = -1;
-    for (unsigned int i = 0; i < pMaterial->mNumProperties;++i)
-    {
-        aiMaterialProperty* prop = pMaterial->mProperties[i];
-        if (!::strcmp(prop->mKey.data,"$tex.file") && prop->mSemantic == type)  {
+    for (unsigned int i = 0; i < pMaterial->mNumProperties;++i) {
+        aiMaterialProperty* prop = pMaterial->mProperties[ i ];
+        ai_assert(nullptr != prop);
+        if ( !::strcmp(prop->mKey.data,"$tex.file") && prop->mSemantic == static_cast<unsigned int>(type))  {
             iIndex = std::max(iIndex, (int) prop->mIndex);
             ++iNumIndices;
 
-            if (aiPTI_String != prop->mType)
-                ReportError("Material property %s is expected to be a string",prop->mKey.data);
+            if (aiPTI_String != prop->mType) {
+                ReportError("Material property %s is expected to be a string", prop->mKey.data);
+            }
         }
     }
     if (iIndex +1 != iNumIndices)   {
@@ -773,8 +779,10 @@ void ValidateDSProcess::Validate( const aiTexture* pTexture)
     }
     if (pTexture->mHeight)
     {
-        if (!pTexture->mWidth)ReportError("aiTexture::mWidth is zero "
-            "(aiTexture::mHeight is %i, uncompressed texture)",pTexture->mHeight);
+        if (!pTexture->mWidth){
+        	ReportError("aiTexture::mWidth is zero (aiTexture::mHeight is %i, uncompressed texture)",
+        			pTexture->mHeight);
+        }
     }
     else
     {
@@ -805,15 +813,15 @@ void ValidateDSProcess::Validate( const aiAnimation* pAnimation,
 {
     Validate(&pNodeAnim->mNodeName);
 
-    if (!pNodeAnim->mNumPositionKeys && !pNodeAnim->mScalingKeys && !pNodeAnim->mNumRotationKeys)
+    if (!pNodeAnim->mNumPositionKeys && !pNodeAnim->mScalingKeys && !pNodeAnim->mNumRotationKeys) {
         ReportError("Empty node animation channel");
-
+    }
     // otherwise check whether one of the keys exceeds the total duration of the animation
     if (pNodeAnim->mNumPositionKeys)
     {
         if (!pNodeAnim->mPositionKeys)
         {
-            this->ReportError("aiNodeAnim::mPositionKeys is NULL (aiNodeAnim::mNumPositionKeys is %i)",
+        	ReportError("aiNodeAnim::mPositionKeys is NULL (aiNodeAnim::mNumPositionKeys is %i)",
                 pNodeAnim->mNumPositionKeys);
         }
         double dLast = -10e10;
@@ -844,7 +852,7 @@ void ValidateDSProcess::Validate( const aiAnimation* pAnimation,
     {
         if (!pNodeAnim->mRotationKeys)
         {
-            this->ReportError("aiNodeAnim::mRotationKeys is NULL (aiNodeAnim::mNumRotationKeys is %i)",
+            ReportError("aiNodeAnim::mRotationKeys is NULL (aiNodeAnim::mNumRotationKeys is %i)",
                 pNodeAnim->mNumRotationKeys);
         }
         double dLast = -10e10;
@@ -905,19 +913,23 @@ void ValidateDSProcess::Validate( const aiAnimation* pAnimation,
 // ------------------------------------------------------------------------------------------------
 void ValidateDSProcess::Validate( const aiNode* pNode)
 {
-    if (!pNode)ReportError("A node of the scenegraph is NULL");
-    if (pNode != mScene->mRootNode && !pNode->mParent)
-        this->ReportError("A node has no valid parent (aiNode::mParent is NULL)");
-
+    if (!pNode) {
+    	ReportError("A node of the scenegraph is NULL");
+    }
+    // Validate node name string first so that it's safe to use in below expressions
     this->Validate(&pNode->mName);
+    const char* nodeName = (&pNode->mName)->C_Str();
+    if (pNode != mScene->mRootNode && !pNode->mParent){
+        ReportError("Non-root node %s lacks a valid parent (aiNode::mParent is NULL) ", nodeName);
+    }
 
     // validate all meshes
     if (pNode->mNumMeshes)
     {
         if (!pNode->mMeshes)
         {
-            ReportError("aiNode::mMeshes is NULL (aiNode::mNumMeshes is %i)",
-                pNode->mNumMeshes);
+            ReportError("aiNode::mMeshes is NULL for node %s (aiNode::mNumMeshes is %i)",
+            		  nodeName, pNode->mNumMeshes);
         }
         std::vector<bool> abHadMesh;
         abHadMesh.resize(mScene->mNumMeshes,false);
@@ -925,13 +937,13 @@ void ValidateDSProcess::Validate( const aiNode* pNode)
         {
             if (pNode->mMeshes[i] >= mScene->mNumMeshes)
             {
-                ReportError("aiNode::mMeshes[%i] is out of range (maximum is %i)",
-                    pNode->mMeshes[i],mScene->mNumMeshes-1);
+                ReportError("aiNode::mMeshes[%i] is out of range for node %s (maximum is %i)",
+                    pNode->mMeshes[i], nodeName, mScene->mNumMeshes-1);
             }
             if (abHadMesh[pNode->mMeshes[i]])
             {
-                ReportError("aiNode::mMeshes[%i] is already referenced by this node (value: %i)",
-                    i,pNode->mMeshes[i]);
+                ReportError("aiNode::mMeshes[%i] is already referenced by this node %s (value: %i)",
+                    i, nodeName, pNode->mMeshes[i]);
             }
             abHadMesh[pNode->mMeshes[i]] = true;
         }
@@ -939,8 +951,8 @@ void ValidateDSProcess::Validate( const aiNode* pNode)
     if (pNode->mNumChildren)
     {
         if (!pNode->mChildren)  {
-            ReportError("aiNode::mChildren is NULL (aiNode::mNumChildren is %i)",
-                pNode->mNumChildren);
+            ReportError("aiNode::mChildren is NULL for node %s (aiNode::mNumChildren is %i)",
+            		nodeName, pNode->mNumChildren);
         }
         for (unsigned int i = 0; i < pNode->mNumChildren;++i)   {
             Validate(pNode->mChildren[i]);
@@ -953,7 +965,7 @@ void ValidateDSProcess::Validate( const aiString* pString)
 {
     if (pString->length > MAXLEN)
     {
-        this->ReportError("aiString::length is too large (%i, maximum is %lu)",
+        ReportError("aiString::length is too large (%lu, maximum is %lu)",
             pString->length,MAXLEN);
     }
     const char* sz = pString->data;
@@ -961,12 +973,14 @@ void ValidateDSProcess::Validate( const aiString* pString)
     {
         if ('\0' == *sz)
         {
-            if (pString->length != (unsigned int)(sz-pString->data))
+            if (pString->length != (unsigned int)(sz-pString->data)) {
                 ReportError("aiString::data is invalid: the terminal zero is at a wrong offset");
+            }
             break;
         }
-        else if (sz >= &pString->data[MAXLEN])
+        else if (sz >= &pString->data[MAXLEN]) {
             ReportError("aiString::data is invalid. There is no terminal character");
+        }
         ++sz;
     }
 }

+ 18 - 0
contrib/zip/.gitignore

@@ -36,3 +36,21 @@
 # Temporary
 *.swp
 .DS_Store
+
+# CMake
+CMakeScripts
+*.cmake
+
+# Xcode
+*.build
+*.xcodeproj
+zip.sln
+zip.vcxproj.filters
+zip.vcxproj
+ALL_BUILD.vcxproj.filters
+ALL_BUILD.vcxproj
+CMakeFiles/
+zip.dir/
+test/test.exe.vcxproj.filters
+test/test.exe.vcxproj
+test/test.exe.dir/

+ 18 - 0
contrib/zip/.travis.sh

@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Build script for travis-ci.org builds.
+#
+if [ $ANALYZE = "true" ] && [ "$CC" = "clang" ]; then
+    # scan-build -h
+    scan-build cmake -G "Unix Makefiles"
+    scan-build -enable-checker security.FloatLoopCounter \
+        -enable-checker security.insecureAPI.UncheckedReturn \
+        --status-bugs -v \
+        make -j 8 \
+        make -j 8 test
+else
+    cmake -DCMAKE_BUILD_TYPE=Debug -DSANITIZE_ADDRESS=On -DCMAKE_INSTALL_PREFIX=_install
+    make -j 8
+    make install
+    ASAN_OPTIONS=detect_leaks=0 LSAN_OPTIONS=verbosity=1:log_threads=1 ctest -V
+fi

+ 15 - 3
contrib/zip/.travis.yml

@@ -1,10 +1,22 @@
 language: c
+addons:
+  apt:
+    packages: &1
+    - lcov
 # Compiler selection
 compiler:
   - clang
   - gcc
+env:
+  - ANALYZE=false
+  - ANALYZE=true
 # Build steps
 script:
-  - mkdir build
-  - cd build
-  - cmake -DCMAKE_BUILD_TYPE=Debug .. && make && make test
+  - ./.travis.sh
+after_success:
+  # Creating report
+  - cmake -DENABLE_COVERAGE=ON
+  - make
+  - make test
+  # Uploading report to CodeCov
+  - bash <(curl -s https://codecov.io/bash)

+ 36 - 7
contrib/zip/CMakeLists.txt

@@ -1,18 +1,47 @@
 cmake_minimum_required(VERSION 2.8)
 project(zip)
+enable_language(C)
+set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
 
 if (MSVC)
-    # Use secure functions by defaualt 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")
+  # Use secure functions by defaualt 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")
+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")
 endif (MSVC)
 
 # zip
 set(SRC src/miniz.h src/zip.h src/zip.c)
-add_library(${CMAKE_PROJECT_NAME} ${SRC})
+add_library(${PROJECT_NAME} ${SRC})
+target_include_directories(${PROJECT_NAME} INTERFACE src)
 
 # test
-enable_testing()
-add_subdirectory(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)
+endif()
 
+install(TARGETS ${PROJECT_NAME}
+        RUNTIME DESTINATION bin
+        ARCHIVE DESTINATION lib
+        LIBRARY DESTINATION lib
+        COMPONENT library)
+install(FILES ${PROJECT_SOURCE_DIR}/src/zip.h DESTINATION include)
+
+# uninstall target (https://gitlab.kitware.com/cmake/community/wikis/FAQ#can-i-do-make-uninstall-with-cmake)
+if(NOT TARGET uninstall)
+    configure_file(
+        "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in"
+        "${CMAKE_CURRENT_BINARY_DIR}/cmake/cmake_uninstall.cmake"
+        IMMEDIATE @ONLY)
+
+    add_custom_target(uninstall
+        COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake/cmake_uninstall.cmake)
+endif()

+ 249 - 79
contrib/zip/README.md

@@ -1,12 +1,11 @@
 ### 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][win-badge]][win-link] [![OS X][osx-linux-badge]][osx-linux-link]
+[![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)
+[![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)
 
-[win-badge]: https://img.shields.io/appveyor/ci/kuba--/zip/master.svg?label=windows "AppVeyor build status"
-[win-link]:  https://ci.appveyor.com/project/kuba--/zip "AppVeyor build status"
-[osx-linux-badge]: https://img.shields.io/travis/kuba--/zip/master.svg?label=linux/osx "Travis CI build status"
-[osx-linux-link]:  https://travis-ci.org/kuba--/zip "Travis CI build status"
 
 # The Idea
 <img src="zip.png" name="zip" />
@@ -23,117 +22,288 @@ It was the reason, why I decided to write zip module on top of the miniz. It req
 
 * Create a new zip archive with default compression level.
 ```c
-    struct zip_t *zip = zip_open("foo.zip", ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
+struct zip_t *zip = zip_open("foo.zip", ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
+{
+    zip_entry_open(zip, "foo-1.txt");
     {
-        zip_entry_open(zip, "foo-1.txt");
-        {
-            char *buf = "Some data here...";
-            zip_entry_write(zip, buf, strlen(buf));
-        }
-        zip_entry_close(zip);
-
-        zip_entry_open(zip, "foo-2.txt");
-        {
-            // merge 3 files into one entry and compress them on-the-fly.
-            zip_entry_fwrite(zip, "foo-2.1.txt");
-            zip_entry_fwrite(zip, "foo-2.2.txt");
-            zip_entry_fwrite(zip, "foo-2.3.txt");
-        }
-        zip_entry_close(zip);
+        const char *buf = "Some data here...\0";
+        zip_entry_write(zip, buf, strlen(buf));
     }
-    zip_close(zip);
+    zip_entry_close(zip);
+
+    zip_entry_open(zip, "foo-2.txt");
+    {
+        // merge 3 files into one entry and compress them on-the-fly.
+        zip_entry_fwrite(zip, "foo-2.1.txt");
+        zip_entry_fwrite(zip, "foo-2.2.txt");
+        zip_entry_fwrite(zip, "foo-2.3.txt");
+    }
+    zip_entry_close(zip);
+}
+zip_close(zip);
 ```
 
 * Append to the existing zip archive.
 ```c
-    struct zip_t *zip = zip_open("foo.zip", ZIP_DEFAULT_COMPRESSION_LEVEL, 'a');
+struct zip_t *zip = zip_open("foo.zip", ZIP_DEFAULT_COMPRESSION_LEVEL, 'a');
+{
+    zip_entry_open(zip, "foo-3.txt");
     {
-        zip_entry_open(zip, "foo-3.txt");
-        {
-            char *buf = "Append some data here...";
-            zip_entry_write(zip, buf, strlen(buf));
-        }
-        zip_entry_close(zip);
+        const char *buf = "Append some data here...\0";
+        zip_entry_write(zip, buf, strlen(buf));
     }
-    zip_close(zip);
+    zip_entry_close(zip);
+}
+zip_close(zip);
 ```
 
 * Extract a zip archive into a folder.
 ```c
-    int on_extract_entry(const char *filename, void *arg) {
-        static int i = 0;
-        int n = *(int *)arg;
-        printf("Extracted: %s (%d of %d)\n", filename, ++i, n);
+int on_extract_entry(const char *filename, void *arg) {
+    static int i = 0;
+    int n = *(int *)arg;
+    printf("Extracted: %s (%d of %d)\n", filename, ++i, n);
+
+    return 0;
+}
+
+int arg = 2;
+zip_extract("foo.zip", "/tmp", on_extract_entry, &arg);
+```
+
+*   Extract a zip entry into memory.
+```c
+void *buf = NULL;
+size_t bufsize;
 
-        return 0;
+struct zip_t *zip = zip_open("foo.zip", 0, 'r');
+{
+    zip_entry_open(zip, "foo-1.txt");
+    {
+        zip_entry_read(zip, &buf, &bufsize);
     }
+    zip_entry_close(zip);
+}
+zip_close(zip);
 
-    int arg = 2;
-    zip_extract("foo.zip", "/tmp", on_extract_entry, &arg);
+free(buf);
 ```
 
-* Extract a zip entry into memory.
+*   Extract a zip entry into memory (no internal allocation).
 ```c
-    void *buf = NULL;
-    size_t bufsize;
+unsigned char *buf;
+size_t bufsize;
 
-    struct zip_t *zip = zip_open("foo.zip", 0, 'r');
+struct zip_t *zip = zip_open("foo.zip", 0, 'r');
+{
+    zip_entry_open(zip, "foo-1.txt");
     {
-        zip_entry_open(zip, "foo-1.txt");
-        {
-            zip_entry_read(zip, &buf, &bufsize);
-        }
-        zip_entry_close(zip);
+        bufsize = zip_entry_size(zip);
+        buf = calloc(sizeof(unsigned char), bufsize);
+
+        zip_entry_noallocread(zip, (void *)buf, bufsize);
     }
-    zip_close(zip);
+    zip_entry_close(zip);
+}
+zip_close(zip);
 
-    free(buf);
+free(buf);
 ```
 
-* Extract a zip entry into memory using callback.
+*   Extract a zip entry into memory using callback.
 ```c
-    struct buffer_t {
-        char *data;
-        size_t size;
-    };
+struct buffer_t {
+    char *data;
+    size_t size;
+};
 
-    static size_t on_extract(void *arg, unsigned long long offset, const void *data, size_t size) {
-        struct buffer_t *buf = (struct buffer_t *)arg;
-        buf->data = realloc(buf->data, buf->size + size + 1);
-        assert(NULL != buf->data);
+static size_t on_extract(void *arg, unsigned long long offset, const void *data, size_t size) {
+    struct buffer_t *buf = (struct buffer_t *)arg;
+    buf->data = realloc(buf->data, buf->size + size + 1);
+    assert(NULL != buf->data);
 
-        memcpy(&(buf->data[buf->size]), data, size);
-        buf->size += size;
-        buf->data[buf->size] = 0;
+    memcpy(&(buf->data[buf->size]), data, size);
+    buf->size += size;
+    buf->data[buf->size] = 0;
 
-        return size;
-    }
+    return size;
+}
 
-    struct buffer_t buf = {0};
-    struct zip_t *zip = zip_open("foo.zip", 0, 'r');
+struct buffer_t buf = {0};
+struct zip_t *zip = zip_open("foo.zip", 0, 'r');
+{
+    zip_entry_open(zip, "foo-1.txt");
     {
-        zip_entry_open(zip, "foo-1.txt");
-        {
-            zip_entry_extract(zip, on_extract, &buf);
-        }
-        zip_entry_close(zip);
+        zip_entry_extract(zip, on_extract, &buf);
     }
-    zip_close(zip);
+    zip_entry_close(zip);
+}
+zip_close(zip);
 
-    free(buf.data);
+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');
+struct zip_t *zip = zip_open("foo.zip", 0, 'r');
+{
+    zip_entry_open(zip, "foo-2.txt");
     {
-        zip_entry_open(zip, "foo-2.txt");
-        {
-            zip_entry_fread(zip, "foo-2.txt");
-        }
-        zip_entry_close(zip);
+        zip_entry_fread(zip, "foo-2.txt");
+    }
+    zip_entry_close(zip);
+}
+zip_close(zip);
+```
+
+*   List of all zip entries
+```c
+struct zip_t *zip = zip_open("foo.zip", 0, 'r');
+int i, n = zip_total_entries(zip);
+for (i = 0; i < n; ++i) {
+    zip_entry_openbyindex(zip, i);
+    {
+        const char *name = zip_entry_name(zip);
+        int isdir = zip_entry_isdir(zip);
+        unsigned long long size = zip_entry_size(zip);
+        unsigned int crc32 = zip_entry_crc32(zip);
+    }
+    zip_entry_close(zip);
+}
+zip_close(zip);
+```
+
+## Bindings
+Compile zip library as a dynamic library.
+```shell
+$ mkdir build
+$ cd build
+$ cmake -DBUILD_SHARED_LIBS=true ..
+$ make
+```
+
+### Go (cgo)
+```go
+package main
+
+/*
+#cgo CFLAGS: -I../src
+#cgo LDFLAGS: -L. -lzip
+#include <zip.h>
+*/
+import "C"
+import "unsafe"
+
+func main() {
+	path := C.CString("/tmp/go.zip")
+	zip := C.zip_open(path, 6, 'w')
+
+	entryname := C.CString("test")
+	C.zip_entry_open(zip, entryname)
+
+	content := "test content"
+	buf := unsafe.Pointer(C.CString(content))
+	bufsize := C.size_t(len(content))
+	C.zip_entry_write(zip, buf, bufsize)
+
+	C.zip_entry_close(zip)
+
+	C.zip_close(zip)
+}
+```
+
+### Ruby (ffi)
+Install _ffi_ gem.
+```shell
+$ gem install ffi
+```
+
+Bind in your module.
+```ruby
+require 'ffi'
+
+module Zip
+  extend FFI::Library
+  ffi_lib "./libzip.#{::FFI::Platform::LIBSUFFIX}"
+
+  attach_function :zip_open, [:string, :int, :char], :pointer
+  attach_function :zip_close, [:pointer], :void
+
+  attach_function :zip_entry_open, [:pointer, :string], :int
+  attach_function :zip_entry_close, [:pointer], :void
+  attach_function :zip_entry_write, [:pointer, :string, :int], :int
+end
+
+ptr = Zip.zip_open("/tmp/ruby.zip", 6, "w".bytes()[0])
+
+status = Zip.zip_entry_open(ptr, "test")
+
+content = "test content"
+status = Zip.zip_entry_write(ptr, content, content.size())
+
+Zip.zip_entry_close(ptr)
+Zip.zip_close(ptr)
+```
+
+### Python (cffi)
+Install _cffi_ package
+```shell
+$ pip install cffi
+```
+
+Bind in your package.
+```python
+import ctypes.util
+from cffi import FFI
+
+ffi = FFI()
+ffi.cdef("""
+    struct zip_t *zip_open(const char *zipname, int level, char mode);
+    void zip_close(struct zip_t *zip);
+
+    int zip_entry_open(struct zip_t *zip, const char *entryname);
+    int zip_entry_close(struct zip_t *zip);
+    int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize);
+""")
+
+Zip = ffi.dlopen(ctypes.util.find_library("zip"))
+
+ptr = Zip.zip_open("/tmp/python.zip", 6, 'w')
+
+status = Zip.zip_entry_open(ptr, "test")
+
+content = "test content"
+status = Zip.zip_entry_write(ptr, content, len(content))
+
+Zip.zip_entry_close(ptr)
+Zip.zip_close(ptr)
+```
+
+### Ring
+The language comes with RingZip based on this library
+```ring
+load "ziplib.ring"
+
+new Zip {
+    setFileName("myfile.zip")
+    open("w")
+    newEntry() {
+        open("test.c")
+        writefile("test.c")
+        close()
     }
-    zip_close(zip);
+    close()
+}
+```
+
+# Contribution Rules/Coding Standards
+No need to throw away your coding style, just do your best to follow default clang-format style.
+Apply `clang-format` to the source files before commit:
+```sh
+for file in $(git ls-files | \grep -E '\.(c|h)$' | \grep -v -- '#')
+do
+    clang-format -i $file
+done
 ```
 

+ 1 - 1
contrib/zip/appveyor.yml

@@ -1,4 +1,4 @@
-version: 1.0.{build}
+version: zip-0.1.9.{build}
 build_script:
 - cmd: >-
     cd c:\projects\zip

+ 55 - 0
contrib/zip/cmake/asan-wrapper

@@ -0,0 +1,55 @@
+#!/bin/sh
+
+# The MIT License (MIT)
+#
+# Copyright (c)
+#   2013 Matthew Arsenault
+#   2015-2016 RWTH Aachen University, Federal Republic of Germany
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+# This script is a wrapper for AddressSanitizer. In some special cases you need
+# to preload AddressSanitizer to avoid error messages - e.g. if you're
+# preloading another library to your application. At the moment this script will
+# only do something, if we're running on a Linux platform. OSX might not be
+# affected.
+
+
+# Exit immediately, if platform is not Linux.
+if [ "$(uname)" != "Linux" ]
+then
+    exec $@
+fi
+
+
+# Get the used libasan of the application ($1). If a libasan was found, it will
+# be prepended to LD_PRELOAD.
+libasan=$(ldd $1 | grep libasan | sed "s/^[[:space:]]//" | cut -d' ' -f1)
+if [ -n "$libasan" ]
+then
+    if [ -n "$LD_PRELOAD" ]
+    then
+        export LD_PRELOAD="$libasan:$LD_PRELOAD"
+    else
+        export LD_PRELOAD="$libasan"
+    fi
+fi
+
+# Execute the application.
+exec $@

+ 23 - 0
contrib/zip/cmake/cmake_uninstall.cmake.in

@@ -0,0 +1,23 @@
+# copied from https://gitlab.kitware.com/cmake/community/wikis/FAQ#can-i-do-make-uninstall-with-cmake
+if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
+  message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt")
+endif(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt")
+
+file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files)
+string(REGEX REPLACE "\n" ";" files "${files}")
+foreach(file ${files})
+  message(STATUS "Uninstalling $ENV{DESTDIR}${file}")
+  if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
+    exec_program(
+      "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\""
+      OUTPUT_VARIABLE rm_out
+      RETURN_VALUE rm_retval
+      )
+    if(NOT "${rm_retval}" STREQUAL 0)
+      message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}")
+    endif(NOT "${rm_retval}" STREQUAL 0)
+  else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
+    message(STATUS "File $ENV{DESTDIR}${file} does not exist.")
+  endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}")
+endforeach(file)
+

File diff suppressed because it is too large
+ 517 - 317
contrib/zip/src/miniz.h


+ 782 - 496
contrib/zip/src/zip.c

@@ -7,29 +7,40 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  * OTHER DEALINGS IN THE SOFTWARE.
  */
-
-#include "zip.h"
-#include "miniz.h"
+#define __STDC_WANT_LIB_EXT1__ 1
 
 #include <errno.h>
 #include <sys/stat.h>
 #include <time.h>
 
-#if defined _WIN32 || defined __WIN32__
-/* Win32, DOS */
+#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) ||              \
+    defined(__MINGW32__)
+/* Win32, DOS, MSVC, MSVS */
 #include <direct.h>
 
 #define MKDIR(DIRNAME) _mkdir(DIRNAME)
 #define STRCLONE(STR) ((STR) ? _strdup(STR) : NULL)
 #define HAS_DEVICE(P)                                                          \
-    ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) && \
-     (P)[1] == ':')
+  ((((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
+
+#include <unistd.h> // needed for symlink() on BSD
+int symlink(const char *target, const char *linkpath); // needed on Linux
+
 #define MKDIR(DIRNAME) mkdir(DIRNAME, 0755)
 #define STRCLONE(STR) ((STR) ? strdup(STR) : NULL)
+
+#endif
+
+#include "miniz.h"
+#include "zip.h"
+
+#ifndef HAS_DEVICE
+#define HAS_DEVICE(P) 0
 #endif
 
 #ifndef FILESYSTEM_PREFIX_LEN
@@ -40,601 +51,876 @@
 #define ISSLASH(C) ((C) == '/')
 #endif
 
-#define CLEANUP(ptr)           \
-    do {                       \
-        if (ptr) {             \
-            free((void *)ptr); \
-            ptr = NULL;        \
-        }                      \
-    } while (0)
-
-static char *basename(const char *name) {
-    char const *p;
-    char const *base = name += FILESYSTEM_PREFIX_LEN(name);
-    int all_slashes = 1;
-
-    for (p = name; *p; p++) {
-        if (ISSLASH(*p))
-            base = p + 1;
-        else
-            all_slashes = 0;
-    }
-
-    /* If NAME is all slashes, arrange to return `/'. */
-    if (*base == '\0' && ISSLASH(*name) && all_slashes) --base;
-
-    return (char *)base;
+#define CLEANUP(ptr)                                                           \
+  do {                                                                         \
+    if (ptr) {                                                                 \
+      free((void *)ptr);                                                       \
+      ptr = NULL;                                                              \
+    }                                                                          \
+  } while (0)
+
+static const char *base_name(const char *name) {
+  char const *p;
+  char const *base = name += FILESYSTEM_PREFIX_LEN(name);
+  int all_slashes = 1;
+
+  for (p = name; *p; p++) {
+    if (ISSLASH(*p))
+      base = p + 1;
+    else
+      all_slashes = 0;
+  }
+
+  /* If NAME is all slashes, arrange to return `/'. */
+  if (*base == '\0' && ISSLASH(*name) && all_slashes)
+    --base;
+
+  return base;
 }
 
 static int mkpath(const char *path) {
-    char const *p;
-    char npath[MAX_PATH + 1] = {0};
-    int len = 0;
-
-    for (p = path; *p && len < MAX_PATH; p++) {
-        if (ISSLASH(*p) && len > 0) {
-            if (MKDIR(npath) == -1)
-                if (errno != EEXIST) return -1;
-        }
-        npath[len++] = *p;
-    }
-
-    return 0;
+  char const *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
+    
+  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)
+          return -1;
+    }
+    npath[len++] = *p;
+  }
+
+  return 0;
 }
 
-static char *strrpl(const char *str, char oldchar, char newchar) {
-    char *rpl = (char *)malloc(sizeof(char) * (1 + strlen(str)));
-    char *begin = rpl;
-    char c;
-    while((c = *str++)) {
-        if (c == oldchar) {
-            c = newchar;
-        }
-        *rpl++ = c;
+static char *strrpl(const char *str, size_t n, char oldchar, char newchar) {
+  char c;
+  size_t i;
+  char *rpl = (char *)calloc((1 + n), sizeof(char));
+  char *begin = rpl;
+  if (!rpl) {
+    return NULL;
+  }
+
+  for (i = 0; (i < n) && (c = *str++); ++i) {
+    if (c == oldchar) {
+      c = newchar;
     }
-    *rpl = '\0';
+    *rpl++ = c;
+  }
 
-    return begin;
+  return begin;
 }
 
 struct zip_entry_t {
-    int index;
-    const char *name;
-    mz_uint64 uncomp_size;
-    mz_uint64 comp_size;
-    mz_uint32 uncomp_crc32;
-    mz_uint64 offset;
-    mz_uint8 header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE];
-    mz_uint64 header_offset;
-    mz_uint16 method;
-    mz_zip_writer_add_state state;
-    tdefl_compressor comp;
+  int index;
+  char *name;
+  mz_uint64 uncomp_size;
+  mz_uint64 comp_size;
+  mz_uint32 uncomp_crc32;
+  mz_uint64 offset;
+  mz_uint8 header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE];
+  mz_uint64 header_offset;
+  mz_uint16 method;
+  mz_zip_writer_add_state state;
+  tdefl_compressor comp;
+  mz_uint32 external_attr;
+  time_t m_time;
 };
 
 struct zip_t {
-    mz_zip_archive archive;
-    mz_uint level;
-    struct zip_entry_t entry;
-    char mode;
+  mz_zip_archive archive;
+  mz_uint level;
+  struct zip_entry_t entry;
 };
 
 struct zip_t *zip_open(const char *zipname, int level, char mode) {
-    struct zip_t *zip = NULL;
-
-    if (!zipname || strlen(zipname) < 1) {
-        // zip_t archive name is empty or NULL
-        goto cleanup;
+  struct zip_t *zip = NULL;
+
+  if (!zipname || strlen(zipname) < 1) {
+    // zip_t archive name is empty or NULL
+    goto cleanup;
+  }
+
+  if (level < 0)
+    level = MZ_DEFAULT_LEVEL;
+  if ((level & 0xF) > MZ_UBER_COMPRESSION) {
+    // Wrong compression level
+    goto cleanup;
+  }
+
+  zip = (struct zip_t *)calloc((size_t)1, sizeof(struct zip_t));
+  if (!zip)
+    goto cleanup;
+
+  zip->level = (mz_uint)level;
+  switch (mode) {
+  case 'w':
+    // Create a new archive.
+    if (!mz_zip_writer_init_file(&(zip->archive), zipname, 0)) {
+      // Cannot initialize zip_archive writer
+      goto cleanup;
     }
+    break;
 
-    if (level < 0) level = MZ_DEFAULT_LEVEL;
-    if ((level & 0xF) > MZ_UBER_COMPRESSION) {
-        // Wrong compression level
-        goto cleanup;
+  case 'r':
+  case 'a':
+    if (!mz_zip_reader_init_file(
+            &(zip->archive), zipname,
+            zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) {
+      // An archive file does not exist or cannot initialize
+      // zip_archive reader
+      goto cleanup;
     }
-
-    zip = (struct zip_t *)calloc((size_t)1, sizeof(struct zip_t));
-    if (!zip) goto cleanup;
-
-    zip->level = level;
-    zip->mode = mode;
-    switch (mode) {
-        case 'w':
-            // Create a new archive.
-            if (!mz_zip_writer_init_file(&(zip->archive), zipname, 0)) {
-                // Cannot initialize zip_archive writer
-                goto cleanup;
-            }
-            break;
-
-        case 'r':
-        case 'a':
-            if (!mz_zip_reader_init_file(
-                    &(zip->archive), zipname,
-                    level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) {
-                // An archive file does not exist or cannot initialize
-                // zip_archive reader
-                goto cleanup;
-            }
-
-            if (mode == 'a' &&
-                !mz_zip_writer_init_from_reader(&(zip->archive), zipname)) {
-                mz_zip_reader_end(&(zip->archive));
-                goto cleanup;
-            }
-
-            break;
-
-        default:
-            goto cleanup;
+    if (mode == 'a' &&
+        !mz_zip_writer_init_from_reader(&(zip->archive), zipname)) {
+      mz_zip_reader_end(&(zip->archive));
+      goto cleanup;
     }
+    break;
+
+  default:
+    goto cleanup;
+  }
 
-    return zip;
+  return zip;
 
 cleanup:
-    CLEANUP(zip);
-    return NULL;
+  CLEANUP(zip);
+  return NULL;
 }
 
 void zip_close(struct zip_t *zip) {
-    if (zip) {
-        // Always finalize, even if adding failed for some reason, so we have a
-        // valid central directory.
-        mz_zip_writer_finalize_archive(&(zip->archive));
+  if (zip) {
+    // Always finalize, even if adding failed for some reason, so we have a
+    // valid central directory.
+    mz_zip_writer_finalize_archive(&(zip->archive));
 
-        mz_zip_writer_end(&(zip->archive));
-        mz_zip_reader_end(&(zip->archive));
+    mz_zip_writer_end(&(zip->archive));
+    mz_zip_reader_end(&(zip->archive));
 
-        CLEANUP(zip);
-    }
+    CLEANUP(zip);
+  }
 }
 
 int zip_entry_open(struct zip_t *zip, const char *entryname) {
-    char *locname = NULL;
-    size_t entrylen = 0;
-    mz_zip_archive *pzip = NULL;
-    mz_uint num_alignment_padding_bytes, level;
-
-    if (!zip || !entryname) {
-        return -1;
-    }
-
-    entrylen = strlen(entryname);
-    if (entrylen < 1) {
-        return -1;
-    }
-
-    pzip = &(zip->archive);
-    /*
-      .ZIP File Format Specification Version: 6.3.3
-
-      4.4.17.1 The name of the file, with optional relative path.
-      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.  If input came from standard
-      input, there is no file name field.
-    */
-    locname = strrpl(entryname, '\\', '/');
-
-    if (zip->mode == 'r') {
-        zip->entry.index = mz_zip_reader_locate_file(pzip, locname, NULL, 0);
-        CLEANUP(locname);
-        return (zip->entry.index < 0) ? -1 : 0;
-    }
-
-    zip->entry.index = zip->archive.m_total_files;
-    zip->entry.name = locname;
-    if (!zip->entry.name) {
-        // Cannot parse zip entry name
-        return -1;
-    }
+  size_t entrylen = 0;
+  mz_zip_archive *pzip = NULL;
+  mz_uint num_alignment_padding_bytes, level;
+  mz_zip_archive_file_stat stats;
+
+  if (!zip || !entryname) {
+    return -1;
+  }
+
+  entrylen = strlen(entryname);
+  if (entrylen < 1) {
+    return -1;
+  }
+
+  /*
+    .ZIP File Format Specification Version: 6.3.3
+
+    4.4.17.1 The name of the file, with optional relative path.
+    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.  If input came from standard
+    input, there is no file name field.
+  */
+  zip->entry.name = strrpl(entryname, entrylen, '\\', '/');
+  if (!zip->entry.name) {
+    // Cannot parse zip entry name
+    return -1;
+  }
+
+  pzip = &(zip->archive);
+  if (pzip->m_zip_mode == MZ_ZIP_MODE_READING) {
+    zip->entry.index =
+        mz_zip_reader_locate_file(pzip, zip->entry.name, NULL, 0);
+    if (zip->entry.index < 0) {
+      goto cleanup;
+    }
+
+    if (!mz_zip_reader_file_stat(pzip, (mz_uint)zip->entry.index, &stats)) {
+      goto cleanup;
+    }
+
+    zip->entry.comp_size = stats.m_comp_size;
+    zip->entry.uncomp_size = stats.m_uncomp_size;
+    zip->entry.uncomp_crc32 = stats.m_crc32;
+    zip->entry.offset = stats.m_central_dir_ofs;
+    zip->entry.header_offset = stats.m_local_header_ofs;
+    zip->entry.method = stats.m_method;
+    zip->entry.external_attr = stats.m_external_attr;
+    zip->entry.m_time = stats.m_time;
 
-    zip->entry.comp_size = 0;
-    zip->entry.uncomp_size = 0;
-    zip->entry.uncomp_crc32 = MZ_CRC32_INIT;
-    zip->entry.offset = zip->archive.m_archive_size;
-    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;
-
-    num_alignment_padding_bytes =
-        mz_zip_writer_compute_padding_needed_for_file_alignment(pzip);
-
-    if (!pzip->m_pState || (pzip->m_zip_mode != MZ_ZIP_MODE_WRITING)) {
-        // Wrong zip mode
-        return -1;
-    }
-    if (zip->level & MZ_ZIP_FLAG_COMPRESSED_DATA) {
-        // Wrong zip compression level
-        return -1;
-    }
-    // no zip64 support yet
-    if ((pzip->m_total_files == 0xFFFF) ||
-        ((pzip->m_archive_size + num_alignment_padding_bytes +
-          MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE +
-          entrylen) > 0xFFFFFFFF)) {
-        // No zip64 support yet
-        return -1;
-    }
-    if (!mz_zip_writer_write_zeros(
-            pzip, zip->entry.offset,
-            num_alignment_padding_bytes + sizeof(zip->entry.header))) {
-        // Cannot memset zip entry header
-        return -1;
-    }
+    return 0;
+  }
+
+  zip->entry.index = (int)zip->archive.m_total_files;
+  zip->entry.comp_size = 0;
+  zip->entry.uncomp_size = 0;
+  zip->entry.uncomp_crc32 = MZ_CRC32_INIT;
+  zip->entry.offset = zip->archive.m_archive_size;
+  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;
+  zip->entry.external_attr = 0;
+
+  num_alignment_padding_bytes =
+      mz_zip_writer_compute_padding_needed_for_file_alignment(pzip);
+
+  if (!pzip->m_pState || (pzip->m_zip_mode != MZ_ZIP_MODE_WRITING)) {
+    // Wrong zip mode
+    goto cleanup;
+  }
+  if (zip->level & MZ_ZIP_FLAG_COMPRESSED_DATA) {
+    // Wrong zip compression level
+    goto cleanup;
+  }
+  // no zip64 support yet
+  if ((pzip->m_total_files == 0xFFFF) ||
+      ((pzip->m_archive_size + num_alignment_padding_bytes +
+        MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE +
+        entrylen) > 0xFFFFFFFF)) {
+    // No zip64 support yet
+    goto cleanup;
+  }
+  if (!mz_zip_writer_write_zeros(pzip, zip->entry.offset,
+                                 num_alignment_padding_bytes +
+                                     sizeof(zip->entry.header))) {
+    // Cannot memset zip entry header
+    goto cleanup;
+  }
+
+  zip->entry.header_offset += num_alignment_padding_bytes;
+  if (pzip->m_file_offset_alignment) {
+    MZ_ASSERT(
+        (zip->entry.header_offset & (pzip->m_file_offset_alignment - 1)) == 0);
+  }
+  zip->entry.offset += num_alignment_padding_bytes + sizeof(zip->entry.header);
+
+  if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.offset, zip->entry.name,
+                     entrylen) != entrylen) {
+    // Cannot write data to zip entry
+    goto cleanup;
+  }
+
+  zip->entry.offset += entrylen;
+  level = zip->level & 0xF;
+  if (level) {
+    zip->entry.state.m_pZip = pzip;
+    zip->entry.state.m_cur_archive_file_ofs = zip->entry.offset;
+    zip->entry.state.m_comp_size = 0;
+
+    if (tdefl_init(&(zip->entry.comp), mz_zip_writer_add_put_buf_callback,
+                   &(zip->entry.state),
+                   (int)tdefl_create_comp_flags_from_zip_params(
+                       (int)level, -15, MZ_DEFAULT_STRATEGY)) !=
+        TDEFL_STATUS_OKAY) {
+      // Cannot initialize the zip compressor
+      goto cleanup;
+    }
+  }
+
+  zip->entry.m_time = time(NULL);
+
+  return 0;
 
-    zip->entry.header_offset += num_alignment_padding_bytes;
-    if (pzip->m_file_offset_alignment) {
-        MZ_ASSERT((zip->entry.header_offset &
-                   (pzip->m_file_offset_alignment - 1)) == 0);
-    }
-    zip->entry.offset +=
-        num_alignment_padding_bytes + sizeof(zip->entry.header);
+cleanup:
+  CLEANUP(zip->entry.name);
+  return -1;
+}
 
-    if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.offset, zip->entry.name,
-                       entrylen) != entrylen) {
-        // Cannot write data to zip entry
-        return -1;
-    }
+int zip_entry_openbyindex(struct zip_t *zip, int index) {
+  mz_zip_archive *pZip = NULL;
+  mz_zip_archive_file_stat stats;
+  mz_uint namelen;
+  const mz_uint8 *pHeader;
+  const char *pFilename;
+
+  if (!zip) {
+    // zip_t handler is not initialized
+    return -1;
+  }
+
+  pZip = &(zip->archive);
+  if (pZip->m_zip_mode != MZ_ZIP_MODE_READING) {
+    // open by index requires readonly mode
+    return -1;
+  }
+
+  if (index < 0 || (mz_uint)index >= pZip->m_total_files) {
+    // index out of range
+    return -1;
+  }
+
+  if (!(pHeader = &MZ_ZIP_ARRAY_ELEMENT(
+            &pZip->m_pState->m_central_dir, mz_uint8,
+            MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets,
+                                 mz_uint32, index)))) {
+    // cannot find header in central directory
+    return -1;
+  }
+
+  namelen = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS);
+  pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE;
+
+  /*
+    .ZIP File Format Specification Version: 6.3.3
+
+    4.4.17.1 The name of the file, with optional relative path.
+    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.  If input came from standard
+    input, there is no file name field.
+  */
+  zip->entry.name = strrpl(pFilename, namelen, '\\', '/');
+  if (!zip->entry.name) {
+    // local entry name is NULL
+    return -1;
+  }
+
+  if (!mz_zip_reader_file_stat(pZip, (mz_uint)index, &stats)) {
+    return -1;
+  }
+
+  zip->entry.index = index;
+  zip->entry.comp_size = stats.m_comp_size;
+  zip->entry.uncomp_size = stats.m_uncomp_size;
+  zip->entry.uncomp_crc32 = stats.m_crc32;
+  zip->entry.offset = stats.m_central_dir_ofs;
+  zip->entry.header_offset = stats.m_local_header_ofs;
+  zip->entry.method = stats.m_method;
+  zip->entry.external_attr = stats.m_external_attr;
+  zip->entry.m_time = stats.m_time;
+
+  return 0;
+}
 
-    zip->entry.offset += entrylen;
-    level = zip->level & 0xF;
-    if (level) {
-        zip->entry.state.m_pZip = pzip;
-        zip->entry.state.m_cur_archive_file_ofs = zip->entry.offset;
-        zip->entry.state.m_comp_size = 0;
-
-        if (tdefl_init(&(zip->entry.comp), mz_zip_writer_add_put_buf_callback,
-                       &(zip->entry.state),
-                       tdefl_create_comp_flags_from_zip_params(
-                           level, -15, MZ_DEFAULT_STRATEGY)) !=
-            TDEFL_STATUS_OKAY) {
-            // Cannot initialize the zip compressor
-            return -1;
-        }
-    }
+int zip_entry_close(struct zip_t *zip) {
+  mz_zip_archive *pzip = NULL;
+  mz_uint level;
+  tdefl_status done;
+  mz_uint16 entrylen;
+  mz_uint16 dos_time, dos_date;
+  int status = -1;
+
+  if (!zip) {
+    // zip_t handler is not initialized
+    goto cleanup;
+  }
+
+  pzip = &(zip->archive);
+  if (pzip->m_zip_mode == MZ_ZIP_MODE_READING) {
+    status = 0;
+    goto cleanup;
+  }
+
+  level = zip->level & 0xF;
+  if (level) {
+    done = tdefl_compress_buffer(&(zip->entry.comp), "", 0, TDEFL_FINISH);
+    if (done != TDEFL_STATUS_DONE && done != TDEFL_STATUS_OKAY) {
+      // Cannot flush compressed buffer
+      goto cleanup;
+    }
+    zip->entry.comp_size = zip->entry.state.m_comp_size;
+    zip->entry.offset = zip->entry.state.m_cur_archive_file_ofs;
+    zip->entry.method = MZ_DEFLATED;
+  }
+
+  entrylen = (mz_uint16)strlen(zip->entry.name);
+  // no zip64 support yet
+  if ((zip->entry.comp_size > 0xFFFFFFFF) || (zip->entry.offset > 0xFFFFFFFF)) {
+    // No zip64 support, yet
+    goto cleanup;
+  }
+
+  mz_zip_time_t_to_dos_time(zip->entry.m_time, &dos_time, &dos_date);
+  if (!mz_zip_writer_create_local_dir_header(
+          pzip, zip->entry.header, entrylen, 0, zip->entry.uncomp_size,
+          zip->entry.comp_size, zip->entry.uncomp_crc32, zip->entry.method, 0,
+          dos_time, dos_date)) {
+    // Cannot create zip entry header
+    goto cleanup;
+  }
+
+  if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.header_offset,
+                     zip->entry.header,
+                     sizeof(zip->entry.header)) != sizeof(zip->entry.header)) {
+    // Cannot write zip entry header
+    goto cleanup;
+  }
+
+  if (!mz_zip_writer_add_to_central_dir(
+          pzip, zip->entry.name, entrylen, NULL, 0, "", 0,
+          zip->entry.uncomp_size, zip->entry.comp_size, zip->entry.uncomp_crc32,
+          zip->entry.method, 0, dos_time, dos_date, zip->entry.header_offset,
+          zip->entry.external_attr)) {
+    // Cannot write to zip central dir
+    goto cleanup;
+  }
+
+  pzip->m_total_files++;
+  pzip->m_archive_size = zip->entry.offset;
+  status = 0;
 
-    return 0;
+cleanup:
+  if (zip) {
+    zip->entry.m_time = 0;
+    CLEANUP(zip->entry.name);
+  }
+  return status;
 }
 
-int zip_entry_close(struct zip_t *zip) {
-    mz_zip_archive *pzip = NULL;
-    mz_uint level;
-    tdefl_status done;
-    mz_uint16 entrylen;
-    time_t t;
-    struct tm *tm;
-    mz_uint16 dos_time, dos_date;
-    int status = -1;
-
-    if (!zip) {
-        // zip_t handler is not initialized
-        return -1;
-    }
+const char *zip_entry_name(struct zip_t *zip) {
+  if (!zip) {
+    // zip_t handler is not initialized
+    return NULL;
+  }
 
-    if (zip->mode == 'r') {
-        return 0;
-    }
+  return zip->entry.name;
+}
 
-    pzip = &(zip->archive);
-    level = zip->level & 0xF;
-    if (level) {
-        done = tdefl_compress_buffer(&(zip->entry.comp), "", 0, TDEFL_FINISH);
-        if (done != TDEFL_STATUS_DONE && done != TDEFL_STATUS_OKAY) {
-            // Cannot flush compressed buffer
-            goto cleanup;
-        }
-        zip->entry.comp_size = zip->entry.state.m_comp_size;
-        zip->entry.offset = zip->entry.state.m_cur_archive_file_ofs;
-        zip->entry.method = MZ_DEFLATED;
-    }
+int zip_entry_index(struct zip_t *zip) {
+  if (!zip) {
+    // zip_t handler is not initialized
+    return -1;
+  }
 
-    entrylen = (mz_uint16)strlen(zip->entry.name);
-    t = time(NULL);
-    tm = localtime(&t);
-    dos_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) +
-                           ((tm->tm_sec) >> 1));
-    dos_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) +
-                           ((tm->tm_mon + 1) << 5) + tm->tm_mday);
-
-    // no zip64 support yet
-    if ((zip->entry.comp_size > 0xFFFFFFFF) ||
-        (zip->entry.offset > 0xFFFFFFFF)) {
-        // No zip64 support, yet
-        goto cleanup;
-    }
+  return zip->entry.index;
+}
 
-    if (!mz_zip_writer_create_local_dir_header(
-            pzip, zip->entry.header, entrylen, 0, zip->entry.uncomp_size,
-            zip->entry.comp_size, zip->entry.uncomp_crc32, zip->entry.method, 0,
-            dos_time, dos_date)) {
-        // Cannot create zip entry header
-        goto cleanup;
-    }
+int zip_entry_isdir(struct zip_t *zip) {
+  if (!zip) {
+    // zip_t handler is not initialized
+    return -1;
+  }
 
-    if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.header_offset,
-                       zip->entry.header, sizeof(zip->entry.header)) !=
-        sizeof(zip->entry.header)) {
-        // Cannot write zip entry header
-        goto cleanup;
-    }
+  if (zip->entry.index < 0) {
+    // zip entry is not opened
+    return -1;
+  }
 
-    if (!mz_zip_writer_add_to_central_dir(
-            pzip, zip->entry.name, entrylen, NULL, 0, "", 0,
-            zip->entry.uncomp_size, zip->entry.comp_size,
-            zip->entry.uncomp_crc32, zip->entry.method, 0, dos_time, dos_date,
-            zip->entry.header_offset, 0)) {
-        // Cannot write to zip central dir
-        goto cleanup;
-    }
+  return (int)mz_zip_reader_is_file_a_directory(&zip->archive,
+                                                (mz_uint)zip->entry.index);
+}
 
-    pzip->m_total_files++;
-    pzip->m_archive_size = zip->entry.offset;
-    status = 0;
+unsigned long long zip_entry_size(struct zip_t *zip) {
+  return zip ? zip->entry.uncomp_size : 0;
+}
 
-cleanup:
-    CLEANUP(zip->entry.name);
-    return status;
+unsigned int zip_entry_crc32(struct zip_t *zip) {
+  return zip ? zip->entry.uncomp_crc32 : 0;
 }
 
 int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize) {
-    mz_uint level;
-    mz_zip_archive *pzip = NULL;
-    tdefl_status status;
+  mz_uint level;
+  mz_zip_archive *pzip = NULL;
+  tdefl_status status;
 
-    if (!zip) {
-        // zip_t handler is not initialized
-        return -1;
-    }
+  if (!zip) {
+    // zip_t handler is not initialized
+    return -1;
+  }
 
-    pzip = &(zip->archive);
-    if (buf && bufsize > 0) {
-        zip->entry.uncomp_size += bufsize;
-        zip->entry.uncomp_crc32 = (mz_uint32)mz_crc32(
-            zip->entry.uncomp_crc32, (const mz_uint8 *)buf, bufsize);
-
-        level = zip->level & 0xF;
-        if (!level) {
-            if ((pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.offset, buf,
-                                bufsize) != bufsize)) {
-                // Cannot write buffer
-                return -1;
-            }
-            zip->entry.offset += bufsize;
-            zip->entry.comp_size += bufsize;
-        } else {
-            status = tdefl_compress_buffer(&(zip->entry.comp), buf, bufsize,
-                                           TDEFL_NO_FLUSH);
-            if (status != TDEFL_STATUS_DONE && status != TDEFL_STATUS_OKAY) {
-                // Cannot compress buffer
-                return -1;
-            }
-        }
+  pzip = &(zip->archive);
+  if (buf && bufsize > 0) {
+    zip->entry.uncomp_size += bufsize;
+    zip->entry.uncomp_crc32 = (mz_uint32)mz_crc32(
+        zip->entry.uncomp_crc32, (const mz_uint8 *)buf, bufsize);
+
+    level = zip->level & 0xF;
+    if (!level) {
+      if ((pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.offset, buf,
+                          bufsize) != bufsize)) {
+        // Cannot write buffer
+        return -1;
+      }
+      zip->entry.offset += bufsize;
+      zip->entry.comp_size += bufsize;
+    } else {
+      status = tdefl_compress_buffer(&(zip->entry.comp), buf, bufsize,
+                                     TDEFL_NO_FLUSH);
+      if (status != TDEFL_STATUS_DONE && status != TDEFL_STATUS_OKAY) {
+        // Cannot compress buffer
+        return -1;
+      }
     }
+  }
 
-    return 0;
+  return 0;
 }
 
 int zip_entry_fwrite(struct zip_t *zip, const char *filename) {
-    int status = 0;
-    size_t n = 0;
-    FILE *stream = NULL;
-    mz_uint8 buf[MZ_ZIP_MAX_IO_BUF_SIZE] = {0};
-
-    if (!zip) {
-        // zip_t handler is not initialized
-        return -1;
-    }
+  int status = 0;
+  size_t n = 0;
+  FILE *stream = NULL;
+  mz_uint8 buf[MZ_ZIP_MAX_IO_BUF_SIZE];
+  struct MZ_FILE_STAT_STRUCT file_stat;
+
+  if (!zip) {
+    // zip_t handler is not initialized
+    return -1;
+  }
+
+  memset(buf, 0, MZ_ZIP_MAX_IO_BUF_SIZE);
+  memset((void *)&file_stat, 0, sizeof(struct MZ_FILE_STAT_STRUCT));
+  if (MZ_FILE_STAT(filename, &file_stat) != 0) {
+    // problem getting information - check errno
+    return -1;
+  }
+
+  if ((file_stat.st_mode & 0200) == 0) {
+    // MS-DOS read-only attribute
+    zip->entry.external_attr |= 0x01;
+  }
+  zip->entry.external_attr |= (mz_uint32)((file_stat.st_mode & 0xFFFF) << 16);
+  zip->entry.m_time = file_stat.st_mtime;
+
+#if defined(_MSC_VER)
+  if (fopen_s(&stream, filename, "rb"))
+#else
+  if (!(stream = fopen(filename, "rb")))
+#endif
+  {
+    // Cannot open filename
+    return -1;
+  }
 
-    stream = fopen(filename, "rb");
-    if (!stream) {
-        // Cannot open filename
-        return -1;
+  while ((n = fread(buf, sizeof(mz_uint8), MZ_ZIP_MAX_IO_BUF_SIZE, stream)) >
+         0) {
+    if (zip_entry_write(zip, buf, n) < 0) {
+      status = -1;
+      break;
     }
+  }
+  fclose(stream);
 
-    while ((n = fread(buf, sizeof(mz_uint8), MZ_ZIP_MAX_IO_BUF_SIZE, stream)) >
-           0) {
-        if (zip_entry_write(zip, buf, n) < 0) {
-            status = -1;
-            break;
-        }
-    }
-    fclose(stream);
+  return status;
+}
 
-    return status;
+ssize_t zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize) {
+  mz_zip_archive *pzip = NULL;
+  mz_uint idx;
+  size_t size = 0;
+
+  if (!zip) {
+    // zip_t handler is not initialized
+    return -1;
+  }
+
+  pzip = &(zip->archive);
+  if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < 0) {
+    // the entry is not found or we do not have read access
+    return -1;
+  }
+
+  idx = (mz_uint)zip->entry.index;
+  if (mz_zip_reader_is_file_a_directory(pzip, idx)) {
+    // the entry is a directory
+    return -1;
+  }
+
+  *buf = mz_zip_reader_extract_to_heap(pzip, idx, &size, 0);
+  if (*buf && bufsize) {
+    *bufsize = size;
+  }
+  return size;
 }
 
-int zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize) {
-    mz_zip_archive *pzip = NULL;
-    mz_uint idx;
+ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, size_t bufsize) {
+  mz_zip_archive *pzip = NULL;
 
-    if (!zip) {
-        // zip_t handler is not initialized
-        return -1;
-    }
+  if (!zip) {
+    // zip_t handler is not initialized
+    return -1;
+  }
 
-    if (zip->mode != 'r' || zip->entry.index < 0) {
-        // the entry is not found or we do not have read access
-        return -1;
-    }
+  pzip = &(zip->archive);
+  if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < 0) {
+    // the entry is not found or we do not have read access
+    return -1;
+  }
 
-    pzip = &(zip->archive);
-    idx = (mz_uint)zip->entry.index;
-    if (mz_zip_reader_is_file_a_directory(pzip, idx)) {
-        // the entry is a directory
-        return -1;
-    }
+  if (!mz_zip_reader_extract_to_mem_no_alloc(pzip, (mz_uint)zip->entry.index,
+  buf, bufsize, 0, NULL,  0)) {
+    return -1;
+  }
 
-    *buf = mz_zip_reader_extract_to_heap(pzip, idx, bufsize, 0);
-    return (*buf) ? 0 : -1;
+  return (ssize_t)zip->entry.uncomp_size;
 }
 
 int zip_entry_fread(struct zip_t *zip, const char *filename) {
-    mz_zip_archive *pzip = NULL;
-    mz_uint idx;
-
-    if (!zip) {
-        // zip_t handler is not initialized
-        return -1;
-    }
-
-    if (zip->mode != 'r' || zip->entry.index < 0) {
-        // the entry is not found or we do not have read access
-        return -1;
-    }
+  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) {
+    // zip_t handler is not initialized
+    return -1;
+  }
+
+  memset((void *)&info, 0, sizeof(mz_zip_archive_file_stat));
+  pzip = &(zip->archive);
+  if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < 0) {
+    // the entry is not found or we do not have read access
+    return -1;
+  }
+
+  idx = (mz_uint)zip->entry.index;
+  if (mz_zip_reader_is_file_a_directory(pzip, idx)) {
+    // the entry is a directory
+    return -1;
+  }
+
+  if (!mz_zip_reader_extract_to_file(pzip, idx, filename, 0)) {
+    return -1;
+  }
+
+#if defined(_MSC_VER)
+#else
+  if (!mz_zip_reader_file_stat(pzip, idx, &info)) {
+    // Cannot get information about zip archive;
+    return -1;
+  }
 
-    pzip = &(zip->archive);
-    idx = (mz_uint)zip->entry.index;
-    if (mz_zip_reader_is_file_a_directory(pzip, idx)) {
-        // the entry is a directory
-        return -1;
+  xattr = (info.m_external_attr >> 16) & 0xFFFF;
+  if (xattr > 0) {
+    if (chmod(filename, (mode_t)xattr) < 0) {
+      return -1;
     }
+  }
+#endif
 
-    return (mz_zip_reader_extract_to_file(pzip, idx, filename, 0)) ? 0 : -1;
+  return 0;
 }
 
 int zip_entry_extract(struct zip_t *zip,
                       size_t (*on_extract)(void *arg, unsigned long long offset,
                                            const void *buf, size_t bufsize),
                       void *arg) {
-    mz_zip_archive *pzip = NULL;
-    mz_uint idx;
-
-    if (!zip) {
-        // zip_t handler is not initialized
-        return -1;
-    }
-
-    if (zip->mode != 'r' || zip->entry.index < 0) {
-        // the entry is not found or we do not have read access
-        return -1;
-    }
+  mz_zip_archive *pzip = NULL;
+  mz_uint idx;
+
+  if (!zip) {
+    // zip_t handler is not initialized
+    return -1;
+  }
+
+  pzip = &(zip->archive);
+  if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < 0) {
+    // the entry is not found or we do not have read access
+    return -1;
+  }
+
+  idx = (mz_uint)zip->entry.index;
+  return (mz_zip_reader_extract_to_callback(pzip, idx, on_extract, arg, 0))
+             ? 0
+             : -1;
+}
 
-    pzip = &(zip->archive);
-    idx = (mz_uint)zip->entry.index;
+int zip_total_entries(struct zip_t *zip) {
+  if (!zip) {
+    // zip_t handler is not initialized
+    return -1;
+  }
 
-    return (mz_zip_reader_extract_to_callback(pzip, idx, on_extract, arg, 0))
-               ? 0
-               : -1;
+  return (int)zip->archive.m_total_files;
 }
 
 int zip_create(const char *zipname, const char *filenames[], size_t len) {
-    int status = 0;
-    size_t i;
-    mz_zip_archive zip_archive;
+  int status = 0;
+  size_t i;
+  mz_zip_archive zip_archive;
+  struct MZ_FILE_STAT_STRUCT file_stat;
+  mz_uint32 ext_attributes = 0;
 
-    if (!zipname || strlen(zipname) < 1) {
-        // zip_t archive name is empty or NULL
-        return -1;
-    }
+  if (!zipname || strlen(zipname) < 1) {
+    // zip_t archive name is empty or NULL
+    return -1;
+  }
 
-    // Create a new archive.
-    if (!memset(&(zip_archive), 0, sizeof(zip_archive))) {
-        // Cannot memset zip archive
-        return -1;
-    }
+  // Create a new archive.
+  if (!memset(&(zip_archive), 0, sizeof(zip_archive))) {
+    // Cannot memset zip archive
+    return -1;
+  }
 
-    if (!mz_zip_writer_init_file(&zip_archive, zipname, 0)) {
-        // Cannot initialize zip_archive writer
-        return -1;
-    }
+  if (!mz_zip_writer_init_file(&zip_archive, zipname, 0)) {
+    // Cannot initialize zip_archive writer
+    return -1;
+  }
 
-    for (i = 0; i < len; ++i) {
-        const char *name = filenames[i];
-        if (!name) {
-            status = -1;
-            break;
-        }
+  memset((void *)&file_stat, 0, sizeof(struct MZ_FILE_STAT_STRUCT));
 
-        if (!mz_zip_writer_add_file(&zip_archive, basename(name), name, "", 0,
-                                    ZIP_DEFAULT_COMPRESSION_LEVEL)) {
-            // Cannot add file to zip_archive
-            status = -1;
-            break;
-        }
+  for (i = 0; i < len; ++i) {
+    const char *name = filenames[i];
+    if (!name) {
+      status = -1;
+      break;
     }
 
-    mz_zip_writer_finalize_archive(&zip_archive);
-    mz_zip_writer_end(&zip_archive);
-    return status;
-}
-
-int zip_extract(const char *zipname, const char *dir,
-                int (*on_extract)(const char *filename, void *arg), void *arg) {
-    int status = -1;
-    mz_uint i, n;
-    char path[MAX_PATH + 1] = {0};
-    mz_zip_archive zip_archive;
-    mz_zip_archive_file_stat info;
-    size_t dirlen = 0;
-
-    if (!memset(&(zip_archive), 0, sizeof(zip_archive))) {
-        // Cannot memset zip archive
-        return -1;
+    if (MZ_FILE_STAT(name, &file_stat) != 0) {
+      // problem getting information - check errno
+      return -1;
     }
 
-    if (!zipname || !dir) {
-        // Cannot parse zip archive name
-        return -1;
+    if ((file_stat.st_mode & 0200) == 0) {
+      // MS-DOS read-only attribute
+      ext_attributes |= 0x01;
     }
+    ext_attributes |= (mz_uint32)((file_stat.st_mode & 0xFFFF) << 16);
 
-    dirlen = strlen(dir);
-    if (dirlen + 1 > MAX_PATH) {
-        return -1;
+    if (!mz_zip_writer_add_file(&zip_archive, base_name(name), name, "", 0,
+                                ZIP_DEFAULT_COMPRESSION_LEVEL,
+                                ext_attributes)) {
+      // Cannot add file to zip_archive
+      status = -1;
+      break;
     }
+  }
 
-    // Now try to open the archive.
-    if (!mz_zip_reader_init_file(&zip_archive, zipname, 0)) {
-        // Cannot initialize zip_archive reader
-        return -1;
-    }
+  mz_zip_writer_finalize_archive(&zip_archive);
+  mz_zip_writer_end(&zip_archive);
+  return status;
+}
 
-    strcpy(path, dir);
-    if (!ISSLASH(path[dirlen - 1])) {
-#if defined _WIN32 || defined __WIN32__
-        path[dirlen] = '\\';
+int zip_extract(const char *zipname, const char *dir,
+                int (*on_extract)(const char *filename, void *arg), void *arg) {
+  int status = -1;
+  mz_uint i, n;
+  char path[MAX_PATH + 1];
+  char symlink_to[MAX_PATH + 1];
+  mz_zip_archive zip_archive;
+  mz_zip_archive_file_stat info;
+  size_t dirlen = 0;
+  mz_uint32 xattr = 0;
+
+  memset(path, 0, sizeof(path));
+  memset(symlink_to, 0, sizeof(symlink_to));
+  if (!memset(&(zip_archive), 0, sizeof(zip_archive))) {
+    // Cannot memset zip archive
+    return -1;
+  }
+
+  if (!zipname || !dir) {
+    // Cannot parse zip archive name
+    return -1;
+  }
+
+  dirlen = strlen(dir);
+  if (dirlen + 1 > MAX_PATH) {
+    return -1;
+  }
+
+  // Now try to open the archive.
+  if (!mz_zip_reader_init_file(&zip_archive, zipname, 0)) {
+    // Cannot initialize zip_archive reader
+    return -1;
+  }
+
+  memset((void *)&info, 0, sizeof(mz_zip_archive_file_stat));
+
+#if defined(_MSC_VER)
+  strcpy_s(path, MAX_PATH, dir);
 #else
-        path[dirlen] = '/';
+  strcpy(path, dir);
 #endif
-        ++dirlen;
-    }
 
-    // Get and print information about each file in the archive.
-    n = mz_zip_reader_get_num_files(&zip_archive);
-    for (i = 0; i < n; ++i) {
-        if (!mz_zip_reader_file_stat(&zip_archive, i, &info)) {
-            // Cannot get information about zip archive;
-            goto out;
-        }
-        strncpy(&path[dirlen], info.m_filename, MAX_PATH - dirlen);
-        if (mkpath(path) < 0) {
-            // Cannot make a path
-            goto out;
+  if (!ISSLASH(path[dirlen - 1])) {
+#if defined(_WIN32) || defined(__WIN32__)
+    path[dirlen] = '\\';
+#else
+    path[dirlen] = '/';
+#endif
+    ++dirlen;
+  }
+
+  // Get and print information about each file in the archive.
+  n = mz_zip_reader_get_num_files(&zip_archive);
+  for (i = 0; i < n; ++i) {
+    if (!mz_zip_reader_file_stat(&zip_archive, i, &info)) {
+      // Cannot get information about zip archive;
+      goto out;
+    }
+#if defined(_MSC_VER)
+    strncpy_s(&path[dirlen], MAX_PATH - dirlen, info.m_filename,
+              MAX_PATH - dirlen);
+#else
+    strncpy(&path[dirlen], info.m_filename, MAX_PATH - dirlen);
+#endif
+    if (mkpath(path) < 0) {
+      // Cannot make a path
+      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 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)) {
+        goto out;
+      }
+      symlink_to[info.m_uncomp_size] = '\0';
+      if (symlink(symlink_to, path) != 0) {
+        goto out;
+      }
+#endif
+    } else {
+      if (!mz_zip_reader_is_file_a_directory(&zip_archive, i)) {
+        if (!mz_zip_reader_extract_to_file(&zip_archive, i, path, 0)) {
+          // Cannot extract zip archive to file
+          goto out;
         }
+      }
 
-        if (!mz_zip_reader_is_file_a_directory(&zip_archive, i)) {
-            if (!mz_zip_reader_extract_to_file(&zip_archive, i, path, 0)) {
-                // Cannot extract zip archive to file
-                goto out;
-            }
+#if defined(_MSC_VER)
+#else
+      xattr = (info.m_external_attr >> 16) & 0xFFFF;
+      if (xattr > 0) {
+        if (chmod(path, (mode_t)xattr) < 0) {
+          goto out;
         }
+      }
+#endif
+    }
 
-        if (on_extract) {
-            if (on_extract(path, arg) < 0) {
-                goto out;
-            }
-        }
+    if (on_extract) {
+      if (on_extract(path, arg) < 0) {
+        goto out;
+      }
     }
-    status = 0;
+  }
+  status = 0;
 
 out:
-    // Close the archive, freeing any resources it was using
-    if (!mz_zip_reader_end(&zip_archive)) {
-        // Cannot end zip reader
-        status = -1;
-    }
+  // Close the archive, freeing any resources it was using
+  if (!mz_zip_reader_end(&zip_archive)) {
+    // Cannot end zip reader
+    status = -1;
+  }
 
-    return status;
+  return status;
 }

+ 135 - 12
contrib/zip/src/zip.h

@@ -13,11 +13,24 @@
 #define ZIP_H
 
 #include <string.h>
+#include <sys/types.h>
 
 #ifdef __cplusplus
 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
+// 64-bit Windows is the only mainstream platform
+// where sizeof(long) != sizeof(void*)
+#ifdef _WIN64
+typedef long long  ssize_t;  /* byte count or error */
+#else
+typedef long  ssize_t;  /* byte count or error */
+#endif
+#endif
+
 #ifndef MAX_PATH
 #define MAX_PATH 32767 /* # chars in a path name including NULL */
 #endif
@@ -47,7 +60,7 @@ struct zip_t;
 extern struct zip_t *zip_open(const char *zipname, int level, char mode);
 
 /*
-  Closes zip archive, releases resources - always finalize.
+  Closes the zip archive, releases resources - always finalize.
 
   Args:
     zip: zip archive handler.
@@ -55,7 +68,10 @@ extern struct zip_t *zip_open(const char *zipname, int level, char mode);
 extern void zip_close(struct zip_t *zip);
 
 /*
-  Opens a new entry for writing in a zip archive.
+  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.
@@ -66,6 +82,19 @@ extern void zip_close(struct zip_t *zip);
 */
 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.
+*/
+extern int zip_entry_openbyindex(struct zip_t *zip, int index);
+
 /*
   Closes a zip entry, flushes buffer and releases resources.
 
@@ -77,6 +106,67 @@ extern int zip_entry_open(struct zip_t *zip, const char *entryname);
 */
 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.
+*/
+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.
+*/
+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.
+*/
+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.
+*/
+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.
+*/
+extern unsigned int zip_entry_crc32(struct zip_t *zip);
+
 /*
   Compresses an input buffer for the current zip entry.
 
@@ -116,9 +206,31 @@ extern int zip_entry_fwrite(struct zip_t *zip, const char *filename);
     - for large entries, please take a look at zip_entry_extract function.
 
   Returns:
-    The return code - 0 on success, negative number (< 0) on error.
+    The return code - the number of bytes actually read on success.
+    Otherwise a -1 on error.
 */
-extern int zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize);
+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.
@@ -133,9 +245,9 @@ extern int zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize);
 extern int zip_entry_fread(struct zip_t *zip, const char *filename);
 
 /*
-   Extract the current zip entry using a callback function (on_extract).
+  Extracts the current zip entry using a callback function (on_extract).
 
-   Args:
+  Args:
     zip: zip archive handler.
     on_extract: callback function.
     arg: opaque pointer (optional argument,
@@ -144,12 +256,23 @@ extern int zip_entry_fread(struct zip_t *zip, const char *filename);
    Returns:
     The return code - 0 on success, negative number (< 0) on error.
  */
-extern int zip_entry_extract(struct zip_t *zip,
-                             size_t (*on_extract)(void *arg,
-                                                  unsigned long long offset,
-                                                  const void *data,
-                                                  size_t size),
-                             void *arg);
+extern int
+zip_entry_extract(struct zip_t *zip,
+                  size_t (*on_extract)(void *arg, unsigned long long offset,
+                                       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.
+*/
+extern int zip_total_entries(struct zip_t *zip);
 
 /*
   Creates a new archive and puts files into a single zip archive.

+ 12 - 0
contrib/zip/test/CMakeLists.txt

@@ -1,7 +1,19 @@
 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)
 
 add_test(NAME test COMMAND test.exe)
+add_test(NAME test_miniz COMMAND test_miniz.exe)

+ 415 - 61
contrib/zip/test/test.c

@@ -4,102 +4,456 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/stat.h>
 
-#define ZIPNAME "test.zip"
+#if defined(_MSC_VER) || defined(__MINGW64__) || defined(__MINGW32__)
+#define MZ_FILE_STAT_STRUCT _stat
+#define MZ_FILE_STAT _stat
+#else
+#define MZ_FILE_STAT_STRUCT stat
+#define MZ_FILE_STAT stat
+#endif
+
+#define ZIPNAME "test.zip\0"
 #define TESTDATA1 "Some test data 1...\0"
+#define CRC32DATA1 2220805626
 #define TESTDATA2 "Some test data 2...\0"
+#define CRC32DATA2 2532008468
+
+#define RFILE "4.txt\0"
+#define RMODE 0100444
+
+#define WFILE "6.txt\0"
+#define WMODE 0100666
+
+#define XFILE "7.txt\0"
+#define XMODE 0100777
+
+#define UNUSED(x) (void)x
+
+static int total_entries = 0;
 
 static void test_write(void) {
-    struct zip_t *zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
-    assert(zip != NULL);
+  struct zip_t *zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
+  assert(zip != NULL);
 
-    assert(0 == zip_entry_open(zip, "test/test-1.txt"));
-    assert(0 == zip_entry_write(zip, TESTDATA1, strlen(TESTDATA1)));
-    assert(0 == zip_entry_close(zip));
+  assert(0 == zip_entry_open(zip, "test/test-1.txt"));
+  assert(0 == zip_entry_write(zip, TESTDATA1, strlen(TESTDATA1)));
+  assert(0 == strcmp(zip_entry_name(zip), "test/test-1.txt"));
+  assert(total_entries == zip_entry_index(zip));
+  assert(strlen(TESTDATA1) == zip_entry_size(zip));
+  assert(CRC32DATA1 == zip_entry_crc32(zip));
+  ++total_entries;
+  assert(0 == zip_entry_close(zip));
 
-    zip_close(zip);
+  zip_close(zip);
 }
 
 static void test_append(void) {
-    struct zip_t *zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'a');
-    assert(zip != NULL);
+  struct zip_t *zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'a');
+  assert(zip != NULL);
 
-    assert(0 == zip_entry_open(zip, "test\\test-2.txt"));
-    assert(0 == zip_entry_write(zip, TESTDATA2, strlen(TESTDATA2)));
-    assert(0 == zip_entry_close(zip));
+  assert(0 == zip_entry_open(zip, "test\\test-2.txt"));
+  assert(0 == strcmp(zip_entry_name(zip), "test/test-2.txt"));
+  assert(total_entries == zip_entry_index(zip));
+  assert(0 == zip_entry_write(zip, TESTDATA2, strlen(TESTDATA2)));
+  assert(strlen(TESTDATA2) == zip_entry_size(zip));
+  assert(CRC32DATA2 == zip_entry_crc32(zip));
+
+  ++total_entries;
+  assert(0 == zip_entry_close(zip));
+
+  assert(0 == zip_entry_open(zip, "test\\empty/"));
+  assert(0 == strcmp(zip_entry_name(zip), "test/empty/"));
+  assert(0 == zip_entry_size(zip));
+  assert(0 == zip_entry_crc32(zip));
+
+  assert(total_entries == zip_entry_index(zip));
+  ++total_entries;
+  assert(0 == zip_entry_close(zip));
 
-    zip_close(zip);
+  assert(0 == zip_entry_open(zip, "empty/"));
+  assert(0 == strcmp(zip_entry_name(zip), "empty/"));
+  assert(0 == zip_entry_size(zip));
+  assert(0 == zip_entry_crc32(zip));
+
+  assert(total_entries == zip_entry_index(zip));
+  ++total_entries;
+  assert(0 == zip_entry_close(zip));
+
+  zip_close(zip);
 }
 
 static void test_read(void) {
-    char *buf = NULL;
-    size_t bufsize;
-    struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
-    assert(zip != NULL);
-
-    assert(0 == zip_entry_open(zip, "test\\test-1.txt"));
-    assert(0 == zip_entry_read(zip, (void **)&buf, &bufsize));
-    assert(bufsize == strlen(TESTDATA1));
-    assert(0 == strncmp(buf, TESTDATA1, bufsize));
-    assert(0 == zip_entry_close(zip));
-    free(buf);
-    buf = NULL;
-    bufsize = 0;
-
-    assert(0 == zip_entry_open(zip, "test/test-2.txt"));
-    assert(0 == zip_entry_read(zip, (void **)&buf, &bufsize));
-    assert(bufsize == strlen(TESTDATA2));
-    assert(0 == strncmp(buf, TESTDATA2, bufsize));
-    assert(0 == zip_entry_close(zip));
-    free(buf);
-    buf = NULL;
-    bufsize = 0;
+  char *buf = NULL;
+  ssize_t bufsize;
+  size_t buftmp;
+  struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
+  assert(zip != NULL);
+
+  assert(0 == zip_entry_open(zip, "test\\test-1.txt"));
+  assert(strlen(TESTDATA1) == zip_entry_size(zip));
+  assert(CRC32DATA1 == zip_entry_crc32(zip));
+
+  bufsize = zip_entry_read(zip, (void **)&buf, &buftmp);
+  assert(bufsize == strlen(TESTDATA1));
+  assert((size_t)bufsize == buftmp);
+  assert(0 == strncmp(buf, TESTDATA1, bufsize));
+  assert(0 == zip_entry_close(zip));
+  free(buf);
+  buf = NULL;
+  
+  assert(0 == zip_entry_open(zip, "test/test-2.txt"));
+  assert(strlen(TESTDATA2) == zip_entry_size(zip));
+  assert(CRC32DATA2 == zip_entry_crc32(zip));
+
+  bufsize = zip_entry_read(zip, (void **)&buf, NULL);
+  assert((size_t)bufsize == strlen(TESTDATA2));
+  assert(0 == strncmp(buf, TESTDATA2, (size_t)bufsize));
+  assert(0 == zip_entry_close(zip));
+  free(buf);
+  buf = NULL;
+  bufsize = 0;
+
+  assert(0 == zip_entry_open(zip, "test\\empty/"));
+  assert(0 == strcmp(zip_entry_name(zip), "test/empty/"));
+  assert(0 == zip_entry_size(zip));
+  assert(0 == zip_entry_crc32(zip));
+  assert(0 == zip_entry_close(zip));
+
+  buftmp = strlen(TESTDATA2);
+  buf = calloc(buftmp, sizeof(char));
+  assert(0 == zip_entry_open(zip, "test/test-2.txt"));
+
+  bufsize = zip_entry_noallocread(zip, (void *)buf, buftmp);
+  assert(buftmp == (size_t)bufsize);
+  assert(0 == strncmp(buf, TESTDATA2, buftmp));
+  assert(0 == zip_entry_close(zip));
+  free(buf);
+  buf = NULL;
+  
+  buftmp = strlen(TESTDATA1);
+  buf = calloc(buftmp, sizeof(char));
+  assert(0 == zip_entry_open(zip, "test/test-1.txt"));
+
+  bufsize = zip_entry_noallocread(zip, (void *)buf, buftmp);
+  assert(buftmp == (size_t)bufsize);
+  assert(0 == strncmp(buf, TESTDATA1, buftmp));
+  assert(0 == zip_entry_close(zip));
+  free(buf);
+  buf = NULL;
+  bufsize = 0;
 
-    zip_close(zip);
+  zip_close(zip);
 }
 
 struct buffer_t {
-    char *data;
-    size_t size;
+  char *data;
+  size_t size;
 };
 
 static size_t on_extract(void *arg, unsigned long long offset, const void *data,
                          size_t size) {
-    struct buffer_t *buf = (struct buffer_t *)arg;
-    buf->data = realloc(buf->data, buf->size + size + 1);
-    assert(NULL != buf->data);
+  UNUSED(offset);
 
-    memcpy(&(buf->data[buf->size]), data, size);
-    buf->size += size;
-    buf->data[buf->size] = 0;
+  struct buffer_t *buf = (struct buffer_t *)arg;
+  buf->data = realloc(buf->data, buf->size + size + 1);
+  assert(NULL != buf->data);
 
-    return size;
+  memcpy(&(buf->data[buf->size]), data, size);
+  buf->size += size;
+  buf->data[buf->size] = 0;
+
+  return size;
 }
 
 static void test_extract(void) {
-    struct buffer_t buf = {0};
+  struct buffer_t buf;
+
+  struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
+  assert(zip != NULL);
+  memset((void *)&buf, 0, sizeof(struct buffer_t));
+
+  assert(0 == zip_entry_open(zip, "test/test-1.txt"));
+  assert(0 == zip_entry_extract(zip, on_extract, &buf));
+
+  assert(buf.size == strlen(TESTDATA1));
+  assert(0 == strncmp(buf.data, TESTDATA1, buf.size));
+  assert(0 == zip_entry_close(zip));
+  free(buf.data);
+  buf.data = NULL;
+  buf.size = 0;
+
+  zip_close(zip);
+}
+
+static void test_total_entries(void) {
+  struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
+  assert(zip != NULL);
+
+  int n = zip_total_entries(zip);
+  zip_close(zip);
+
+  assert(n == total_entries);
+}
+
+static void test_entry_name(void) {
+  struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
+  assert(zip != NULL);
+
+  assert(zip_entry_name(zip) == NULL);
+
+  assert(0 == zip_entry_open(zip, "test\\test-1.txt"));
+  assert(NULL != zip_entry_name(zip));
+  assert(0 == strcmp(zip_entry_name(zip), "test/test-1.txt"));
+  assert(strlen(TESTDATA1) == zip_entry_size(zip));
+  assert(CRC32DATA1 == zip_entry_crc32(zip));
+  assert(0 == zip_entry_index(zip));
+
+  assert(0 == zip_entry_close(zip));
+
+  assert(0 == zip_entry_open(zip, "test/test-2.txt"));
+  assert(NULL != zip_entry_name(zip));
+  assert(0 == strcmp(zip_entry_name(zip), "test/test-2.txt"));
+  assert(strlen(TESTDATA2) == zip_entry_size(zip));
+  assert(CRC32DATA2 == zip_entry_crc32(zip));
+  assert(1 == zip_entry_index(zip));
+
+  assert(0 == zip_entry_close(zip));
+
+  zip_close(zip);
+}
+
+static void test_entry_index(void) {
+  struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
+  assert(zip != NULL);
+
+  assert(0 == zip_entry_open(zip, "test\\test-1.txt"));
+  assert(0 == zip_entry_index(zip));
+  assert(0 == strcmp(zip_entry_name(zip), "test/test-1.txt"));
+  assert(strlen(TESTDATA1) == zip_entry_size(zip));
+  assert(CRC32DATA1 == zip_entry_crc32(zip));
+  assert(0 == zip_entry_close(zip));
+
+  assert(0 == zip_entry_open(zip, "test/test-2.txt"));
+  assert(1 == zip_entry_index(zip));
+  assert(0 == strcmp(zip_entry_name(zip), "test/test-2.txt"));
+  assert(strlen(TESTDATA2) == zip_entry_size(zip));
+  assert(CRC32DATA2 == zip_entry_crc32(zip));
+  assert(0 == zip_entry_close(zip));
+
+  zip_close(zip);
+}
+
+static void test_entry_openbyindex(void) {
+  struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
+  assert(zip != NULL);
+
+  assert(0 == zip_entry_openbyindex(zip, 1));
+  assert(1 == zip_entry_index(zip));
+  assert(strlen(TESTDATA2) == zip_entry_size(zip));
+  assert(CRC32DATA2 == zip_entry_crc32(zip));
+  assert(0 == strcmp(zip_entry_name(zip), "test/test-2.txt"));
+  assert(0 == zip_entry_close(zip));
 
-    struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
-    assert(zip != NULL);
+  assert(0 == zip_entry_openbyindex(zip, 0));
+  assert(0 == zip_entry_index(zip));
+  assert(strlen(TESTDATA1) == zip_entry_size(zip));
+  assert(CRC32DATA1 == zip_entry_crc32(zip));
+  assert(0 == strcmp(zip_entry_name(zip), "test/test-1.txt"));
+  assert(0 == zip_entry_close(zip));
 
-    assert(0 == zip_entry_open(zip, "test/test-1.txt"));
-    assert(0 == zip_entry_extract(zip, on_extract, &buf));
+  zip_close(zip);
+}
+
+static void test_list_entries(void) {
+  struct zip_t *zip = zip_open(ZIPNAME, 0, 'r');
+  assert(zip != NULL);
 
-    assert(buf.size == strlen(TESTDATA1));
-    assert(0 == strncmp(buf.data, TESTDATA1, buf.size));
+  int i = 0, n = zip_total_entries(zip);
+  for (; i < n; ++i) {
+    assert(0 == zip_entry_openbyindex(zip, i));
+    fprintf(stdout, "[%d]: %s", i, zip_entry_name(zip));
+    if (zip_entry_isdir(zip)) {
+      fprintf(stdout, " (DIR)");
+    }
+    fprintf(stdout, "\n");
     assert(0 == zip_entry_close(zip));
-    free(buf.data);
-    buf.data = NULL;
-    buf.size = 0;
+  }
+
+  zip_close(zip);
+}
+
+static void test_fwrite(void) {
+  const char *filename = WFILE;
+  FILE *stream = NULL;
+  struct zip_t *zip = NULL;
+#if defined(_MSC_VER)
+  if (0 != fopen_s(&stream, filename, "w+"))
+#else
+  if (!(stream = fopen(filename, "w+")))
+#endif
+  {
+    // Cannot open filename
+    fprintf(stdout, "Cannot open filename\n");
+    assert(0 == -1);
+  }
+  fwrite(TESTDATA1, sizeof(char), strlen(TESTDATA1), stream);
+  assert(0 == fclose(stream));
+
+  zip = zip_open(ZIPNAME, 9, 'w');
+  assert(zip != NULL);
+  assert(0 == zip_entry_open(zip, WFILE));
+  assert(0 == zip_entry_fwrite(zip, WFILE));
+  assert(0 == zip_entry_close(zip));
+
+  zip_close(zip);
+  remove(WFILE);
+  remove(ZIPNAME);
+}
+
+static void test_exe_permissions(void) {
+#if defined(_WIN32) || defined(__WIN32__)
+#else
+  struct MZ_FILE_STAT_STRUCT file_stats;
+  const char *filenames[] = {XFILE};
+  FILE *f = fopen(XFILE, "w");
+  fclose(f);
+  chmod(XFILE, XMODE);
+
+  remove(ZIPNAME);
+
+  assert(0 == zip_create(ZIPNAME, filenames, 1));
+
+  remove(XFILE);
+
+  assert(0 == zip_extract(ZIPNAME, ".", NULL, NULL));
+
+  assert(0 == MZ_FILE_STAT(XFILE, &file_stats));
+  assert(XMODE == file_stats.st_mode);
+
+  remove(XFILE);
+  remove(ZIPNAME);
+#endif
+}
+
+static void test_read_permissions(void) {
+#if defined(_MSC_VER)
+#else
+
+  struct MZ_FILE_STAT_STRUCT file_stats;
+  const char *filenames[] = {RFILE};
+  FILE *f = fopen(RFILE, "w");
+  fclose(f);
+  chmod(RFILE, RMODE);
+
+  remove(ZIPNAME);
 
-    zip_close(zip);
+  assert(0 == zip_create(ZIPNAME, filenames, 1));
+
+  // chmod from 444 to 666 to be able delete the file on windows
+  chmod(RFILE, WMODE);
+  remove(RFILE);
+
+  assert(0 == zip_extract(ZIPNAME, ".", NULL, NULL));
+
+  assert(0 == MZ_FILE_STAT(RFILE, &file_stats));
+  assert(RMODE == file_stats.st_mode);
+
+  chmod(RFILE, WMODE);
+  remove(RFILE);
+  remove(ZIPNAME);
+#endif
+}
+
+static void test_write_permissions(void) {
+#if defined(_MSC_VER)
+#else
+
+  struct MZ_FILE_STAT_STRUCT file_stats;
+  const char *filenames[] = {WFILE};
+  FILE *f = fopen(WFILE, "w");
+  fclose(f);
+  chmod(WFILE, WMODE);
+
+  remove(ZIPNAME);
+
+  assert(0 == zip_create(ZIPNAME, filenames, 1));
+
+  remove(WFILE);
+
+  assert(0 == zip_extract(ZIPNAME, ".", NULL, NULL));
+
+  assert(0 == MZ_FILE_STAT(WFILE, &file_stats));
+  assert(WMODE == file_stats.st_mode);
+
+  remove(WFILE);
+  remove(ZIPNAME);
+#endif
+}
+
+static void test_mtime(void) {
+  struct MZ_FILE_STAT_STRUCT file_stat1, file_stat2;
+
+  const char *filename = WFILE;
+  FILE *stream = NULL;
+  struct zip_t *zip = NULL;
+#if defined(_MSC_VER)
+  if (0 != fopen_s(&stream, filename, "w+"))
+#else
+  if (!(stream = fopen(filename, "w+")))
+#endif
+  {
+    // Cannot open filename
+    fprintf(stdout, "Cannot open filename\n");
+    assert(0 == -1);
+  }
+  fwrite(TESTDATA1, sizeof(char), strlen(TESTDATA1), stream);
+  assert(0 == fclose(stream));
+
+  memset(&file_stat1, 0, sizeof(file_stat1));
+  memset(&file_stat2, 0, sizeof(file_stat2));
+  zip = zip_open(ZIPNAME, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w');
+  assert(zip != NULL);
+  assert(0 == zip_entry_open(zip, filename));
+  assert(0 == zip_entry_fwrite(zip, filename));
+  assert(0 == zip_entry_close(zip));
+  zip_close(zip);
+
+  assert(0 == MZ_FILE_STAT(filename, &file_stat1));
+
+  remove(filename);
+  assert(0 == zip_extract(ZIPNAME, ".", NULL, NULL));
+  assert(0 == MZ_FILE_STAT(filename, &file_stat2));
+  fprintf(stdout, "file_stat1.st_mtime: %lu\n", file_stat1.st_mtime);
+  fprintf(stdout, "file_stat2.st_mtime: %lu\n", file_stat2.st_mtime);
+  assert(labs(file_stat1.st_mtime - file_stat2.st_mtime) <= 1);
+
+  remove(filename);
+  remove(ZIPNAME);
 }
 
 int main(int argc, char *argv[]) {
-    test_write();
-    test_append();
-    test_read();
-    test_extract();
+  UNUSED(argc);
+  UNUSED(argv);
+
+  remove(ZIPNAME);
+
+  test_write();
+  test_append();
+  test_read();
+  test_extract();
+  test_total_entries();
+  test_entry_name();
+  test_entry_index();
+  test_entry_openbyindex();
+  test_list_entries();
+  test_fwrite();
+  test_read_permissions();
+  test_write_permissions();
+  test_exe_permissions();
+  test_mtime();
 
-    return remove(ZIPNAME);
+  remove(ZIPNAME);
+  return 0;
 }

+ 104 - 0
contrib/zip/test/test_miniz.c

@@ -0,0 +1,104 @@
+// Demonstrates miniz.c's compress() and uncompress() functions
+// (same as zlib's). Public domain, May 15 2011, Rich Geldreich,
+// [email protected]. See "unlicense" statement at the end of tinfl.c.
+
+#include <miniz.h>
+#include <stdio.h>
+
+typedef unsigned char uint8;
+typedef unsigned short uint16;
+typedef unsigned int uint;
+
+// The string to compress.
+static const char *s_pStr =
+    "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson."
+    "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson."
+    "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson."
+    "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson."
+    "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson."
+    "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson."
+    "Good morning Dr. Chandra. This is Hal. I am ready for my first lesson.";
+
+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;
+  uint8 *pCmp, *pUncomp;
+  uint total_succeeded = 0;
+  (void)argc, (void)argv;
+
+  printf("miniz.c version: %s\n", MZ_VERSION);
+
+  do {
+    // Allocate buffers to hold compressed and uncompressed data.
+    pCmp = (mz_uint8 *)malloc((size_t)cmp_len);
+    pUncomp = (mz_uint8 *)malloc((size_t)src_len);
+    if ((!pCmp) || (!pUncomp)) {
+      printf("Out of memory!\n");
+      return EXIT_FAILURE;
+    }
+
+    // Compress the string.
+    cmp_status =
+        compress(pCmp, &cmp_len, (const unsigned char *)s_pStr, src_len);
+    if (cmp_status != Z_OK) {
+      printf("compress() failed!\n");
+      free(pCmp);
+      free(pUncomp);
+      return EXIT_FAILURE;
+    }
+
+    printf("Compressed from %u to %u bytes\n", (mz_uint32)src_len,
+           (mz_uint32)cmp_len);
+
+    if (step) {
+      // Purposely corrupt the compressed data if fuzzy testing (this is a
+      // very crude fuzzy test).
+      uint n = 1 + (rand() % 3);
+      while (n--) {
+        uint i = rand() % cmp_len;
+        pCmp[i] ^= (rand() & 0xFF);
+      }
+    }
+
+    // Decompress.
+    cmp_status = uncompress(pUncomp, &uncomp_len, pCmp, cmp_len);
+    total_succeeded += (cmp_status == Z_OK);
+
+    if (step) {
+      printf("Simple fuzzy test: step %u total_succeeded: %u\n", step,
+             total_succeeded);
+    } else {
+      if (cmp_status != Z_OK) {
+        printf("uncompress failed!\n");
+        free(pCmp);
+        free(pUncomp);
+        return EXIT_FAILURE;
+      }
+
+      printf("Decompressed from %u to %u bytes\n", (mz_uint32)cmp_len,
+             (mz_uint32)uncomp_len);
+
+      // Ensure uncompress() returned the expected data.
+      if ((uncomp_len != src_len) ||
+          (memcmp(pUncomp, s_pStr, (size_t)src_len))) {
+        printf("Decompression failed!\n");
+        free(pCmp);
+        free(pUncomp);
+        return EXIT_FAILURE;
+      }
+    }
+
+    free(pCmp);
+    free(pUncomp);
+
+    step++;
+
+    // Keep on fuzzy testing if there's a non-empty command line.
+  } while (argc >= 2);
+
+  printf("Success.\n");
+  return EXIT_SUCCESS;
+}

+ 12 - 0
doc/dox.h

@@ -1173,6 +1173,18 @@ float4 PimpMyPixel (float4 prev)
 
 @endcode
 
+@section shdacc How to access shader-code from a texture (AI_MATKEY_GLOBAL_SHADERLANG and AI_MATKEY_SHADER_VERTEX, ...)
+
+You can get assigned shader sources by using the following material keys:
+
+<li>AI_MATKEY_GLOBAL_SHADERLANG</li>To get the used shader language.
+<li>AI_MATKEY_SHADER_VERTEX</li> Assigned vertex shader code stored as a string.
+<li>AI_MATKEY_SHADER_FRAGMENT</li> Assigned fragment shader code stored as a string.
+<li>AI_MATKEY_SHADER_GEO</li> Assigned geometry shader code stored as a string.
+<li>AI_MATKEY_SHADER_TESSELATION</li> Assigned tesselation shader code stored as a string.
+<li>AI_MATKEY_SHADER_PRIMITIVE</li> Assigned primitive shader code stored as a string.
+<li>AI_MATKEY_SHADER_COMPUTE</li> Assigned compute shader code stored as a string.
+
 */
 
 

+ 9 - 6
include/assimp/material.h

@@ -198,8 +198,6 @@ enum aiTextureType
      */
     aiTextureType_NONE = 0x0,
 
-
-
     /** The texture is combined with the result of the diffuse
      *  lighting equation.
      */
@@ -278,7 +276,7 @@ enum aiTextureType
      *
      *  A texture reference that does not match any of the definitions
      *  above is considered to be 'unknown'. It is still imported,
-     *  but is excluded from any further postprocessing.
+     *  but is excluded from any further post-processing.
     */
     aiTextureType_UNKNOWN = 0xC,
 
@@ -375,7 +373,7 @@ enum aiShadingMode
 */
 enum aiTextureFlags
 {
-    /** The texture's color values have to be inverted (componentwise 1-n)
+    /** The texture's color values have to be inverted (component-wise 1-n)
      */
     aiTextureFlags_Invert = 0x1,
 
@@ -914,6 +912,13 @@ extern "C" {
 #define AI_MATKEY_COLOR_TRANSPARENT "$clr.transparent",0,0
 #define AI_MATKEY_COLOR_REFLECTIVE "$clr.reflective",0,0
 #define AI_MATKEY_GLOBAL_BACKGROUND_IMAGE "?bg.global",0,0
+#define AI_MATKEY_GLOBAL_SHADERLANG "?sh.lang",0,0
+#define AI_MATKEY_SHADER_VERTEX "?sh.vs",0,0
+#define AI_MATKEY_SHADER_FRAGMENT "?sh.fs",0,0
+#define AI_MATKEY_SHADER_GEO "?sh.gs",0,0
+#define AI_MATKEY_SHADER_TESSELATION "?sh.ts",0,0
+#define AI_MATKEY_SHADER_PRIMITIVE "?sh.ps",0,0
+#define AI_MATKEY_SHADER_COMPUTE "?sh.cs",0,0
 
 // ---------------------------------------------------------------------------
 // Pure key names for all texture-related properties
@@ -1457,8 +1462,6 @@ inline aiReturn aiGetMaterialInteger(const C_STRUCT aiMaterial* pMat,
 
 #endif //!__cplusplus
 
-
-
 // ---------------------------------------------------------------------------
 /** @brief Retrieve a color value from the material property table
 *

+ 4 - 4
scripts/StepImporter/CppGenerator.py

@@ -52,8 +52,8 @@ use_ifc_template = False
 	
 input_step_template_h   = 'StepReaderGen.h.template'
 input_step_template_cpp = 'StepReaderGen.cpp.template'
-input_ifc_template_h        = 'IFCReaderGen.h.template'
-input_ifc_template_cpp      = 'IFCReaderGen.cpp.template'
+input_ifc_template_h    = 'IFCReaderGen.h.template'
+input_ifc_template_cpp  = 'IFCReaderGen.cpp.template'
 
 cpp_keywords = "class"
 
@@ -87,7 +87,7 @@ template_type = r"""
 
 template_stub_decl = '\tDECL_CONV_STUB({type});\n'
 template_schema = '\t\tSchemaEntry("{normalized_name}",&STEP::ObjectHelper<{type},{argcnt}>::Construct )\n'
-template_schema_type = '\t\tSchemaEntry("{normalized_name}",NULL )\n'
+template_schema_type = '\t\tSchemaEntry("{normalized_name}",nullptr )\n'
 template_converter = r"""
 // -----------------------------------------------------------------------------------------------------------
 template <> size_t GenericFill<{type}>(const DB& db, const LIST& params, {type}* in)
@@ -99,7 +99,7 @@ template_converter_prologue_a = '\tsize_t base = GenericFill(db,params,static_ca
 template_converter_prologue_b = '\tsize_t base = 0;\n'
 template_converter_check_argcnt = '\tif (params.GetSize() < {max_arg}) {{ throw STEP::TypeError("expected {max_arg} arguments to {name}"); }}'
 template_converter_code_per_field = r"""    do {{ // convert the '{fieldname}' argument
-        boost::shared_ptr<const DataType> arg = params[base++];{handle_unset}{convert}
+        std::shared_ptr<const DataType> arg = params[base++];{handle_unset}{convert}
     }} while(0);
 """
 template_allow_optional = r"""

+ 2 - 2
scripts/StepImporter/StepReaderGen.cpp.template

@@ -2,7 +2,7 @@
 Open Asset Import Library (ASSIMP)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2018, ASSIMP Development Team
+Copyright (c) 2006-2019, ASSIMP Development Team
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms, 
@@ -66,7 +66,7 @@ namespace STEP {
 // -----------------------------------------------------------------------------------------------------------
 template <> size_t GenericFill<NotImplemented>(const STEP::DB& db, const LIST& params, NotImplemented* in)
 {
-	return 0;
+    return 0u;
 }
 
 

+ 18 - 19
scripts/StepImporter/StepReaderGen.h.template

@@ -2,7 +2,7 @@
 Open Asset Import Library (ASSIMP)
 ----------------------------------------------------------------------
 
-Copyright (c) 2006-2018, ASSIMP Development Team
+Copyright (c) 2006-2019, ASSIMP Development Team
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms, 
@@ -47,25 +47,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 namespace Assimp {
 namespace StepFile {
-	using namespace STEP;
-	using namespace STEP::EXPRESS;
-	
-	
-	struct NotImplemented : public ObjectHelper<NotImplemented,0> {
-		
-	};
-	
 
-	// ******************************************************************************
-	// StepFile Custom data types
-	// ******************************************************************************
+using namespace STEP;
+using namespace STEP::EXPRESS;
 
-{types}
+struct NotImplemented : public ObjectHelper<NotImplemented,0> {
 
+};
 
-	// ******************************************************************************
-	// StepFile Entities
-	// ******************************************************************************
+// ******************************************************************************
+// StepFile Custom data types
+// ******************************************************************************
+
+{types}
+
+// ******************************************************************************
+// StepFile Entities
+// ******************************************************************************
 
 {predefs}
 {entities}
@@ -73,11 +71,12 @@ namespace StepFile {
 	void GetSchema(EXPRESS::ConversionSchema& out);
 
 } //! StepFile
+
 namespace STEP {
 
-	// ******************************************************************************
-	// Converter stubs
-	// ******************************************************************************
+// ******************************************************************************
+// Converter stubs
+// ******************************************************************************
 	
 #define DECL_CONV_STUB(type) template <> size_t GenericFill<IFC::type>(const STEP::DB& db, const EXPRESS::LIST& params, IFC::type* in)
 	

+ 2 - 2
test/unit/utSTLImportExport.cpp

@@ -152,10 +152,10 @@ TEST_F(utSTLImporterExporter, test_export_pointclouds) {
 
     auto pMesh = scene.mMeshes[0];
 
-    long numValidPoints = points.size();
+    size_t numValidPoints = points.size();
 
     pMesh->mVertices = new aiVector3D[numValidPoints];
-    pMesh->mNumVertices = numValidPoints;
+    pMesh->mNumVertices = static_cast<unsigned int>( numValidPoints );
 
     int i = 0;
     for (XYZ &p : points) {

+ 199 - 0
test/unit/utValidateDataStructure.cpp

@@ -0,0 +1,199 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2019, assimp team
+
+
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+#include "UnitTestPCH.h"
+
+#include <assimp/mesh.h>
+#include <assimp/scene.h>
+#include <ValidateDataStructure.h>
+
+using namespace std;
+using namespace Assimp;
+
+
+class ValidateDataStructureTest : public ::testing::Test
+{
+public:
+
+    virtual void SetUp();
+    virtual void TearDown();
+
+protected:
+
+
+    ValidateDSProcess* vds;
+    aiScene* scene;
+};
+
+// ------------------------------------------------------------------------------------------------
+void ValidateDataStructureTest::SetUp()
+{
+    // setup a dummy scene with a single node
+    scene = new aiScene();
+    scene->mRootNode = new aiNode();
+    scene->mRootNode->mName.Set("<test>");
+
+    // add some translation
+    scene->mRootNode->mTransformation.a4 = 1.f;
+    scene->mRootNode->mTransformation.b4 = 2.f;
+    scene->mRootNode->mTransformation.c4 = 3.f;
+
+    // and allocate a ScenePreprocessor to operate on the scene
+    vds = new ValidateDSProcess();
+}
+
+// ------------------------------------------------------------------------------------------------
+void ValidateDataStructureTest::TearDown()
+{
+    delete vds;
+    delete scene;
+}
+
+
+
+// ------------------------------------------------------------------------------------------------
+//Template
+//TEST_F(ScenePreprocessorTest, test)
+//{
+//}
+// TODO Conditions not yet checked:
+//132: ReportError("aiScene::%s is NULL (aiScene::%s is %i)",
+//139: ReportError("aiScene::%s[%i] is NULL (aiScene::%s is %i)",
+//156: ReportError("aiScene::%s is NULL (aiScene::%s is %i)",
+//163: ReportError("aiScene::%s[%i] is NULL (aiScene::%s is %i)",
+//173: ReportError("aiScene::%s[%i] has the same name as "
+//192: ReportError("aiScene::%s[%i] has no corresponding node in the scene graph (%s)",
+//196: ReportError("aiScene::%s[%i]: there are more than one nodes with %s as name",
+//217: ReportError("aiScene::mNumMeshes is 0. At least one mesh must be there");
+//220: ReportError("aiScene::mMeshes is non-null although there are no meshes");
+//229: ReportError("aiScene::mAnimations is non-null although there are no animations");
+//238: ReportError("aiScene::mCameras is non-null although there are no cameras");
+//247: ReportError("aiScene::mLights is non-null although there are no lights");
+//256: ReportError("aiScene::mTextures is non-null although there are no textures");
+//266: ReportError("aiScene::mNumMaterials is 0. At least one material must be there");
+//270: ReportError("aiScene::mMaterials is non-null although there are no materials");
+//281: ReportWarning("aiLight::mType is aiLightSource_UNDEFINED");
+//286: ReportWarning("aiLight::mAttenuationXXX - all are zero");
+//290: ReportError("aiLight::mAngleInnerCone is larger than aiLight::mAngleOuterCone");
+//295: ReportWarning("aiLight::mColorXXX - all are black and won't have any influence");
+//303: ReportError("aiCamera::mClipPlaneFar must be >= aiCamera::mClipPlaneNear");
+//308: ReportWarning("%f is not a valid value for aiCamera::mHorizontalFOV",pCamera->mHorizontalFOV);
+//317: ReportError("aiMesh::mMaterialIndex is invalid (value: %i maximum: %i)",
+//332: ReportError("aiMesh::mFaces[%i].mNumIndices is 0",i);
+//336: ReportError("aiMesh::mFaces[%i] is a POINT but aiMesh::mPrimitiveTypes "
+//337: "does not report the POINT flag",i);
+//343: ReportError("aiMesh::mFaces[%i] is a LINE but aiMesh::mPrimitiveTypes "
+//344: "does not report the LINE flag",i);
+//350: ReportError("aiMesh::mFaces[%i] is a TRIANGLE but aiMesh::mPrimitiveTypes "
+//351: "does not report the TRIANGLE flag",i);
+//357: this->ReportError("aiMesh::mFaces[%i] is a POLYGON but aiMesh::mPrimitiveTypes "
+//358: "does not report the POLYGON flag",i);
+//365: ReportError("aiMesh::mFaces[%i].mIndices is NULL",i);
+//370: ReportError("The mesh %s contains no vertices", pMesh->mName.C_Str());
+//374: ReportError("Mesh has too many vertices: %u, but the limit is %u",pMesh->mNumVertices,AI_MAX_VERTICES);
+//377: ReportError("Mesh has too many faces: %u, but the limit is %u",pMesh->mNumFaces,AI_MAX_FACES);
+//382: ReportError("If there are tangents, bitangent vectors must be present as well");
+//387: ReportError("Mesh %s contains no faces", pMesh->mName.C_Str());
+//398: ReportError("Face %u has too many faces: %u, but the limit is %u",i,face.mNumIndices,AI_MAX_FACE_INDICES);
+//404: ReportError("aiMesh::mFaces[%i]::mIndices[%i] is out of range",i,a);
+//412: ReportError("aiMesh::mVertices[%i] is referenced twice - second "
+//426: ReportWarning("There are unreferenced vertices");
+//439: ReportError("Texture coordinate channel %i exists "
+//453: ReportError("Vertex color channel %i is exists "
+//464: ReportError("aiMesh::mBones is NULL (aiMesh::mNumBones is %i)",
+//480: ReportError("Bone %u has too many weights: %u, but the limit is %u",i,bone->mNumWeights,AI_MAX_BONE_WEIGHTS);
+//485: ReportError("aiMesh::mBones[%i] is NULL (aiMesh::mNumBones is %i)",
+//498: ReportError("aiMesh::mBones[%i], name = \"%s\" has the same name as "
+//507: ReportWarning("aiMesh::mVertices[%i]: bone weight sum != 1.0 (sum is %f)",i,afSum[i]);
+//513: ReportError("aiMesh::mBones is non-null although there are no bones");
+//524: ReportError("aiBone::mNumWeights is zero");
+//531: ReportError("aiBone::mWeights[%i].mVertexId is out of range",i);
+//534: ReportWarning("aiBone::mWeights[%i].mWeight has an invalid value",i);
+//549: ReportError("aiAnimation::mChannels is NULL (aiAnimation::mNumChannels is %i)",
+//556: ReportError("aiAnimation::mChannels[%i] is NULL (aiAnimation::mNumChannels is %i)",
+//563: ReportError("aiAnimation::mNumChannels is 0. At least one node animation channel must be there.");
+//567: // if (!pAnimation->mDuration)this->ReportError("aiAnimation::mDuration is zero");
+//592: ReportError("Material property %s is expected to be a string",prop->mKey.data);
+//596: ReportError("%s #%i is set, but there are only %i %s textures",
+//611: ReportError("Found texture property with index %i, although there "
+//619: ReportError("Material property %s%i is expected to be an integer (size is %i)",
+//627: ReportError("Material property %s%i is expected to be 5 floats large (size is %i)",
+//635: ReportError("Material property %s%i is expected to be an integer (size is %i)",
+//656: ReportWarning("Invalid UV index: %i (key %s). Mesh %i has only %i UV channels",
+//676: ReportWarning("UV-mapped texture, but there are no UV coords");
+//690: ReportError("aiMaterial::mProperties[%i] is NULL (aiMaterial::mNumProperties is %i)",
+//694: ReportError("aiMaterial::mProperties[%i].mDataLength or "
+//702: ReportError("aiMaterial::mProperties[%i].mDataLength is "
+//707: ReportError("Missing null-terminator in string material property");
+//713: ReportError("aiMaterial::mProperties[%i].mDataLength is "
+//720: ReportError("aiMaterial::mProperties[%i].mDataLength is "
+//739: ReportWarning("A specular shading model is specified but there is no "
+//743: ReportWarning("A specular shading model is specified but the value of the "
+//752: ReportWarning("Invalid opacity value (must be 0 < opacity < 1.0)");
+//776: ReportError("aiTexture::pcData is NULL");
+//781: ReportError("aiTexture::mWidth is zero (aiTexture::mHeight is %i, uncompressed texture)",
+//788: ReportError("aiTexture::mWidth is zero (compressed texture)");
+//791: ReportWarning("aiTexture::achFormatHint must be zero-terminated");
+//794: ReportWarning("aiTexture::achFormatHint should contain a file extension "
+//804: ReportError("aiTexture::achFormatHint contains non-lowercase letters");
+//815: ReportError("Empty node animation channel");
+//822: ReportError("aiNodeAnim::mPositionKeys is NULL (aiNodeAnim::mNumPositionKeys is %i)",
+//833: ReportError("aiNodeAnim::mPositionKeys[%i].mTime (%.5f) is larger "
+//840: ReportWarning("aiNodeAnim::mPositionKeys[%i].mTime (%.5f) is smaller "
+//853: ReportError("aiNodeAnim::mRotationKeys is NULL (aiNodeAnim::mNumRotationKeys is %i)",
+//861: ReportError("aiNodeAnim::mRotationKeys[%i].mTime (%.5f) is larger "
+//868: ReportWarning("aiNodeAnim::mRotationKeys[%i].mTime (%.5f) is smaller "
+//880: ReportError("aiNodeAnim::mScalingKeys is NULL (aiNodeAnim::mNumScalingKeys is %i)",
+//888: ReportError("aiNodeAnim::mScalingKeys[%i].mTime (%.5f) is larger "
+//895: ReportWarning("aiNodeAnim::mScalingKeys[%i].mTime (%.5f) is smaller "
+//907: ReportError("A node animation channel must have at least one subtrack");
+//915: ReportError("A node of the scenegraph is NULL");
+//920: ReportError("Non-root node %s lacks a valid parent (aiNode::mParent is NULL) ",pNode->mName);
+//928: ReportError("aiNode::mMeshes is NULL for node %s (aiNode::mNumMeshes is %i)",
+//937: ReportError("aiNode::mMeshes[%i] is out of range for node %s (maximum is %i)",
+//942: ReportError("aiNode::mMeshes[%i] is already referenced by this node %s (value: %i)",
+//951: ReportError("aiNode::mChildren is NULL for node %s (aiNode::mNumChildren is %i)",
+//965: ReportError("aiString::length is too large (%i, maximum is %lu)",
+//974: ReportError("aiString::data is invalid: the terminal zero is at a wrong offset");
+//979: ReportError("aiString::data is invalid. There is no terminal character");
+}
+

+ 18 - 26
tools/assimp_cmd/Info.cpp

@@ -51,27 +51,26 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <string>
 
 const char* AICMD_MSG_INFO_HELP_E =
-"assimp info <file> [-r] [-v]\n"
-"\tPrint basic structure of a 3D model\n"
-"\t-r,--raw: No postprocessing, do a raw import\n"
-"\t-v,--verbose: Print verbose info such as node transform data\n"
-"\t-s, --silent: Print only minimal info\n";
-
-const std::string TREE_BRANCH_ASCII = "|-";
-const std::string TREE_BRANCH_UTF8 = "\xe2\x94\x9c\xe2\x95\xb4";
-const std::string TREE_STOP_ASCII = "'-";
-const std::string TREE_STOP_UTF8 = "\xe2\x94\x94\xe2\x95\xb4";
-const std::string TREE_CONTINUE_ASCII = "| ";
-const std::string TREE_CONTINUE_UTF8 = "\xe2\x94\x82 ";
-
-// note: by default this is outputing utf-8 text.
+    "assimp info <file> [-r] [-v]\n"
+    "\tPrint basic structure of a 3D model\n"
+    "\t-r,--raw: No postprocessing, do a raw import\n"
+    "\t-v,--verbose: Print verbose info such as node transform data\n"
+    "\t-s, --silent: Print only minimal info\n";
+
+const char *TREE_BRANCH_ASCII = "|-";
+const char *TREE_BRANCH_UTF8 = "\xe2\x94\x9c\xe2\x95\xb4";
+const char *TREE_STOP_ASCII = "'-";
+const char *TREE_STOP_UTF8 = "\xe2\x94\x94\xe2\x95\xb4";
+const char *TREE_CONTINUE_ASCII = "| ";
+const char *TREE_CONTINUE_UTF8 = "\xe2\x94\x82 ";
+
+// note: by default this is using utf-8 text.
 // this is well supported on pretty much any linux terminal.
 // if this causes problems on some platform,
 // put an #ifdef to use the ascii version for that platform.
-const std::string TREE_BRANCH = TREE_BRANCH_UTF8;
-const std::string TREE_STOP = TREE_STOP_UTF8;
-const std::string TREE_CONTINUE = TREE_CONTINUE_UTF8;
-
+const char *TREE_BRANCH = TREE_BRANCH_UTF8;
+const char *TREE_STOP = TREE_STOP_UTF8;
+const char *TREE_CONTINUE = TREE_CONTINUE_UTF8;
 
 // -----------------------------------------------------------------------------------
 unsigned int CountNodes(const aiNode* root)
@@ -280,14 +279,7 @@ void PrintHierarchy(
 
 // -----------------------------------------------------------------------------------
 // Implementation of the assimp info utility to print basic file info
-int Assimp_Info (const char* const* params, unsigned int num)
-{
-	if (num < 1) {
-		printf("assimp info: Invalid number of arguments. "
-			"See \'assimp info --help\'\n");
-		return 1;
-	}
-
+int Assimp_Info (const char* const* params, unsigned int num) {
 	// --help
 	if (!strcmp( params[0],"-h")||!strcmp( params[0],"--help")||!strcmp( params[0],"-?") ) {
 		printf("%s",AICMD_MSG_INFO_HELP_E);

+ 6 - 7
tools/assimp_cmd/WriteDumb.cpp

@@ -276,9 +276,12 @@ inline uint32_t WriteBounds(const T* in, unsigned int size)
 void ChangeInteger(uint32_t ofs,uint32_t n)
 {
 	const uint32_t cur = ftell(out);
-	fseek(out,ofs,SEEK_SET);
-	fwrite(&n,4,1,out);
-	fseek(out,cur,SEEK_SET);
+    int retCode;
+    retCode = fseek(out, ofs, SEEK_SET);
+    ai_assert(0 == retCode);
+	fwrite(&n, 4, 1, out);
+    retCode = fseek(out, cur, SEEK_SET);
+    ai_assert(0 == retCode);
 }
 
 // -----------------------------------------------------------------------------------
@@ -1333,10 +1336,6 @@ int Assimp_Dump (const char* const* params, unsigned int num)
 {
 	const char* fail = "assimp dump: Invalid number of arguments. "
 			"See \'assimp dump --help\'\r\n";
-	if (num < 1) {
-		printf("%s", fail);
-		return 1;
-	}
 
 	// --help
 	if (!strcmp( params[0], "-h") || !strcmp( params[0], "--help") || !strcmp( params[0], "-?") ) {

+ 1 - 1
tools/assimp_view/SceneAnimator.cpp

@@ -90,7 +90,7 @@ void SceneAnimator::SetAnimIndex( size_t pAnimIndex) {
     delete mAnimEvaluator;  mAnimEvaluator = nullptr;
     mNodesByName.clear();
 
-    mCurrentAnimIndex = pAnimIndex;
+    mCurrentAnimIndex = static_cast<int>( pAnimIndex );
 
     // create the internal node tree. Do this even in case of invalid animation index
     // so that the transformation matrices are properly set up to mimic the current scene

Some files were not shown because too many files changed in this diff