2
0
Эх сурвалжийг харах

Merge pull request #2 from assimp/master

Update to Assimp Master 0973f50
RichardTea 6 жил өмнө
parent
commit
2a3626577f
76 өөрчлөгдсөн 4002 нэмэгдсэн , 2255 устгасан
  1. 1 1
      .travis.yml
  2. 12 2
      CMakeLists.txt
  3. 7 4
      INSTALL
  4. 1 0
      Readme.md
  5. 4 4
      appveyor.yml
  6. 1 1
      code/AssbinLoader.cpp
  7. 0 2
      code/AssxmlExporter.cpp
  8. 1 1
      code/BlenderDNA.h
  9. 63 92
      code/C4DImporter.cpp
  10. 6 12
      code/C4DImporter.h
  11. 113 31
      code/CMakeLists.txt
  12. 5 5
      code/COBLoader.cpp
  13. 4 1
      code/D3MFOpcPackage.cpp
  14. 44 23
      code/FBXConverter.cpp
  15. 12 8
      code/FBXConverter.h
  16. 2 2
      code/FBXExportNode.cpp
  17. 18 1
      code/FBXMaterial.cpp
  18. 10 10
      code/FindInstancesProcess.cpp
  19. 12 13
      code/IRRLoader.cpp
  20. 3 3
      code/Importer/STEPParser/STEPFileReader.cpp
  21. 3 1
      code/ImproveCacheLocality.cpp
  22. 1 4
      code/LWOMaterial.cpp
  23. 5 5
      code/MD3FileData.h
  24. 27 25
      code/MD3Loader.cpp
  25. 9 9
      code/MD5Loader.cpp
  26. 3 0
      code/ObjFileData.h
  27. 1 1
      code/ObjFileImporter.cpp
  28. 4 2
      code/ObjFileParser.cpp
  29. 1 1
      code/ObjFileParser.h
  30. 3 3
      code/PostStepRegistry.cpp
  31. 2 2
      code/SMDLoader.cpp
  32. 1 1
      code/STLExporter.cpp
  33. 19 18
      code/SortByPTypeProcess.cpp
  34. 20 20
      code/StandardShapes.cpp
  35. 46 32
      code/ValidateDataStructure.cpp
  36. 4 3
      code/glTF2Asset.h
  37. 4 4
      code/glTFAssetWriter.inl
  38. 1 1
      code/glTFExporter.cpp
  39. 4 1
      contrib/irrXML/CMakeLists.txt
  40. 9 4
      contrib/irrXML/CXMLReaderImpl.h
  41. 2 2
      contrib/irrXML/irrXML.cpp
  42. 15 9
      contrib/irrXML/irrXML.h
  43. 18 0
      contrib/zip/.gitignore
  44. 18 0
      contrib/zip/.travis.sh
  45. 15 3
      contrib/zip/.travis.yml
  46. 36 7
      contrib/zip/CMakeLists.txt
  47. 249 79
      contrib/zip/README.md
  48. 1 1
      contrib/zip/appveyor.yml
  49. 55 0
      contrib/zip/cmake/asan-wrapper
  50. 23 0
      contrib/zip/cmake/cmake_uninstall.cmake.in
  51. 517 317
      contrib/zip/src/miniz.h
  52. 782 496
      contrib/zip/src/zip.c
  53. 135 12
      contrib/zip/src/zip.h
  54. 12 0
      contrib/zip/test/CMakeLists.txt
  55. 415 61
      contrib/zip/test/test.c
  56. 104 0
      contrib/zip/test/test_miniz.c
  57. 12 0
      doc/dox.h
  58. 10 6
      include/assimp/material.h
  59. 545 547
      port/PyAssimp/pyassimp/core.py
  60. 1 1
      port/PyAssimp/pyassimp/formats.py
  61. 279 280
      port/PyAssimp/pyassimp/helper.py
  62. 1 0
      port/PyAssimp/pyassimp/postprocess.py
  63. 2 2
      port/PyAssimp/pyassimp/structs.py
  64. 3 2
      port/PyAssimp/scripts/fixed_pipeline_3d_viewer.py
  65. 3 3
      port/PyAssimp/scripts/sample.py
  66. 3 1
      scripts/BlenderImporter/genblenddna.py
  67. 9 9
      scripts/StepImporter/CppGenerator.py
  68. 2 1
      scripts/StepImporter/ExpressReader.py
  69. 2 2
      scripts/StepImporter/StepReaderGen.cpp.template
  70. 18 19
      scripts/StepImporter/StepReaderGen.h.template
  71. 2 2
      test/unit/utSTLImportExport.cpp
  72. 199 0
      test/unit/utValidateDataStructure.cpp
  73. 3 6
      tools/assimp_cmd/ImageExtractor.cpp
  74. 18 26
      tools/assimp_cmd/Info.cpp
  75. 6 7
      tools/assimp_cmd/WriteDumb.cpp
  76. 1 1
      tools/assimp_view/SceneAnimator.cpp

+ 1 - 1
.travis.yml

@@ -70,6 +70,6 @@ addons:
     project:
     project:
       name: "assimp/assimp"
       name: "assimp/assimp"
     notification_email: [email protected]
     notification_email: [email protected]
-    build_command_prepend: "cmake . -DASSIMP_ENABLE_BOOST_WORKAROUND=YES"
+    build_command_prepend: "cmake ./"
     build_command: "make -j4"
     build_command: "make -j4"
     branch_pattern: coverity_scan
     branch_pattern: coverity_scan

+ 12 - 2
CMakeLists.txt

@@ -67,7 +67,7 @@ OPTION( ASSIMP_NO_EXPORT
 )
 )
 OPTION( ASSIMP_BUILD_ZLIB
 OPTION( ASSIMP_BUILD_ZLIB
   "Build your own zlib"
   "Build your own zlib"
-  OFF
+  OFF  
 )
 )
 OPTION( ASSIMP_BUILD_ASSIMP_TOOLS
 OPTION( ASSIMP_BUILD_ASSIMP_TOOLS
   "If the supplementary tools for Assimp are built in addition to the library."
   "If the supplementary tools for Assimp are built in addition to the library."
@@ -159,7 +159,7 @@ SET (PROJECT_VERSION "${ASSIMP_VERSION}")
 
 
 SET( ASSIMP_PACKAGE_VERSION "0" CACHE STRING "the package-specific version used for uploading the sources" )
 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 )
 set_property( GLOBAL PROPERTY CXX_STANDARD 11 )
 
 
 IF(NOT IGNORE_GIT_HASH)
 IF(NOT IGNORE_GIT_HASH)
@@ -353,6 +353,15 @@ IF( NOT ZLIB_FOUND )
   INCLUDE(CheckIncludeFile)
   INCLUDE(CheckIncludeFile)
   INCLUDE(CheckTypeSize)
   INCLUDE(CheckTypeSize)
   INCLUDE(CheckFunctionExists)
   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
   # compile from sources
   ADD_SUBDIRECTORY(contrib/zlib)
   ADD_SUBDIRECTORY(contrib/zlib)
   SET(ZLIB_FOUND 1)
   SET(ZLIB_FOUND 1)
@@ -470,6 +479,7 @@ ENDIF ( ASSIMP_BUILD_ASSIMP_TOOLS )
 IF ( ASSIMP_BUILD_SAMPLES)
 IF ( ASSIMP_BUILD_SAMPLES)
   IF ( WIN32 )
   IF ( WIN32 )
     ADD_SUBDIRECTORY( samples/SimpleTexturedOpenGL/ )
     ADD_SUBDIRECTORY( samples/SimpleTexturedOpenGL/ )
+    ADD_SUBDIRECTORY( samples/SimpleTexturedDirectx11 )
   ENDIF ( WIN32 )
   ENDIF ( WIN32 )
   ADD_SUBDIRECTORY( samples/SimpleOpenGL/ )
   ADD_SUBDIRECTORY( samples/SimpleOpenGL/ )
 ENDIF ( ASSIMP_BUILD_SAMPLES )
 ENDIF ( ASSIMP_BUILD_SAMPLES )

+ 7 - 4
INSTALL

@@ -35,13 +35,16 @@ http://www.cmake.org/.
 
 
 For Unix:
 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:
 For Windows:
 1. Open a command prompt
 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:
 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
 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)
 * [Javascript (Alpha)](https://github.com/makc/assimp2json)
 * [Unity 3d Plugin](https://www.assetstore.unity3d.com/en/#!/content/91777)
 * [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))
 * [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 ###
 ### Other tools ###
 [open3mod](https://github.com/acgessler/open3mod) is a powerful 3D model viewer based on Assimp's import and export abilities.
 [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:
 image:
   - Visual Studio 2013
   - Visual Studio 2013
-#  - Visual Studio 2015
-#  - Visual Studio 2017
+  - Previous Visual Studio 2015
+  - Previous Visual Studio 2017
     
     
 platform:
 platform:
   - Win32
   - Win32
@@ -28,8 +28,8 @@ install:
   - set PATH=C:\Ruby24-x64\bin;%PATH%
   - set PATH=C:\Ruby24-x64\bin;%PATH%
   - set CMAKE_DEFINES -DASSIMP_WERROR=ON
   - 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 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
   - if "%platform%"=="x64" set CMAKE_GENERATOR_NAME=%CMAKE_GENERATOR_NAME% Win64
   - cmake %CMAKE_DEFINES% -G "%CMAKE_GENERATOR_NAME%"
   - cmake %CMAKE_DEFINES% -G "%CMAKE_GENERATOR_NAME%"
   - set PATH=%PATH%;"C:\\Program Files (x86)\\Inno Setup 5"
   - 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 ];
         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)
         if(res != Z_OK)
         {
         {
             delete [] uncompressedData;
             delete [] uncompressedData;

+ 0 - 2
code/AssxmlExporter.cpp

@@ -555,8 +555,6 @@ void WriteDump(const aiScene* scene, IOStream* io, bool shortened) {
                             mesh->mNormals[n].z);
                             mesh->mNormals[n].z);
                     }
                     }
                 }
                 }
-                else {
-                }
                 ioprintf(io,"\t\t</Normals>\n");
                 ioprintf(io,"\t\t</Normals>\n");
             }
             }
 
 

+ 1 - 1
code/BlenderDNA.h

@@ -416,7 +416,7 @@ template <> struct Structure :: _defaultInitializer<ErrorPolicy_Fail> {
     void operator ()(T& /*out*/,const char* = "") {
     void operator ()(T& /*out*/,const char* = "") {
         // obviously, it is crucial that _DefaultInitializer is used
         // obviously, it is crucial that _DefaultInitializer is used
         // only from within a catch clause.
         // only from within a catch clause.
-        throw;
+        throw DeadlyImportError("Constructing BlenderDNA Structure encountered an error");
     }
     }
 };
 };
 
 

+ 63 - 92
code/C4DImporter.cpp

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2012, assimp team
+Copyright (c) 2006-2019, assimp team
 All rights reserved.
 All rights reserved.
 
 
 Redistribution and use of this software in source and binary forms,
 Redistribution and use of this software in source and binary forms,
@@ -68,8 +68,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 using namespace melange;
 using namespace melange;
 
 
 // overload this function and fill in your own unique data
 // overload this function and fill in your own unique data
-void GetWriterInfo(int &id, String &appname)
-{
+void GetWriterInfo(int &id, String &appname) {
     id = 2424226;
     id = 2424226;
     appname = "Open Asset Import Library";
     appname = "Open Asset Import Library";
 }
 }
@@ -78,7 +77,10 @@ using namespace Assimp;
 using namespace Assimp::Formatter;
 using namespace Assimp::Formatter;
 
 
 namespace Assimp {
 namespace Assimp {
-    template<> const std::string LogFunctions<C4DImporter>::log_prefix = "C4D: ";
+    template<> const char* LogFunctions<C4DImporter>::Prefix() {
+        static auto prefix = "C4D: ";
+        return prefix;
+    }
 }
 }
 
 
 static const aiImporterDesc desc = {
 static const aiImporterDesc desc = {
@@ -97,47 +99,44 @@ static const aiImporterDesc desc = {
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 C4DImporter::C4DImporter()
 C4DImporter::C4DImporter()
-{}
+: BaseImporter() {
+    // empty
+}
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-C4DImporter::~C4DImporter()
-{}
+C4DImporter::~C4DImporter() {
+    // empty
+}
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-bool C4DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
-{
+bool C4DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const {
     const std::string& extension = GetExtension(pFile);
     const std::string& extension = GetExtension(pFile);
     if (extension == "c4d") {
     if (extension == "c4d") {
         return true;
         return true;
-    }
-
-    else if ((!extension.length() || checkSig) && pIOHandler)   {
+    } else if ((!extension.length() || checkSig) && pIOHandler)   {
         // TODO
         // TODO
     }
     }
+
     return false;
     return false;
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-const aiImporterDesc* C4DImporter::GetInfo () const
-{
+const aiImporterDesc* C4DImporter::GetInfo () const {
     return &desc;
     return &desc;
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-void C4DImporter::SetupProperties(const Importer* /*pImp*/)
-{
+void C4DImporter::SetupProperties(const Importer* /*pImp*/) {
     // nothing to be done for the moment
     // nothing to be done for the moment
 }
 }
 
 
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Imports the given file into the given scene structure.
 // Imports the given file into the given scene structure.
-void C4DImporter::InternReadFile( const std::string& pFile,
-    aiScene* pScene, IOSystem* pIOHandler)
-{
+void C4DImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) {
     std::unique_ptr<IOStream> file( pIOHandler->Open( pFile));
     std::unique_ptr<IOStream> file( pIOHandler->Open( pFile));
 
 
-    if( file.get() == NULL) {
+    if( file.get() == nullptr ) {
         ThrowException("failed to open file " + pFile);
         ThrowException("failed to open file " + pFile);
     }
     }
 
 
@@ -151,7 +150,7 @@ void C4DImporter::InternReadFile( const std::string& pFile,
 
 
     // open document first
     // open document first
     BaseDocument* doc = LoadDocument(f, SCENEFILTER_OBJECTS | SCENEFILTER_MATERIALS);
     BaseDocument* doc = LoadDocument(f, SCENEFILTER_OBJECTS | SCENEFILTER_MATERIALS);
-    if(doc == NULL) {
+    if(doc == nullptr ) {
         ThrowException("failed to read document " + pFile);
         ThrowException("failed to read document " + pFile);
     }
     }
 
 
@@ -160,11 +159,10 @@ void C4DImporter::InternReadFile( const std::string& pFile,
     // first convert all materials
     // first convert all materials
     ReadMaterials(doc->GetFirstMaterial());
     ReadMaterials(doc->GetFirstMaterial());
 
 
-    // process C4D scenegraph recursively
+    // process C4D scene-graph recursively
     try {
     try {
         RecurseHierarchy(doc->GetFirstObject(), pScene->mRootNode);
         RecurseHierarchy(doc->GetFirstObject(), pScene->mRootNode);
-    }
-    catch(...) {
+    } catch(...) {
         for(aiMesh* mesh : meshes) {
         for(aiMesh* mesh : meshes) {
             delete mesh;
             delete mesh;
         }
         }
@@ -201,8 +199,7 @@ void C4DImporter::InternReadFile( const std::string& pFile,
 
 
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-bool C4DImporter::ReadShader(aiMaterial* out, melange::BaseShader* shader)
-{
+bool C4DImporter::ReadShader(aiMaterial* out, melange::BaseShader* shader) {
     // based on Melange sample code (C4DImportExport.cpp)
     // based on Melange sample code (C4DImportExport.cpp)
     while(shader) {
     while(shader) {
         if(shader->GetType() == Xlayer) {
         if(shader->GetType() == Xlayer) {
@@ -220,15 +217,12 @@ bool C4DImporter::ReadShader(aiMaterial* out, melange::BaseShader* shader)
             // Ignore the actual layer blending - models for real-time rendering should not
             // Ignore the actual layer blending - models for real-time rendering should not
             // use them in a non-trivial way. Just try to find textures that we can apply
             // use them in a non-trivial way. Just try to find textures that we can apply
             // to the model.
             // to the model.
-            while (lsl)
-            {
-                if (lsl->GetType() == TypeFolder)
-                {
+            while (lsl) {
+                if (lsl->GetType() == TypeFolder) {
                     BlendFolder* const folder = dynamic_cast<BlendFolder*>(lsl);
                     BlendFolder* const folder = dynamic_cast<BlendFolder*>(lsl);
                     LayerShaderLayer *subLsl = dynamic_cast<LayerShaderLayer*>(folder->m_Children.GetObject(0));
                     LayerShaderLayer *subLsl = dynamic_cast<LayerShaderLayer*>(folder->m_Children.GetObject(0));
 
 
-                    while (subLsl)
-                    {
+                    while (subLsl) {
                         if (subLsl->GetType() == TypeShader) {
                         if (subLsl->GetType() == TypeShader) {
                             BlendShader* const shader = dynamic_cast<BlendShader*>(subLsl);
                             BlendShader* const shader = dynamic_cast<BlendShader*>(subLsl);
                             if(ReadShader(out, static_cast<BaseShader*>(shader->m_pLink->GetLink()))) {
                             if(ReadShader(out, static_cast<BaseShader*>(shader->m_pLink->GetLink()))) {
@@ -238,8 +232,7 @@ bool C4DImporter::ReadShader(aiMaterial* out, melange::BaseShader* shader)
 
 
                         subLsl = subLsl->GetNext();
                         subLsl = subLsl->GetNext();
                     }
                     }
-                }
-                else if (lsl->GetType() == TypeShader) {
+                } else if (lsl->GetType() == TypeShader) {
                     BlendShader* const shader = dynamic_cast<BlendShader*>(lsl);
                     BlendShader* const shader = dynamic_cast<BlendShader*>(lsl);
                     if(ReadShader(out, static_cast<BaseShader*>(shader->m_pLink->GetLink()))) {
                     if(ReadShader(out, static_cast<BaseShader*>(shader->m_pLink->GetLink()))) {
                         return true;
                         return true;
@@ -248,33 +241,27 @@ bool C4DImporter::ReadShader(aiMaterial* out, melange::BaseShader* shader)
 
 
                 lsl = lsl->GetNext();
                 lsl = lsl->GetNext();
             }
             }
-        }
-        else if ( shader->GetType() == Xbitmap )
-        {
+        } else if ( shader->GetType() == Xbitmap ) {
             aiString path;
             aiString path;
             shader->GetFileName().GetString().GetCString(path.data, MAXLEN-1);
             shader->GetFileName().GetString().GetCString(path.data, MAXLEN-1);
             path.length = ::strlen(path.data);
             path.length = ::strlen(path.data);
             out->AddProperty(&path, AI_MATKEY_TEXTURE_DIFFUSE(0));
             out->AddProperty(&path, AI_MATKEY_TEXTURE_DIFFUSE(0));
             return true;
             return true;
-        }
-        else {
+        } else {
             LogWarn("ignoring shader type: " + std::string(GetObjectTypeName(shader->GetType())));
             LogWarn("ignoring shader type: " + std::string(GetObjectTypeName(shader->GetType())));
         }
         }
         shader = shader->GetNext();
         shader = shader->GetNext();
     }
     }
+
     return false;
     return false;
 }
 }
 
 
-
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-void C4DImporter::ReadMaterials(melange::BaseMaterial* mat)
-{
+void C4DImporter::ReadMaterials(melange::BaseMaterial* mat) {
     // based on Melange sample code
     // based on Melange sample code
-    while (mat)
-    {
+    while (mat) {
         const String& name = mat->GetName();
         const String& name = mat->GetName();
-        if (mat->GetType() == Mmaterial)
-        {
+        if (mat->GetType() == Mmaterial) {
             aiMaterial* out = new aiMaterial();
             aiMaterial* out = new aiMaterial();
             material_mapping[mat] = static_cast<unsigned int>(materials.size());
             material_mapping[mat] = static_cast<unsigned int>(materials.size());
             materials.push_back(out);
             materials.push_back(out);
@@ -286,8 +273,7 @@ void C4DImporter::ReadMaterials(melange::BaseMaterial* mat)
 
 
             Material& m = dynamic_cast<Material&>(*mat);
             Material& m = dynamic_cast<Material&>(*mat);
 
 
-            if (m.GetChannelState(CHANNEL_COLOR))
-            {
+            if (m.GetChannelState(CHANNEL_COLOR)) {
                 GeData data;
                 GeData data;
                 mat->GetParameter(MATERIAL_COLOR_COLOR, data);
                 mat->GetParameter(MATERIAL_COLOR_COLOR, data);
                 Vector color = data.GetVector();
                 Vector color = data.GetVector();
@@ -307,9 +293,7 @@ void C4DImporter::ReadMaterials(melange::BaseMaterial* mat)
             if(shader) {
             if(shader) {
                 ReadShader(out, shader);
                 ReadShader(out, shader);
             }
             }
-        }
-        else
-        {
+        } else {
             LogWarn("ignoring plugin material: " + std::string(GetObjectTypeName(mat->GetType())));
             LogWarn("ignoring plugin material: " + std::string(GetObjectTypeName(mat->GetType())));
         }
         }
         mat = mat->GetNext();
         mat = mat->GetNext();
@@ -317,14 +301,12 @@ void C4DImporter::ReadMaterials(melange::BaseMaterial* mat)
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-void C4DImporter::RecurseHierarchy(BaseObject* object, aiNode* parent)
-{
-    ai_assert(parent != NULL);
+void C4DImporter::RecurseHierarchy(BaseObject* object, aiNode* parent) {
+    ai_assert(parent != nullptr );
     std::vector<aiNode*> nodes;
     std::vector<aiNode*> nodes;
 
 
     // based on Melange sample code
     // based on Melange sample code
-    while (object)
-    {
+    while (object) {
         const String& name = object->GetName();
         const String& name = object->GetName();
         const LONG type = object->GetType();
         const LONG type = object->GetType();
         const Matrix& ml = object->GetMl();
         const Matrix& ml = object->GetMl();
@@ -356,26 +338,20 @@ void C4DImporter::RecurseHierarchy(BaseObject* object, aiNode* parent)
         nodes.push_back(nd);
         nodes.push_back(nd);
 
 
         GeData data;
         GeData data;
-        if (type == Ocamera)
-        {
+        if (type == Ocamera) {
             object->GetParameter(CAMERAOBJECT_FOV, data);
             object->GetParameter(CAMERAOBJECT_FOV, data);
             // TODO: read camera
             // TODO: read camera
-        }
-        else if (type == Olight)
-        {
+        } else if (type == Olight) {
             // TODO: read light
             // TODO: read light
-        }
-        else if (type == Opolygon)
-        {
+        } else if (type == Opolygon) {
             aiMesh* const mesh = ReadMesh(object);
             aiMesh* const mesh = ReadMesh(object);
-            if(mesh != NULL) {
+            if(mesh != nullptr) {
                 nd->mNumMeshes = 1;
                 nd->mNumMeshes = 1;
                 nd->mMeshes = new unsigned int[1];
                 nd->mMeshes = new unsigned int[1];
                 nd->mMeshes[0] = static_cast<unsigned int>(meshes.size());
                 nd->mMeshes[0] = static_cast<unsigned int>(meshes.size());
                 meshes.push_back(mesh);
                 meshes.push_back(mesh);
             }
             }
-        }
-        else {
+        } else {
             LogWarn("ignoring object: " + std::string(GetObjectTypeName(type)));
             LogWarn("ignoring object: " + std::string(GetObjectTypeName(type)));
         }
         }
 
 
@@ -389,28 +365,27 @@ void C4DImporter::RecurseHierarchy(BaseObject* object, aiNode* parent)
     std::copy(nodes.begin(), nodes.end(), parent->mChildren);
     std::copy(nodes.begin(), nodes.end(), parent->mChildren);
 }
 }
 
 
-
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-aiMesh* C4DImporter::ReadMesh(BaseObject* object)
-{
-    ai_assert(object != NULL && object->GetType() == Opolygon);
+aiMesh* C4DImporter::ReadMesh(BaseObject* object) {
+    ai_assert(object != nullptr);
+    ai_assert( object->GetType() == Opolygon );
 
 
     // based on Melange sample code
     // based on Melange sample code
     PolygonObject* const polyObject = dynamic_cast<PolygonObject*>(object);
     PolygonObject* const polyObject = dynamic_cast<PolygonObject*>(object);
-    ai_assert(polyObject != NULL);
+    ai_assert(polyObject != nullptr);
 
 
     const LONG pointCount = polyObject->GetPointCount();
     const LONG pointCount = polyObject->GetPointCount();
     const LONG polyCount = polyObject->GetPolygonCount();
     const LONG polyCount = polyObject->GetPolygonCount();
     if(!polyObject || !pointCount) {
     if(!polyObject || !pointCount) {
         LogWarn("ignoring mesh with zero vertices or faces");
         LogWarn("ignoring mesh with zero vertices or faces");
-        return NULL;
+        return nullptr;
     }
     }
 
 
     const Vector* points = polyObject->GetPointR();
     const Vector* points = polyObject->GetPointR();
-    ai_assert(points != NULL);
+    ai_assert(points != nullptr);
 
 
     const CPolygon* polys = polyObject->GetPolygonR();
     const CPolygon* polys = polyObject->GetPolygonR();
-    ai_assert(polys != NULL);
+    ai_assert(polys != nullptr);
 
 
     std::unique_ptr<aiMesh> mesh(new aiMesh());
     std::unique_ptr<aiMesh> mesh(new aiMesh());
     mesh->mNumFaces = static_cast<unsigned int>(polyCount);
     mesh->mNumFaces = static_cast<unsigned int>(polyCount);
@@ -443,14 +418,14 @@ aiMesh* C4DImporter::ReadMesh(BaseObject* object)
 
 
     // check if there are normals, tangents or UVW coordinates
     // check if there are normals, tangents or UVW coordinates
     BaseTag* tag = object->GetTag(Tnormal);
     BaseTag* tag = object->GetTag(Tnormal);
-    NormalTag* normals_src = NULL;
+    NormalTag* normals_src = nullptr;
     if(tag) {
     if(tag) {
         normals_src = dynamic_cast<NormalTag*>(tag);
         normals_src = dynamic_cast<NormalTag*>(tag);
         normals = mesh->mNormals = new aiVector3D[mesh->mNumVertices]();
         normals = mesh->mNormals = new aiVector3D[mesh->mNumVertices]();
     }
     }
 
 
     tag = object->GetTag(Ttangent);
     tag = object->GetTag(Ttangent);
-    TangentTag* tangents_src = NULL;
+    TangentTag* tangents_src = nullptr;
     if(tag) {
     if(tag) {
         tangents_src = dynamic_cast<TangentTag*>(tag);
         tangents_src = dynamic_cast<TangentTag*>(tag);
         tangents = mesh->mTangents = new aiVector3D[mesh->mNumVertices]();
         tangents = mesh->mTangents = new aiVector3D[mesh->mNumVertices]();
@@ -458,15 +433,14 @@ aiMesh* C4DImporter::ReadMesh(BaseObject* object)
     }
     }
 
 
     tag = object->GetTag(Tuvw);
     tag = object->GetTag(Tuvw);
-    UVWTag* uvs_src = NULL;
+    UVWTag* uvs_src = nullptr;
     if(tag) {
     if(tag) {
         uvs_src = dynamic_cast<UVWTag*>(tag);
         uvs_src = dynamic_cast<UVWTag*>(tag);
         uvs = mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices]();
         uvs = mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices]();
     }
     }
 
 
     // copy vertices and extra channels over and populate faces
     // copy vertices and extra channels over and populate faces
-    for (LONG i = 0; i < polyCount; ++i, ++face)
-    {
+    for (LONG i = 0; i < polyCount; ++i, ++face) {
         ai_assert(polys[i].a < pointCount && polys[i].a >= 0);
         ai_assert(polys[i].a < pointCount && polys[i].a >= 0);
         const Vector& pointA = points[polys[i].a];
         const Vector& pointA = points[polys[i].a];
         verts->x = pointA.x;
         verts->x = pointA.x;
@@ -489,8 +463,7 @@ aiMesh* C4DImporter::ReadMesh(BaseObject* object)
         ++verts;
         ++verts;
 
 
         // TODO: do we also need to handle lines or points with similar checks?
         // TODO: do we also need to handle lines or points with similar checks?
-        if (polys[i].c != polys[i].d)
-        {
+        if (polys[i].c != polys[i].d) {
             ai_assert(polys[i].d < pointCount && polys[i].d >= 0);
             ai_assert(polys[i].d < pointCount && polys[i].d >= 0);
 
 
             face->mNumIndices = 4;
             face->mNumIndices = 4;
@@ -500,8 +473,7 @@ aiMesh* C4DImporter::ReadMesh(BaseObject* object)
             verts->y = pointD.y;
             verts->y = pointD.y;
             verts->z = pointD.z;
             verts->z = pointD.z;
             ++verts;
             ++verts;
-        }
-        else {
+        } else {
             face->mNumIndices = 3;
             face->mNumIndices = 3;
         }
         }
         face->mIndices = new unsigned int[face->mNumIndices];
         face->mIndices = new unsigned int[face->mNumIndices];
@@ -513,8 +485,7 @@ aiMesh* C4DImporter::ReadMesh(BaseObject* object)
         if (normals_src) {
         if (normals_src) {
             if(i >= normals_src->GetDataCount()) {
             if(i >= normals_src->GetDataCount()) {
                 LogError("unexpected number of normals, ignoring");
                 LogError("unexpected number of normals, ignoring");
-            }
-            else {
+            } else {
                 ConstNormalHandle normal_handle = normals_src->GetDataAddressR();
                 ConstNormalHandle normal_handle = normals_src->GetDataAddressR();
                 NormalStruct nor;
                 NormalStruct nor;
                 NormalTag::Get(normal_handle, i, nor);
                 NormalTag::Get(normal_handle, i, nor);
@@ -616,26 +587,25 @@ aiMesh* C4DImporter::ReadMesh(BaseObject* object)
     }
     }
 
 
     mesh->mMaterialIndex = ResolveMaterial(polyObject);
     mesh->mMaterialIndex = ResolveMaterial(polyObject);
+
     return mesh.release();
     return mesh.release();
 }
 }
 
 
-
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-unsigned int C4DImporter::ResolveMaterial(PolygonObject* obj)
-{
-    ai_assert(obj != NULL);
+unsigned int C4DImporter::ResolveMaterial(PolygonObject* obj) {
+    ai_assert(obj != nullptr);
 
 
     const unsigned int mat_count = static_cast<unsigned int>(materials.size());
     const unsigned int mat_count = static_cast<unsigned int>(materials.size());
 
 
     BaseTag* tag = obj->GetTag(Ttexture);
     BaseTag* tag = obj->GetTag(Ttexture);
-    if(tag == NULL) {
+    if(tag == nullptr) {
         return mat_count;
         return mat_count;
     }
     }
 
 
     TextureTag& ttag = dynamic_cast<TextureTag&>(*tag);
     TextureTag& ttag = dynamic_cast<TextureTag&>(*tag);
 
 
     BaseMaterial* const mat = ttag.GetMaterial();
     BaseMaterial* const mat = ttag.GetMaterial();
-    ai_assert(mat != NULL);
+    ai_assert(mat != nullptr);
 
 
     const MaterialMap::const_iterator it = material_mapping.find(mat);
     const MaterialMap::const_iterator it = material_mapping.find(mat);
     if(it == material_mapping.end()) {
     if(it == material_mapping.end()) {
@@ -643,6 +613,7 @@ unsigned int C4DImporter::ResolveMaterial(PolygonObject* obj)
     }
     }
 
 
     ai_assert((*it).second < mat_count);
     ai_assert((*it).second < mat_count);
+
     return (*it).second;
     return (*it).second;
 }
 }
 
 

+ 6 - 12
code/C4DImporter.h

@@ -2,7 +2,7 @@
 Open Asset Import Library (assimp)
 Open Asset Import Library (assimp)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2012, assimp team
+Copyright (c) 2006-2019, assimp team
 All rights reserved.
 All rights reserved.
 
 
 Redistribution and use of this software in source and binary forms,
 Redistribution and use of this software in source and binary forms,
@@ -48,6 +48,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/LogAux.h>
 #include <assimp/LogAux.h>
 
 
 #include <map>
 #include <map>
+
+// Forward declarations
 struct aiNode;
 struct aiNode;
 struct aiMesh;
 struct aiMesh;
 struct aiMaterial;
 struct aiMaterial;
@@ -61,8 +63,7 @@ namespace melange {
     class BaseShader;
     class BaseShader;
 }
 }
 
 
-namespace Assimp    {
-
+namespace Assimp  {
     // TinyFormatter.h
     // TinyFormatter.h
     namespace Formatter {
     namespace Formatter {
         template <typename T,typename TR, typename A> class basic_formatter;
         template <typename T,typename TR, typename A> class basic_formatter;
@@ -75,17 +76,10 @@ namespace Assimp    {
  *
  *
  *  Note that Melange is not free software. */
  *  Note that Melange is not free software. */
 // -------------------------------------------------------------------------------------------
 // -------------------------------------------------------------------------------------------
-class C4DImporter : public BaseImporter, public LogFunctions<C4DImporter>
-{
+class C4DImporter : public BaseImporter, public LogFunctions<C4DImporter> {
 public:
 public:
-
     C4DImporter();
     C4DImporter();
     ~C4DImporter();
     ~C4DImporter();
-
-
-public:
-
-    // --------------------
     bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
     bool CanRead( const std::string& pFile, IOSystem* pIOHandler,
         bool checkSig) const;
         bool checkSig) const;
 
 
@@ -119,5 +113,5 @@ private:
 }; // !class C4DImporter
 }; // !class C4DImporter
 
 
 } // end of namespace Assimp
 } // end of namespace Assimp
-#endif // INCLUDED_AI_CINEMA_4D_LOADER_H
 
 
+#endif // INCLUDED_AI_CINEMA_4D_LOADER_H

+ 113 - 31
code/CMakeLists.txt

@@ -220,7 +220,7 @@ ENDIF ( ASSIMP_BUILD_NONFREE_C4D_IMPORTER )
 # ASSIMP_BUILD_XXX_IMPORTER to FALSE for each 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
 # if this variable is set to FALSE, the user can manually enable importers by setting
 # ASSIMP_BUILD_XXX_IMPORTER to TRUE for each importer
 # 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
 # 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)
 # this way selective loaders can be compiled (reduces filesize + compile time)
@@ -236,19 +236,51 @@ MACRO(ADD_ASSIMP_IMPORTER name)
   IF (ASSIMP_IMPORTER_ENABLED)
   IF (ASSIMP_IMPORTER_ENABLED)
     LIST(APPEND ASSIMP_LOADER_SRCS ${ARGN})
     LIST(APPEND ASSIMP_LOADER_SRCS ${ARGN})
     SET(ASSIMP_IMPORTERS_ENABLED "${ASSIMP_IMPORTERS_ENABLED} ${name}")
     SET(ASSIMP_IMPORTERS_ENABLED "${ASSIMP_IMPORTERS_ENABLED} ${name}")
-    SET(${name}_SRCS ${ARGN})
     SOURCE_GROUP(${name} FILES ${ARGN})
     SOURCE_GROUP(${name} FILES ${ARGN})
   ELSE()
   ELSE()
     SET(${name}_SRC "")
     SET(${name}_SRC "")
     SET(ASSIMP_IMPORTERS_DISABLED "${ASSIMP_IMPORTERS_DISABLED} ${name}")
     SET(ASSIMP_IMPORTERS_DISABLED "${ASSIMP_IMPORTERS_DISABLED} ${name}")
     add_definitions(-DASSIMP_BUILD_NO_${name}_IMPORTER)
     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)
     add_definitions(-DASSIMP_BUILD_NO_${name}_EXPORTER)
   ENDIF()
   ENDIF()
 ENDMACRO()
 ENDMACRO()
 
 
 SET(ASSIMP_LOADER_SRCS "")
 SET(ASSIMP_LOADER_SRCS "")
 SET(ASSIMP_IMPORTERS_ENABLED "") # list of enabled importers
 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
 ADD_ASSIMP_IMPORTER( AMF
   AMFImporter.hpp
   AMFImporter.hpp
@@ -265,6 +297,9 @@ ADD_ASSIMP_IMPORTER( 3DS
   3DSHelper.h
   3DSHelper.h
   3DSLoader.cpp
   3DSLoader.cpp
   3DSLoader.h
   3DSLoader.h
+)
+
+ADD_ASSIMP_EXPORTER( 3DS
   3DSExporter.h
   3DSExporter.h
   3DSExporter.cpp
   3DSExporter.cpp
 )
 )
@@ -282,12 +317,15 @@ ADD_ASSIMP_IMPORTER( ASE
 )
 )
 
 
 ADD_ASSIMP_IMPORTER( ASSBIN
 ADD_ASSIMP_IMPORTER( ASSBIN
-  AssbinExporter.h
-  AssbinExporter.cpp
   AssbinLoader.h
   AssbinLoader.h
   AssbinLoader.cpp
   AssbinLoader.cpp
 )
 )
 
 
+ADD_ASSIMP_EXPORTER( ASSBIN
+  AssbinExporter.h
+  AssbinExporter.cpp
+)
+
 ADD_ASSIMP_IMPORTER( ASSXML
 ADD_ASSIMP_IMPORTER( ASSXML
   AssxmlExporter.h
   AssxmlExporter.h
   AssxmlExporter.cpp
   AssxmlExporter.cpp
@@ -309,6 +347,9 @@ ADD_ASSIMP_IMPORTER( COLLADA
   ColladaLoader.h
   ColladaLoader.h
   ColladaParser.cpp
   ColladaParser.cpp
   ColladaParser.h
   ColladaParser.h
+)
+
+ADD_ASSIMP_EXPORTER( COLLADA
   ColladaExporter.h
   ColladaExporter.h
   ColladaExporter.cpp
   ColladaExporter.cpp
 )
 )
@@ -425,6 +466,9 @@ ADD_ASSIMP_IMPORTER( OBJ
   ObjFileParser.cpp
   ObjFileParser.cpp
   ObjFileParser.h
   ObjFileParser.h
   ObjTools.h
   ObjTools.h
+)
+
+ADD_ASSIMP_EXPORTER( OBJ
   ObjExporter.h
   ObjExporter.h
   ObjExporter.cpp
   ObjExporter.cpp
 )
 )
@@ -443,18 +487,24 @@ ADD_ASSIMP_IMPORTER( OGRE
 )
 )
 
 
 ADD_ASSIMP_IMPORTER( OPENGEX
 ADD_ASSIMP_IMPORTER( OPENGEX
-  OpenGEXExporter.cpp
-  OpenGEXExporter.h
   OpenGEXImporter.cpp
   OpenGEXImporter.cpp
   OpenGEXImporter.h
   OpenGEXImporter.h
   OpenGEXStructs.h
   OpenGEXStructs.h
 )
 )
 
 
+ADD_ASSIMP_EXPORTER( OPENGEX
+  OpenGEXExporter.cpp
+  OpenGEXExporter.h
+)
+
 ADD_ASSIMP_IMPORTER( PLY
 ADD_ASSIMP_IMPORTER( PLY
   PlyLoader.cpp
   PlyLoader.cpp
   PlyLoader.h
   PlyLoader.h
   PlyParser.cpp
   PlyParser.cpp
   PlyParser.h
   PlyParser.h
+)
+
+ADD_ASSIMP_EXPORTER( PLY
   PlyExporter.cpp
   PlyExporter.cpp
   PlyExporter.h
   PlyExporter.h
 )
 )
@@ -545,13 +595,16 @@ ADD_ASSIMP_IMPORTER( FBX
   FBXDeformer.cpp
   FBXDeformer.cpp
   FBXBinaryTokenizer.cpp
   FBXBinaryTokenizer.cpp
   FBXDocumentUtil.cpp
   FBXDocumentUtil.cpp
+  FBXCommon.h
+)
+
+ADD_ASSIMP_EXPORTER( FBX
   FBXExporter.h
   FBXExporter.h
   FBXExporter.cpp
   FBXExporter.cpp
   FBXExportNode.h
   FBXExportNode.h
   FBXExportNode.cpp
   FBXExportNode.cpp
   FBXExportProperty.h
   FBXExportProperty.h
   FBXExportProperty.cpp
   FBXExportProperty.cpp
-  FBXCommon.h
 )
 )
 
 
 SET( PostProcessing_SRCS
 SET( PostProcessing_SRCS
@@ -651,6 +704,9 @@ ADD_ASSIMP_IMPORTER( SMD
 ADD_ASSIMP_IMPORTER( STL
 ADD_ASSIMP_IMPORTER( STL
   STLLoader.cpp
   STLLoader.cpp
   STLLoader.h
   STLLoader.h
+)
+
+ADD_ASSIMP_EXPORTER( STL
   STLExporter.h
   STLExporter.h
   STLExporter.cpp
   STLExporter.cpp
 )
 )
@@ -671,13 +727,14 @@ ADD_ASSIMP_IMPORTER( X
   XFileImporter.h
   XFileImporter.h
   XFileParser.cpp
   XFileParser.cpp
   XFileParser.h
   XFileParser.h
+)
+
+ADD_ASSIMP_EXPORTER( X
   XFileExporter.h
   XFileExporter.h
   XFileExporter.cpp
   XFileExporter.cpp
 )
 )
 
 
 ADD_ASSIMP_IMPORTER( X3D
 ADD_ASSIMP_IMPORTER( X3D
-  X3DExporter.cpp
-  X3DExporter.hpp
   X3DImporter.cpp
   X3DImporter.cpp
   X3DImporter.hpp
   X3DImporter.hpp
   X3DImporter_Geometry2D.cpp
   X3DImporter_Geometry2D.cpp
@@ -697,6 +754,11 @@ ADD_ASSIMP_IMPORTER( X3D
   X3DVocabulary.cpp
   X3DVocabulary.cpp
 )
 )
 
 
+ADD_ASSIMP_EXPORTER( X3D
+  X3DExporter.cpp
+  X3DExporter.hpp
+)
+
 ADD_ASSIMP_IMPORTER( GLTF
 ADD_ASSIMP_IMPORTER( GLTF
   glTFAsset.h
   glTFAsset.h
   glTFAsset.inl
   glTFAsset.inl
@@ -704,28 +766,34 @@ ADD_ASSIMP_IMPORTER( GLTF
   glTFAssetWriter.inl
   glTFAssetWriter.inl
   glTFImporter.cpp
   glTFImporter.cpp
   glTFImporter.h
   glTFImporter.h
-  glTFExporter.h
-  glTFExporter.cpp
   glTF2Asset.h
   glTF2Asset.h
   glTF2Asset.inl
   glTF2Asset.inl
   glTF2AssetWriter.h
   glTF2AssetWriter.h
   glTF2AssetWriter.inl
   glTF2AssetWriter.inl
   glTF2Importer.cpp
   glTF2Importer.cpp
   glTF2Importer.h
   glTF2Importer.h
+)
+
+ADD_ASSIMP_EXPORTER( GLTF
+  glTFExporter.h
+  glTFExporter.cpp
   glTF2Exporter.h
   glTF2Exporter.h
   glTF2Exporter.cpp
   glTF2Exporter.cpp
 )
 )
 
 
 ADD_ASSIMP_IMPORTER( 3MF
 ADD_ASSIMP_IMPORTER( 3MF
-    D3MFImporter.h
+	D3MFImporter.h
     D3MFImporter.cpp
     D3MFImporter.cpp
-    D3MFExporter.h
-    D3MFExporter.cpp
-    D3MFOpcPackage.h
+	D3MFOpcPackage.h
     D3MFOpcPackage.cpp
     D3MFOpcPackage.cpp
     3MFXmlTags.h
     3MFXmlTags.h
 )
 )
 
 
+ADD_ASSIMP_EXPORTER( 3MF
+    D3MFExporter.h
+    D3MFExporter.cpp
+)
+
 ADD_ASSIMP_IMPORTER( MMD
 ADD_ASSIMP_IMPORTER( MMD
   MMDCpp14.h
   MMDCpp14.h
   MMDImporter.cpp
   MMDImporter.cpp
@@ -744,28 +812,32 @@ ADD_ASSIMP_IMPORTER( STEP
     Importer/StepFile/StepFileGen2.cpp
     Importer/StepFile/StepFileGen2.cpp
     Importer/StepFile/StepFileGen3.cpp
     Importer/StepFile/StepFileGen3.cpp
     Importer/StepFile/StepReaderGen.h
     Importer/StepFile/StepReaderGen.h
+)
+
+ADD_ASSIMP_EXPORTER( STEP
     StepExporter.h
     StepExporter.h
     StepExporter.cpp
     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
 SET( Extra_SRCS
   MD4FileData.h
   MD4FileData.h
 )
 )
 SOURCE_GROUP( Extra FILES ${Extra_SRCS})
 SOURCE_GROUP( Extra FILES ${Extra_SRCS})
 
 
-
 SET( Clipper_SRCS
 SET( Clipper_SRCS
   ../contrib/clipper/clipper.hpp
   ../contrib/clipper/clipper.hpp
   ../contrib/clipper/clipper.cpp
   ../contrib/clipper/clipper.cpp
 )
 )
-SOURCE_GROUP( Clipper FILES ${Clipper_SRCS})
+SOURCE_GROUP( Contrib\\Clipper FILES ${Clipper_SRCS})
 
 
 SET( Poly2Tri_SRCS
 SET( Poly2Tri_SRCS
   ../contrib/poly2tri/poly2tri/common/shapes.cc
   ../contrib/poly2tri/poly2tri/common/shapes.cc
@@ -780,7 +852,7 @@ SET( Poly2Tri_SRCS
   ../contrib/poly2tri/poly2tri/sweep/sweep_context.cc
   ../contrib/poly2tri/poly2tri/sweep/sweep_context.cc
   ../contrib/poly2tri/poly2tri/sweep/sweep_context.h
   ../contrib/poly2tri/poly2tri/sweep/sweep_context.h
 )
 )
-SOURCE_GROUP( Poly2Tri FILES ${Poly2Tri_SRCS})
+SOURCE_GROUP( Contrib\\Poly2Tri FILES ${Poly2Tri_SRCS})
 
 
 SET( unzip_SRCS
 SET( unzip_SRCS
   ../contrib/unzip/crypt.h
   ../contrib/unzip/crypt.h
@@ -789,7 +861,7 @@ SET( unzip_SRCS
   ../contrib/unzip/unzip.c
   ../contrib/unzip/unzip.c
   ../contrib/unzip/unzip.h
   ../contrib/unzip/unzip.h
 )
 )
-SOURCE_GROUP( unzip FILES ${unzip_SRCS})
+SOURCE_GROUP(Contrib\\unzip FILES ${unzip_SRCS})
 
 
 SET( ziplib_SRCS
 SET( ziplib_SRCS
   ../contrib/zip/src/miniz.h
   ../contrib/zip/src/miniz.h
@@ -797,6 +869,13 @@ SET( ziplib_SRCS
   ../contrib/zip/src/zip.h
   ../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} )
 SOURCE_GROUP( ziplib FILES ${ziplib_SRCS} )
 
 
 SET ( openddl_parser_SRCS
 SET ( openddl_parser_SRCS
@@ -814,7 +893,7 @@ SET ( openddl_parser_SRCS
   ../contrib/openddlparser/include/openddlparser/DDLNode.h
   ../contrib/openddlparser/include/openddlparser/DDLNode.h
   ../contrib/openddlparser/include/openddlparser/Value.h
   ../contrib/openddlparser/include/openddlparser/Value.h
 )
 )
-SOURCE_GROUP( openddl_parser FILES ${openddl_parser_SRCS})
+SOURCE_GROUP( Contrib\\openddl_parser FILES ${openddl_parser_SRCS})
 
 
 SET ( open3dgc_SRCS
 SET ( open3dgc_SRCS
   ../contrib/Open3DGC/o3dgcAdjacencyInfo.h
   ../contrib/Open3DGC/o3dgcAdjacencyInfo.h
@@ -847,7 +926,7 @@ SET ( open3dgc_SRCS
   ../contrib/Open3DGC/o3dgcVector.h
   ../contrib/Open3DGC/o3dgcVector.h
   ../contrib/Open3DGC/o3dgcVector.inl
   ../contrib/Open3DGC/o3dgcVector.inl
 )
 )
-SOURCE_GROUP( open3dgc FILES ${open3dgc_SRCS})
+SOURCE_GROUP( Contrib\\open3dgc FILES ${open3dgc_SRCS})
 
 
 # Check dependencies for glTF importer with Open3DGC-compression.
 # Check dependencies for glTF importer with Open3DGC-compression.
 # RT-extensions is used in "contrib/Open3DGC/o3dgcTimer.h" for collecting statistics. Pointed file
 # RT-extensions is used in "contrib/Open3DGC/o3dgcTimer.h" for collecting statistics. Pointed file
@@ -887,8 +966,11 @@ else (UNZIP_FOUND)
   INCLUDE_DIRECTORIES( "../contrib/unzip/" )
   INCLUDE_DIRECTORIES( "../contrib/unzip/" )
 endif (UNZIP_FOUND)
 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
 SET( assimp_src
   # Assimp Files
   # Assimp Files
@@ -903,6 +985,7 @@ SET( assimp_src
 
 
   # Model Support
   # Model Support
   ${ASSIMP_LOADER_SRCS}
   ${ASSIMP_LOADER_SRCS}
+  ${ASSIMP_EXPORTER_SRCS}
 
 
   # Third-party libraries
   # Third-party libraries
   ${IrrXML_SRCS}
   ${IrrXML_SRCS}
@@ -916,7 +999,6 @@ SET( assimp_src
 
 
   ${PUBLIC_HEADERS}
   ${PUBLIC_HEADERS}
   ${COMPILER_HEADERS}
   ${COMPILER_HEADERS}
-
 )
 )
 ADD_DEFINITIONS( -DOPENDDLPARSER_BUILD )
 ADD_DEFINITIONS( -DOPENDDLPARSER_BUILD )
 
 

+ 5 - 5
code/COBLoader.cpp

@@ -144,7 +144,7 @@ void COBImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
     // check header
     // check header
     char head[32];
     char head[32];
     stream->CopyAndAdvance(head,32);
     stream->CopyAndAdvance(head,32);
-    if (strncmp(head,"Caligari ",9)) {
+    if (strncmp(head,"Caligari ",9) != 0) {
         ThrowException("Could not found magic id: `Caligari`");
         ThrowException("Could not found magic id: `Caligari`");
     }
     }
 
 
@@ -656,14 +656,14 @@ void COBImporter::ReadLght_Ascii(Scene& out, LineSplitter& splitter, const Chunk
     ReadFloat3Tuple_Ascii(msh.color ,&rgb);
     ReadFloat3Tuple_Ascii(msh.color ,&rgb);
 
 
     SkipSpaces(&rgb);
     SkipSpaces(&rgb);
-    if (strncmp(rgb,"cone angle",10)) {
+    if (strncmp(rgb,"cone angle",10) != 0) {
         ASSIMP_LOG_WARN_F( "Expected `cone angle` entity in `color` line in `Lght` chunk ", nfo.id );
         ASSIMP_LOG_WARN_F( "Expected `cone angle` entity in `color` line in `Lght` chunk ", nfo.id );
     }
     }
     SkipSpaces(rgb+10,&rgb);
     SkipSpaces(rgb+10,&rgb);
     msh.angle = fast_atof(&rgb);
     msh.angle = fast_atof(&rgb);
 
 
     SkipSpaces(&rgb);
     SkipSpaces(&rgb);
-    if (strncmp(rgb,"inner angle",11)) {
+    if (strncmp(rgb,"inner angle",11) != 0) {
         ASSIMP_LOG_WARN_F( "Expected `inner angle` entity in `color` line in `Lght` chunk ", nfo.id);
         ASSIMP_LOG_WARN_F( "Expected `inner angle` entity in `color` line in `Lght` chunk ", nfo.id);
     }
     }
     SkipSpaces(rgb+11,&rgb);
     SkipSpaces(rgb+11,&rgb);
@@ -903,7 +903,7 @@ public:
         if(nfo.size != static_cast<unsigned int>(-1)) {
         if(nfo.size != static_cast<unsigned int>(-1)) {
             try {
             try {
                 reader.IncPtr( static_cast< int >( nfo.size ) - reader.GetCurrentPos() + cur );
                 reader.IncPtr( static_cast< int >( nfo.size ) - reader.GetCurrentPos() + cur );
-            } catch ( DeadlyImportError e ) {
+            } catch (const DeadlyImportError& e ) {
                 // out of limit so correct the value
                 // out of limit so correct the value
                 reader.IncPtr( reader.GetReadLimit() );
                 reader.IncPtr( reader.GetReadLimit() );
             }
             }
@@ -1214,7 +1214,7 @@ void COBImporter::ReadGrou_Binary(COB::Scene& out, StreamReaderLE& reader, const
 
 
     const chunk_guard cn(nfo,reader);
     const chunk_guard cn(nfo,reader);
 
 
-    out.nodes.push_back(std::shared_ptr<Group>(new Group()));
+    out.nodes.push_back(std::make_shared<Group>());
     Group& msh = (Group&)(*out.nodes.back().get());
     Group& msh = (Group&)(*out.nodes.back().get());
     msh = nfo;
     msh = nfo;
 
 

+ 4 - 1
code/D3MFOpcPackage.cpp

@@ -476,8 +476,11 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem* pIOHandler, const std::string& rFile)
             mZipArchive->Close( fileStream );
             mZipArchive->Close( fileStream );
 
 
         } else if( file == D3MF::XmlTag::CONTENT_TYPES_ARCHIVE) {
         } else if( file == D3MF::XmlTag::CONTENT_TYPES_ARCHIVE) {
-
+            ASSIMP_LOG_WARN_F("Ignored file of unsupported type CONTENT_TYPES_ARCHIVES",file);
+        } else {
+            ASSIMP_LOG_WARN_F("Ignored file of unknown type: ",file);
         }
         }
+
     }
     }
 }
 }
 
 

+ 44 - 23
code/FBXConverter.cpp

@@ -78,6 +78,16 @@ namespace Assimp {
 
 
         FBXConverter::FBXConverter(aiScene* out, const Document& doc)
         FBXConverter::FBXConverter(aiScene* out, const Document& doc)
             : defaultMaterialIndex()
             : defaultMaterialIndex()
+            , lights()
+            , cameras()
+            , textures()
+            , materials_converted()
+            , textures_converted()
+            , meshes_converted()
+            , node_anim_chain_bits()
+            , mNodeNameInstances()
+            , mNodeNames()
+            , anim_fps()
             , out(out)
             , out(out)
             , doc(doc) {
             , doc(doc) {
             // animations need to be converted first since this will
             // animations need to be converted first since this will
@@ -410,19 +420,24 @@ namespace Assimp {
 
 
         void FBXConverter::GetUniqueName(const std::string &name, std::string &uniqueName)
         void FBXConverter::GetUniqueName(const std::string &name, std::string &uniqueName)
         {
         {
-            int i = 0;
             uniqueName = name;
             uniqueName = name;
-            while (mNodeNames.find(uniqueName) != mNodeNames.end())
+            int i = 0;
+            auto it = mNodeNameInstances.find(name); // duplicate node name instance count
+            if (it != mNodeNameInstances.end())
             {
             {
-                ++i;
-                std::stringstream ext;
-                ext << name << std::setfill('0') << std::setw(3) << i;
-                uniqueName = ext.str();
+                i = it->second;
+                while (mNodeNames.find(uniqueName) != mNodeNames.end())
+                {
+                    i++;
+                    std::stringstream ext;
+                    ext << name << std::setfill('0') << std::setw(3) << i;
+                    uniqueName = ext.str();
+                }
             }
             }
+            mNodeNameInstances[name] = i;
             mNodeNames.insert(uniqueName);
             mNodeNames.insert(uniqueName);
         }
         }
 
 
-
         const char* FBXConverter::NameTransformationComp(TransformationComp comp) {
         const char* FBXConverter::NameTransformationComp(TransformationComp comp) {
             switch (comp) {
             switch (comp) {
             case TransformationComp_Translation:
             case TransformationComp_Translation:
@@ -964,7 +979,7 @@ namespace Assimp {
             {
             {
                 if (indices[i] < 0) epcount++;
                 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;
             unsigned int scount = out_mesh->mNumFaces = pcount - epcount;
 
 
             aiFace* fac = out_mesh->mFaces = new aiFace[scount]();
             aiFace* fac = out_mesh->mFaces = new aiFace[scount]();
@@ -1712,22 +1727,22 @@ namespace Assimp {
                         if (!mesh)
                         if (!mesh)
                         {
                         {
                             for (const MeshMap::value_type& v : meshes_converted) {
                             for (const MeshMap::value_type& v : meshes_converted) {
-                                const MeshGeometry* const mesh = dynamic_cast<const MeshGeometry*> (v.first);
-                                if (!mesh) {
+                                const MeshGeometry* const meshGeom = dynamic_cast<const MeshGeometry*> (v.first);
+                                if (!meshGeom) {
                                     continue;
                                     continue;
                                 }
                                 }
 
 
-                                const MatIndexArray& mats = mesh->GetMaterialIndices();
+                                const MatIndexArray& mats = meshGeom->GetMaterialIndices();
                                 if (std::find(mats.begin(), mats.end(), matIndex) == mats.end()) {
                                 if (std::find(mats.begin(), mats.end(), matIndex) == mats.end()) {
                                     continue;
                                     continue;
                                 }
                                 }
 
 
                                 int index = -1;
                                 int index = -1;
                                 for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
                                 for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
-                                    if (mesh->GetTextureCoords(i).empty()) {
+                                    if (meshGeom->GetTextureCoords(i).empty()) {
                                         break;
                                         break;
                                     }
                                     }
-                                    const std::string& name = mesh->GetTextureCoordChannelName(i);
+                                    const std::string& name = meshGeom->GetTextureCoordChannelName(i);
                                     if (name == uvSet) {
                                     if (name == uvSet) {
                                         index = static_cast<int>(i);
                                         index = static_cast<int>(i);
                                         break;
                                         break;
@@ -1835,22 +1850,22 @@ namespace Assimp {
                         if (!mesh)
                         if (!mesh)
                         {
                         {
                             for (const MeshMap::value_type& v : meshes_converted) {
                             for (const MeshMap::value_type& v : meshes_converted) {
-                                const MeshGeometry* const mesh = dynamic_cast<const MeshGeometry*> (v.first);
-                                if (!mesh) {
+                                const MeshGeometry* const meshGeom = dynamic_cast<const MeshGeometry*> (v.first);
+                                if (!meshGeom) {
                                     continue;
                                     continue;
                                 }
                                 }
 
 
-                                const MatIndexArray& mats = mesh->GetMaterialIndices();
+                                const MatIndexArray& mats = meshGeom->GetMaterialIndices();
                                 if (std::find(mats.begin(), mats.end(), matIndex) == mats.end()) {
                                 if (std::find(mats.begin(), mats.end(), matIndex) == mats.end()) {
                                     continue;
                                     continue;
                                 }
                                 }
 
 
                                 int index = -1;
                                 int index = -1;
                                 for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
                                 for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
-                                    if (mesh->GetTextureCoords(i).empty()) {
+                                    if (meshGeom->GetTextureCoords(i).empty()) {
                                         break;
                                         break;
                                     }
                                     }
-                                    const std::string& name = mesh->GetTextureCoordChannelName(i);
+                                    const std::string& name = meshGeom->GetTextureCoordChannelName(i);
                                     if (name == uvSet) {
                                     if (name == uvSet) {
                                         index = static_cast<int>(i);
                                         index = static_cast<int>(i);
                                         break;
                                         break;
@@ -2041,6 +2056,12 @@ namespace Assimp {
                 CalculatedOpacity = 1.0f - ((Transparent.r + Transparent.g + Transparent.b) / 3.0f);
                 CalculatedOpacity = 1.0f - ((Transparent.r + Transparent.g + Transparent.b) / 3.0f);
             }
             }
 
 
+            // try to get the transparency factor
+            const float TransparencyFactor = PropertyGet<float>(props, "TransparencyFactor", ok);
+            if (ok) {
+                out_mat->AddProperty(&TransparencyFactor, 1, AI_MATKEY_TRANSPARENCYFACTOR);
+            }
+
             // use of TransparencyFactor is inconsistent.
             // use of TransparencyFactor is inconsistent.
             // Maya always stores it as 1.0,
             // Maya always stores it as 1.0,
             // so we can't use it to set AI_MATKEY_OPACITY.
             // so we can't use it to set AI_MATKEY_OPACITY.
@@ -2191,22 +2212,22 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
                     if (!mesh)
                     if (!mesh)
                     {
                     {
                         for (const MeshMap::value_type& v : meshes_converted) {
                         for (const MeshMap::value_type& v : meshes_converted) {
-                            const MeshGeometry* const mesh = dynamic_cast<const MeshGeometry*>(v.first);
-                            if (!mesh) {
+                            const MeshGeometry* const meshGeom = dynamic_cast<const MeshGeometry*>(v.first);
+                            if (!meshGeom) {
                                 continue;
                                 continue;
                             }
                             }
 
 
-                            const MatIndexArray& mats = mesh->GetMaterialIndices();
+                            const MatIndexArray& mats = meshGeom->GetMaterialIndices();
                             if (std::find(mats.begin(), mats.end(), matIndex) == mats.end()) {
                             if (std::find(mats.begin(), mats.end(), matIndex) == mats.end()) {
                                 continue;
                                 continue;
                             }
                             }
 
 
                             int index = -1;
                             int index = -1;
                             for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
                             for (unsigned int i = 0; i < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++i) {
-                                if (mesh->GetTextureCoords(i).empty()) {
+                                if (meshGeom->GetTextureCoords(i).empty()) {
                                     break;
                                     break;
                                 }
                                 }
-                                const std::string& name = mesh->GetTextureCoordChannelName(i);
+                                const std::string& name = meshGeom->GetTextureCoordChannelName(i);
                                 if (name == uvSet) {
                                 if (name == uvSet) {
                                     index = static_cast<int>(i);
                                     index = static_cast<int>(i);
                                     break;
                                     break;

+ 12 - 8
code/FBXConverter.h

@@ -58,6 +58,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/texture.h>
 #include <assimp/texture.h>
 #include <assimp/camera.h>
 #include <assimp/camera.h>
 #include <assimp/StringComparison.h>
 #include <assimp/StringComparison.h>
+#include <unordered_map>
+#include <unordered_set>
 
 
 struct aiScene;
 struct aiScene;
 struct aiNode;
 struct aiNode;
@@ -74,8 +76,6 @@ namespace FBX {
 
 
 class Document;
 class Document;
 
 
-using NodeNameCache = std::set<std::string>;
-
 /** 
 /** 
  *  Convert a FBX #Document to #aiScene
  *  Convert a FBX #Document to #aiScene
  *  @param out Empty scene to be populated
  *  @param out Empty scene to be populated
@@ -419,7 +419,6 @@ private:
     void TransferDataToScene();
     void TransferDataToScene();
 
 
 private:
 private:
-
     // 0: not assigned yet, others: index is value - 1
     // 0: not assigned yet, others: index is value - 1
     unsigned int defaultMaterialIndex;
     unsigned int defaultMaterialIndex;
 
 
@@ -429,22 +428,27 @@ private:
     std::vector<aiLight*> lights;
     std::vector<aiLight*> lights;
     std::vector<aiCamera*> cameras;
     std::vector<aiCamera*> cameras;
     std::vector<aiTexture*> textures;
     std::vector<aiTexture*> textures;
-    
 
 
-    typedef std::map<const Material*, unsigned int> MaterialMap;
+    using MaterialMap = std::map<const Material*, unsigned int>;
     MaterialMap materials_converted;
     MaterialMap materials_converted;
 
 
-    typedef std::map<const Video*, unsigned int> VideoMap;
+    using VideoMap = std::map<const Video*, unsigned int>;
     VideoMap textures_converted;
     VideoMap textures_converted;
 
 
-    typedef std::map<const Geometry*, std::vector<unsigned int> > MeshMap;
+    using MeshMap = std::map<const Geometry*, std::vector<unsigned int> >;
     MeshMap meshes_converted;
     MeshMap meshes_converted;
 
 
     // fixed node name -> which trafo chain components have animations?
     // fixed node name -> which trafo chain components have animations?
-    typedef std::map<std::string, unsigned int> NodeAnimBitMap;
+    using NodeAnimBitMap = std::map<std::string, unsigned int> ;
     NodeAnimBitMap node_anim_chain_bits;
     NodeAnimBitMap node_anim_chain_bits;
 
 
+    // number of nodes with the same name
+    using NodeAnimNameMap = std::unordered_map<std::string, unsigned int>;
+    NodeAnimNameMap mNodeNameInstances;
+
+    using NodeNameCache = std::unordered_set<std::string>;
     NodeNameCache mNodeNames;
     NodeNameCache mNodeNames;
+
     double anim_fps;
     double anim_fps;
 
 
     aiScene* const out;
     aiScene* const out;

+ 2 - 2
code/FBXExportNode.cpp

@@ -432,7 +432,7 @@ void FBX::Node::WritePropertyNodeAscii(
     char buffer[32];
     char buffer[32];
     FBX::Node node(name);
     FBX::Node node(name);
     node.Begin(s, false, indent);
     node.Begin(s, false, indent);
-    std::string vsize = std::to_string(v.size());
+    std::string vsize = to_string(v.size());
     // *<size> {
     // *<size> {
     s.PutChar('*'); s.PutString(vsize); s.PutString(" {\n");
     s.PutChar('*'); s.PutString(vsize); s.PutString(" {\n");
     // indent + 1
     // indent + 1
@@ -468,7 +468,7 @@ void FBX::Node::WritePropertyNodeAscii(
     char buffer[32];
     char buffer[32];
     FBX::Node node(name);
     FBX::Node node(name);
     node.Begin(s, false, indent);
     node.Begin(s, false, indent);
-    std::string vsize = std::to_string(v.size());
+    std::string vsize = to_string(v.size());
     // *<size> {
     // *<size> {
     s.PutChar('*'); s.PutString(vsize); s.PutString(" {\n");
     s.PutChar('*'); s.PutString(vsize); s.PutString(" {\n");
     // indent + 1
     // indent + 1

+ 18 - 1
code/FBXMaterial.cpp

@@ -206,6 +206,20 @@ Texture::Texture(uint64_t id, const Element& element, const Document& doc, const
 
 
     props = GetPropertyTable(doc,"Texture.FbxFileTexture",element,sc);
     props = GetPropertyTable(doc,"Texture.FbxFileTexture",element,sc);
 
 
+    // 3DS Max and FBX SDK use "Scaling" and "Translation" instead of "ModelUVScaling" and "ModelUVTranslation". Use these properties if available.
+    bool ok;
+    const aiVector3D& scaling = PropertyGet<aiVector3D>(*props, "Scaling", ok);
+    if (ok) {
+        uvScaling.x = scaling.x;
+        uvScaling.y = scaling.y;
+    }
+
+    const aiVector3D& trans = PropertyGet<aiVector3D>(*props, "Translation", ok);
+    if (ok) {
+        uvTrans.x = trans.x;
+        uvTrans.y = trans.y;
+    }
+
     // resolve video links
     // resolve video links
     if(doc.Settings().readTextures) {
     if(doc.Settings().readTextures) {
         const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID());
         const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(ID());
@@ -326,8 +340,11 @@ Video::Video(uint64_t id, const Element& element, const Document& doc, const std
                 content = new uint8_t[len];
                 content = new uint8_t[len];
                 ::memcpy(content, data + 5, len);
                 ::memcpy(content, data + 5, len);
             }
             }
-        } catch (runtime_error runtimeError) {
+        } catch (const runtime_error& runtimeError)
+        {
             //we don't need the content data for contents that has already been loaded
             //we don't need the content data for contents that has already been loaded
+            ASSIMP_LOG_DEBUG_F("Caught exception in FBXMaterial (likely because content was already loaded): ",
+                    runtimeError.what());
         }
         }
     }
     }
 
 

+ 10 - 10
code/FindInstancesProcess.cpp

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

+ 12 - 13
code/IRRLoader.cpp

@@ -300,13 +300,10 @@ int ClampSpline(int idx, int size) {
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 inline void FindSuitableMultiple(int& angle)
 inline void FindSuitableMultiple(int& angle)
 {
 {
-    if (angle < 3)angle = 3;
+    if (angle < 3) angle = 3;
     else if (angle < 10) angle = 10;
     else if (angle < 10) angle = 10;
     else if (angle < 20) angle = 20;
     else if (angle < 20) angle = 20;
     else if (angle < 30) angle = 30;
     else if (angle < 30) angle = 30;
-    else
-    {
-    }
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
@@ -317,6 +314,8 @@ void IRRImporter::ComputeAnimations(Node* root, aiNode* real, std::vector<aiNode
     // XXX totally WIP - doesn't produce proper results, need to evaluate
     // XXX totally WIP - doesn't produce proper results, need to evaluate
     // whether there's any use for Irrlicht's proprietary scene format
     // whether there's any use for Irrlicht's proprietary scene format
     // outside Irrlicht ...
     // outside Irrlicht ...
+    // This also applies to the above function of FindSuitableMultiple and ClampSpline which are
+    // solely used in this function
 
 
     if (root->animators.empty()) {
     if (root->animators.empty()) {
         return;
         return;
@@ -674,38 +673,38 @@ void IRRImporter::GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene,
             // Get the loaded mesh from the scene and add it to
             // Get the loaded mesh from the scene and add it to
             // the list of all scenes to be attached to the
             // the list of all scenes to be attached to the
             // graph we're currently building
             // graph we're currently building
-            aiScene* scene = batch.GetImport(root->id);
-            if (!scene) {
+            aiScene* localScene = batch.GetImport(root->id);
+            if (!localScene) {
                 ASSIMP_LOG_ERROR("IRR: Unable to load external file: " + root->meshPath);
                 ASSIMP_LOG_ERROR("IRR: Unable to load external file: " + root->meshPath);
                 break;
                 break;
             }
             }
-            attach.push_back(AttachmentInfo(scene,rootOut));
+            attach.push_back(AttachmentInfo(localScene,rootOut));
 
 
             // Now combine the material we've loaded for this mesh
             // Now combine the material we've loaded for this mesh
             // with the real materials we got from the file. As we
             // with the real materials we got from the file. As we
             // don't execute any pp-steps on the file, the numbers
             // don't execute any pp-steps on the file, the numbers
             // should be equal. If they are not, we can impossibly
             // should be equal. If they are not, we can impossibly
             // do this  ...
             // do this  ...
-            if (root->materials.size() != (unsigned int)scene->mNumMaterials)   {
+            if (root->materials.size() != (unsigned int)localScene->mNumMaterials)   {
                 ASSIMP_LOG_WARN("IRR: Failed to match imported materials "
                 ASSIMP_LOG_WARN("IRR: Failed to match imported materials "
                     "with the materials found in the IRR scene file");
                     "with the materials found in the IRR scene file");
 
 
                 break;
                 break;
             }
             }
-            for (unsigned int i = 0; i < scene->mNumMaterials;++i)  {
+            for (unsigned int i = 0; i < localScene->mNumMaterials;++i)  {
                 // Delete the old material, we don't need it anymore
                 // Delete the old material, we don't need it anymore
-                delete scene->mMaterials[i];
+                delete localScene->mMaterials[i];
 
 
                 std::pair<aiMaterial*, unsigned int>& src = root->materials[i];
                 std::pair<aiMaterial*, unsigned int>& src = root->materials[i];
-                scene->mMaterials[i] = src.first;
+                localScene->mMaterials[i] = src.first;
             }
             }
 
 
             // NOTE: Each mesh should have exactly one material assigned,
             // NOTE: Each mesh should have exactly one material assigned,
             // but we do it in a separate loop if this behaviour changes
             // but we do it in a separate loop if this behaviour changes
             // in future.
             // in future.
-            for (unsigned int i = 0; i < scene->mNumMeshes;++i) {
+            for (unsigned int i = 0; i < localScene->mNumMeshes;++i) {
                 // Process material flags
                 // Process material flags
-                aiMesh* mesh = scene->mMeshes[i];
+                aiMesh* mesh = localScene->mMeshes[i];
 
 
 
 
                 // If "trans_vertex_alpha" mode is enabled, search all vertex colors
                 // If "trans_vertex_alpha" mode is enabled, search all vertex colors

+ 3 - 3
code/Importer/STEPParser/STEPFileReader.cpp

@@ -278,10 +278,10 @@ void STEP::ReadFile(DB& db,const EXPRESS::ConversionSchema& scheme,
         std::transform( type.begin(), type.end(), type.begin(), &Assimp::ToLower<char>  );
         std::transform( type.begin(), type.end(), type.begin(), &Assimp::ToLower<char>  );
         const char* sz = scheme.GetStaticStringForToken(type);
         const char* sz = scheme.GetStaticStringForToken(type);
         if(sz) {
         if(sz) {
-            const std::string::size_type len = n2-n1+1;
-            char* const copysz = new char[len+1];
+            const std::string::size_type szLen = n2-n1+1;
+            char* const copysz = new char[szLen+1];
             std::copy(s.c_str()+n1,s.c_str()+n2+1,copysz);
             std::copy(s.c_str()+n1,s.c_str()+n2+1,copysz);
-            copysz[len] = '\0';
+            copysz[szLen] = '\0';
             db.InternInsert(new LazyObject(db,id,line,sz,copysz));
             db.InternInsert(new LazyObject(db,id,line,sz,copysz));
         }
         }
         if(!has_next) {
         if(!has_next) {

+ 3 - 1
code/ImproveCacheLocality.cpp

@@ -112,7 +112,9 @@ void ImproveCacheLocalityProcess::Execute( aiScene* pScene)
         }
         }
     }
     }
     if (!DefaultLogger::isNullLogger()) {
     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. ");
         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
     // opacity ... either additive or default-blended, please
     if (0.0 != surf.mAdditiveTransparency)  {
     if (0.0 != surf.mAdditiveTransparency)  {
-
         const int add = aiBlendMode_Additive;
         const int add = aiBlendMode_Additive;
         pcMat->AddProperty(&surf.mAdditiveTransparency,1,AI_MATKEY_OPACITY);
         pcMat->AddProperty(&surf.mAdditiveTransparency,1,AI_MATKEY_OPACITY);
         pcMat->AddProperty(&add,1,AI_MATKEY_BLEND_FUNC);
         pcMat->AddProperty(&add,1,AI_MATKEY_BLEND_FUNC);
-    }
-
-    else if (10e10f != surf.mTransparency)  {
+    } else if (10e10f != surf.mTransparency)  {
         const int def = aiBlendMode_Default;
         const int def = aiBlendMode_Default;
         const float f = 1.0f-surf.mTransparency;
         const float f = 1.0f-surf.mTransparency;
         pcMat->AddProperty(&f,1,AI_MATKEY_OPACITY);
         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>
 #include <assimp/Compiler/pushpack1.h>
 
 
-namespace Assimp    {
+namespace Assimp {
 namespace MD3   {
 namespace MD3   {
 
 
 // to make it easier for us, we test the magic word against both "endianesses"
 // 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 = int(57.2957795f * ( std::acos( p_vIn[2] ) ) * ( 255.0f / 360.0f ));
         b &= 0xff;
         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
 #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;
             continue;
 
 
         fill.textures.push_back(SkinData::TextureEntry());
         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;
     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.
 // 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;
     mFile = pFile;
     mScene = pScene;
     mScene = pScene;
     mIOHandler = pIOHandler;
     mIOHandler = pIOHandler;
@@ -730,11 +728,13 @@ void MD3Importer::InternReadFile( const std::string& pFile,
     std::string::size_type s = mFile.find_last_of("/\\");
     std::string::size_type s = mFile.find_last_of("/\\");
     if (s == std::string::npos) {
     if (s == std::string::npos) {
         s = 0;
         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
     // Load multi-part model file, if necessary
     if (configHandleMP) {
     if (configHandleMP) {
@@ -905,15 +905,15 @@ void MD3Importer::InternReadFile( const std::string& pFile,
         // Now search the current shader for a record with this name (
         // Now search the current shader for a record with this name (
         // excluding texture file extension)
         // excluding texture file extension)
         if (!shaders.blocks.empty()) {
         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);
             std::list< Q3Shader::ShaderDataBlock >::const_iterator dit = std::find(shaders.blocks.begin(),shaders.blocks.end(),without_ext);
             if (dit != shaders.blocks.end()) {
             if (dit != shaders.blocks.end()) {
-                // Hurra, wir haben einen. Tolle Sache.
+                // We made it!
                 shader = &*dit;
                 shader = &*dit;
                 ASSIMP_LOG_INFO("Found shader record for " +without_ext );
                 ASSIMP_LOG_INFO("Found shader record for " +without_ext );
             } else {
             } else {
@@ -945,8 +945,7 @@ void MD3Importer::InternReadFile( const std::string& pFile,
             aiString szString;
             aiString szString;
             if (convertedPath.length()) {
             if (convertedPath.length()) {
                 szString.Set(convertedPath);
                 szString.Set(convertedPath);
-            }
-            else    {
+            } else    {
                 ASSIMP_LOG_WARN("Texture file name has zero length. Using default name");
                 ASSIMP_LOG_WARN("Texture file name has zero length. Using default name");
                 szString.Set("dummy_texture.bmp");
                 szString.Set("dummy_texture.bmp");
             }
             }
@@ -955,8 +954,7 @@ void MD3Importer::InternReadFile( const std::string& pFile,
             // prevent transparency by default
             // prevent transparency by default
             int no_alpha = aiTextureFlags_IgnoreAlpha;
             int no_alpha = aiTextureFlags_IgnoreAlpha;
             pcHelper->AddProperty(&no_alpha,1,AI_MATKEY_TEXFLAGS_DIFFUSE(0));
             pcHelper->AddProperty(&no_alpha,1,AI_MATKEY_TEXFLAGS_DIFFUSE(0));
-        }
-        else {
+        } else {
             Q3Shader::ConvertShaderToMaterial(pcHelper,*shader);
             Q3Shader::ConvertShaderToMaterial(pcHelper,*shader);
         }
         }
 
 
@@ -1026,7 +1024,7 @@ void MD3Importer::InternReadFile( const std::string& pFile,
             if (!shader || shader->cull == Q3Shader::CULL_CW) {
             if (!shader || shader->cull == Q3Shader::CULL_CW) {
                 std::swap(pcMesh->mFaces[i].mIndices[2],pcMesh->mFaces[i].mIndices[1]);
                 std::swap(pcMesh->mFaces[i].mIndices[2],pcMesh->mFaces[i].mIndices[1]);
             }
             }
-            pcTriangles++;
+            ++pcTriangles;
         }
         }
 
 
         // Go to the next surface
         // 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");
         throw DeadlyImportError( "MD3: File contains no valid mesh");
+    }
     pScene->mNumMaterials = iNumMaterials;
     pScene->mNumMaterials = iNumMaterials;
 
 
     // Now we need to generate an empty node graph
     // 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];
         pScene->mRootNode->mChildren = new aiNode*[pcHeader->NUM_TAGS];
 
 
         for (unsigned int i = 0; i < pcHeader->NUM_TAGS; ++i, ++pcTags) {
         for (unsigned int i = 0; i < pcHeader->NUM_TAGS; ++i, ++pcTags) {
-
             aiNode* nd = pScene->mRootNode->mChildren[i] = new aiNode();
             aiNode* nd = pScene->mRootNode->mChildren[i] = new aiNode();
             nd->mName.Set((const char*)pcTags->NAME);
             nd->mName.Set((const char*)pcTags->NAME);
             nd->mParent = pScene->mRootNode;
             nd->mParent = pScene->mRootNode;
@@ -1085,8 +1083,12 @@ void MD3Importer::InternReadFile( const std::string& pFile,
         pScene->mRootNode->mMeshes[i] = i;
         pScene->mRootNode->mMeshes[i] = i;
 
 
     // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system
     // 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
 #endif // !! ASSIMP_BUILD_NO_MD3_IMPORTER

+ 9 - 9
code/MD5Loader.cpp

@@ -443,10 +443,10 @@ void MD5Importer::LoadMD5MeshFile ()
         for (MD5::VertexList::const_iterator iter =  meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) {
         for (MD5::VertexList::const_iterator iter =  meshSrc.mVertices.begin();iter != meshSrc.mVertices.end();++iter,++pv) {
             for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w)
             for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w)
             {
             {
-                MD5::WeightDesc& desc = meshSrc.mWeights[w];
+                MD5::WeightDesc& weightDesc = meshSrc.mWeights[w];
                 /* FIX for some invalid exporters */
                 /* FIX for some invalid exporters */
-                if (!(desc.mWeight < AI_MD5_WEIGHT_EPSILON && desc.mWeight >= -AI_MD5_WEIGHT_EPSILON ))
-                    ++piCount[desc.mBone];
+                if (!(weightDesc.mWeight < AI_MD5_WEIGHT_EPSILON && weightDesc.mWeight >= -AI_MD5_WEIGHT_EPSILON ))
+                    ++piCount[weightDesc.mBone];
             }
             }
         }
         }
 
 
@@ -493,20 +493,20 @@ void MD5Importer::LoadMD5MeshFile ()
                     if (w >= meshSrc.mWeights.size())
                     if (w >= meshSrc.mWeights.size())
                         throw DeadlyImportError("MD5MESH: Invalid weight index");
                         throw DeadlyImportError("MD5MESH: Invalid weight index");
 
 
-                    MD5::WeightDesc& desc = meshSrc.mWeights[w];
-                    if ( desc.mWeight < AI_MD5_WEIGHT_EPSILON && desc.mWeight >= -AI_MD5_WEIGHT_EPSILON) {
+                    MD5::WeightDesc& weightDesc = meshSrc.mWeights[w];
+                    if ( weightDesc.mWeight < AI_MD5_WEIGHT_EPSILON && weightDesc.mWeight >= -AI_MD5_WEIGHT_EPSILON) {
                         continue;
                         continue;
                     }
                     }
 
 
-                    const ai_real fNewWeight = desc.mWeight / fSum;
+                    const ai_real fNewWeight = weightDesc.mWeight / fSum;
 
 
                     // transform the local position into worldspace
                     // transform the local position into worldspace
-                    MD5::BoneDesc& boneSrc = meshParser.mJoints[desc.mBone];
-                    const aiVector3D v = boneSrc.mRotationQuatConverted.Rotate (desc.vOffsetPosition);
+                    MD5::BoneDesc& boneSrc = meshParser.mJoints[weightDesc.mBone];
+                    const aiVector3D v = boneSrc.mRotationQuatConverted.Rotate (weightDesc.vOffsetPosition);
 
 
                     // use the original weight to compute the vertex position
                     // use the original weight to compute the vertex position
                     // (some MD5s seem to depend on the invalid weight values ...)
                     // (some MD5s seem to depend on the invalid weight values ...)
-                    *pv += ((boneSrc.mPositionXYZ+v)* (ai_real)desc.mWeight);
+                    *pv += ((boneSrc.mPositionXYZ+v)* (ai_real)weightDesc.mWeight);
 
 
                     aiBone* bone = mesh->mBones[boneSrc.mMap];
                     aiBone* bone = mesh->mBones[boneSrc.mMap];
                     *bone->mWeights++ = aiVertexWeight((unsigned int)(pv-mesh->mVertices),fNewWeight);
                     *bone->mWeights++ = aiVertexWeight((unsigned int)(pv-mesh->mVertices),fNewWeight);

+ 3 - 0
code/ObjFileData.h

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

+ 1 - 1
code/ObjFileImporter.cpp

@@ -442,7 +442,7 @@ void ObjFileImporter::createVertexArray(const ObjFile::Model* pModel,
     // Allocate buffer for texture coordinates
     // Allocate buffer for texture coordinates
     if ( !pModel->m_TextureCoord.empty() && pObjMesh->m_uiUVCoordinates[0] )
     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 ];
         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') {
                 } else if (*m_DataIt == 't') {
                     // read in texture coordinate ( 2D or 3D )
                     // read in texture coordinate ( 2D or 3D )
                     ++m_DataIt;
                     ++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') {
                 } else if (*m_DataIt == 'n') {
                     // Read in normal vector definition
                     // Read in normal vector definition
                     ++m_DataIt;
                     ++m_DataIt;
@@ -296,7 +297,7 @@ size_t ObjFileParser::getNumComponentsInDataDefinition() {
     return numComponents;
     return numComponents;
 }
 }
 
 
-void ObjFileParser::getVector( std::vector<aiVector3D> &point3d_array ) {
+size_t ObjFileParser::getVector( std::vector<aiVector3D> &point3d_array ) {
     size_t numComponents = getNumComponentsInDataDefinition();
     size_t numComponents = getNumComponentsInDataDefinition();
     ai_real x, y, z;
     ai_real x, y, z;
     if( 2 == numComponents ) {
     if( 2 == numComponents ) {
@@ -320,6 +321,7 @@ void ObjFileParser::getVector( std::vector<aiVector3D> &point3d_array ) {
     }
     }
     point3d_array.push_back( aiVector3D( x, y, z ) );
     point3d_array.push_back( aiVector3D( x, y, z ) );
     m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
     m_DataIt = skipLine<DataArrayIt>( m_DataIt, m_DataItEnd, m_uiLine );
+    return numComponents;
 }
 }
 
 
 void ObjFileParser::getVector3( std::vector<aiVector3D> &point3d_array ) {
 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.
     /// Get the number of components in a line.
     size_t getNumComponentsInDataDefinition();
     size_t getNumComponentsInDataDefinition();
     /// Stores the vector
     /// Stores the vector
-    void getVector( std::vector<aiVector3D> &point3d_array );
+    size_t getVector( std::vector<aiVector3D> &point3d_array );
     /// Stores the following 3d vector.
     /// Stores the following 3d vector.
     void getVector3( std::vector<aiVector3D> &point3d_array );
     void getVector3( std::vector<aiVector3D> &point3d_array );
     /// Stores the following homogeneous vector as a 3D vector
     /// 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
 #ifndef ASSIMP_BUILD_NO_TRANSFORMTEXCOORDS_PROCESS
     out.push_back( new TextureTransformStep());
     out.push_back( new TextureTransformStep());
 #endif
 #endif
+#if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS)
+    out.push_back( new ScaleProcess());
+#endif
 #if (!defined ASSIMP_BUILD_NO_PRETRANSFORMVERTICES_PROCESS)
 #if (!defined ASSIMP_BUILD_NO_PRETRANSFORMVERTICES_PROCESS)
     out.push_back( new PretransformVertices());
     out.push_back( new PretransformVertices());
 #endif
 #endif
@@ -208,9 +211,6 @@ void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out)
 #endif
 #endif
 #if (!defined ASSIMP_BUILD_NO_GENFACENORMALS_PROCESS)
 #if (!defined ASSIMP_BUILD_NO_GENFACENORMALS_PROCESS)
     out.push_back( new GenFaceNormalsProcess());
     out.push_back( new GenFaceNormalsProcess());
-#endif
-#if (!defined ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS)
-    out.push_back( new ScaleProcess());
 #endif
 #endif
     // .........................................................................
     // .........................................................................
     // DON'T change the order of these five ..
     // 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) {
     if (bLoadAnimationList) {
         GetAnimationFileList(pFile, pIOHandler, animFileList);
         GetAnimationFileList(pFile, pIOHandler, animFileList);
     }
     }
-    int animCount = animFileList.size() + 1;
+    int animCount = static_cast<int>( animFileList.size() + 1u );
     pScene->mNumAnimations = 1;
     pScene->mNumAnimations = 1;
     pScene->mAnimations = new aiAnimation*[animCount];
     pScene->mAnimations = new aiAnimation*[animCount];
     memset(pScene->mAnimations, 0, sizeof(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->mName.Set(name.c_str());
     }
     }
     anim->mDuration = dLengthOfAnim;
     anim->mDuration = dLengthOfAnim;
-    anim->mNumChannels = asBones.size();
+    anim->mNumChannels = static_cast<unsigned int>( asBones.size() );
     anim->mTicksPerSecond = 25.0; // FIXME: is this correct?
     anim->mTicksPerSecond = 25.0; // FIXME: is this correct?
 
 
     aiNodeAnim** pp = anim->mChannels = new aiNodeAnim*[anim->mNumChannels];
     aiNodeAnim** pp = anim->mChannels = new aiNodeAnim*[anim->mNumChannels];

+ 1 - 1
code/STLExporter.cpp

@@ -127,7 +127,7 @@ STLExporter::STLExporter(const char* _filename, const aiScene* pScene, bool expo
         mOutput.write((char *)&meshnum, 4);
         mOutput.write((char *)&meshnum, 4);
 
 
         if (exportPointClouds) {
         if (exportPointClouds) {
-
+            throw DeadlyExportError("This functionality is not yet implemented for binary output.");
         }
         }
 
 
         for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
         for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) {

+ 19 - 18
code/SortByPTypeProcess.cpp

@@ -228,36 +228,37 @@ void SortByPTypeProcess::Execute( aiScene* pScene) {
 
 
             out->mNumVertices = (3 == real ? numPolyVerts : out->mNumFaces * (real+1));
             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];
             aiVector3D *uv   [AI_MAX_NUMBER_OF_TEXTURECOORDS];
             aiColor4D  *cols [AI_MAX_NUMBER_OF_COLOR_SETS];
             aiColor4D  *cols [AI_MAX_NUMBER_OF_COLOR_SETS];
 
 
-            if (mesh->mVertices)
+            if (mesh->mVertices) {
                 vert = out->mVertices = new aiVector3D[out->mNumVertices];
                 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];
                 tan = out->mTangents   = new aiVector3D[out->mNumVertices];
                 bit = out->mBitangents = 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;
             typedef std::vector< aiVertexWeight > TempBoneInfo;
@@ -323,7 +324,7 @@ void SortByPTypeProcess::Execute( aiScene* pScene) {
                     in.mIndices[q] = outIdx++;
                     in.mIndices[q] = outIdx++;
                 }
                 }
 
 
-                in.mIndices = NULL;
+                in.mIndices = nullptr;
                 ++outFaces;
                 ++outFaces;
             }
             }
             ai_assert(outFaces == out->mFaces + out->mNumFaces);
             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
     // Determine which kinds of primitives the mesh consists of
     aiMesh* out = new aiMesh();
     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->mNumFaces = (unsigned int)positions.size() / numIndices;
     out->mFaces = new aiFace[out->mNumFaces];
     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];
         aiFace& f = out->mFaces[i];
         f.mNumIndices = numIndices;
         f.mNumIndices = numIndices;
         f.mIndices = new unsigned int[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->mNumVertices = (unsigned int)positions.size();
     out->mVertices = new aiVector3D[out->mNumVertices];
     out->mVertices = new aiVector3D[out->mNumVertices];
     ::memcpy(out->mVertices,&positions[0],out->mNumVertices*sizeof(aiVector3D));
     ::memcpy(out->mVertices,&positions[0],out->mNumVertices*sizeof(aiVector3D));
+
     return out;
     return out;
 }
 }
 
 
@@ -466,8 +466,8 @@ void StandardShapes::MakeCone(ai_real height,ai_real radius1,
 
 
     // Need to flip face order?
     // Need to flip face order?
     if ( SIZE_MAX != old )  {
     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])
             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);
                     firstName,i,secondName,size);
             }
             }
             Validate(parray[i]);
             Validate(parray[i]);
@@ -170,8 +170,8 @@ inline void ValidateDSProcess::DoValidationEx(T** parray, unsigned int size,
             {
             {
                 if (parray[i]->mName == parray[a]->mName)
                 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:
             case 0:
                 ReportError("aiMesh::mFaces[%i].mNumIndices is 0",i);
                 ReportError("aiMesh::mFaces[%i].mNumIndices is 0",i);
+                break;
             case 1:
             case 1:
                 if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_POINT))
                 if (0 == (pMesh->mPrimitiveTypes & aiPrimitiveType_POINT))
                 {
                 {
@@ -422,7 +423,9 @@ void ValidateDSProcess::Validate( const aiMesh* pMesh)
         if (!abRefList[i])b = true;
         if (!abRefList[i])b = true;
     }
     }
     abRefList.clear();
     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 ...
     // 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]);
             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.
     // 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");
     // if (!pAnimation->mDuration)this->ReportError("aiAnimation::mDuration is zero");
@@ -577,15 +582,16 @@ void ValidateDSProcess::SearchForInvalidTextures(const aiMaterial* pMaterial,
 
 
     int iNumIndices = 0;
     int iNumIndices = 0;
     int iIndex = -1;
     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);
             iIndex = std::max(iIndex, (int) prop->mIndex);
             ++iNumIndices;
             ++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)   {
     if (iIndex +1 != iNumIndices)   {
@@ -773,8 +779,10 @@ void ValidateDSProcess::Validate( const aiTexture* pTexture)
     }
     }
     if (pTexture->mHeight)
     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
     else
     {
     {
@@ -805,15 +813,15 @@ void ValidateDSProcess::Validate( const aiAnimation* pAnimation,
 {
 {
     Validate(&pNodeAnim->mNodeName);
     Validate(&pNodeAnim->mNodeName);
 
 
-    if (!pNodeAnim->mNumPositionKeys && !pNodeAnim->mScalingKeys && !pNodeAnim->mNumRotationKeys)
+    if (!pNodeAnim->mNumPositionKeys && !pNodeAnim->mScalingKeys && !pNodeAnim->mNumRotationKeys) {
         ReportError("Empty node animation channel");
         ReportError("Empty node animation channel");
-
+    }
     // otherwise check whether one of the keys exceeds the total duration of the animation
     // otherwise check whether one of the keys exceeds the total duration of the animation
     if (pNodeAnim->mNumPositionKeys)
     if (pNodeAnim->mNumPositionKeys)
     {
     {
         if (!pNodeAnim->mPositionKeys)
         if (!pNodeAnim->mPositionKeys)
         {
         {
-            this->ReportError("aiNodeAnim::mPositionKeys is NULL (aiNodeAnim::mNumPositionKeys is %i)",
+        	ReportError("aiNodeAnim::mPositionKeys is NULL (aiNodeAnim::mNumPositionKeys is %i)",
                 pNodeAnim->mNumPositionKeys);
                 pNodeAnim->mNumPositionKeys);
         }
         }
         double dLast = -10e10;
         double dLast = -10e10;
@@ -844,7 +852,7 @@ void ValidateDSProcess::Validate( const aiAnimation* pAnimation,
     {
     {
         if (!pNodeAnim->mRotationKeys)
         if (!pNodeAnim->mRotationKeys)
         {
         {
-            this->ReportError("aiNodeAnim::mRotationKeys is NULL (aiNodeAnim::mNumRotationKeys is %i)",
+            ReportError("aiNodeAnim::mRotationKeys is NULL (aiNodeAnim::mNumRotationKeys is %i)",
                 pNodeAnim->mNumRotationKeys);
                 pNodeAnim->mNumRotationKeys);
         }
         }
         double dLast = -10e10;
         double dLast = -10e10;
@@ -905,19 +913,23 @@ void ValidateDSProcess::Validate( const aiAnimation* pAnimation,
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 void ValidateDSProcess::Validate( const aiNode* pNode)
 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);
     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
     // validate all meshes
     if (pNode->mNumMeshes)
     if (pNode->mNumMeshes)
     {
     {
         if (!pNode->mMeshes)
         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;
         std::vector<bool> abHadMesh;
         abHadMesh.resize(mScene->mNumMeshes,false);
         abHadMesh.resize(mScene->mNumMeshes,false);
@@ -925,13 +937,13 @@ void ValidateDSProcess::Validate( const aiNode* pNode)
         {
         {
             if (pNode->mMeshes[i] >= mScene->mNumMeshes)
             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]])
             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;
             abHadMesh[pNode->mMeshes[i]] = true;
         }
         }
@@ -939,8 +951,8 @@ void ValidateDSProcess::Validate( const aiNode* pNode)
     if (pNode->mNumChildren)
     if (pNode->mNumChildren)
     {
     {
         if (!pNode->mChildren)  {
         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)   {
         for (unsigned int i = 0; i < pNode->mNumChildren;++i)   {
             Validate(pNode->mChildren[i]);
             Validate(pNode->mChildren[i]);
@@ -953,7 +965,7 @@ void ValidateDSProcess::Validate( const aiString* pString)
 {
 {
     if (pString->length > MAXLEN)
     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);
             pString->length,MAXLEN);
     }
     }
     const char* sz = pString->data;
     const char* sz = pString->data;
@@ -961,12 +973,14 @@ void ValidateDSProcess::Validate( const aiString* pString)
     {
     {
         if ('\0' == *sz)
         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");
                 ReportError("aiString::data is invalid: the terminal zero is at a wrong offset");
+            }
             break;
             break;
         }
         }
-        else if (sz >= &pString->data[MAXLEN])
+        else if (sz >= &pString->data[MAXLEN]) {
             ReportError("aiString::data is invalid. There is no terminal character");
             ReportError("aiString::data is invalid. There is no terminal character");
+        }
         ++sz;
         ++sz;
     }
     }
 }
 }

+ 4 - 3
code/glTF2Asset.h

@@ -223,7 +223,8 @@ namespace glTF2
         ComponentType_FLOAT = 5126
         ComponentType_FLOAT = 5126
     };
     };
 
 
-    inline unsigned int ComponentTypeSize(ComponentType t)
+    inline
+    unsigned int ComponentTypeSize(ComponentType t)
     {
     {
         switch (t) {
         switch (t) {
             case ComponentType_SHORT:
             case ComponentType_SHORT:
@@ -250,7 +251,7 @@ namespace glTF2
     };
     };
 
 
     //! Values for the Sampler::magFilter field
     //! Values for the Sampler::magFilter field
-    enum class SamplerMagFilter: unsigned int
+    enum class SamplerMagFilter : unsigned int
     {
     {
         UNSET = 0,
         UNSET = 0,
         SamplerMagFilter_Nearest = 9728,
         SamplerMagFilter_Nearest = 9728,
@@ -258,7 +259,7 @@ namespace glTF2
     };
     };
 
 
     //! Values for the Sampler::minFilter field
     //! Values for the Sampler::minFilter field
-    enum class SamplerMinFilter: unsigned int
+    enum class SamplerMinFilter : unsigned int
     {
     {
         UNSET = 0,
         UNSET = 0,
         SamplerMinFilter_Nearest = 9728,
         SamplerMinFilter_Nearest = 9728,

+ 4 - 4
code/glTFAssetWriter.inl

@@ -294,17 +294,17 @@ namespace glTF {
 							// filling object "compressedData"
 							// filling object "compressedData"
 							json_comp_data.SetObject();
 							json_comp_data.SetObject();
 							json_comp_data.AddMember("buffer", ptr_ext_comp->Buffer, w.mAl);
 							json_comp_data.AddMember("buffer", ptr_ext_comp->Buffer, w.mAl);
-							json_comp_data.AddMember("byteOffset", ptr_ext_comp->Offset, w.mAl);
+							json_comp_data.AddMember("byteOffset", static_cast<uint64_t>(ptr_ext_comp->Offset), w.mAl);
 							json_comp_data.AddMember("componentType", 5121, w.mAl);
 							json_comp_data.AddMember("componentType", 5121, w.mAl);
 							json_comp_data.AddMember("type", "SCALAR", w.mAl);
 							json_comp_data.AddMember("type", "SCALAR", w.mAl);
-							json_comp_data.AddMember("count", ptr_ext_comp->Count, w.mAl);
+							json_comp_data.AddMember("count", static_cast<uint64_t>(ptr_ext_comp->Count), w.mAl);
 							if(ptr_ext_comp->Binary)
 							if(ptr_ext_comp->Binary)
 								json_comp_data.AddMember("mode", "binary", w.mAl);
 								json_comp_data.AddMember("mode", "binary", w.mAl);
 							else
 							else
 								json_comp_data.AddMember("mode", "ascii", w.mAl);
 								json_comp_data.AddMember("mode", "ascii", w.mAl);
 
 
-							json_comp_data.AddMember("indicesCount", ptr_ext_comp->IndicesCount, w.mAl);
-							json_comp_data.AddMember("verticesCount", ptr_ext_comp->VerticesCount, w.mAl);
+							json_comp_data.AddMember("indicesCount", static_cast<uint64_t>(ptr_ext_comp->IndicesCount), w.mAl);
+							json_comp_data.AddMember("verticesCount", static_cast<uint64_t>(ptr_ext_comp->VerticesCount), w.mAl);
 							// filling object "Open3DGC-compression"
 							// filling object "Open3DGC-compression"
 							Value json_o3dgc;
 							Value json_o3dgc;
 
 

+ 1 - 1
code/glTFExporter.cpp

@@ -245,7 +245,7 @@ inline Ref<Accessor> ExportData(Asset& a, std::string& meshName, Ref<Buffer>& bu
 
 
 namespace {
 namespace {
     void GetMatScalar(const aiMaterial* mat, float& val, const char* propName, int type, int idx) {
     void GetMatScalar(const aiMaterial* mat, float& val, const char* propName, int type, int idx) {
-        if (mat->Get(propName, type, idx, val) == AI_SUCCESS) {}
+        ai_assert(mat->Get(propName, type, idx, val) == AI_SUCCESS);
     }
     }
 }
 }
 
 

+ 4 - 1
contrib/irrXML/CMakeLists.txt

@@ -13,10 +13,13 @@ if ( MSVC )
   ADD_DEFINITIONS( -D_CRT_SECURE_NO_WARNINGS )
   ADD_DEFINITIONS( -D_CRT_SECURE_NO_WARNINGS )
 endif ( MSVC )
 endif ( MSVC )
 
 
-add_library(IrrXML STATIC ${IrrXML_SRCS})
+add_library(IrrXML ${IrrXML_SRCS})
 set(IRRXML_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" CACHE INTERNAL "IrrXML_Include" )
 set(IRRXML_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" CACHE INTERNAL "IrrXML_Include" )
 set(IRRXML_LIBRARY "IrrXML" CACHE INTERNAL "IrrXML" )
 set(IRRXML_LIBRARY "IrrXML" CACHE INTERNAL "IrrXML" )
 
 
 install(TARGETS IrrXML
 install(TARGETS IrrXML
+  LIBRARY DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
   ARCHIVE DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
   ARCHIVE DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
+  RUNTIME DESTINATION ${ASSIMP_BIN_INSTALL_DIR}
+  FRAMEWORK DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
   COMPONENT ${LIBASSIMP_COMPONENT})
   COMPONENT ${LIBASSIMP_COMPONENT})

+ 9 - 4
contrib/irrXML/CXMLReaderImpl.h

@@ -10,8 +10,11 @@
 #include "irrArray.h"
 #include "irrArray.h"
 
 
 #include <cassert>
 #include <cassert>
+#include <stdlib.h>    
+#include <cctype>
+#include <cstdint>
+//using namespace Assimp;
 
 
-using namespace Assimp;
 
 
 #ifdef _DEBUG
 #ifdef _DEBUG
 #define IRR_DEBUGPRINT(x) printf((x));
 #define IRR_DEBUGPRINT(x) printf((x));
@@ -162,7 +165,8 @@ public:
 			return 0;
 			return 0;
 
 
 		core::stringc c = attr->Value.c_str();
 		core::stringc c = attr->Value.c_str();
-		return fast_atof(c.c_str());
+        return static_cast<float>(atof(c.c_str()));
+        //return fast_atof(c.c_str());
 	}
 	}
 
 
 
 
@@ -174,7 +178,8 @@ public:
 			return 0;
 			return 0;
 
 
 		core::stringc c = attrvalue;
 		core::stringc c = attrvalue;
-		return fast_atof(c.c_str());
+        return static_cast<float>(atof(c.c_str()));
+		//return fast_atof(c.c_str());
 	}
 	}
 
 
 
 
@@ -428,7 +433,7 @@ private:
 			++P;
 			++P;
 
 
     // remove trailing whitespace, if any
     // remove trailing whitespace, if any
-    while( isspace( P[-1]))
+    while( std::isspace( P[-1]))
       --P;
       --P;
 
 
 		NodeName = core::string<char_type>(pBeginClose, (int)(P - pBeginClose));
 		NodeName = core::string<char_type>(pBeginClose, (int)(P - pBeginClose));

+ 2 - 2
contrib/irrXML/irrXML.cpp

@@ -9,7 +9,7 @@
 #include "irrXML.h"
 #include "irrXML.h"
 #include "irrString.h"
 #include "irrString.h"
 #include "irrArray.h"
 #include "irrArray.h"
-#include <assimp/fast_atof.h>
+//#include <assimp/fast_atof.h>
 #include "CXMLReaderImpl.h"
 #include "CXMLReaderImpl.h"
 
 
 namespace irr
 namespace irr
@@ -18,7 +18,7 @@ namespace io
 {
 {
 
 
 //! Implementation of the file read callback for ordinary files
 //! Implementation of the file read callback for ordinary files
-class CFileReadCallBack : public IFileReadCallBack
+class IRRXML_API CFileReadCallBack : public IFileReadCallBack
 {
 {
 public:
 public:
 
 

+ 15 - 9
contrib/irrXML/irrXML.h

@@ -7,6 +7,12 @@
 
 
 #include <stdio.h>
 #include <stdio.h>
 
 
+#ifdef _WIN32
+#   define IRRXML_API __declspec(dllexport)
+#else
+#   define IRRXML_API __attribute__ ((visibility("default")))
+#endif // _WIN32
+
 /** \mainpage irrXML 1.2 API documentation
 /** \mainpage irrXML 1.2 API documentation
  <div align="center"><img src="logobig.png" ></div>
  <div align="center"><img src="logobig.png" ></div>
 
 
@@ -409,7 +415,7 @@ namespace io
 	\return Returns a pointer to the created xml parser. This pointer should be 
 	\return Returns a pointer to the created xml parser. This pointer should be 
 	deleted using 'delete' after no longer needed. Returns 0 if an error occured
 	deleted using 'delete' after no longer needed. Returns 0 if an error occured
 	and the file could not be opened. */
 	and the file could not be opened. */
-	IrrXMLReader* createIrrXMLReader(const char* filename);
+    IRRXML_API IrrXMLReader* createIrrXMLReader(const char* filename);
 
 
 	//! Creates an instance of an UFT-8 or ASCII character xml parser.
 	//! Creates an instance of an UFT-8 or ASCII character xml parser.
 	/** This means that all character data will be returned in 8 bit ASCII or UTF-8. The file to read can 
 	/** This means that all character data will be returned in 8 bit ASCII or UTF-8. The file to read can 
@@ -421,7 +427,7 @@ namespace io
 	\return Returns a pointer to the created xml parser. This pointer should be 
 	\return Returns a pointer to the created xml parser. This pointer should be 
 	deleted using 'delete' after no longer needed. Returns 0 if an error occured
 	deleted using 'delete' after no longer needed. Returns 0 if an error occured
 	and the file could not be opened. */
 	and the file could not be opened. */
-	IrrXMLReader* createIrrXMLReader(FILE* file);
+    IRRXML_API IrrXMLReader* createIrrXMLReader(FILE* file);
 
 
 	//! Creates an instance of an UFT-8 or ASCII character xml parser. 
 	//! Creates an instance of an UFT-8 or ASCII character xml parser. 
 	/** This means that all character data will be returned in 8 bit ASCII or UTF-8. The file to read can 
 	/** This means that all character data will be returned in 8 bit ASCII or UTF-8. The file to read can 
@@ -434,7 +440,7 @@ namespace io
 	 \return Returns a pointer to the created xml parser. This pointer should be 
 	 \return Returns a pointer to the created xml parser. This pointer should be 
 	 deleted using 'delete' after no longer needed. Returns 0 if an error occured
 	 deleted using 'delete' after no longer needed. Returns 0 if an error occured
 	 and the file could not be opened. */
 	 and the file could not be opened. */
-	IrrXMLReader* createIrrXMLReader(IFileReadCallBack* callback);
+    IRRXML_API IrrXMLReader* createIrrXMLReader(IFileReadCallBack* callback);
 
 
 	//! Creates an instance of an UFT-16 xml parser. 
 	//! Creates an instance of an UFT-16 xml parser. 
 	/** This means that
 	/** This means that
@@ -446,7 +452,7 @@ namespace io
 	\return Returns a pointer to the created xml parser. This pointer should be 
 	\return Returns a pointer to the created xml parser. This pointer should be 
 	deleted using 'delete' after no longer needed. Returns 0 if an error occured
 	deleted using 'delete' after no longer needed. Returns 0 if an error occured
 	and the file could not be opened. */
 	and the file could not be opened. */
-	IrrXMLReaderUTF16* createIrrXMLReaderUTF16(const char* filename);
+    IRRXML_API IrrXMLReaderUTF16* createIrrXMLReaderUTF16(const char* filename);
 
 
 	//! Creates an instance of an UFT-16 xml parser. 
 	//! Creates an instance of an UFT-16 xml parser. 
 	/** This means that all character data will be returned in UTF-16. The file to read can 
 	/** This means that all character data will be returned in UTF-16. The file to read can 
@@ -458,7 +464,7 @@ namespace io
 	\return Returns a pointer to the created xml parser. This pointer should be 
 	\return Returns a pointer to the created xml parser. This pointer should be 
 	deleted using 'delete' after no longer needed. Returns 0 if an error occured
 	deleted using 'delete' after no longer needed. Returns 0 if an error occured
 	and the file could not be opened. */
 	and the file could not be opened. */
-	IrrXMLReaderUTF16* createIrrXMLReaderUTF16(FILE* file);
+    IRRXML_API IrrXMLReaderUTF16* createIrrXMLReaderUTF16(FILE* file);
 
 
 	//! Creates an instance of an UFT-16 xml parser. 
 	//! Creates an instance of an UFT-16 xml parser. 
 	/** This means that all character data will be returned in UTF-16. The file to read can 
 	/** This means that all character data will be returned in UTF-16. The file to read can 
@@ -471,7 +477,7 @@ namespace io
 	\return Returns a pointer to the created xml parser. This pointer should be 
 	\return Returns a pointer to the created xml parser. This pointer should be 
 	deleted using 'delete' after no longer needed. Returns 0 if an error occured
 	deleted using 'delete' after no longer needed. Returns 0 if an error occured
 	and the file could not be opened. */
 	and the file could not be opened. */
-	IrrXMLReaderUTF16* createIrrXMLReaderUTF16(IFileReadCallBack* callback);
+    IRRXML_API IrrXMLReaderUTF16* createIrrXMLReaderUTF16(IFileReadCallBack* callback);
 
 
 
 
 	//! Creates an instance of an UFT-32 xml parser. 
 	//! Creates an instance of an UFT-32 xml parser. 
@@ -483,7 +489,7 @@ namespace io
 	\return Returns a pointer to the created xml parser. This pointer should be 
 	\return Returns a pointer to the created xml parser. This pointer should be 
 	deleted using 'delete' after no longer needed. Returns 0 if an error occured
 	deleted using 'delete' after no longer needed. Returns 0 if an error occured
 	and the file could not be opened. */
 	and the file could not be opened. */
-	IrrXMLReaderUTF32* createIrrXMLReaderUTF32(const char* filename);
+    IRRXML_API IrrXMLReaderUTF32* createIrrXMLReaderUTF32(const char* filename);
 
 
 	//! Creates an instance of an UFT-32 xml parser. 
 	//! Creates an instance of an UFT-32 xml parser. 
 	/** This means that all character data will be returned in UTF-32. The file to read can 
 	/** This means that all character data will be returned in UTF-32. The file to read can 
@@ -495,7 +501,7 @@ namespace io
 	\return Returns a pointer to the created xml parser. This pointer should be 
 	\return Returns a pointer to the created xml parser. This pointer should be 
 	deleted using 'delete' after no longer needed. Returns 0 if an error occured
 	deleted using 'delete' after no longer needed. Returns 0 if an error occured
 	and the file could not be opened. */
 	and the file could not be opened. */
-	IrrXMLReaderUTF32* createIrrXMLReaderUTF32(FILE* file);
+    IRRXML_API IrrXMLReaderUTF32* createIrrXMLReaderUTF32(FILE* file);
 
 
 	//! Creates an instance of an UFT-32 xml parser. 
 	//! Creates an instance of an UFT-32 xml parser. 
 	/** This means that
 	/** This means that
@@ -509,7 +515,7 @@ namespace io
 	\return Returns a pointer to the created xml parser. This pointer should be 
 	\return Returns a pointer to the created xml parser. This pointer should be 
 	deleted using 'delete' after no longer needed. Returns 0 if an error occured
 	deleted using 'delete' after no longer needed. Returns 0 if an error occured
 	and the file could not be opened. */
 	and the file could not be opened. */
-	IrrXMLReaderUTF32* createIrrXMLReaderUTF32(IFileReadCallBack* callback);
+    IRRXML_API IrrXMLReaderUTF32* createIrrXMLReaderUTF32(IFileReadCallBack* callback);
 	
 	
 
 
 	/*! \file irrxml.h
 	/*! \file irrxml.h

+ 18 - 0
contrib/zip/.gitignore

@@ -36,3 +36,21 @@
 # Temporary
 # Temporary
 *.swp
 *.swp
 .DS_Store
 .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
 language: c
+addons:
+  apt:
+    packages: &1
+    - lcov
 # Compiler selection
 # Compiler selection
 compiler:
 compiler:
   - clang
   - clang
   - gcc
   - gcc
+env:
+  - ANALYZE=false
+  - ANALYZE=true
 # Build steps
 # Build steps
 script:
 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)
 cmake_minimum_required(VERSION 2.8)
 project(zip)
 project(zip)
+enable_language(C)
+set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
 
 
 if (MSVC)
 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)
 endif (MSVC)
 
 
 # zip
 # zip
 set(SRC src/miniz.h src/zip.h src/zip.c)
 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
 # 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
 ### 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.
 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
 # The Idea
 <img src="zip.png" name="zip" />
 <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.
 * Create a new zip archive with default compression level.
 ```c
 ```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.
 * Append to the existing zip archive.
 ```c
 ```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.
 * Extract a zip archive into a folder.
 ```c
 ```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
 ```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
 ```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
 ```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:
 build_script:
 - cmd: >-
 - cmd: >-
     cd c:\projects\zip
     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)
+

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 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
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  * OTHER DEALINGS IN THE SOFTWARE.
  * OTHER DEALINGS IN THE SOFTWARE.
  */
  */
-
-#include "zip.h"
-#include "miniz.h"
+#define __STDC_WANT_LIB_EXT1__ 1
 
 
 #include <errno.h>
 #include <errno.h>
 #include <sys/stat.h>
 #include <sys/stat.h>
 #include <time.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>
 #include <direct.h>
 
 
 #define MKDIR(DIRNAME) _mkdir(DIRNAME)
 #define MKDIR(DIRNAME) _mkdir(DIRNAME)
 #define STRCLONE(STR) ((STR) ? _strdup(STR) : NULL)
 #define STRCLONE(STR) ((STR) ? _strdup(STR) : NULL)
 #define HAS_DEVICE(P)                                                          \
 #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 FILESYSTEM_PREFIX_LEN(P) (HAS_DEVICE(P) ? 2 : 0)
 #define ISSLASH(C) ((C) == '/' || (C) == '\\')
 #define ISSLASH(C) ((C) == '/' || (C) == '\\')
 
 
 #else
 #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 MKDIR(DIRNAME) mkdir(DIRNAME, 0755)
 #define STRCLONE(STR) ((STR) ? strdup(STR) : NULL)
 #define STRCLONE(STR) ((STR) ? strdup(STR) : NULL)
+
+#endif
+
+#include "miniz.h"
+#include "zip.h"
+
+#ifndef HAS_DEVICE
+#define HAS_DEVICE(P) 0
 #endif
 #endif
 
 
 #ifndef FILESYSTEM_PREFIX_LEN
 #ifndef FILESYSTEM_PREFIX_LEN
@@ -40,601 +51,876 @@
 #define ISSLASH(C) ((C) == '/')
 #define ISSLASH(C) ((C) == '/')
 #endif
 #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) {
 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 {
 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 {
 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_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:
-    CLEANUP(zip);
-    return NULL;
+  CLEANUP(zip);
+  return NULL;
 }
 }
 
 
 void zip_close(struct zip_t *zip) {
 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) {
 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) {
 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 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) {
 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,
 int zip_entry_extract(struct zip_t *zip,
                       size_t (*on_extract)(void *arg, unsigned long long offset,
                       size_t (*on_extract)(void *arg, unsigned long long offset,
                                            const void *buf, size_t bufsize),
                                            const void *buf, size_t bufsize),
                       void *arg) {
                       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 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
 #else
-        path[dirlen] = '/';
+  strcpy(path, dir);
 #endif
 #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:
 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
 #define ZIP_H
 
 
 #include <string.h>
 #include <string.h>
+#include <sys/types.h>
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 extern "C" {
 extern "C" {
 #endif
 #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
 #ifndef MAX_PATH
 #define MAX_PATH 32767 /* # chars in a path name including NULL */
 #define MAX_PATH 32767 /* # chars in a path name including NULL */
 #endif
 #endif
@@ -47,7 +60,7 @@ struct zip_t;
 extern struct zip_t *zip_open(const char *zipname, int level, char mode);
 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:
   Args:
     zip: zip archive handler.
     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);
 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:
   Args:
     zip: zip archive handler.
     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);
 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.
   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);
 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.
   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.
     - for large entries, please take a look at zip_entry_extract function.
 
 
   Returns:
   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.
   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);
 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.
     zip: zip archive handler.
     on_extract: callback function.
     on_extract: callback function.
     arg: opaque pointer (optional argument,
     arg: opaque pointer (optional argument,
@@ -144,12 +256,23 @@ extern int zip_entry_fread(struct zip_t *zip, const char *filename);
    Returns:
    Returns:
     The return code - 0 on success, negative number (< 0) on error.
     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.
   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)
 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
 # test
 include_directories(../src)
 include_directories(../src)
 add_executable(test.exe test.c ../src/zip.c)
 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 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 <stdio.h>
 #include <stdlib.h>
 #include <stdlib.h>
 #include <string.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 TESTDATA1 "Some test data 1...\0"
+#define CRC32DATA1 2220805626
 #define TESTDATA2 "Some test data 2...\0"
 #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) {
 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) {
 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) {
 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 {
 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,
 static size_t on_extract(void *arg, unsigned long long offset, const void *data,
                          size_t size) {
                          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) {
 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));
     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[]) {
 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
 @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.
+
 */
 */
 
 
 
 

+ 10 - 6
include/assimp/material.h

@@ -198,8 +198,6 @@ enum aiTextureType
      */
      */
     aiTextureType_NONE = 0x0,
     aiTextureType_NONE = 0x0,
 
 
-
-
     /** The texture is combined with the result of the diffuse
     /** The texture is combined with the result of the diffuse
      *  lighting equation.
      *  lighting equation.
      */
      */
@@ -278,7 +276,7 @@ enum aiTextureType
      *
      *
      *  A texture reference that does not match any of the definitions
      *  A texture reference that does not match any of the definitions
      *  above is considered to be 'unknown'. It is still imported,
      *  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,
     aiTextureType_UNKNOWN = 0xC,
 
 
@@ -375,7 +373,7 @@ enum aiShadingMode
 */
 */
 enum aiTextureFlags
 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,
     aiTextureFlags_Invert = 0x1,
 
 
@@ -902,6 +900,7 @@ extern "C" {
 #define AI_MATKEY_ENABLE_WIREFRAME "$mat.wireframe",0,0
 #define AI_MATKEY_ENABLE_WIREFRAME "$mat.wireframe",0,0
 #define AI_MATKEY_BLEND_FUNC "$mat.blend",0,0
 #define AI_MATKEY_BLEND_FUNC "$mat.blend",0,0
 #define AI_MATKEY_OPACITY "$mat.opacity",0,0
 #define AI_MATKEY_OPACITY "$mat.opacity",0,0
+#define AI_MATKEY_TRANSPARENCYFACTOR "$mat.transparencyfactor",0,0
 #define AI_MATKEY_BUMPSCALING "$mat.bumpscaling",0,0
 #define AI_MATKEY_BUMPSCALING "$mat.bumpscaling",0,0
 #define AI_MATKEY_SHININESS "$mat.shininess",0,0
 #define AI_MATKEY_SHININESS "$mat.shininess",0,0
 #define AI_MATKEY_REFLECTIVITY "$mat.reflectivity",0,0
 #define AI_MATKEY_REFLECTIVITY "$mat.reflectivity",0,0
@@ -914,6 +913,13 @@ extern "C" {
 #define AI_MATKEY_COLOR_TRANSPARENT "$clr.transparent",0,0
 #define AI_MATKEY_COLOR_TRANSPARENT "$clr.transparent",0,0
 #define AI_MATKEY_COLOR_REFLECTIVE "$clr.reflective",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_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
 // Pure key names for all texture-related properties
@@ -1457,8 +1463,6 @@ inline aiReturn aiGetMaterialInteger(const C_STRUCT aiMaterial* pMat,
 
 
 #endif //!__cplusplus
 #endif //!__cplusplus
 
 
-
-
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** @brief Retrieve a color value from the material property table
 /** @brief Retrieve a color value from the material property table
 *
 *

+ 545 - 547
port/PyAssimp/pyassimp/core.py

@@ -1,547 +1,545 @@
-"""
-PyAssimp
-
-This is the main-module of PyAssimp.
-"""
-
-import sys
-if sys.version_info < (2,6):
-    raise 'pyassimp: need python 2.6 or newer'
-
-# xrange was renamed range in Python 3 and the original range from Python 2 was removed.
-# To keep compatibility with both Python 2 and 3, xrange is set to range for version 3.0 and up.
-if sys.version_info >= (3,0):
-    xrange = range
-
-import ctypes
-import os
-
-try: import numpy
-except: numpy = None
-
-import logging
-logger = logging.getLogger("pyassimp")
-# attach default null handler to logger so it doesn't complain
-# even if you don't attach another handler to logger
-logger.addHandler(logging.NullHandler())
-
-from . import structs
-from . import helper
-from . import postprocess
-from .errors import AssimpError
-
-class AssimpLib(object):
-    """
-    Assimp-Singleton
-    """
-    load, load_mem, export, export_blob, release, dll = helper.search_library()
-_assimp_lib = AssimpLib()
-
-def make_tuple(ai_obj, type = None):
-    res = None
-
-    #notes:
-    # ai_obj._fields_ = [ ("attr", c_type), ... ]
-    # getattr(ai_obj, e[0]).__class__ == float
-
-    if isinstance(ai_obj, structs.Matrix4x4):
-        if numpy:
-            res = numpy.array([getattr(ai_obj, e[0]) for e in ai_obj._fields_]).reshape((4,4))
-            #import pdb;pdb.set_trace()
-        else:
-            res = [getattr(ai_obj, e[0]) for e in ai_obj._fields_]
-            res = [res[i:i+4] for i in xrange(0,16,4)]
-    elif isinstance(ai_obj, structs.Matrix3x3):
-        if numpy:
-            res = numpy.array([getattr(ai_obj, e[0]) for e in ai_obj._fields_]).reshape((3,3))
-        else:
-            res = [getattr(ai_obj, e[0]) for e in ai_obj._fields_]
-            res = [res[i:i+3] for i in xrange(0,9,3)]
-    else:
-        if numpy:
-            res = numpy.array([getattr(ai_obj, e[0]) for e in ai_obj._fields_])
-        else:
-            res = [getattr(ai_obj, e[0]) for e in ai_obj._fields_]
-
-    return res
-
-# Returns unicode object for Python 2, and str object for Python 3.
-def _convert_assimp_string(assimp_string):
-    try:
-        return unicode(assimp_string.data, errors='ignore')
-    except:
-        return str(assimp_string.data, errors='ignore')
-
-# It is faster and more correct to have an init function for each assimp class
-def _init_face(aiFace):
-    aiFace.indices = [aiFace.mIndices[i] for i in range(aiFace.mNumIndices)]
-assimp_struct_inits =  { structs.Face : _init_face }
-
-def call_init(obj, caller = None):
-    if helper.hasattr_silent(obj,'contents'): #pointer
-        _init(obj.contents, obj, caller)
-    else:
-        _init(obj,parent=caller)
-
-def _is_init_type(obj):
-    if helper.hasattr_silent(obj,'contents'): #pointer
-        return _is_init_type(obj[0])
-    # null-pointer case that arises when we reach a mesh attribute
-    # like mBitangents which use mNumVertices rather than mNumBitangents
-    # so it breaks the 'is iterable' check.
-    # Basically:
-    # FIXME!
-    elif not bool(obj):
-        return False
-    tname = obj.__class__.__name__
-    return not (tname[:2] == 'c_' or tname == 'Structure' \
-            or tname == 'POINTER') and not isinstance(obj,int)
-
-def _init(self, target = None, parent = None):
-    """
-    Custom initialize() for C structs, adds safely accessible member functionality.
-
-    :param target: set the object which receive the added methods. Useful when manipulating
-    pointers, to skip the intermediate 'contents' deferencing.
-    """
-    if not target:
-        target = self
-
-    dirself = dir(self)
-    for m in dirself:
-
-        if m.startswith("_"):
-            continue
-
-        if m.startswith('mNum'):
-            if 'm' + m[4:] in dirself:
-                continue # will be processed later on
-            else:
-                name = m[1:].lower()
-
-                obj = getattr(self, m)
-                setattr(target, name, obj)
-                continue
-
-        if m == 'mName':
-            target.name = str(_convert_assimp_string(self.mName))
-            target.__class__.__repr__ = lambda x: str(x.__class__) + "(" + getattr(x, 'name','') + ")"
-            target.__class__.__str__ = lambda x: getattr(x, 'name', '')
-            continue
-
-        name = m[1:].lower()
-
-        obj = getattr(self, m)
-
-        # Create tuples
-        if isinstance(obj, structs.assimp_structs_as_tuple):
-            setattr(target, name, make_tuple(obj))
-            logger.debug(str(self) + ": Added array " + str(getattr(target, name)) +  " as self." + name.lower())
-            continue
-
-        if m.startswith('m'):
-
-            if name == "parent":
-                setattr(target, name, parent)
-                logger.debug("Added a parent as self." + name)
-                continue
-
-            if helper.hasattr_silent(self, 'mNum' + m[1:]):
-
-                length =  getattr(self, 'mNum' + m[1:])
-
-                # -> special case: properties are
-                # stored as a dict.
-                if m == 'mProperties':
-                    setattr(target, name, _get_properties(obj, length))
-                    continue
-
-
-                if not length: # empty!
-                    setattr(target, name, [])
-                    logger.debug(str(self) + ": " + name + " is an empty list.")
-                    continue
-
-
-                try:
-                    if obj._type_ in structs.assimp_structs_as_tuple:
-                        if numpy:
-                            setattr(target, name, numpy.array([make_tuple(obj[i]) for i in range(length)], dtype=numpy.float32))
-
-                            logger.debug(str(self) + ": Added an array of numpy arrays (type "+ str(type(obj)) + ") as self." + name)
-                        else:
-                            setattr(target, name, [make_tuple(obj[i]) for i in range(length)])
-
-                            logger.debug(str(self) + ": Added a list of lists (type "+ str(type(obj)) + ") as self." + name)
-
-                    else:
-                        setattr(target, name, [obj[i] for i in range(length)]) #TODO: maybe not necessary to recreate an array?
-
-                        logger.debug(str(self) + ": Added list of " + str(obj) + " " + name + " as self." + name + " (type: " + str(type(obj)) + ")")
-
-                        # initialize array elements
-                        try:
-                            init = assimp_struct_inits[type(obj[0])]
-                        except KeyError:
-                            if _is_init_type(obj[0]):
-                                for e in getattr(target, name):
-                                    call_init(e, target)
-                        else:
-                            for e in getattr(target, name):
-                                init(e)
-
-
-                except IndexError:
-                    logger.error("in " + str(self) +" : mismatch between mNum" + name + " and the actual amount of data in m" + name + ". This may be due to version mismatch between libassimp and pyassimp. Quitting now.")
-                    sys.exit(1)
-
-                except ValueError as e:
-
-                    logger.error("In " + str(self) +  "->" + name + ": " + str(e) + ". Quitting now.")
-                    if "setting an array element with a sequence" in str(e):
-                        logger.error("Note that pyassimp does not currently "
-                                     "support meshes with mixed triangles "
-                                     "and quads. Try to load your mesh with"
-                                     " a post-processing to triangulate your"
-                                     " faces.")
-                    raise e
-
-
-
-            else: # starts with 'm' but not iterable
-                setattr(target, name, obj)
-                logger.debug("Added " + name + " as self." + name + " (type: " + str(type(obj)) + ")")
-
-                if _is_init_type(obj):
-                    call_init(obj, target)
-
-    if isinstance(self, structs.Mesh):
-        _finalize_mesh(self, target)
-
-    if isinstance(self, structs.Texture):
-        _finalize_texture(self, target)
-
-    if isinstance(self, structs.Metadata):
-        _finalize_metadata(self, target)
-
-
-    return self
-
-
-def pythonize_assimp(type, obj, scene):
-    """ This method modify the Assimp data structures
-    to make them easier to work with in Python.
-
-    Supported operations:
-     - MESH: replace a list of mesh IDs by reference to these meshes
-     - ADDTRANSFORMATION: add a reference to an object's transformation taken from their associated node.
-
-    :param type: the type of modification to operate (cf above)
-    :param obj: the input object to modify
-    :param scene: a reference to the whole scene
-    """
-
-    if type == "MESH":
-        meshes = []
-        for i in obj:
-            meshes.append(scene.meshes[i])
-        return meshes
-
-    if type == "ADDTRANSFORMATION":
-        def getnode(node, name):
-            if node.name == name: return node
-            for child in node.children:
-                n = getnode(child, name)
-                if n: return n
-
-        node = getnode(scene.rootnode, obj.name)
-        if not node:
-            raise AssimpError("Object " + str(obj) + " has no associated node!")
-        setattr(obj, "transformation", node.transformation)
-
-def recur_pythonize(node, scene):
-    '''
-    Recursively call pythonize_assimp on
-    nodes tree to apply several post-processing to
-    pythonize the assimp datastructures.
-    '''
-    node.meshes = pythonize_assimp("MESH", node.meshes, scene)
-    for mesh in node.meshes:
-        mesh.material = scene.materials[mesh.materialindex]
-    for cam in scene.cameras:
-        pythonize_assimp("ADDTRANSFORMATION", cam, scene)
-    for c in node.children:
-        recur_pythonize(c, scene)
-
-def load(filename,
-         file_type  = None,
-         processing = postprocess.aiProcess_Triangulate):
-    '''
-    Load a model into a scene. On failure throws AssimpError.
-
-    Arguments
-    ---------
-    filename:   Either a filename or a file object to load model from.
-                If a file object is passed, file_type MUST be specified
-                Otherwise Assimp has no idea which importer to use.
-                This is named 'filename' so as to not break legacy code.
-    processing: assimp postprocessing parameters. Verbose keywords are imported
-                from postprocessing, and the parameters can be combined bitwise to
-                generate the final processing value. Note that the default value will
-                triangulate quad faces. Example of generating other possible values:
-                processing = (pyassimp.postprocess.aiProcess_Triangulate |
-                              pyassimp.postprocess.aiProcess_OptimizeMeshes)
-    file_type:  string of file extension, such as 'stl'
-
-    Returns
-    ---------
-    Scene object with model data
-    '''
-
-    if hasattr(filename, 'read'):
-        # This is the case where a file object has been passed to load.
-        # It is calling the following function:
-        # const aiScene* aiImportFileFromMemory(const char* pBuffer,
-        #                                      unsigned int pLength,
-        #                                      unsigned int pFlags,
-        #                                      const char* pHint)
-        if file_type == None:
-            raise AssimpError('File type must be specified when passing file objects!')
-        data  = filename.read()
-        model = _assimp_lib.load_mem(data,
-                                     len(data),
-                                     processing,
-                                     file_type)
-    else:
-        # a filename string has been passed
-        model = _assimp_lib.load(filename.encode(sys.getfilesystemencoding()), processing)
-
-    if not model:
-        raise AssimpError('Could not import file!')
-    scene = _init(model.contents)
-    recur_pythonize(scene.rootnode, scene)
-    return scene
-
-def export(scene,
-           filename,
-           file_type  = None,
-           processing = postprocess.aiProcess_Triangulate):
-    '''
-    Export a scene. On failure throws AssimpError.
-
-    Arguments
-    ---------
-    scene: scene to export.
-    filename: Filename that the scene should be exported to.
-    file_type: string of file exporter to use. For example "collada".
-    processing: assimp postprocessing parameters. Verbose keywords are imported
-                from postprocessing, and the parameters can be combined bitwise to
-                generate the final processing value. Note that the default value will
-                triangulate quad faces. Example of generating other possible values:
-                processing = (pyassimp.postprocess.aiProcess_Triangulate |
-                              pyassimp.postprocess.aiProcess_OptimizeMeshes)
-
-    '''
-
-    from ctypes import pointer
-    exportStatus = _assimp_lib.export(pointer(scene), file_type.encode("ascii"), filename.encode(sys.getfilesystemencoding()), processing)
-
-    if exportStatus != 0:
-        raise AssimpError('Could not export scene!')
-
-def export_blob(scene,
-                file_type = None,
-                processing = postprocess.aiProcess_Triangulate):
-    '''
-    Export a scene and return a blob in the correct format. On failure throws AssimpError.
-
-    Arguments
-    ---------
-    scene: scene to export.
-    file_type: string of file exporter to use. For example "collada".
-    processing: assimp postprocessing parameters. Verbose keywords are imported
-                from postprocessing, and the parameters can be combined bitwise to
-                generate the final processing value. Note that the default value will
-                triangulate quad faces. Example of generating other possible values:
-                processing = (pyassimp.postprocess.aiProcess_Triangulate |
-                              pyassimp.postprocess.aiProcess_OptimizeMeshes)
-    Returns
-    ---------
-    Pointer to structs.ExportDataBlob
-    '''
-    from ctypes import pointer
-    exportBlobPtr = _assimp_lib.export_blob(pointer(scene), file_type.encode("ascii"), processing)
-
-    if exportBlobPtr == 0:
-        raise AssimpError('Could not export scene to blob!')
-    return exportBlobPtr
-
-def release(scene):
-    from ctypes import pointer
-    _assimp_lib.release(pointer(scene))
-
-def _finalize_texture(tex, target):
-    setattr(target, "achformathint", tex.achFormatHint)
-    if numpy:
-        data = numpy.array([make_tuple(getattr(tex, "pcData")[i]) for i in range(tex.mWidth * tex.mHeight)])
-    else:
-        data = [make_tuple(getattr(tex, "pcData")[i]) for i in range(tex.mWidth * tex.mHeight)]
-    setattr(target, "data", data)
-
-def _finalize_mesh(mesh, target):
-    """ Building of meshes is a bit specific.
-
-    We override here the various datasets that can
-    not be process as regular fields.
-
-    For instance, the length of the normals array is
-    mNumVertices (no mNumNormals is available)
-    """
-    nb_vertices = getattr(mesh, "mNumVertices")
-
-    def fill(name):
-        mAttr = getattr(mesh, name)
-        if numpy:
-            if mAttr:
-                data = numpy.array([make_tuple(getattr(mesh, name)[i]) for i in range(nb_vertices)], dtype=numpy.float32)
-                setattr(target, name[1:].lower(), data)
-            else:
-                setattr(target, name[1:].lower(), numpy.array([], dtype="float32"))
-        else:
-            if mAttr:
-                data = [make_tuple(getattr(mesh, name)[i]) for i in range(nb_vertices)]
-                setattr(target, name[1:].lower(), data)
-            else:
-                setattr(target, name[1:].lower(), [])
-
-    def fillarray(name):
-        mAttr = getattr(mesh, name)
-
-        data = []
-        for index, mSubAttr in enumerate(mAttr):
-            if mSubAttr:
-                data.append([make_tuple(getattr(mesh, name)[index][i]) for i in range(nb_vertices)])
-
-        if numpy:
-            setattr(target, name[1:].lower(), numpy.array(data, dtype=numpy.float32))
-        else:
-            setattr(target, name[1:].lower(), data)
-
-    fill("mNormals")
-    fill("mTangents")
-    fill("mBitangents")
-
-    fillarray("mColors")
-    fillarray("mTextureCoords")
-
-    # prepare faces
-    if numpy:
-        faces = numpy.array([f.indices for f in target.faces], dtype=numpy.int32)
-    else:
-        faces = [f.indices for f in target.faces]
-    setattr(target, 'faces', faces)
-
-def _init_metadata_entry(entry):
-    from ctypes import POINTER, c_bool, c_int32, c_uint64, c_float, c_double, cast
-
-    entry.type = entry.mType
-    if entry.type == structs.MetadataEntry.AI_BOOL:
-        entry.data = cast(entry.mData, POINTER(c_bool)).contents.value
-    elif entry.type == structs.MetadataEntry.AI_INT32:
-        entry.data = cast(entry.mData, POINTER(c_int32)).contents.value
-    elif entry.type == structs.MetadataEntry.AI_UINT64:
-        entry.data = cast(entry.mData, POINTER(c_uint64)).contents.value
-    elif entry.type == structs.MetadataEntry.AI_FLOAT:
-        entry.data = cast(entry.mData, POINTER(c_float)).contents.value
-    elif entry.type == structs.MetadataEntry.AI_DOUBLE:
-        entry.data = cast(entry.mData, POINTER(c_double)).contents.value
-    elif entry.type == structs.MetadataEntry.AI_AISTRING:
-        assimp_string = cast(entry.mData, POINTER(structs.String)).contents
-        entry.data = _convert_assimp_string(assimp_string)
-    elif entry.type == structs.MetadataEntry.AI_AIVECTOR3D:
-        assimp_vector = cast(entry.mData, POINTER(structs.Vector3D)).contents
-        entry.data = make_tuple(assimp_vector)
-
-    return entry
-
-def _finalize_metadata(metadata, target):
-    """ Building the metadata object is a bit specific.
-
-    Firstly, there are two separate arrays: one with metadata keys and one
-    with metadata values, and there are no corresponding mNum* attributes,
-    so the C arrays are not converted to Python arrays using the generic
-    code in the _init function.
-
-    Secondly, a metadata entry value has to be cast according to declared
-    metadata entry type.
-    """
-    length = metadata.mNumProperties
-    setattr(target, 'keys', [str(_convert_assimp_string(metadata.mKeys[i])) for i in range(length)])
-    setattr(target, 'values', [_init_metadata_entry(metadata.mValues[i]) for i in range(length)])
-
-class PropertyGetter(dict):
-    def __getitem__(self, key):
-        semantic = 0
-        if isinstance(key, tuple):
-            key, semantic = key
-
-        return dict.__getitem__(self, (key, semantic))
-
-    def keys(self):
-        for k in dict.keys(self):
-            yield k[0]
-
-    def __iter__(self):
-        return self.keys()
-
-    def items(self):
-        for k, v in dict.items(self):
-            yield k[0], v
-
-
-def _get_properties(properties, length):
-    """
-    Convenience Function to get the material properties as a dict
-    and values in a python format.
-    """
-    result = {}
-    #read all properties
-    for p in [properties[i] for i in range(length)]:
-        #the name
-        p = p.contents
-        key = str(_convert_assimp_string(p.mKey))
-        key = (key.split('.')[1], p.mSemantic)
-
-        #the data
-        from ctypes import POINTER, cast, c_int, c_float, sizeof
-        if p.mType == 1:
-            arr = cast(p.mData, POINTER(c_float * int(p.mDataLength/sizeof(c_float)) )).contents
-            value = [x for x in arr]
-        elif p.mType == 3: #string can't be an array
-            value = _convert_assimp_string(cast(p.mData, POINTER(structs.MaterialPropertyString)).contents)
-
-        elif p.mType == 4:
-            arr = cast(p.mData, POINTER(c_int * int(p.mDataLength/sizeof(c_int)) )).contents
-            value = [x for x in arr]
-        else:
-            value = p.mData[:p.mDataLength]
-
-        if len(value) == 1:
-            [value] = value
-
-        result[key] = value
-
-    return PropertyGetter(result)
-
-def decompose_matrix(matrix):
-    if not isinstance(matrix, structs.Matrix4x4):
-        raise AssimpError("pyassimp.decompose_matrix failed: Not a Matrix4x4!")
-
-    scaling = structs.Vector3D()
-    rotation = structs.Quaternion()
-    position = structs.Vector3D()
-
-    from ctypes import byref, pointer
-    _assimp_lib.dll.aiDecomposeMatrix(pointer(matrix), byref(scaling), byref(rotation), byref(position))
-    return scaling._init(), rotation._init(), position._init()
-    
+"""
+PyAssimp
+
+This is the main-module of PyAssimp.
+"""
+
+import sys
+if sys.version_info < (2,6):
+    raise RuntimeError('pyassimp: need python 2.6 or newer')
+
+# xrange was renamed range in Python 3 and the original range from Python 2 was removed.
+# To keep compatibility with both Python 2 and 3, xrange is set to range for version 3.0 and up.
+if sys.version_info >= (3,0):
+    xrange = range
+
+
+try: import numpy
+except ImportError: numpy = None
+import logging
+import ctypes
+logger = logging.getLogger("pyassimp")
+# attach default null handler to logger so it doesn't complain
+# even if you don't attach another handler to logger
+logger.addHandler(logging.NullHandler())
+
+from . import structs
+from . import helper
+from . import postprocess
+from .errors import AssimpError
+
+class AssimpLib(object):
+    """
+    Assimp-Singleton
+    """
+    load, load_mem, export, export_blob, release, dll = helper.search_library()
+_assimp_lib = AssimpLib()
+
+def make_tuple(ai_obj, type = None):
+    res = None
+
+    #notes:
+    # ai_obj._fields_ = [ ("attr", c_type), ... ]
+    # getattr(ai_obj, e[0]).__class__ == float
+
+    if isinstance(ai_obj, structs.Matrix4x4):
+        if numpy:
+            res = numpy.array([getattr(ai_obj, e[0]) for e in ai_obj._fields_]).reshape((4,4))
+            #import pdb;pdb.set_trace()
+        else:
+            res = [getattr(ai_obj, e[0]) for e in ai_obj._fields_]
+            res = [res[i:i+4] for i in xrange(0,16,4)]
+    elif isinstance(ai_obj, structs.Matrix3x3):
+        if numpy:
+            res = numpy.array([getattr(ai_obj, e[0]) for e in ai_obj._fields_]).reshape((3,3))
+        else:
+            res = [getattr(ai_obj, e[0]) for e in ai_obj._fields_]
+            res = [res[i:i+3] for i in xrange(0,9,3)]
+    else:
+        if numpy:
+            res = numpy.array([getattr(ai_obj, e[0]) for e in ai_obj._fields_])
+        else:
+            res = [getattr(ai_obj, e[0]) for e in ai_obj._fields_]
+
+    return res
+
+# Returns unicode object for Python 2, and str object for Python 3.
+def _convert_assimp_string(assimp_string):
+    if sys.version_info >= (3, 0):
+        return str(assimp_string.data, errors='ignore')
+    else:
+        return unicode(assimp_string.data, errors='ignore')
+
+# It is faster and more correct to have an init function for each assimp class
+def _init_face(aiFace):
+    aiFace.indices = [aiFace.mIndices[i] for i in range(aiFace.mNumIndices)]
+assimp_struct_inits =  { structs.Face : _init_face }
+
+def call_init(obj, caller = None):
+    if helper.hasattr_silent(obj,'contents'): #pointer
+        _init(obj.contents, obj, caller)
+    else:
+        _init(obj,parent=caller)
+
+def _is_init_type(obj):
+    if helper.hasattr_silent(obj,'contents'): #pointer
+        return _is_init_type(obj[0])
+    # null-pointer case that arises when we reach a mesh attribute
+    # like mBitangents which use mNumVertices rather than mNumBitangents
+    # so it breaks the 'is iterable' check.
+    # Basically:
+    # FIXME!
+    elif not bool(obj):
+        return False
+    tname = obj.__class__.__name__
+    return not (tname[:2] == 'c_' or tname == 'Structure' \
+            or tname == 'POINTER') and not isinstance(obj,int)
+
+def _init(self, target = None, parent = None):
+    """
+    Custom initialize() for C structs, adds safely accessible member functionality.
+
+    :param target: set the object which receive the added methods. Useful when manipulating
+    pointers, to skip the intermediate 'contents' deferencing.
+    """
+    if not target:
+        target = self
+
+    dirself = dir(self)
+    for m in dirself:
+
+        if m.startswith("_"):
+            continue
+
+        if m.startswith('mNum'):
+            if 'm' + m[4:] in dirself:
+                continue # will be processed later on
+            else:
+                name = m[1:].lower()
+
+                obj = getattr(self, m)
+                setattr(target, name, obj)
+                continue
+
+        if m == 'mName':
+            target.name = str(_convert_assimp_string(self.mName))
+            target.__class__.__repr__ = lambda x: str(x.__class__) + "(" + getattr(x, 'name','') + ")"
+            target.__class__.__str__ = lambda x: getattr(x, 'name', '')
+            continue
+
+        name = m[1:].lower()
+
+        obj = getattr(self, m)
+
+        # Create tuples
+        if isinstance(obj, structs.assimp_structs_as_tuple):
+            setattr(target, name, make_tuple(obj))
+            logger.debug(str(self) + ": Added array " + str(getattr(target, name)) +  " as self." + name.lower())
+            continue
+
+        if m.startswith('m'):
+
+            if name == "parent":
+                setattr(target, name, parent)
+                logger.debug("Added a parent as self." + name)
+                continue
+
+            if helper.hasattr_silent(self, 'mNum' + m[1:]):
+
+                length =  getattr(self, 'mNum' + m[1:])
+
+                # -> special case: properties are
+                # stored as a dict.
+                if m == 'mProperties':
+                    setattr(target, name, _get_properties(obj, length))
+                    continue
+
+
+                if not length: # empty!
+                    setattr(target, name, [])
+                    logger.debug(str(self) + ": " + name + " is an empty list.")
+                    continue
+
+
+                try:
+                    if obj._type_ in structs.assimp_structs_as_tuple:
+                        if numpy:
+                            setattr(target, name, numpy.array([make_tuple(obj[i]) for i in range(length)], dtype=numpy.float32))
+
+                            logger.debug(str(self) + ": Added an array of numpy arrays (type "+ str(type(obj)) + ") as self." + name)
+                        else:
+                            setattr(target, name, [make_tuple(obj[i]) for i in range(length)])
+
+                            logger.debug(str(self) + ": Added a list of lists (type "+ str(type(obj)) + ") as self." + name)
+
+                    else:
+                        setattr(target, name, [obj[i] for i in range(length)]) #TODO: maybe not necessary to recreate an array?
+
+                        logger.debug(str(self) + ": Added list of " + str(obj) + " " + name + " as self." + name + " (type: " + str(type(obj)) + ")")
+
+                        # initialize array elements
+                        try:
+                            init = assimp_struct_inits[type(obj[0])]
+                        except KeyError:
+                            if _is_init_type(obj[0]):
+                                for e in getattr(target, name):
+                                    call_init(e, target)
+                        else:
+                            for e in getattr(target, name):
+                                init(e)
+
+
+                except IndexError:
+                    logger.error("in " + str(self) +" : mismatch between mNum" + name + " and the actual amount of data in m" + name + ". This may be due to version mismatch between libassimp and pyassimp. Quitting now.")
+                    sys.exit(1)
+
+                except ValueError as e:
+
+                    logger.error("In " + str(self) +  "->" + name + ": " + str(e) + ". Quitting now.")
+                    if "setting an array element with a sequence" in str(e):
+                        logger.error("Note that pyassimp does not currently "
+                                     "support meshes with mixed triangles "
+                                     "and quads. Try to load your mesh with"
+                                     " a post-processing to triangulate your"
+                                     " faces.")
+                    raise e
+
+
+
+            else: # starts with 'm' but not iterable
+                setattr(target, name, obj)
+                logger.debug("Added " + name + " as self." + name + " (type: " + str(type(obj)) + ")")
+
+                if _is_init_type(obj):
+                    call_init(obj, target)
+
+    if isinstance(self, structs.Mesh):
+        _finalize_mesh(self, target)
+
+    if isinstance(self, structs.Texture):
+        _finalize_texture(self, target)
+
+    if isinstance(self, structs.Metadata):
+        _finalize_metadata(self, target)
+
+
+    return self
+
+
+def pythonize_assimp(type, obj, scene):
+    """ This method modify the Assimp data structures
+    to make them easier to work with in Python.
+
+    Supported operations:
+     - MESH: replace a list of mesh IDs by reference to these meshes
+     - ADDTRANSFORMATION: add a reference to an object's transformation taken from their associated node.
+
+    :param type: the type of modification to operate (cf above)
+    :param obj: the input object to modify
+    :param scene: a reference to the whole scene
+    """
+
+    if type == "MESH":
+        meshes = []
+        for i in obj:
+            meshes.append(scene.meshes[i])
+        return meshes
+
+    if type == "ADDTRANSFORMATION":
+        def getnode(node, name):
+            if node.name == name: return node
+            for child in node.children:
+                n = getnode(child, name)
+                if n: return n
+
+        node = getnode(scene.rootnode, obj.name)
+        if not node:
+            raise AssimpError("Object " + str(obj) + " has no associated node!")
+        setattr(obj, "transformation", node.transformation)
+
+def recur_pythonize(node, scene):
+    '''
+    Recursively call pythonize_assimp on
+    nodes tree to apply several post-processing to
+    pythonize the assimp datastructures.
+    '''
+    node.meshes = pythonize_assimp("MESH", node.meshes, scene)
+    for mesh in node.meshes:
+        mesh.material = scene.materials[mesh.materialindex]
+    for cam in scene.cameras:
+        pythonize_assimp("ADDTRANSFORMATION", cam, scene)
+    for c in node.children:
+        recur_pythonize(c, scene)
+
+def load(filename,
+         file_type  = None,
+         processing = postprocess.aiProcess_Triangulate):
+    '''
+    Load a model into a scene. On failure throws AssimpError.
+
+    Arguments
+    ---------
+    filename:   Either a filename or a file object to load model from.
+                If a file object is passed, file_type MUST be specified
+                Otherwise Assimp has no idea which importer to use.
+                This is named 'filename' so as to not break legacy code.
+    processing: assimp postprocessing parameters. Verbose keywords are imported
+                from postprocessing, and the parameters can be combined bitwise to
+                generate the final processing value. Note that the default value will
+                triangulate quad faces. Example of generating other possible values:
+                processing = (pyassimp.postprocess.aiProcess_Triangulate |
+                              pyassimp.postprocess.aiProcess_OptimizeMeshes)
+    file_type:  string of file extension, such as 'stl'
+
+    Returns
+    ---------
+    Scene object with model data
+    '''
+
+    if hasattr(filename, 'read'):
+        # This is the case where a file object has been passed to load.
+        # It is calling the following function:
+        # const aiScene* aiImportFileFromMemory(const char* pBuffer,
+        #                                      unsigned int pLength,
+        #                                      unsigned int pFlags,
+        #                                      const char* pHint)
+        if file_type is None:
+            raise AssimpError('File type must be specified when passing file objects!')
+        data  = filename.read()
+        model = _assimp_lib.load_mem(data,
+                                     len(data),
+                                     processing,
+                                     file_type)
+    else:
+        # a filename string has been passed
+        model = _assimp_lib.load(filename.encode(sys.getfilesystemencoding()), processing)
+
+    if not model:
+        raise AssimpError('Could not import file!')
+    scene = _init(model.contents)
+    recur_pythonize(scene.rootnode, scene)
+    return scene
+
+def export(scene,
+           filename,
+           file_type  = None,
+           processing = postprocess.aiProcess_Triangulate):
+    '''
+    Export a scene. On failure throws AssimpError.
+
+    Arguments
+    ---------
+    scene: scene to export.
+    filename: Filename that the scene should be exported to.
+    file_type: string of file exporter to use. For example "collada".
+    processing: assimp postprocessing parameters. Verbose keywords are imported
+                from postprocessing, and the parameters can be combined bitwise to
+                generate the final processing value. Note that the default value will
+                triangulate quad faces. Example of generating other possible values:
+                processing = (pyassimp.postprocess.aiProcess_Triangulate |
+                              pyassimp.postprocess.aiProcess_OptimizeMeshes)
+
+    '''
+
+    exportStatus = _assimp_lib.export(ctypes.pointer(scene), file_type.encode("ascii"), filename.encode(sys.getfilesystemencoding()), processing)
+
+    if exportStatus != 0:
+        raise AssimpError('Could not export scene!')
+
+def export_blob(scene,
+                file_type = None,
+                processing = postprocess.aiProcess_Triangulate):
+    '''
+    Export a scene and return a blob in the correct format. On failure throws AssimpError.
+
+    Arguments
+    ---------
+    scene: scene to export.
+    file_type: string of file exporter to use. For example "collada".
+    processing: assimp postprocessing parameters. Verbose keywords are imported
+                from postprocessing, and the parameters can be combined bitwise to
+                generate the final processing value. Note that the default value will
+                triangulate quad faces. Example of generating other possible values:
+                processing = (pyassimp.postprocess.aiProcess_Triangulate |
+                              pyassimp.postprocess.aiProcess_OptimizeMeshes)
+    Returns
+    ---------
+    Pointer to structs.ExportDataBlob
+    '''
+    exportBlobPtr = _assimp_lib.export_blob(ctypes.pointer(scene), file_type.encode("ascii"), processing)
+
+    if exportBlobPtr == 0:
+        raise AssimpError('Could not export scene to blob!')
+    return exportBlobPtr
+
+def release(scene):
+    _assimp_lib.release(ctypes.pointer(scene))
+
+def _finalize_texture(tex, target):
+    setattr(target, "achformathint", tex.achFormatHint)
+    if numpy:
+        data = numpy.array([make_tuple(getattr(tex, "pcData")[i]) for i in range(tex.mWidth * tex.mHeight)])
+    else:
+        data = [make_tuple(getattr(tex, "pcData")[i]) for i in range(tex.mWidth * tex.mHeight)]
+    setattr(target, "data", data)
+
+def _finalize_mesh(mesh, target):
+    """ Building of meshes is a bit specific.
+
+    We override here the various datasets that can
+    not be process as regular fields.
+
+    For instance, the length of the normals array is
+    mNumVertices (no mNumNormals is available)
+    """
+    nb_vertices = getattr(mesh, "mNumVertices")
+
+    def fill(name):
+        mAttr = getattr(mesh, name)
+        if numpy:
+            if mAttr:
+                data = numpy.array([make_tuple(getattr(mesh, name)[i]) for i in range(nb_vertices)], dtype=numpy.float32)
+                setattr(target, name[1:].lower(), data)
+            else:
+                setattr(target, name[1:].lower(), numpy.array([], dtype="float32"))
+        else:
+            if mAttr:
+                data = [make_tuple(getattr(mesh, name)[i]) for i in range(nb_vertices)]
+                setattr(target, name[1:].lower(), data)
+            else:
+                setattr(target, name[1:].lower(), [])
+
+    def fillarray(name):
+        mAttr = getattr(mesh, name)
+
+        data = []
+        for index, mSubAttr in enumerate(mAttr):
+            if mSubAttr:
+                data.append([make_tuple(getattr(mesh, name)[index][i]) for i in range(nb_vertices)])
+
+        if numpy:
+            setattr(target, name[1:].lower(), numpy.array(data, dtype=numpy.float32))
+        else:
+            setattr(target, name[1:].lower(), data)
+
+    fill("mNormals")
+    fill("mTangents")
+    fill("mBitangents")
+
+    fillarray("mColors")
+    fillarray("mTextureCoords")
+
+    # prepare faces
+    if numpy:
+        faces = numpy.array([f.indices for f in target.faces], dtype=numpy.int32)
+    else:
+        faces = [f.indices for f in target.faces]
+    setattr(target, 'faces', faces)
+
+def _init_metadata_entry(entry):
+    entry.type = entry.mType
+    if entry.type == structs.MetadataEntry.AI_BOOL:
+        entry.data = ctypes.cast(entry.mData, ctypes.POINTER(ctypes.c_bool)).contents.value
+    elif entry.type == structs.MetadataEntry.AI_INT32:
+        entry.data = ctypes.cast(entry.mData, ctypes.POINTER(ctypes.c_int32)).contents.value
+    elif entry.type == structs.MetadataEntry.AI_UINT64:
+        entry.data = ctypes.cast(entry.mData, ctypes.POINTER(ctypes.c_uint64)).contents.value
+    elif entry.type == structs.MetadataEntry.AI_FLOAT:
+        entry.data = ctypes.cast(entry.mData, ctypes.POINTER(ctypes.c_float)).contents.value
+    elif entry.type == structs.MetadataEntry.AI_DOUBLE:
+        entry.data = ctypes.cast(entry.mData, ctypes.POINTER(ctypes.c_double)).contents.value
+    elif entry.type == structs.MetadataEntry.AI_AISTRING:
+        assimp_string = ctypes.cast(entry.mData, ctypes.POINTER(structs.String)).contents
+        entry.data = _convert_assimp_string(assimp_string)
+    elif entry.type == structs.MetadataEntry.AI_AIVECTOR3D:
+        assimp_vector = ctypes.cast(entry.mData, ctypes.POINTER(structs.Vector3D)).contents
+        entry.data = make_tuple(assimp_vector)
+
+    return entry
+
+def _finalize_metadata(metadata, target):
+    """ Building the metadata object is a bit specific.
+
+    Firstly, there are two separate arrays: one with metadata keys and one
+    with metadata values, and there are no corresponding mNum* attributes,
+    so the C arrays are not converted to Python arrays using the generic
+    code in the _init function.
+
+    Secondly, a metadata entry value has to be cast according to declared
+    metadata entry type.
+    """
+    length = metadata.mNumProperties
+    setattr(target, 'keys', [str(_convert_assimp_string(metadata.mKeys[i])) for i in range(length)])
+    setattr(target, 'values', [_init_metadata_entry(metadata.mValues[i]) for i in range(length)])
+
+class PropertyGetter(dict):
+    def __getitem__(self, key):
+        semantic = 0
+        if isinstance(key, tuple):
+            key, semantic = key
+
+        return dict.__getitem__(self, (key, semantic))
+
+    def keys(self):
+        for k in dict.keys(self):
+            yield k[0]
+
+    def __iter__(self):
+        return self.keys()
+
+    def items(self):
+        for k, v in dict.items(self):
+            yield k[0], v
+
+
+def _get_properties(properties, length):
+    """
+    Convenience Function to get the material properties as a dict
+    and values in a python format.
+    """
+    result = {}
+    #read all properties
+    for p in [properties[i] for i in range(length)]:
+        #the name
+        p = p.contents
+        key = str(_convert_assimp_string(p.mKey))
+        key = (key.split('.')[1], p.mSemantic)
+
+        #the data
+        if p.mType == 1:
+            arr = ctypes.cast(p.mData,
+                              ctypes.POINTER(ctypes.c_float * int(p.mDataLength/ctypes.sizeof(ctypes.c_float)))
+                              ).contents
+            value = [x for x in arr]
+        elif p.mType == 3: #string can't be an array
+            value = _convert_assimp_string(ctypes.cast(p.mData, ctypes.POINTER(structs.MaterialPropertyString)).contents)
+
+        elif p.mType == 4:
+            arr = ctypes.cast(p.mData,
+                              ctypes.POINTER(ctypes.c_int * int(p.mDataLength/ctypes.sizeof(ctypes.c_int)))
+                              ).contents
+            value = [x for x in arr]
+        else:
+            value = p.mData[:p.mDataLength]
+
+        if len(value) == 1:
+            [value] = value
+
+        result[key] = value
+
+    return PropertyGetter(result)
+
+def decompose_matrix(matrix):
+    if not isinstance(matrix, structs.Matrix4x4):
+        raise AssimpError("pyassimp.decompose_matrix failed: Not a Matrix4x4!")
+
+    scaling = structs.Vector3D()
+    rotation = structs.Quaternion()
+    position = structs.Vector3D()
+
+    _assimp_lib.dll.aiDecomposeMatrix(ctypes.pointer(matrix),
+                                      ctypes.byref(scaling),
+                                      ctypes.byref(rotation),
+                                      ctypes.byref(position))
+    return scaling._init(), rotation._init(), position._init()
+    

+ 1 - 1
port/PyAssimp/pyassimp/formats.py

@@ -21,7 +21,7 @@ FORMATS = ["CSM",
             "STL", 
             "STL", 
             "IRR", 
             "IRR", 
             "Q3O",
             "Q3O",
-            "Q3D"
+            "Q3D",
             "MS3D", 
             "MS3D", 
             "Q3S", 
             "Q3S", 
             "ZGL", 
             "ZGL", 

+ 279 - 280
port/PyAssimp/pyassimp/helper.py

@@ -1,280 +1,279 @@
-#-*- coding: UTF-8 -*-
-
-"""
-Some fancy helper functions.
-"""
-
-import os
-import ctypes
-from ctypes import POINTER
-import operator
-
-from distutils.sysconfig import get_python_lib
-import re
-import sys
-
-try: import numpy
-except: numpy = None
-
-import logging;logger = logging.getLogger("pyassimp")
-
-from .errors import AssimpError
-
-additional_dirs, ext_whitelist = [],[]
-
-# populate search directories and lists of allowed file extensions
-# depending on the platform we're running on.
-if os.name=='posix':
-    additional_dirs.append('./')
-    additional_dirs.append('/usr/lib/')
-    additional_dirs.append('/usr/lib/x86_64-linux-gnu/')
-    additional_dirs.append('/usr/local/lib/')
-
-    if 'LD_LIBRARY_PATH' in os.environ:
-        additional_dirs.extend([item for item in os.environ['LD_LIBRARY_PATH'].split(':') if item])
-
-    # check if running from anaconda.
-    if "conda" or "continuum" in sys.version.lower():
-      cur_path = get_python_lib()
-      pattern = re.compile('.*\/lib\/')
-      conda_lib = pattern.match(cur_path).group()
-      logger.info("Adding Anaconda lib path:"+ conda_lib)
-      additional_dirs.append(conda_lib)
-
-    # note - this won't catch libassimp.so.N.n, but
-    # currently there's always a symlink called
-    # libassimp.so in /usr/local/lib.
-    ext_whitelist.append('.so')
-    # libassimp.dylib in /usr/local/lib
-    ext_whitelist.append('.dylib')
-
-elif os.name=='nt':
-    ext_whitelist.append('.dll')
-    path_dirs = os.environ['PATH'].split(';')
-    additional_dirs.extend(path_dirs)
-
-def vec2tuple(x):
-    """ Converts a VECTOR3D to a Tuple """
-    return (x.x, x.y, x.z)
-
-def transform(vector3, matrix4x4):
-    """ Apply a transformation matrix on a 3D vector.
-
-    :param vector3: array with 3 elements
-    :param matrix4x4: 4x4 matrix
-    """
-    if numpy:
-        return numpy.dot(matrix4x4, numpy.append(vector3, 1.))
-    else:
-        m0,m1,m2,m3 = matrix4x4; x,y,z = vector3
-        return [
-            m0[0]*x + m0[1]*y + m0[2]*z + m0[3],
-            m1[0]*x + m1[1]*y + m1[2]*z + m1[3],
-            m2[0]*x + m2[1]*y + m2[2]*z + m2[3],
-            m3[0]*x + m3[1]*y + m3[2]*z + m3[3]
-            ]
-
-def _inv(matrix4x4):
-    m0,m1,m2,m3 = matrix4x4
-
-    det  =  m0[3]*m1[2]*m2[1]*m3[0] - m0[2]*m1[3]*m2[1]*m3[0] - \
-            m0[3]*m1[1]*m2[2]*m3[0] + m0[1]*m1[3]*m2[2]*m3[0] + \
-            m0[2]*m1[1]*m2[3]*m3[0] - m0[1]*m1[2]*m2[3]*m3[0] - \
-            m0[3]*m1[2]*m2[0]*m3[1] + m0[2]*m1[3]*m2[0]*m3[1] + \
-            m0[3]*m1[0]*m2[2]*m3[1] - m0[0]*m1[3]*m2[2]*m3[1] - \
-            m0[2]*m1[0]*m2[3]*m3[1] + m0[0]*m1[2]*m2[3]*m3[1] + \
-            m0[3]*m1[1]*m2[0]*m3[2] - m0[1]*m1[3]*m2[0]*m3[2] - \
-            m0[3]*m1[0]*m2[1]*m3[2] + m0[0]*m1[3]*m2[1]*m3[2] + \
-            m0[1]*m1[0]*m2[3]*m3[2] - m0[0]*m1[1]*m2[3]*m3[2] - \
-            m0[2]*m1[1]*m2[0]*m3[3] + m0[1]*m1[2]*m2[0]*m3[3] + \
-            m0[2]*m1[0]*m2[1]*m3[3] - m0[0]*m1[2]*m2[1]*m3[3] - \
-            m0[1]*m1[0]*m2[2]*m3[3] + m0[0]*m1[1]*m2[2]*m3[3]
-
-    return[[( m1[2]*m2[3]*m3[1] - m1[3]*m2[2]*m3[1] + m1[3]*m2[1]*m3[2] - m1[1]*m2[3]*m3[2] - m1[2]*m2[1]*m3[3] + m1[1]*m2[2]*m3[3]) /det,
-            ( m0[3]*m2[2]*m3[1] - m0[2]*m2[3]*m3[1] - m0[3]*m2[1]*m3[2] + m0[1]*m2[3]*m3[2] + m0[2]*m2[1]*m3[3] - m0[1]*m2[2]*m3[3]) /det,
-            ( m0[2]*m1[3]*m3[1] - m0[3]*m1[2]*m3[1] + m0[3]*m1[1]*m3[2] - m0[1]*m1[3]*m3[2] - m0[2]*m1[1]*m3[3] + m0[1]*m1[2]*m3[3]) /det,
-            ( m0[3]*m1[2]*m2[1] - m0[2]*m1[3]*m2[1] - m0[3]*m1[1]*m2[2] + m0[1]*m1[3]*m2[2] + m0[2]*m1[1]*m2[3] - m0[1]*m1[2]*m2[3]) /det],
-           [( m1[3]*m2[2]*m3[0] - m1[2]*m2[3]*m3[0] - m1[3]*m2[0]*m3[2] + m1[0]*m2[3]*m3[2] + m1[2]*m2[0]*m3[3] - m1[0]*m2[2]*m3[3]) /det,
-            ( m0[2]*m2[3]*m3[0] - m0[3]*m2[2]*m3[0] + m0[3]*m2[0]*m3[2] - m0[0]*m2[3]*m3[2] - m0[2]*m2[0]*m3[3] + m0[0]*m2[2]*m3[3]) /det,
-            ( m0[3]*m1[2]*m3[0] - m0[2]*m1[3]*m3[0] - m0[3]*m1[0]*m3[2] + m0[0]*m1[3]*m3[2] + m0[2]*m1[0]*m3[3] - m0[0]*m1[2]*m3[3]) /det,
-            ( m0[2]*m1[3]*m2[0] - m0[3]*m1[2]*m2[0] + m0[3]*m1[0]*m2[2] - m0[0]*m1[3]*m2[2] - m0[2]*m1[0]*m2[3] + m0[0]*m1[2]*m2[3]) /det],
-           [( m1[1]*m2[3]*m3[0] - m1[3]*m2[1]*m3[0] + m1[3]*m2[0]*m3[1] - m1[0]*m2[3]*m3[1] - m1[1]*m2[0]*m3[3] + m1[0]*m2[1]*m3[3]) /det,
-            ( m0[3]*m2[1]*m3[0] - m0[1]*m2[3]*m3[0] - m0[3]*m2[0]*m3[1] + m0[0]*m2[3]*m3[1] + m0[1]*m2[0]*m3[3] - m0[0]*m2[1]*m3[3]) /det,
-            ( m0[1]*m1[3]*m3[0] - m0[3]*m1[1]*m3[0] + m0[3]*m1[0]*m3[1] - m0[0]*m1[3]*m3[1] - m0[1]*m1[0]*m3[3] + m0[0]*m1[1]*m3[3]) /det,
-            ( m0[3]*m1[1]*m2[0] - m0[1]*m1[3]*m2[0] - m0[3]*m1[0]*m2[1] + m0[0]*m1[3]*m2[1] + m0[1]*m1[0]*m2[3] - m0[0]*m1[1]*m2[3]) /det],
-           [( m1[2]*m2[1]*m3[0] - m1[1]*m2[2]*m3[0] - m1[2]*m2[0]*m3[1] + m1[0]*m2[2]*m3[1] + m1[1]*m2[0]*m3[2] - m1[0]*m2[1]*m3[2]) /det,
-            ( m0[1]*m2[2]*m3[0] - m0[2]*m2[1]*m3[0] + m0[2]*m2[0]*m3[1] - m0[0]*m2[2]*m3[1] - m0[1]*m2[0]*m3[2] + m0[0]*m2[1]*m3[2]) /det,
-            ( m0[2]*m1[1]*m3[0] - m0[1]*m1[2]*m3[0] - m0[2]*m1[0]*m3[1] + m0[0]*m1[2]*m3[1] + m0[1]*m1[0]*m3[2] - m0[0]*m1[1]*m3[2]) /det,
-            ( m0[1]*m1[2]*m2[0] - m0[2]*m1[1]*m2[0] + m0[2]*m1[0]*m2[1] - m0[0]*m1[2]*m2[1] - m0[1]*m1[0]*m2[2] + m0[0]*m1[1]*m2[2]) /det]]
-
-def get_bounding_box(scene):
-    bb_min = [1e10, 1e10, 1e10] # x,y,z
-    bb_max = [-1e10, -1e10, -1e10] # x,y,z
-    inv = numpy.linalg.inv if numpy else _inv
-    return get_bounding_box_for_node(scene.rootnode, bb_min, bb_max, inv(scene.rootnode.transformation))
-
-def get_bounding_box_for_node(node, bb_min, bb_max, transformation):
-
-    if numpy:
-        transformation = numpy.dot(transformation, node.transformation)
-    else:
-        t0,t1,t2,t3 = transformation
-        T0,T1,T2,T3 = node.transformation
-        transformation = [ [
-                t0[0]*T0[0] + t0[1]*T1[0] + t0[2]*T2[0] + t0[3]*T3[0],
-                t0[0]*T0[1] + t0[1]*T1[1] + t0[2]*T2[1] + t0[3]*T3[1],
-                t0[0]*T0[2] + t0[1]*T1[2] + t0[2]*T2[2] + t0[3]*T3[2],
-                t0[0]*T0[3] + t0[1]*T1[3] + t0[2]*T2[3] + t0[3]*T3[3]
-            ],[
-                t1[0]*T0[0] + t1[1]*T1[0] + t1[2]*T2[0] + t1[3]*T3[0],
-                t1[0]*T0[1] + t1[1]*T1[1] + t1[2]*T2[1] + t1[3]*T3[1],
-                t1[0]*T0[2] + t1[1]*T1[2] + t1[2]*T2[2] + t1[3]*T3[2],
-                t1[0]*T0[3] + t1[1]*T1[3] + t1[2]*T2[3] + t1[3]*T3[3]
-            ],[
-                t2[0]*T0[0] + t2[1]*T1[0] + t2[2]*T2[0] + t2[3]*T3[0],
-                t2[0]*T0[1] + t2[1]*T1[1] + t2[2]*T2[1] + t2[3]*T3[1],
-                t2[0]*T0[2] + t2[1]*T1[2] + t2[2]*T2[2] + t2[3]*T3[2],
-                t2[0]*T0[3] + t2[1]*T1[3] + t2[2]*T2[3] + t2[3]*T3[3]
-            ],[
-                t3[0]*T0[0] + t3[1]*T1[0] + t3[2]*T2[0] + t3[3]*T3[0],
-                t3[0]*T0[1] + t3[1]*T1[1] + t3[2]*T2[1] + t3[3]*T3[1],
-                t3[0]*T0[2] + t3[1]*T1[2] + t3[2]*T2[2] + t3[3]*T3[2],
-                t3[0]*T0[3] + t3[1]*T1[3] + t3[2]*T2[3] + t3[3]*T3[3]
-            ] ]
-
-    for mesh in node.meshes:
-        for v in mesh.vertices:
-            v = transform(v, transformation)
-            bb_min[0] = min(bb_min[0], v[0])
-            bb_min[1] = min(bb_min[1], v[1])
-            bb_min[2] = min(bb_min[2], v[2])
-            bb_max[0] = max(bb_max[0], v[0])
-            bb_max[1] = max(bb_max[1], v[1])
-            bb_max[2] = max(bb_max[2], v[2])
-
-
-    for child in node.children:
-        bb_min, bb_max = get_bounding_box_for_node(child, bb_min, bb_max, transformation)
-
-    return bb_min, bb_max
-
-def try_load_functions(library_path, dll):
-    '''
-    Try to bind to aiImportFile and aiReleaseImport
-
-    Arguments
-    ---------
-    library_path: path to current lib
-    dll:          ctypes handle to library
-
-    Returns
-    ---------
-    If unsuccessful:
-        None
-    If successful:
-        Tuple containing (library_path,
-                          load from filename function,
-                          load from memory function,
-                          export to filename function,
-                          export to blob function,
-                          release function,
-                          ctypes handle to assimp library)
-    '''
-
-    try:
-        load     = dll.aiImportFile
-        release  = dll.aiReleaseImport
-        load_mem = dll.aiImportFileFromMemory
-        export   = dll.aiExportScene
-        export2blob = dll.aiExportSceneToBlob
-    except AttributeError:
-        #OK, this is a library, but it doesn't have the functions we need
-        return None
-
-    # library found!
-    from .structs import Scene, ExportDataBlob
-    load.restype = POINTER(Scene)
-    load_mem.restype = POINTER(Scene)
-    export2blob.restype = POINTER(ExportDataBlob)
-    return (library_path, load, load_mem, export, export2blob, release, dll)
-
-def search_library():
-    '''
-    Loads the assimp library.
-    Throws exception AssimpError if no library_path is found
-
-    Returns: tuple, (load from filename function,
-                     load from memory function,
-                     export to filename function,
-                     export to blob function,
-                     release function,
-                     dll)
-    '''
-    #this path
-    folder = os.path.dirname(__file__)
-
-    # silence 'DLL not found' message boxes on win
-    try:
-        ctypes.windll.kernel32.SetErrorMode(0x8007)
-    except AttributeError:
-        pass
-
-    candidates = []
-    # test every file
-    for curfolder in [folder]+additional_dirs:
-        if os.path.isdir(curfolder):
-            for filename in os.listdir(curfolder):
-                # our minimum requirement for candidates is that
-                # they should contain 'assimp' somewhere in
-                # their name                                  
-                if filename.lower().find('assimp')==-1 : 
-                    continue
-                is_out=1
-                for et in ext_whitelist:
-                  if et in filename.lower():
-                    is_out=0
-                    break
-                if is_out:
-                  continue
-                
-                library_path = os.path.join(curfolder, filename)
-                logger.debug('Try ' + library_path)
-                try:
-                    dll = ctypes.cdll.LoadLibrary(library_path)
-                except Exception as e:
-                    logger.warning(str(e))
-                    # OK, this except is evil. But different OSs will throw different
-                    # errors. So just ignore any errors.
-                    continue
-                # see if the functions we need are in the dll
-                loaded = try_load_functions(library_path, dll)
-                if loaded: candidates.append(loaded)
-
-    if not candidates:
-        # no library found
-        raise AssimpError("assimp library not found")
-    else:
-        # get the newest library_path
-        candidates = map(lambda x: (os.lstat(x[0])[-2], x), candidates)
-        res = max(candidates, key=operator.itemgetter(0))[1]
-        logger.debug('Using assimp library located at ' + res[0])
-
-        # XXX: if there are 1000 dll/so files containing 'assimp'
-        # in their name, do we have all of them in our address
-        # space now until gc kicks in?
-
-        # XXX: take version postfix of the .so on linux?
-        return res[1:]
-
-def hasattr_silent(object, name):
-    """
-        Calls hasttr() with the given parameters and preserves the legacy (pre-Python 3.2)
-        functionality of silently catching exceptions.
-
-        Returns the result of hasatter() or False if an exception was raised.
-    """
-
-    try:
-        return hasattr(object, name)
-    except:
-        return False
+#-*- coding: UTF-8 -*-
+
+"""
+Some fancy helper functions.
+"""
+
+import os
+import ctypes
+import operator
+
+from distutils.sysconfig import get_python_lib
+import re
+import sys
+
+try: import numpy
+except ImportError: numpy = None
+
+import logging;logger = logging.getLogger("pyassimp")
+
+from .errors import AssimpError
+
+additional_dirs, ext_whitelist = [],[]
+
+# populate search directories and lists of allowed file extensions
+# depending on the platform we're running on.
+if os.name=='posix':
+    additional_dirs.append('./')
+    additional_dirs.append('/usr/lib/')
+    additional_dirs.append('/usr/lib/x86_64-linux-gnu/')
+    additional_dirs.append('/usr/local/lib/')
+
+    if 'LD_LIBRARY_PATH' in os.environ:
+        additional_dirs.extend([item for item in os.environ['LD_LIBRARY_PATH'].split(':') if item])
+
+    # check if running from anaconda.
+    if "conda" or "continuum" in sys.version.lower():
+      cur_path = get_python_lib()
+      pattern = re.compile('.*\/lib\/')
+      conda_lib = pattern.match(cur_path).group()
+      logger.info("Adding Anaconda lib path:"+ conda_lib)
+      additional_dirs.append(conda_lib)
+
+    # note - this won't catch libassimp.so.N.n, but
+    # currently there's always a symlink called
+    # libassimp.so in /usr/local/lib.
+    ext_whitelist.append('.so')
+    # libassimp.dylib in /usr/local/lib
+    ext_whitelist.append('.dylib')
+
+elif os.name=='nt':
+    ext_whitelist.append('.dll')
+    path_dirs = os.environ['PATH'].split(';')
+    additional_dirs.extend(path_dirs)
+
+def vec2tuple(x):
+    """ Converts a VECTOR3D to a Tuple """
+    return (x.x, x.y, x.z)
+
+def transform(vector3, matrix4x4):
+    """ Apply a transformation matrix on a 3D vector.
+
+    :param vector3: array with 3 elements
+    :param matrix4x4: 4x4 matrix
+    """
+    if numpy:
+        return numpy.dot(matrix4x4, numpy.append(vector3, 1.))
+    else:
+        m0,m1,m2,m3 = matrix4x4; x,y,z = vector3
+        return [
+            m0[0]*x + m0[1]*y + m0[2]*z + m0[3],
+            m1[0]*x + m1[1]*y + m1[2]*z + m1[3],
+            m2[0]*x + m2[1]*y + m2[2]*z + m2[3],
+            m3[0]*x + m3[1]*y + m3[2]*z + m3[3]
+            ]
+
+def _inv(matrix4x4):
+    m0,m1,m2,m3 = matrix4x4
+
+    det  =  m0[3]*m1[2]*m2[1]*m3[0] - m0[2]*m1[3]*m2[1]*m3[0] - \
+            m0[3]*m1[1]*m2[2]*m3[0] + m0[1]*m1[3]*m2[2]*m3[0] + \
+            m0[2]*m1[1]*m2[3]*m3[0] - m0[1]*m1[2]*m2[3]*m3[0] - \
+            m0[3]*m1[2]*m2[0]*m3[1] + m0[2]*m1[3]*m2[0]*m3[1] + \
+            m0[3]*m1[0]*m2[2]*m3[1] - m0[0]*m1[3]*m2[2]*m3[1] - \
+            m0[2]*m1[0]*m2[3]*m3[1] + m0[0]*m1[2]*m2[3]*m3[1] + \
+            m0[3]*m1[1]*m2[0]*m3[2] - m0[1]*m1[3]*m2[0]*m3[2] - \
+            m0[3]*m1[0]*m2[1]*m3[2] + m0[0]*m1[3]*m2[1]*m3[2] + \
+            m0[1]*m1[0]*m2[3]*m3[2] - m0[0]*m1[1]*m2[3]*m3[2] - \
+            m0[2]*m1[1]*m2[0]*m3[3] + m0[1]*m1[2]*m2[0]*m3[3] + \
+            m0[2]*m1[0]*m2[1]*m3[3] - m0[0]*m1[2]*m2[1]*m3[3] - \
+            m0[1]*m1[0]*m2[2]*m3[3] + m0[0]*m1[1]*m2[2]*m3[3]
+
+    return[[( m1[2]*m2[3]*m3[1] - m1[3]*m2[2]*m3[1] + m1[3]*m2[1]*m3[2] - m1[1]*m2[3]*m3[2] - m1[2]*m2[1]*m3[3] + m1[1]*m2[2]*m3[3]) /det,
+            ( m0[3]*m2[2]*m3[1] - m0[2]*m2[3]*m3[1] - m0[3]*m2[1]*m3[2] + m0[1]*m2[3]*m3[2] + m0[2]*m2[1]*m3[3] - m0[1]*m2[2]*m3[3]) /det,
+            ( m0[2]*m1[3]*m3[1] - m0[3]*m1[2]*m3[1] + m0[3]*m1[1]*m3[2] - m0[1]*m1[3]*m3[2] - m0[2]*m1[1]*m3[3] + m0[1]*m1[2]*m3[3]) /det,
+            ( m0[3]*m1[2]*m2[1] - m0[2]*m1[3]*m2[1] - m0[3]*m1[1]*m2[2] + m0[1]*m1[3]*m2[2] + m0[2]*m1[1]*m2[3] - m0[1]*m1[2]*m2[3]) /det],
+           [( m1[3]*m2[2]*m3[0] - m1[2]*m2[3]*m3[0] - m1[3]*m2[0]*m3[2] + m1[0]*m2[3]*m3[2] + m1[2]*m2[0]*m3[3] - m1[0]*m2[2]*m3[3]) /det,
+            ( m0[2]*m2[3]*m3[0] - m0[3]*m2[2]*m3[0] + m0[3]*m2[0]*m3[2] - m0[0]*m2[3]*m3[2] - m0[2]*m2[0]*m3[3] + m0[0]*m2[2]*m3[3]) /det,
+            ( m0[3]*m1[2]*m3[0] - m0[2]*m1[3]*m3[0] - m0[3]*m1[0]*m3[2] + m0[0]*m1[3]*m3[2] + m0[2]*m1[0]*m3[3] - m0[0]*m1[2]*m3[3]) /det,
+            ( m0[2]*m1[3]*m2[0] - m0[3]*m1[2]*m2[0] + m0[3]*m1[0]*m2[2] - m0[0]*m1[3]*m2[2] - m0[2]*m1[0]*m2[3] + m0[0]*m1[2]*m2[3]) /det],
+           [( m1[1]*m2[3]*m3[0] - m1[3]*m2[1]*m3[0] + m1[3]*m2[0]*m3[1] - m1[0]*m2[3]*m3[1] - m1[1]*m2[0]*m3[3] + m1[0]*m2[1]*m3[3]) /det,
+            ( m0[3]*m2[1]*m3[0] - m0[1]*m2[3]*m3[0] - m0[3]*m2[0]*m3[1] + m0[0]*m2[3]*m3[1] + m0[1]*m2[0]*m3[3] - m0[0]*m2[1]*m3[3]) /det,
+            ( m0[1]*m1[3]*m3[0] - m0[3]*m1[1]*m3[0] + m0[3]*m1[0]*m3[1] - m0[0]*m1[3]*m3[1] - m0[1]*m1[0]*m3[3] + m0[0]*m1[1]*m3[3]) /det,
+            ( m0[3]*m1[1]*m2[0] - m0[1]*m1[3]*m2[0] - m0[3]*m1[0]*m2[1] + m0[0]*m1[3]*m2[1] + m0[1]*m1[0]*m2[3] - m0[0]*m1[1]*m2[3]) /det],
+           [( m1[2]*m2[1]*m3[0] - m1[1]*m2[2]*m3[0] - m1[2]*m2[0]*m3[1] + m1[0]*m2[2]*m3[1] + m1[1]*m2[0]*m3[2] - m1[0]*m2[1]*m3[2]) /det,
+            ( m0[1]*m2[2]*m3[0] - m0[2]*m2[1]*m3[0] + m0[2]*m2[0]*m3[1] - m0[0]*m2[2]*m3[1] - m0[1]*m2[0]*m3[2] + m0[0]*m2[1]*m3[2]) /det,
+            ( m0[2]*m1[1]*m3[0] - m0[1]*m1[2]*m3[0] - m0[2]*m1[0]*m3[1] + m0[0]*m1[2]*m3[1] + m0[1]*m1[0]*m3[2] - m0[0]*m1[1]*m3[2]) /det,
+            ( m0[1]*m1[2]*m2[0] - m0[2]*m1[1]*m2[0] + m0[2]*m1[0]*m2[1] - m0[0]*m1[2]*m2[1] - m0[1]*m1[0]*m2[2] + m0[0]*m1[1]*m2[2]) /det]]
+
+def get_bounding_box(scene):
+    bb_min = [1e10, 1e10, 1e10] # x,y,z
+    bb_max = [-1e10, -1e10, -1e10] # x,y,z
+    inv = numpy.linalg.inv if numpy else _inv
+    return get_bounding_box_for_node(scene.rootnode, bb_min, bb_max, inv(scene.rootnode.transformation))
+
+def get_bounding_box_for_node(node, bb_min, bb_max, transformation):
+
+    if numpy:
+        transformation = numpy.dot(transformation, node.transformation)
+    else:
+        t0,t1,t2,t3 = transformation
+        T0,T1,T2,T3 = node.transformation
+        transformation = [ [
+                t0[0]*T0[0] + t0[1]*T1[0] + t0[2]*T2[0] + t0[3]*T3[0],
+                t0[0]*T0[1] + t0[1]*T1[1] + t0[2]*T2[1] + t0[3]*T3[1],
+                t0[0]*T0[2] + t0[1]*T1[2] + t0[2]*T2[2] + t0[3]*T3[2],
+                t0[0]*T0[3] + t0[1]*T1[3] + t0[2]*T2[3] + t0[3]*T3[3]
+            ],[
+                t1[0]*T0[0] + t1[1]*T1[0] + t1[2]*T2[0] + t1[3]*T3[0],
+                t1[0]*T0[1] + t1[1]*T1[1] + t1[2]*T2[1] + t1[3]*T3[1],
+                t1[0]*T0[2] + t1[1]*T1[2] + t1[2]*T2[2] + t1[3]*T3[2],
+                t1[0]*T0[3] + t1[1]*T1[3] + t1[2]*T2[3] + t1[3]*T3[3]
+            ],[
+                t2[0]*T0[0] + t2[1]*T1[0] + t2[2]*T2[0] + t2[3]*T3[0],
+                t2[0]*T0[1] + t2[1]*T1[1] + t2[2]*T2[1] + t2[3]*T3[1],
+                t2[0]*T0[2] + t2[1]*T1[2] + t2[2]*T2[2] + t2[3]*T3[2],
+                t2[0]*T0[3] + t2[1]*T1[3] + t2[2]*T2[3] + t2[3]*T3[3]
+            ],[
+                t3[0]*T0[0] + t3[1]*T1[0] + t3[2]*T2[0] + t3[3]*T3[0],
+                t3[0]*T0[1] + t3[1]*T1[1] + t3[2]*T2[1] + t3[3]*T3[1],
+                t3[0]*T0[2] + t3[1]*T1[2] + t3[2]*T2[2] + t3[3]*T3[2],
+                t3[0]*T0[3] + t3[1]*T1[3] + t3[2]*T2[3] + t3[3]*T3[3]
+            ] ]
+
+    for mesh in node.meshes:
+        for v in mesh.vertices:
+            v = transform(v, transformation)
+            bb_min[0] = min(bb_min[0], v[0])
+            bb_min[1] = min(bb_min[1], v[1])
+            bb_min[2] = min(bb_min[2], v[2])
+            bb_max[0] = max(bb_max[0], v[0])
+            bb_max[1] = max(bb_max[1], v[1])
+            bb_max[2] = max(bb_max[2], v[2])
+
+
+    for child in node.children:
+        bb_min, bb_max = get_bounding_box_for_node(child, bb_min, bb_max, transformation)
+
+    return bb_min, bb_max
+
+def try_load_functions(library_path, dll):
+    '''
+    Try to bind to aiImportFile and aiReleaseImport
+
+    Arguments
+    ---------
+    library_path: path to current lib
+    dll:          ctypes handle to library
+
+    Returns
+    ---------
+    If unsuccessful:
+        None
+    If successful:
+        Tuple containing (library_path,
+                          load from filename function,
+                          load from memory function,
+                          export to filename function,
+                          export to blob function,
+                          release function,
+                          ctypes handle to assimp library)
+    '''
+
+    try:
+        load     = dll.aiImportFile
+        release  = dll.aiReleaseImport
+        load_mem = dll.aiImportFileFromMemory
+        export   = dll.aiExportScene
+        export2blob = dll.aiExportSceneToBlob
+    except AttributeError:
+        #OK, this is a library, but it doesn't have the functions we need
+        return None
+
+    # library found!
+    from .structs import Scene, ExportDataBlob
+    load.restype = ctype.POINTER(Scene)
+    load_mem.restype = ctype.POINTER(Scene)
+    export2blob.restype = ctype.POINTER(ExportDataBlob)
+    return (library_path, load, load_mem, export, export2blob, release, dll)
+
+def search_library():
+    '''
+    Loads the assimp library.
+    Throws exception AssimpError if no library_path is found
+
+    Returns: tuple, (load from filename function,
+                     load from memory function,
+                     export to filename function,
+                     export to blob function,
+                     release function,
+                     dll)
+    '''
+    #this path
+    folder = os.path.dirname(__file__)
+
+    # silence 'DLL not found' message boxes on win
+    try:
+        ctypes.windll.kernel32.SetErrorMode(0x8007)
+    except AttributeError:
+        pass
+
+    candidates = []
+    # test every file
+    for curfolder in [folder]+additional_dirs:
+        if os.path.isdir(curfolder):
+            for filename in os.listdir(curfolder):
+                # our minimum requirement for candidates is that
+                # they should contain 'assimp' somewhere in
+                # their name                                  
+                if filename.lower().find('assimp')==-1 : 
+                    continue
+                is_out=1
+                for et in ext_whitelist:
+                  if et in filename.lower():
+                    is_out=0
+                    break
+                if is_out:
+                  continue
+                
+                library_path = os.path.join(curfolder, filename)
+                logger.debug('Try ' + library_path)
+                try:
+                    dll = ctypes.cdll.LoadLibrary(library_path)
+                except Exception as e:
+                    logger.warning(str(e))
+                    # OK, this except is evil. But different OSs will throw different
+                    # errors. So just ignore any errors.
+                    continue
+                # see if the functions we need are in the dll
+                loaded = try_load_functions(library_path, dll)
+                if loaded: candidates.append(loaded)
+
+    if not candidates:
+        # no library found
+        raise AssimpError("assimp library not found")
+    else:
+        # get the newest library_path
+        candidates = map(lambda x: (os.lstat(x[0])[-2], x), candidates)
+        res = max(candidates, key=operator.itemgetter(0))[1]
+        logger.debug('Using assimp library located at ' + res[0])
+
+        # XXX: if there are 1000 dll/so files containing 'assimp'
+        # in their name, do we have all of them in our address
+        # space now until gc kicks in?
+
+        # XXX: take version postfix of the .so on linux?
+        return res[1:]
+
+def hasattr_silent(object, name):
+    """
+        Calls hasttr() with the given parameters and preserves the legacy (pre-Python 3.2)
+        functionality of silently catching exceptions.
+
+        Returns the result of hasatter() or False if an exception was raised.
+    """
+
+    try:
+        return hasattr(object, name)
+    except AttributeError:
+        return False

+ 1 - 0
port/PyAssimp/pyassimp/postprocess.py

@@ -435,6 +435,7 @@ aiProcess_Debone  = 0x4000000
 aiProcess_GenEntityMeshes = 0x100000
 aiProcess_GenEntityMeshes = 0x100000
 aiProcess_OptimizeAnimations = 0x200000
 aiProcess_OptimizeAnimations = 0x200000
 aiProcess_FixTexturePaths = 0x200000
 aiProcess_FixTexturePaths = 0x200000
+aiProcess_EmbedTextures  = 0x10000000,
 
 
 ## @def aiProcess_ConvertToLeftHanded
 ## @def aiProcess_ConvertToLeftHanded
  #  @brief Shortcut flag for Direct3D-based applications. 
  #  @brief Shortcut flag for Direct3D-based applications. 

+ 2 - 2
port/PyAssimp/pyassimp/structs.py

@@ -1,6 +1,6 @@
 #-*- coding: UTF-8 -*-
 #-*- coding: UTF-8 -*-
 
 
-from ctypes import POINTER, c_void_p, c_int, c_uint, c_char, c_float, Structure, c_char_p, c_double, c_ubyte, c_size_t, c_uint32
+from ctypes import POINTER, c_void_p, c_uint, c_char, c_float, Structure, c_char_p, c_double, c_ubyte, c_size_t, c_uint32
 
 
 
 
 class Vector2D(Structure):
 class Vector2D(Structure):
@@ -70,7 +70,7 @@ class String(Structure):
     See 'types.h' for details.
     See 'types.h' for details.
     """ 
     """ 
 
 
-    MAXLEN = 1024
+    MAXLEN = 1024
 
 
     _fields_ = [
     _fields_ = [
             # Binary length of the string excluding the terminal 0. This is NOT the
             # Binary length of the string excluding the terminal 0. This is NOT the

+ 3 - 2
port/PyAssimp/scripts/fixed_pipeline_3d_viewer.py

@@ -24,12 +24,13 @@ This sample is based on several sources, including:
  - ASSIMP's C++ SimpleOpenGL viewer
  - ASSIMP's C++ SimpleOpenGL viewer
 """
 """
 
 
-import os, sys
+import sys
 from OpenGL.GLUT import *
 from OpenGL.GLUT import *
 from OpenGL.GLU import *
 from OpenGL.GLU import *
 from OpenGL.GL import *
 from OpenGL.GL import *
 
 
-import logging;logger = logging.getLogger("pyassimp_opengl")
+import logging
+logger = logging.getLogger("pyassimp_opengl")
 logging.basicConfig(level=logging.INFO)
 logging.basicConfig(level=logging.INFO)
 
 
 import math
 import math

+ 3 - 3
port/PyAssimp/scripts/sample.py

@@ -5,7 +5,7 @@
 This module demonstrates the functionality of PyAssimp.
 This module demonstrates the functionality of PyAssimp.
 """
 """
 
 
-import os, sys
+import sys
 import logging
 import logging
 logging.basicConfig(level=logging.INFO)
 logging.basicConfig(level=logging.INFO)
 
 
@@ -50,8 +50,8 @@ def main(filename=None):
         print("    colors:" + str(len(mesh.colors)))
         print("    colors:" + str(len(mesh.colors)))
         tcs = mesh.texturecoords
         tcs = mesh.texturecoords
         if tcs.any():
         if tcs.any():
-            for index, tc in enumerate(tcs):
-                print("    texture-coords "+ str(index) + ":" + str(len(tcs[index])) + "first3:" + str(tcs[index][:3]))
+            for tc_index, tc in enumerate(tcs):
+                print("    texture-coords "+ str(tc_index) + ":" + str(len(tcs[tc_index])) + "first3:" + str(tcs[tc_index][:3]))
 
 
         else:
         else:
             print("    no texture coordinates")
             print("    no texture coordinates")

+ 3 - 1
scripts/BlenderImporter/genblenddna.py

@@ -291,7 +291,9 @@ def main():
     #s += "#endif\n"
     #s += "#endif\n"
         
         
     output.write(templt.replace("<HERE>",s))
     output.write(templt.replace("<HERE>",s))
-        
+
+    # we got here, so no error
+    return 0
 
 
 if __name__ == "__main__":
 if __name__ == "__main__":
     sys.exit(main())
     sys.exit(main())

+ 9 - 9
scripts/StepImporter/CppGenerator.py

@@ -52,8 +52,8 @@ use_ifc_template = False
 	
 	
 input_step_template_h   = 'StepReaderGen.h.template'
 input_step_template_h   = 'StepReaderGen.h.template'
 input_step_template_cpp = 'StepReaderGen.cpp.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"
 cpp_keywords = "class"
 
 
@@ -87,7 +87,7 @@ template_type = r"""
 
 
 template_stub_decl = '\tDECL_CONV_STUB({type});\n'
 template_stub_decl = '\tDECL_CONV_STUB({type});\n'
 template_schema = '\t\tSchemaEntry("{normalized_name}",&STEP::ObjectHelper<{type},{argcnt}>::Construct )\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_converter = r"""
 // -----------------------------------------------------------------------------------------------------------
 // -----------------------------------------------------------------------------------------------------------
 template <> size_t GenericFill<{type}>(const DB& db, const LIST& params, {type}* in)
 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_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_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
 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);
     }} while(0);
 """
 """
 template_allow_optional = r"""
 template_allow_optional = r"""
@@ -151,11 +151,8 @@ def handle_unset_args(field,entity,schema,argnum):
     return n+template_allow_optional.format()
     return n+template_allow_optional.format()
 
 
 def get_single_conversion(field,schema,argnum=0,classname='?'):
 def get_single_conversion(field,schema,argnum=0,classname='?'):
-    typen = field.type
     name = field.name
     name = field.name
-    if field.collection:
-        typen = 'LIST'
-    return template_convert_single.format(type=typen,name=name,argnum=argnum,classname=classname,full_type=field.fullspec)
+    return template_convert_single.format(name=name,argnum=argnum,classname=classname,full_type=field.fullspec)
 
 
 def count_args_up(entity,schema):
 def count_args_up(entity,schema):
     return len(entity.members) + (count_args_up(schema.entities[entity.parent],schema) if entity.parent else 0)
     return len(entity.members) + (count_args_up(schema.entities[entity.parent],schema) if entity.parent else 0)
@@ -218,7 +215,7 @@ def get_derived(e,schema):
     return res
     return res
 
 
 def get_hierarchy(e,schema):
 def get_hierarchy(e,schema):
-    return get_derived(e.schema)+[e.name]+get_base_classes(e,schema)
+    return get_derived(e, schema)+[e.name]+get_base_classes(e,schema)
 
 
 def sort_entity_list(schema):
 def sort_entity_list(schema):
     deps = []
     deps = []
@@ -300,5 +297,8 @@ def work(filename):
         with open(output_file_cpp,'wt') as outp:
         with open(output_file_cpp,'wt') as outp:
             outp.write(inp.read().replace('{schema-static-table}',schema_table).replace('{converter-impl}',converters))
             outp.write(inp.read().replace('{schema-static-table}',schema_table).replace('{converter-impl}',converters))
 
 
+    # Finished without error, so return 0
+    return 0
+
 if __name__ == "__main__":
 if __name__ == "__main__":
     sys.exit(work(sys.argv[1] if len(sys.argv)>1 else 'schema.exp'))
     sys.exit(work(sys.argv[1] if len(sys.argv)>1 else 'schema.exp'))

+ 2 - 1
scripts/StepImporter/ExpressReader.py

@@ -43,7 +43,8 @@
 """Parse an EXPRESS file and extract basic information on all
 """Parse an EXPRESS file and extract basic information on all
 entities and data types contained"""
 entities and data types contained"""
 
 
-import sys, os, re
+import sys
+import re
 from collections import OrderedDict
 from collections import OrderedDict
 
 
 re_match_entity = re.compile(r"""
 re_match_entity = re.compile(r"""

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

@@ -2,7 +2,7 @@
 Open Asset Import Library (ASSIMP)
 Open Asset Import Library (ASSIMP)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2018, ASSIMP Development Team
+Copyright (c) 2006-2019, ASSIMP Development Team
 All rights reserved.
 All rights reserved.
 
 
 Redistribution and use of this software in source and binary forms, 
 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)
 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)
 Open Asset Import Library (ASSIMP)
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 
 
-Copyright (c) 2006-2018, ASSIMP Development Team
+Copyright (c) 2006-2019, ASSIMP Development Team
 All rights reserved.
 All rights reserved.
 
 
 Redistribution and use of this software in source and binary forms, 
 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 Assimp {
 namespace StepFile {
 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}
 {predefs}
 {entities}
 {entities}
@@ -73,11 +71,12 @@ namespace StepFile {
 	void GetSchema(EXPRESS::ConversionSchema& out);
 	void GetSchema(EXPRESS::ConversionSchema& out);
 
 
 } //! StepFile
 } //! StepFile
+
 namespace STEP {
 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)
 #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];
     auto pMesh = scene.mMeshes[0];
 
 
-    long numValidPoints = points.size();
+    size_t numValidPoints = points.size();
 
 
     pMesh->mVertices = new aiVector3D[numValidPoints];
     pMesh->mVertices = new aiVector3D[numValidPoints];
-    pMesh->mNumVertices = numValidPoints;
+    pMesh->mNumVertices = static_cast<unsigned int>( numValidPoints );
 
 
     int i = 0;
     int i = 0;
     for (XYZ &p : points) {
     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");
+}
+

+ 3 - 6
tools/assimp_cmd/ImageExtractor.cpp

@@ -228,7 +228,8 @@ int DoExport(const aiTexture* tx, FILE* p, const std::string& extension,
 // Implementation of the assimp extract utility
 // Implementation of the assimp extract utility
 int Assimp_Extract (const char* const* params, unsigned int num)
 int Assimp_Extract (const char* const* params, unsigned int num)
 {
 {
-    const char* const invalid = "assimp extract: Invalid number of arguments. See \'assimp extract --help\'\n"; 
+    const char* const invalid = "assimp extract: Invalid number of arguments. See \'assimp extract --help\'\n";
+    // assimp extract in out [options]
     if (num < 1) {
     if (num < 1) {
         printf(invalid);
         printf(invalid);
         return 1;
         return 1;
@@ -240,11 +241,7 @@ int Assimp_Extract (const char* const* params, unsigned int num)
         return 0;
         return 0;
     }
     }
 
 
-    // asssimp extract in out [options]
-    if (num < 1) {
-        printf(invalid);
-        return 1;
-    }
+
 
 
     std::string in  = std::string(params[0]);
     std::string in  = std::string(params[0]);
     std::string out = (num > 1 ? std::string(params[1]) : "-");
     std::string out = (num > 1 ? std::string(params[1]) : "-");

+ 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>
 #include <string>
 
 
 const char* AICMD_MSG_INFO_HELP_E =
 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.
 // this is well supported on pretty much any linux terminal.
 // if this causes problems on some platform,
 // if this causes problems on some platform,
 // put an #ifdef to use the ascii version for that 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)
 unsigned int CountNodes(const aiNode* root)
@@ -280,14 +279,7 @@ void PrintHierarchy(
 
 
 // -----------------------------------------------------------------------------------
 // -----------------------------------------------------------------------------------
 // Implementation of the assimp info utility to print basic file info
 // 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
 	// --help
 	if (!strcmp( params[0],"-h")||!strcmp( params[0],"--help")||!strcmp( params[0],"-?") ) {
 	if (!strcmp( params[0],"-h")||!strcmp( params[0],"--help")||!strcmp( params[0],"-?") ) {
 		printf("%s",AICMD_MSG_INFO_HELP_E);
 		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)
 void ChangeInteger(uint32_t ofs,uint32_t n)
 {
 {
 	const uint32_t cur = ftell(out);
 	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. "
 	const char* fail = "assimp dump: Invalid number of arguments. "
 			"See \'assimp dump --help\'\r\n";
 			"See \'assimp dump --help\'\r\n";
-	if (num < 1) {
-		printf("%s", fail);
-		return 1;
-	}
 
 
 	// --help
 	// --help
 	if (!strcmp( params[0], "-h") || !strcmp( params[0], "--help") || !strcmp( params[0], "-?") ) {
 	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;
     delete mAnimEvaluator;  mAnimEvaluator = nullptr;
     mNodesByName.clear();
     mNodesByName.clear();
 
 
-    mCurrentAnimIndex = pAnimIndex;
+    mCurrentAnimIndex = static_cast<int>( pAnimIndex );
 
 
     // create the internal node tree. Do this even in case of invalid animation index
     // 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
     // so that the transformation matrices are properly set up to mimic the current scene

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно