Browse Source

Merge branch 'master' into kimkulling-patch-3

Kim Kulling 6 years ago
parent
commit
4a46717a77
100 changed files with 4224 additions and 2059 deletions
  1. 8 0
      .gitignore
  2. 1 1
      .travis.yml
  3. 29 14
      Build.md
  4. 53 20
      CMakeLists.txt
  5. 7 4
      INSTALL
  6. 4 1
      Readme.md
  7. 8 2
      appveyor.yml
  8. 66 34
      assimpTargets-debug.cmake.in
  9. 66 33
      assimpTargets-release.cmake.in
  10. 5 1
      assimpTargets.cmake.in
  11. 9 7
      code/3DSLoader.cpp
  12. 1 1
      code/ASELoader.cpp
  13. 0 1
      code/ASEParser.cpp
  14. 5 5
      code/ASEParser.h
  15. 2 2
      code/AssbinLoader.cpp
  16. 0 2
      code/AssxmlExporter.cpp
  17. 1 1
      code/BlenderDNA.h
  18. 27 0
      code/BlenderLoader.cpp
  19. 4 1
      code/BlenderScene.cpp
  20. 4 0
      code/BlenderScene.h
  21. 63 92
      code/C4DImporter.cpp
  22. 6 12
      code/C4DImporter.h
  23. 156 34
      code/CMakeLists.txt
  24. 5 5
      code/COBLoader.cpp
  25. 73 39
      code/ColladaExporter.cpp
  26. 40 20
      code/ColladaLoader.cpp
  27. 77 3
      code/ColladaParser.cpp
  28. 15 0
      code/ColladaParser.h
  29. 4 1
      code/D3MFOpcPackage.cpp
  30. 2 2
      code/Exporter.cpp
  31. 13 9
      code/FBXBinaryTokenizer.cpp
  32. 2 2
      code/FBXCommon.h
  33. 164 73
      code/FBXConverter.cpp
  34. 35 11
      code/FBXConverter.h
  35. 4 5
      code/FBXDocument.cpp
  36. 46 77
      code/FBXDocument.h
  37. 6 8
      code/FBXDocumentUtil.h
  38. 9 6
      code/FBXExportNode.cpp
  39. 6 6
      code/FBXExportNode.h
  40. 141 116
      code/FBXExportProperty.cpp
  41. 24 24
      code/FBXExportProperty.h
  42. 59 25
      code/FBXExporter.cpp
  43. 25 14
      code/FBXImportSettings.h
  44. 16 7
      code/FBXImporter.cpp
  45. 60 3
      code/FBXMaterial.cpp
  46. 6 5
      code/FBXMeshGeometry.cpp
  47. 4 4
      code/FBXParser.cpp
  48. 6 6
      code/FBXTokenizer.h
  49. 124 1
      code/FBXUtil.cpp
  50. 33 1
      code/FBXUtil.h
  51. 1 0
      code/FIReader.cpp
  52. 16 16
      code/FindInstancesProcess.cpp
  53. 6 4
      code/FindInvalidDataProcess.cpp
  54. 12 13
      code/IRRLoader.cpp
  55. 1 1
      code/Importer/IFC/IFCBoolean.cpp
  56. 3 3
      code/Importer/STEPParser/STEPFileReader.cpp
  57. 3 1
      code/ImproveCacheLocality.cpp
  58. 1 4
      code/LWOMaterial.cpp
  59. 5 5
      code/MD3FileData.h
  60. 27 25
      code/MD3Loader.cpp
  61. 9 9
      code/MD5Loader.cpp
  62. 3 0
      code/ObjFileData.h
  63. 31 10
      code/ObjFileImporter.cpp
  64. 30 10
      code/ObjFileParser.cpp
  65. 1 1
      code/ObjFileParser.h
  66. 3 3
      code/PostStepRegistry.cpp
  67. 2 2
      code/SMDLoader.cpp
  68. 1 1
      code/STLExporter.cpp
  69. 1 0
      code/STLLoader.cpp
  70. 19 18
      code/SortByPTypeProcess.cpp
  71. 20 20
      code/StandardShapes.cpp
  72. 59 52
      code/ValidateDataStructure.cpp
  73. 4 3
      code/glTF2Asset.h
  74. 7 4
      code/glTF2Asset.inl
  75. 4 4
      code/glTFAssetWriter.inl
  76. 1 1
      code/glTFExporter.cpp
  77. 0 105
      code/glTFImporter.cpp
  78. 5 5
      code/res/assimp.rc
  79. 2 2
      code/res/resource.h
  80. 8 1
      contrib/irrXML/CMakeLists.txt
  81. 9 4
      contrib/irrXML/CXMLReaderImpl.h
  82. 1 1
      contrib/irrXML/irrString.h
  83. 2 2
      contrib/irrXML/irrXML.cpp
  84. 15 9
      contrib/irrXML/irrXML.h
  85. 1 1
      contrib/poly2tri/AUTHORS
  86. 18 0
      contrib/zip/.gitignore
  87. 18 0
      contrib/zip/.travis.sh
  88. 15 3
      contrib/zip/.travis.yml
  89. 36 7
      contrib/zip/CMakeLists.txt
  90. 249 79
      contrib/zip/README.md
  91. 1 1
      contrib/zip/appveyor.yml
  92. 55 0
      contrib/zip/cmake/asan-wrapper
  93. 23 0
      contrib/zip/cmake/cmake_uninstall.cmake.in
  94. 517 317
      contrib/zip/src/miniz.h
  95. 782 496
      contrib/zip/src/zip.c
  96. 135 12
      contrib/zip/src/zip.h
  97. 12 0
      contrib/zip/test/CMakeLists.txt
  98. 415 61
      contrib/zip/test/test.c
  99. 104 0
      contrib/zip/test/test_miniz.c
  100. 7 2
      contrib/zlib/CMakeLists.txt

+ 8 - 0
.gitignore

@@ -7,6 +7,12 @@ build
 *.sln
 *.sln
 *.ncb
 *.ncb
 *.vcproj
 *.vcproj
+*.vcxproj.user
+*.VC.db
+*.VC.db-shm
+*.VC.db-wal
+*.VC.opendb
+*.ipch
 
 
 # Output
 # Output
 bin/
 bin/
@@ -17,6 +23,7 @@ CMakeLists.txt.user
 
 
 # Generated
 # Generated
 assimp.pc
 assimp.pc
+assimp.aps
 revision.h
 revision.h
 contrib/zlib/zconf.h
 contrib/zlib/zconf.h
 contrib/zlib/zlib.pc
 contrib/zlib/zlib.pc
@@ -31,6 +38,7 @@ cmake_uninstall.cmake
 *.dir/
 *.dir/
 assimp-config.cmake
 assimp-config.cmake
 assimp-config-version.cmake
 assimp-config-version.cmake
+assimpTargets*.cmake
 
 
 # MakeFile
 # MakeFile
 Makefile
 Makefile

+ 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

+ 29 - 14
Build.md

@@ -1,32 +1,32 @@
-# Install CMake
+# Build Instructions
+## Install CMake
 Asset-Importer-Lib can be build for a lot of different platforms. We are using cmake to generate the build environment for these via cmake. So you have to make sure that you have a working cmake-installation on your system. You can download it at https://cmake.org/ or for linux install it via
 Asset-Importer-Lib can be build for a lot of different platforms. We are using cmake to generate the build environment for these via cmake. So you have to make sure that you have a working cmake-installation on your system. You can download it at https://cmake.org/ or for linux install it via
-```
+```bash
 sudo apt-get install cmake
 sudo apt-get install cmake
 ```
 ```
 
 
-# Get the source
+## Get the source
 Make sure you have a working git-installation. Open a command prompt and clone the Asset-Importer-Lib via:
 Make sure you have a working git-installation. Open a command prompt and clone the Asset-Importer-Lib via:
-```
+```bash
 git clone https://github.com/assimp/assimp.git
 git clone https://github.com/assimp/assimp.git
 ```
 ```
 
 
-# Build instructions for Windows with Visual-Studio
+## Build instructions for Windows with Visual-Studio
 
 
 First you have to install Visual-Studio on your windows-system. You can get the Community-Version for free here: https://visualstudio.microsoft.com/de/downloads/
 First you have to install Visual-Studio on your windows-system. You can get the Community-Version for free here: https://visualstudio.microsoft.com/de/downloads/
 To generate the build environment for your IDE open a command prompt, navigate to your repo and type:
 To generate the build environment for your IDE open a command prompt, navigate to your repo and type:
-```
-> cmake CMakeLists.txt
+```bash
+cmake CMakeLists.txt
 ```
 ```
 This will generate the project files for the visual studio. All dependencies used to build Asset-IMporter-Lib shall be part of the repo. If you want to use you own zlib.installation this is possible as well. Check the options for it.
 This will generate the project files for the visual studio. All dependencies used to build Asset-IMporter-Lib shall be part of the repo. If you want to use you own zlib.installation this is possible as well. Check the options for it.
 
 
-# Build instructions for Windows with UWP
-See https://stackoverflow.com/questions/40803170/cmake-uwp-using-cmake-to-build-universal-windows-app
+## Build instructions for Windows with UWP
+See <https://stackoverflow.com/questions/40803170/cmake-uwp-using-cmake-to-build-universal-windows-app>
 
 
-
-# Build instrcutions for Linux / Unix
+## Build instructions for Linux / Unix
 Open a terminal and got to your repository. You can generate the makefiles and build the library via:
 Open a terminal and got to your repository. You can generate the makefiles and build the library via:
 
 
-```
+```bash
 cmake CMakeLists.txt
 cmake CMakeLists.txt
 make -j4
 make -j4
 ```
 ```
@@ -34,7 +34,23 @@ The option -j descripes the number of parallel processes for the build. In this
 
 
 If you want to use a IDE for linux you can try QTCreator for instance. 
 If you want to use a IDE for linux you can try QTCreator for instance. 
 
 
-# CMake build options
+## Build instructions for MinGW
+ Older versions of MinGW's compiler (e.g. 5.1.0) do not support the -mbig_obj flag 
+required to compile some of assimp's files, especially for debug builds.
+Version 7.3.0 of g++-mingw-w64 & gcc-mingw-w64 appears to work.
+
+Please see [CMake Cross Compiling](https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling) for general information on CMake Toolchains.
+
+Some users have had success building assimp using MinGW on Linux using [polly](https://github.com/ruslo/polly/).
+
+The following toolchain, which is not maintained by assimp, seems to work on Linux: [linux-mingw-w64-gnuxx11.cmake](https://github.com/ruslo/polly/blob/master/linux-mingw-w64-gnuxx11.cmake)
+
+The following toolchain may or may not be helpful for building assimp using MinGW on Windows (untested):
+ [mingw-cxx17.cmake](https://github.com/ruslo/polly/blob/master/mingw-cxx17.cmake)
+
+Besides the toolchain, compilation should be the same as for Linux / Unix.
+
+## CMake build options
 The cmake-build-environment provides options to configure the build. The following options can be used:
 The cmake-build-environment provides options to configure the build. The following options can be used:
 - **BUILD_SHARED_LIBS ( default ON )**: Generation of shared libs ( dll for windows, so for Linux ). Set this to OFF to get a static lib.
 - **BUILD_SHARED_LIBS ( default ON )**: Generation of shared libs ( dll for windows, so for Linux ). Set this to OFF to get a static lib.
 - **BUILD_FRAMEWORK ( default OFF, MacOnly)**: Build package as Mac OS X Framework bundle
 - **BUILD_FRAMEWORK ( default OFF, MacOnly)**: Build package as Mac OS X Framework bundle
@@ -55,4 +71,3 @@ The cmake-build-environment provides options to configure the build. The followi
 - **INJECT_DEBUG_POSTFIX( default ON )**: Inject debug postfix in .a/.so lib names
 - **INJECT_DEBUG_POSTFIX( default ON )**: Inject debug postfix in .a/.so lib names
 - **IGNORE_GIT_HASH ( default OFF )**: Don't call git to get the hash.
 - **IGNORE_GIT_HASH ( default OFF )**: Don't call git to get the hash.
 - **ASSIMP_INSTALL_PDB ( default ON )**: Install MSVC debug files.
 - **ASSIMP_INSTALL_PDB ( default ON )**: Install MSVC debug files.
-

+ 53 - 20
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."
@@ -106,7 +106,7 @@ OPTION ( BUILD_DOCS
   OFF
   OFF
 )
 )
 OPTION( INJECT_DEBUG_POSTFIX
 OPTION( INJECT_DEBUG_POSTFIX
-  "Inject debug postfix in .a/.so lib names"
+  "Inject debug postfix in .a/.so/.dll lib names"
   ON
   ON
 )
 )
 
 
@@ -127,12 +127,15 @@ if (WIN32)
   ADD_DEFINITIONS( -DWIN32_LEAN_AND_MEAN )
   ADD_DEFINITIONS( -DWIN32_LEAN_AND_MEAN )
 endif()
 endif()
 
 
-
 IF(MSVC)
 IF(MSVC)
   OPTION( ASSIMP_INSTALL_PDB
   OPTION( ASSIMP_INSTALL_PDB
     "Install MSVC debug files."
     "Install MSVC debug files."
     ON
     ON
   )
   )
+  IF(NOT (MSVC_VERSION LESS 1900))
+    # Multibyte character set is deprecated since at least MSVC2015 (possibly earlier)
+    ADD_DEFINITIONS( -DUNICODE -D_UNICODE )
+  ENDIF()
 ENDIF(MSVC)
 ENDIF(MSVC)
 
 
 IF (BUILD_FRAMEWORK)
 IF (BUILD_FRAMEWORK)
@@ -148,8 +151,8 @@ ELSE()
 ENDIF(NOT BUILD_SHARED_LIBS)
 ENDIF(NOT BUILD_SHARED_LIBS)
 
 
 # Define here the needed parameters
 # Define here the needed parameters
-SET (ASSIMP_VERSION_MAJOR 4)
-SET (ASSIMP_VERSION_MINOR 1)
+SET (ASSIMP_VERSION_MAJOR 5)
+SET (ASSIMP_VERSION_MINOR 0)
 SET (ASSIMP_VERSION_PATCH 0)
 SET (ASSIMP_VERSION_PATCH 0)
 SET (ASSIMP_VERSION ${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}.${ASSIMP_VERSION_PATCH})
 SET (ASSIMP_VERSION ${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}.${ASSIMP_VERSION_PATCH})
 SET (ASSIMP_SOVERSION 4)
 SET (ASSIMP_SOVERSION 4)
@@ -157,7 +160,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)
@@ -198,7 +201,7 @@ CONFIGURE_FILE(
   ${CMAKE_CURRENT_BINARY_DIR}/include/assimp/config.h
   ${CMAKE_CURRENT_BINARY_DIR}/include/assimp/config.h
 )
 )
 
 
-INCLUDE_DIRECTORIES(
+INCLUDE_DIRECTORIES( BEFORE
   ./
   ./
   include
   include
   ${CMAKE_CURRENT_BINARY_DIR}
   ${CMAKE_CURRENT_BINARY_DIR}
@@ -234,6 +237,11 @@ ELSEIF ( "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" )
   SET(CMAKE_CXX_FLAGS "-g -fvisibility=hidden -fPIC -fno-strict-aliasing -Wall -Wno-long-long -std=c++11 ${CMAKE_CXX_FLAGS}" )
   SET(CMAKE_CXX_FLAGS "-g -fvisibility=hidden -fPIC -fno-strict-aliasing -Wall -Wno-long-long -std=c++11 ${CMAKE_CXX_FLAGS}" )
   SET(CMAKE_C_FLAGS "-fPIC -fno-strict-aliasing ${CMAKE_C_FLAGS}")
   SET(CMAKE_C_FLAGS "-fPIC -fno-strict-aliasing ${CMAKE_C_FLAGS}")
 ELSEIF( CMAKE_COMPILER_IS_MINGW )
 ELSEIF( CMAKE_COMPILER_IS_MINGW )
+  IF (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0)
+    message(FATAL_ERROR "MinGW is too old to be supported. Please update MinGW and try again.")
+  ELSEIF(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.3)
+    message(WARNING "MinGW is old, if you experience errors, update MinGW.")
+  ENDIF()
   SET( CMAKE_CXX_FLAGS "-fvisibility=hidden -fno-strict-aliasing -Wall -Wno-long-long -std=c++11 -Wa,-mbig-obj ${CMAKE_CXX_FLAGS}" )
   SET( CMAKE_CXX_FLAGS "-fvisibility=hidden -fno-strict-aliasing -Wall -Wno-long-long -std=c++11 -Wa,-mbig-obj ${CMAKE_CXX_FLAGS}" )
   SET(CMAKE_C_FLAGS "-fPIC -fno-strict-aliasing ${CMAKE_C_FLAGS} ")
   SET(CMAKE_C_FLAGS "-fPIC -fno-strict-aliasing ${CMAKE_C_FLAGS} ")
   ADD_DEFINITIONS( -U__STRICT_ANSI__ )
   ADD_DEFINITIONS( -U__STRICT_ANSI__ )
@@ -247,6 +255,7 @@ IF (CMAKE_BUILD_TYPE STREQUAL "Debug")
 ELSE()
 ELSE()
   SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fembed-bitcode -O3")
   SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fembed-bitcode -O3")
   SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fembed-bitcode -O3")
   SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fembed-bitcode -O3")
+  # Experimental for pdb generation
 ENDIF()
 ENDIF()
 
 
 ENDIF( IOS )
 ENDIF( IOS )
@@ -301,7 +310,9 @@ SET( ASSIMP_INCLUDE_INSTALL_DIR "include" CACHE STRING
 SET( ASSIMP_BIN_INSTALL_DIR "bin" CACHE STRING
 SET( ASSIMP_BIN_INSTALL_DIR "bin" CACHE STRING
   "Path the tool executables are installed to." )
   "Path the tool executables are installed to." )
 
 
-IF (CMAKE_BUILD_TYPE STREQUAL "Debug")
+get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG)
+
+IF (is_multi_config OR (CMAKE_BUILD_TYPE STREQUAL "Debug"))
   SET(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "Debug Postfix for lib, samples and tools")
   SET(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "Debug Postfix for lib, samples and tools")
 ELSE()
 ELSE()
   SET(CMAKE_DEBUG_POSTFIX "" CACHE STRING "Debug Postfix for lib, samples and tools")
   SET(CMAKE_DEBUG_POSTFIX "" CACHE STRING "Debug Postfix for lib, samples and tools")
@@ -317,16 +328,24 @@ ENDIF()
 # cmake configuration files
 # cmake configuration files
 CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimp-config.cmake.in"         "${CMAKE_CURRENT_BINARY_DIR}/assimp-config.cmake" @ONLY IMMEDIATE)
 CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimp-config.cmake.in"         "${CMAKE_CURRENT_BINARY_DIR}/assimp-config.cmake" @ONLY IMMEDIATE)
 CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimpTargets.cmake.in"         "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets.cmake" @ONLY IMMEDIATE)
 CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimpTargets.cmake.in"         "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets.cmake" @ONLY IMMEDIATE)
-CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimpTargets-debug.cmake.in"   "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets-debug.cmake" @ONLY IMMEDIATE)
-CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimpTargets-release.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets-release.cmake" @ONLY IMMEDIATE)
+IF (is_multi_config)
+  CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimpTargets-debug.cmake.in"   "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets-debug.cmake" @ONLY IMMEDIATE)
+  CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimpTargets-release.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets-release.cmake" @ONLY IMMEDIATE)
+  SET(PACKAGE_TARGETS_FILE "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets-debug.cmake" "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets-release.cmake")
+ELSEIF (CMAKE_BUILD_TYPE STREQUAL Debug)
+  CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimpTargets-debug.cmake.in"   "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets-debug.cmake" @ONLY IMMEDIATE)
+  SET(PACKAGE_TARGETS_FILE "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets-debug.cmake")
+ELSE()
+  CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimpTargets-release.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets-release.cmake" @ONLY IMMEDIATE)
+  SET(PACKAGE_TARGETS_FILE "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets-release.cmake")
+ENDIF()
 CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimp-config-version.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/assimp-config-version.cmake" @ONLY IMMEDIATE)
 CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/assimp-config-version.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/assimp-config-version.cmake" @ONLY IMMEDIATE)
 #we should generated these scripts after CMake VERSION 3.0.2 using export(EXPORT ...) and write_basic_package_version_file(...)
 #we should generated these scripts after CMake VERSION 3.0.2 using export(EXPORT ...) and write_basic_package_version_file(...)
 INSTALL(FILES 
 INSTALL(FILES 
   "${CMAKE_CURRENT_BINARY_DIR}/assimp-config.cmake"
   "${CMAKE_CURRENT_BINARY_DIR}/assimp-config.cmake"
   "${CMAKE_CURRENT_BINARY_DIR}/assimp-config-version.cmake"
   "${CMAKE_CURRENT_BINARY_DIR}/assimp-config-version.cmake"
   "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets.cmake"
   "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets.cmake"
-  "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets-debug.cmake"
-  "${CMAKE_CURRENT_BINARY_DIR}/assimpTargets-release.cmake"
+  ${PACKAGE_TARGETS_FILE}
   DESTINATION "${ASSIMP_LIB_INSTALL_DIR}/cmake/assimp-${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}" COMPONENT ${LIBASSIMP-DEV_COMPONENT})
   DESTINATION "${ASSIMP_LIB_INSTALL_DIR}/cmake/assimp-${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}" COMPONENT ${LIBASSIMP-DEV_COMPONENT})
 
 
 FIND_PACKAGE( DirectX )
 FIND_PACKAGE( DirectX )
@@ -351,6 +370,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)
@@ -468,6 +496,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 )
@@ -533,18 +562,22 @@ if(WIN32)
   if (CMAKE_SIZEOF_VOID_P EQUAL 8)
   if (CMAKE_SIZEOF_VOID_P EQUAL 8)
     SET(BIN_DIR "${PROJECT_SOURCE_DIR}/bin64/")
     SET(BIN_DIR "${PROJECT_SOURCE_DIR}/bin64/")
     SET(LIB_DIR "${PROJECT_SOURCE_DIR}/lib64/")
     SET(LIB_DIR "${PROJECT_SOURCE_DIR}/lib64/")
-  elseif()
+  else()
     SET(BIN_DIR "${PROJECT_SOURCE_DIR}/bin32/")
     SET(BIN_DIR "${PROJECT_SOURCE_DIR}/bin32/")
     SET(LIB_DIR "${PROJECT_SOURCE_DIR}/lib32/")
     SET(LIB_DIR "${PROJECT_SOURCE_DIR}/lib32/")
   ENDIF()
   ENDIF()
 
 
-  IF(MSVC12)
-    SET(ASSIMP_MSVC_VERSION "vc120")
-  ELSEIF(MSVC14)
-    SET(ASSIMP_MSVC_VERSION "vc140")
-  ELSEIF(MSVC15)
-    SET(ASSIMP_MSVC_VERSION "vc141")
-  ENDIF(MSVC12)
+  IF(MSVC_TOOLSET_VERSION)
+    set(MSVC_PREFIX "vc${MSVC_TOOLSET_VERSION}")
+  ELSE()
+    IF(MSVC12)
+      SET(ASSIMP_MSVC_VERSION "vc120")
+    ELSEIF(MSVC14)
+      SET(ASSIMP_MSVC_VERSION "vc140")
+    ELSEIF(MSVC15)
+      SET(ASSIMP_MSVC_VERSION "vc141")
+    ENDIF(MSVC12)
+  ENDIF()
 
 
   IF(MSVC12 OR MSVC14 OR MSVC15 )
   IF(MSVC12 OR MSVC14 OR MSVC15 )
     ADD_CUSTOM_TARGET(UpdateAssimpLibsDebugSymbolsAndDLLs COMMENT "Copying Assimp Libraries ..." VERBATIM)
     ADD_CUSTOM_TARGET(UpdateAssimpLibsDebugSymbolsAndDLLs COMMENT "Copying Assimp Libraries ..." VERBATIM)

+ 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

+ 4 - 1
Readme.md

@@ -17,6 +17,8 @@ A library to import and export various 3d-model-formats including scene-post-pro
 
 
 APIs are provided for C and C++. There are various bindings to other languages (C#, Java, Python, Delphi, D). Assimp also runs on Android and iOS.
 APIs are provided for C and C++. There are various bindings to other languages (C#, Java, Python, Delphi, D). Assimp also runs on Android and iOS.
 
 
+[Check the latest doc](https://assimp-docs.readthedocs.io/en/latest/).
+
 Additionally, assimp features various __mesh post processing tools__: normals and tangent space generation, triangulation, vertex cache locality optimization, removal of degenerate primitives and duplicate vertices, sorting by primitive type, merging of redundant materials and many more.
 Additionally, assimp features various __mesh post processing tools__: normals and tangent space generation, triangulation, vertex cache locality optimization, removal of degenerate primitives and duplicate vertices, sorting by primitive type, merging of redundant materials and many more.
 
 
 This is the development repo containing the latest features and bugfixes. For productive use though, we recommend one of the stable releases available from [Github Assimp Releases](https://github.com/assimp/assimp/releases).
 This is the development repo containing the latest features and bugfixes. For productive use though, we recommend one of the stable releases available from [Github Assimp Releases](https://github.com/assimp/assimp/releases).
@@ -99,7 +101,7 @@ __Importers__:
 
 
 Additionally, some formats are supported by dependency on non-free code or external SDKs (not built by default):
 Additionally, some formats are supported by dependency on non-free code or external SDKs (not built by default):
 
 
-- [C4D](https://en.wikipedia.org/wiki/Cinema_4D) (https://github.com/assimp/assimp/wiki/Cinema4D-&-Melange)
+- [C4D](https://en.wikipedia.org/wiki/Cinema_4D) (https://github.com/assimp/assimp/wiki/Cinema4D-&-Melange) IMporting geometry + node hierarchy are currently supported
 
 
 __Exporters__:
 __Exporters__:
 
 
@@ -128,6 +130,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.

+ 8 - 2
appveyor.yml

@@ -31,7 +31,7 @@ install:
   - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" set CMAKE_GENERATOR_NAME=Visual Studio 14 2015
   - 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%"=="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"
   - ps: Invoke-WebRequest -Uri https://download.microsoft.com/download/5/7/b/57b2947c-7221-4f33-b35e-2fc78cb10df4/vc_redist.x64.exe -OutFile .\packaging\windows-innosetup\vc_redist.x64.exe
   - ps: Invoke-WebRequest -Uri https://download.microsoft.com/download/5/7/b/57b2947c-7221-4f33-b35e-2fc78cb10df4/vc_redist.x64.exe -OutFile .\packaging\windows-innosetup\vc_redist.x64.exe
   - ps: Invoke-WebRequest -Uri https://download.microsoft.com/download/1/d/8/1d8137db-b5bb-4925-8c5d-927424a2e4de/vc_redist.x86.exe -OutFile .\packaging\windows-innosetup\vc_redist.x86.exe
   - ps: Invoke-WebRequest -Uri https://download.microsoft.com/download/1/d/8/1d8137db-b5bb-4925-8c5d-927424a2e4de/vc_redist.x86.exe -OutFile .\packaging\windows-innosetup\vc_redist.x86.exe
@@ -53,7 +53,13 @@ build:
   project: Assimp.sln
   project: Assimp.sln
   
   
 after_build:
 after_build:
-  - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" iscc packaging\windows-innosetup\script.iss
+  - if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" (
+        if "%platform%"=="x64" (
+            iscc packaging\windows-innosetup\script_x64.iss
+        ) else (
+            iscc packaging\windows-innosetup\script_x86.iss
+        )
+    )
   - 7z a assimp.7z bin\%CONFIGURATION%\* lib\%CONFIGURATION%\*
   - 7z a assimp.7z bin\%CONFIGURATION%\* lib\%CONFIGURATION%\*
 
 
 test_script:
 test_script:

+ 66 - 34
assimpTargets-debug.cmake.in

@@ -5,48 +5,75 @@
 # Commands may need to know the format version.
 # Commands may need to know the format version.
 set(CMAKE_IMPORT_FILE_VERSION 1)
 set(CMAKE_IMPORT_FILE_VERSION 1)
 
 
+set(ASSIMP_BUILD_SHARED_LIBS @BUILD_SHARED_LIBS@)
+
 if(MSVC)
 if(MSVC)
-  if( MSVC70 OR MSVC71 )
-    set(MSVC_PREFIX "vc70")
-  elseif( MSVC80 )
-    set(MSVC_PREFIX "vc80")
-  elseif( MSVC90 )
-    set(MSVC_PREFIX "vc90")
-  elseif( MSVC10 )
-    set(MSVC_PREFIX "vc100")
-  elseif( MSVC11 )
-    set(MSVC_PREFIX "vc110")
-  elseif( MSVC12 )
-    set(MSVC_PREFIX "vc120")
-  elseif( MSVC14 )
-    set(MSVC_PREFIX "vc140")
+  if(MSVC_TOOLSET_VERSION)
+    set(MSVC_PREFIX "vc${MSVC_TOOLSET_VERSION}")
   else()
   else()
-    set(MSVC_PREFIX "vc150")
+    if( MSVC70 OR MSVC71 )
+      set(MSVC_PREFIX "vc70")
+    elseif( MSVC80 )
+      set(MSVC_PREFIX "vc80")
+    elseif( MSVC90 )
+      set(MSVC_PREFIX "vc90")
+    elseif( MSVC10 )
+      set(MSVC_PREFIX "vc100")
+    elseif( MSVC11 )
+      set(MSVC_PREFIX "vc110")
+    elseif( MSVC12 )
+      set(MSVC_PREFIX "vc120")
+    elseif( MSVC14 )
+      set(MSVC_PREFIX "vc140")
+    else()
+      set(MSVC_PREFIX "vc150")
+    endif()
   endif()
   endif()
   set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@-${MSVC_PREFIX}-mt" CACHE STRING "the suffix for the assimp windows library" )
   set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@-${MSVC_PREFIX}-mt" CACHE STRING "the suffix for the assimp windows library" )
 
 
-  set(sharedLibraryName "assimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_SHARED_LIBRARY_SUFFIX@")
-  set(importLibraryName "assimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_IMPORT_LIBRARY_SUFFIX@")
+  if(ASSIMP_BUILD_SHARED_LIBS)
+    set(sharedLibraryName "assimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_SHARED_LIBRARY_SUFFIX@")
+    set(importLibraryName "assimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_IMPORT_LIBRARY_SUFFIX@")
+
+    # Import target "assimp::assimp" for configuration "Debug"
+    set_property(TARGET assimp::assimp APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG)
+    set_target_properties(assimp::assimp PROPERTIES
+      IMPORTED_IMPLIB_DEBUG "${_IMPORT_PREFIX}/lib/${importLibraryName}"
+      IMPORTED_LOCATION_DEBUG "${_IMPORT_PREFIX}/bin/${sharedLibraryName}"
+    )
+    list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
+    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/lib/${importLibraryName}")
+    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/bin/${sharedLibraryName}" )
+  else()
+    set(staticLibraryName "assimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_STATIC_LIBRARY_SUFFIX@")
 
 
-  # Import target "assimp::assimp" for configuration "Debug"
-  set_property(TARGET assimp::assimp APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG)
-  set_target_properties(assimp::assimp PROPERTIES
-    IMPORTED_IMPLIB_DEBUG "${_IMPORT_PREFIX}/lib/${importLibraryName}"
-    IMPORTED_LOCATION_DEBUG "${_IMPORT_PREFIX}/bin/${sharedLibraryName}"
-    ) 
-  list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
-  list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/lib/${importLibraryName}")
-  list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/bin/${sharedLibraryName}" )
+    # Import target "assimp::assimp" for configuration "Debug"
+    set_property(TARGET assimp::assimp APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG)
+    set_target_properties(assimp::assimp PROPERTIES
+      IMPORTED_LOCATION_DEBUG "${_IMPORT_PREFIX}/lib/${staticLibraryName}"
+    )
+    list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
+    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/lib/${staticLibraryName}")
+  endif()
 
 
 else()
 else()
-  set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@" CACHE STRING "the suffix for the openrave libraries" )
-  set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_SHARED_LIBRARY_SUFFIX@.@ASSIMP_VERSION_MAJOR@")
-  set_target_properties(assimp::assimp PROPERTIES
-    IMPORTED_SONAME_DEBUG "${sharedLibraryName}"
-    IMPORTED_LOCATION_DEBUG "${_IMPORT_PREFIX}/lib/${sharedLibraryName}"
+  set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@" CACHE STRING "the suffix for the assimp libraries" )
+  if(ASSIMP_BUILD_SHARED_LIBS)
+    set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_SHARED_LIBRARY_SUFFIX@.@ASSIMP_VERSION_MAJOR@")
+    set_target_properties(assimp::assimp PROPERTIES
+      IMPORTED_SONAME_DEBUG "${sharedLibraryName}"
+      IMPORTED_LOCATION_DEBUG "${_IMPORT_PREFIX}/lib/${sharedLibraryName}"
     )
     )
-  list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
-  list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/lib/${sharedLibraryName}" )
+    list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
+    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/lib/${sharedLibraryName}" )
+  else()
+    set(staticLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_DEBUG_POSTFIX@@CMAKE_STATIC_LIBRARY_SUFFIX@")
+    set_target_properties(assimp::assimp PROPERTIES
+      IMPORTED_LOCATION_DEBUG "${_IMPORT_PREFIX}/lib/${staticLibraryName}"
+    )
+    list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
+    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/lib/${staticLibraryName}" )
+  endif()
 endif()
 endif()
 
 
 
 
@@ -60,7 +87,11 @@ set( ASSIMP_CXX_FLAGS ) # dynamically linked library
 set( ASSIMP_LINK_FLAGS "" )
 set( ASSIMP_LINK_FLAGS "" )
 set( ASSIMP_LIBRARY_DIRS "${ASSIMP_ROOT_DIR}/@ASSIMP_LIB_INSTALL_DIR@")
 set( ASSIMP_LIBRARY_DIRS "${ASSIMP_ROOT_DIR}/@ASSIMP_LIB_INSTALL_DIR@")
 set( ASSIMP_INCLUDE_DIRS "${ASSIMP_ROOT_DIR}/@ASSIMP_INCLUDE_INSTALL_DIR@")
 set( ASSIMP_INCLUDE_DIRS "${ASSIMP_ROOT_DIR}/@ASSIMP_INCLUDE_INSTALL_DIR@")
-set( ASSIMP_LIBRARIES ${sharedLibraryName})
+if(ASSIMP_BUILD_SHARED_LIBS)
+  set( ASSIMP_LIBRARIES ${sharedLibraryName})
+else()
+  set( ASSIMP_LIBRARIES ${staticLibraryName})
+endif()
 
 
 # for compatibility with pkg-config
 # for compatibility with pkg-config
 set(ASSIMP_CFLAGS_OTHER "${ASSIMP_CXX_FLAGS}")
 set(ASSIMP_CFLAGS_OTHER "${ASSIMP_CXX_FLAGS}")
@@ -75,4 +106,5 @@ MARK_AS_ADVANCED(
   ASSIMP_CFLAGS_OTHER
   ASSIMP_CFLAGS_OTHER
   ASSIMP_LDFLAGS_OTHER
   ASSIMP_LDFLAGS_OTHER
   ASSIMP_LIBRARY_SUFFIX
   ASSIMP_LIBRARY_SUFFIX
+  ASSIMP_BUILD_SHARED_LIBS
 )
 )

+ 66 - 33
assimpTargets-release.cmake.in

@@ -5,59 +5,91 @@
 # Commands may need to know the format version.
 # Commands may need to know the format version.
 set(CMAKE_IMPORT_FILE_VERSION 1)
 set(CMAKE_IMPORT_FILE_VERSION 1)
 
 
+set(ASSIMP_BUILD_SHARED_LIBS @BUILD_SHARED_LIBS@)
+
 if(MSVC)
 if(MSVC)
-  if( MSVC70 OR MSVC71 )
-    set(MSVC_PREFIX "vc70")
-  elseif( MSVC80 )
-    set(MSVC_PREFIX "vc80")
-  elseif( MSVC90 )
-    set(MSVC_PREFIX "vc90")
-  elseif( MSVC10 )
-    set(MSVC_PREFIX "vc100")
-  elseif( MSVC11 )
-    set(MSVC_PREFIX "vc110")
-  elseif( MSVC12 )
-    set(MSVC_PREFIX "vc120")
-  elseif( MSVC14 )
-    set(MSVC_PREFIX "vc140")
+  if(MSVC_TOOLSET_VERSION)
+    set(MSVC_PREFIX "vc${MSVC_TOOLSET_VERSION}")
   else()
   else()
-    set(MSVC_PREFIX "vc150")
+    if( MSVC70 OR MSVC71 )
+      set(MSVC_PREFIX "vc70")
+    elseif( MSVC80 )
+      set(MSVC_PREFIX "vc80")
+    elseif( MSVC90 )
+      set(MSVC_PREFIX "vc90")
+    elseif( MSVC10 )
+      set(MSVC_PREFIX "vc100")
+    elseif( MSVC11 )
+      set(MSVC_PREFIX "vc110")
+    elseif( MSVC12 )
+      set(MSVC_PREFIX "vc120")
+    elseif( MSVC14 )
+      set(MSVC_PREFIX "vc140")
+    else()
+      set(MSVC_PREFIX "vc150")
+    endif()
   endif()
   endif()
   set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@-${MSVC_PREFIX}-mt" CACHE STRING "the suffix for the assimp windows library" )
   set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@-${MSVC_PREFIX}-mt" CACHE STRING "the suffix for the assimp windows library" )
 
 
-  set(sharedLibraryName "assimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_SHARED_LIBRARY_SUFFIX@")
-  set(importLibraryName "assimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_IMPORT_LIBRARY_SUFFIX@")
+  if(ASSIMP_BUILD_SHARED_LIBS)
+    set(sharedLibraryName "assimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_SHARED_LIBRARY_SUFFIX@")
+    set(importLibraryName "assimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_IMPORT_LIBRARY_SUFFIX@")
+
+    # Import target "assimp::assimp" for configuration "Release"
+    set_property(TARGET assimp::assimp APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
+    set_target_properties(assimp::assimp PROPERTIES
+      IMPORTED_IMPLIB_RELEASE "${_IMPORT_PREFIX}/lib/${importLibraryName}"
+      IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/bin/${sharedLibraryName}"
+    )
+    list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
+    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/lib/${importLibraryName}")
+    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/bin/${sharedLibraryName}" )
+  else()
+    set(staticLibraryName "assimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_STATIC_LIBRARY_SUFFIX@")
 
 
-  # Import target "assimp::assimp" for configuration "Release"
-  set_property(TARGET assimp::assimp APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
-  set_target_properties(assimp::assimp PROPERTIES
-    IMPORTED_IMPLIB_RELEASE "${_IMPORT_PREFIX}/lib/${importLibraryName}"
-    IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/bin/${sharedLibraryName}"
+    # Import target "assimp::assimp" for configuration "Release"
+    set_property(TARGET assimp::assimp APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
+    set_target_properties(assimp::assimp PROPERTIES
+      IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/${staticLibraryName}"
     )
     )
-  list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
-  list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/lib/${importLibraryName}")
-  list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/bin/${sharedLibraryName}" )
+    list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
+    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/lib/${staticLibraryName}")
+  endif()
 
 
 else()
 else()
-  set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@" CACHE STRING "the suffix for the openrave libraries" )
-  set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_SHARED_LIBRARY_SUFFIX@.@ASSIMP_VERSION_MAJOR@")
-  set_target_properties(assimp::assimp PROPERTIES
-    IMPORTED_SONAME_RELEASE "${sharedLibraryName}"
-    IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/${sharedLibraryName}"
+  set(ASSIMP_LIBRARY_SUFFIX "@ASSIMP_LIBRARY_SUFFIX@" CACHE STRING "the suffix for the assimp libraries" )
+  if(ASSIMP_BUILD_SHARED_LIBS)
+    set(sharedLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_SHARED_LIBRARY_SUFFIX@.@ASSIMP_VERSION_MAJOR@")
+    set_target_properties(assimp::assimp PROPERTIES
+      IMPORTED_SONAME_RELEASE "${sharedLibraryName}"
+      IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/${sharedLibraryName}"
     )
     )
-  list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
-  list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/lib/${sharedLibraryName}" )
+    list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
+    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/lib/${sharedLibraryName}" )
+  else()
+    set(staticLibraryName "libassimp${ASSIMP_LIBRARY_SUFFIX}@CMAKE_STATIC_LIBRARY_SUFFIX@")
+    set_target_properties(assimp::assimp PROPERTIES
+      IMPORTED_LOCATION_RELEASE "${_IMPORT_PREFIX}/lib/${staticLibraryName}"
+    )
+    list(APPEND _IMPORT_CHECK_TARGETS assimp::assimp )
+    list(APPEND _IMPORT_CHECK_FILES_FOR_assimp::assimp "${_IMPORT_PREFIX}/lib/${staticLibraryName}" )
+  endif()
 endif()
 endif()
 
 
 # Commands beyond this point should not need to know the version.
 # Commands beyond this point should not need to know the version.
 set(CMAKE_IMPORT_FILE_VERSION)
 set(CMAKE_IMPORT_FILE_VERSION)
 
 
 get_filename_component(ASSIMP_ROOT_DIR "@CMAKE_INSTALL_PREFIX@" REALPATH)
 get_filename_component(ASSIMP_ROOT_DIR "@CMAKE_INSTALL_PREFIX@" REALPATH)
+
 set( ASSIMP_CXX_FLAGS ) # dynamically linked library
 set( ASSIMP_CXX_FLAGS ) # dynamically linked library
 set( ASSIMP_LINK_FLAGS "" )
 set( ASSIMP_LINK_FLAGS "" )
 set( ASSIMP_LIBRARY_DIRS "${ASSIMP_ROOT_DIR}/@ASSIMP_LIB_INSTALL_DIR@")
 set( ASSIMP_LIBRARY_DIRS "${ASSIMP_ROOT_DIR}/@ASSIMP_LIB_INSTALL_DIR@")
 set( ASSIMP_INCLUDE_DIRS "${ASSIMP_ROOT_DIR}/@ASSIMP_INCLUDE_INSTALL_DIR@")
 set( ASSIMP_INCLUDE_DIRS "${ASSIMP_ROOT_DIR}/@ASSIMP_INCLUDE_INSTALL_DIR@")
-set( ASSIMP_LIBRARIES ${sharedLibraryName})
+if(ASSIMP_BUILD_SHARED_LIBS)
+  set( ASSIMP_LIBRARIES ${sharedLibraryName})
+else()
+  set( ASSIMP_LIBRARIES ${staticLibraryName})
+endif()
 
 
 # for compatibility with pkg-config
 # for compatibility with pkg-config
 set(ASSIMP_CFLAGS_OTHER "${ASSIMP_CXX_FLAGS}")
 set(ASSIMP_CFLAGS_OTHER "${ASSIMP_CXX_FLAGS}")
@@ -72,4 +104,5 @@ MARK_AS_ADVANCED(
   ASSIMP_CFLAGS_OTHER
   ASSIMP_CFLAGS_OTHER
   ASSIMP_LDFLAGS_OTHER
   ASSIMP_LDFLAGS_OTHER
   ASSIMP_LIBRARY_SUFFIX
   ASSIMP_LIBRARY_SUFFIX
+  ASSIMP_BUILD_SHARED_LIBS
 )
 )

+ 5 - 1
assimpTargets.cmake.in

@@ -51,7 +51,11 @@ if(_IMPORT_PREFIX STREQUAL "/")
 endif()
 endif()
 
 
 # Create imported target assimp::assimp
 # Create imported target assimp::assimp
-add_library(assimp::assimp SHARED IMPORTED)
+if(@BUILD_SHARED_LIBS@)
+  add_library(assimp::assimp SHARED IMPORTED)
+else()
+  add_library(assimp::assimp STATIC IMPORTED)
+endif()
 
 
 set_target_properties(assimp::assimp PROPERTIES
 set_target_properties(assimp::assimp PROPERTIES
   COMPATIBLE_INTERFACE_STRING "assimp_MAJOR_VERSION"
   COMPATIBLE_INTERFACE_STRING "assimp_MAJOR_VERSION"

+ 9 - 7
code/3DSLoader.cpp

@@ -249,13 +249,14 @@ void Discreet3DSImporter::ApplyMasterScale(aiScene* pScene)
 // Reads a new chunk from the file
 // Reads a new chunk from the file
 void Discreet3DSImporter::ReadChunk(Discreet3DS::Chunk* pcOut)
 void Discreet3DSImporter::ReadChunk(Discreet3DS::Chunk* pcOut)
 {
 {
-    ai_assert(pcOut != NULL);
+    ai_assert(pcOut != nullptr);
 
 
     pcOut->Flag = stream->GetI2();
     pcOut->Flag = stream->GetI2();
     pcOut->Size = stream->GetI4();
     pcOut->Size = stream->GetI4();
 
 
-    if (pcOut->Size - sizeof(Discreet3DS::Chunk) > stream->GetRemainingSize())
+    if (pcOut->Size - sizeof(Discreet3DS::Chunk) > stream->GetRemainingSize()) {
         throw DeadlyImportError("Chunk is too large");
         throw DeadlyImportError("Chunk is too large");
+    }
 
 
     if (pcOut->Size - sizeof(Discreet3DS::Chunk) > stream->GetRemainingSizeToLimit()) {
     if (pcOut->Size - sizeof(Discreet3DS::Chunk) > stream->GetRemainingSizeToLimit()) {
         ASSIMP_LOG_ERROR("3DS: Chunk overflow");
         ASSIMP_LOG_ERROR("3DS: Chunk overflow");
@@ -1343,15 +1344,16 @@ void Discreet3DSImporter::ParseTextureChunk(D3DS::Texture* pcOut)
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Read a percentage chunk
 // Read a percentage chunk
-ai_real Discreet3DSImporter::ParsePercentageChunk()
-{
+ai_real Discreet3DSImporter::ParsePercentageChunk() {
     Discreet3DS::Chunk chunk;
     Discreet3DS::Chunk chunk;
     ReadChunk(&chunk);
     ReadChunk(&chunk);
 
 
-    if (Discreet3DS::CHUNK_PERCENTF == chunk.Flag)
-        return stream->GetF4();
-    else if (Discreet3DS::CHUNK_PERCENTW == chunk.Flag)
+    if (Discreet3DS::CHUNK_PERCENTF == chunk.Flag) {
+        return stream->GetF4() * ai_real(100) / ai_real(0xFFFF);
+    } else if (Discreet3DS::CHUNK_PERCENTW == chunk.Flag) {
         return (ai_real)((uint16_t)stream->GetI2()) / (ai_real)0xFFFF;
         return (ai_real)((uint16_t)stream->GetI2()) / (ai_real)0xFFFF;
+    }
+
     return get_qnan();
     return get_qnan();
 }
 }
 
 

+ 1 - 1
code/ASELoader.cpp

@@ -1299,7 +1299,7 @@ void ASEImporter::BuildMaterialIndices()
         }
         }
     }
     }
 
 
-    // Dekete our temporary array
+    // Delete our temporary array
     delete[] pcIntMaterials;
     delete[] pcIntMaterials;
 }
 }
 
 

+ 0 - 1
code/ASEParser.cpp

@@ -45,7 +45,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *  @brief Implementation of the ASE parser class
  *  @brief Implementation of the ASE parser class
  */
  */
 
 
-
 #ifndef ASSIMP_BUILD_NO_ASE_IMPORTER
 #ifndef ASSIMP_BUILD_NO_ASE_IMPORTER
 #ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
 #ifndef ASSIMP_BUILD_NO_3DS_IMPORTER
 
 

+ 5 - 5
code/ASEParser.h

@@ -188,10 +188,11 @@ struct Animation {
     } mRotationType, mScalingType, mPositionType;
     } mRotationType, mScalingType, mPositionType;
 
 
     Animation() AI_NO_EXCEPT
     Animation() AI_NO_EXCEPT
-        :   mRotationType   (TRACK)
-        ,   mScalingType    (TRACK)
-        ,   mPositionType   (TRACK)
-    {}
+    :   mRotationType   (TRACK)
+    ,   mScalingType    (TRACK)
+    ,   mPositionType   (TRACK) {
+        // empty
+    }
 
 
     //! List of track rotation keyframes
     //! List of track rotation keyframes
     std::vector< aiQuatKey > akeyRotations;
     std::vector< aiQuatKey > akeyRotations;
@@ -243,7 +244,6 @@ struct BaseNode {
         mTargetPosition.x = qnan;
         mTargetPosition.x = qnan;
     }
     }
 
 
-
     //! Name of the mesh
     //! Name of the mesh
     std::string mName;
     std::string mName;
 
 

+ 2 - 2
code/AssbinLoader.cpp

@@ -68,7 +68,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 using namespace Assimp;
 using namespace Assimp;
 
 
 static const aiImporterDesc desc = {
 static const aiImporterDesc desc = {
-    ".assbin Importer",
+    "Assimp Binary Importer",
     "Gargaj / Conspiracy",
     "Gargaj / Conspiracy",
     "",
     "",
     "",
     "",
@@ -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");
     }
     }
 };
 };
 
 

+ 27 - 0
code/BlenderLoader.cpp

@@ -1225,6 +1225,16 @@ aiLight* BlenderImporter::ConvertLight(const Scene& /*in*/, const Object* obj, c
         case Lamp::Type_Local:
         case Lamp::Type_Local:
             out->mType = aiLightSource_POINT;
             out->mType = aiLightSource_POINT;
             break;
             break;
+        case Lamp::Type_Spot:
+            out->mType = aiLightSource_SPOT;
+
+            // blender orients directional lights as facing toward -z
+            out->mDirection = aiVector3D(0.f, 0.f, -1.f);
+            out->mUp = aiVector3D(0.f, 1.f, 0.f);
+
+            out->mAngleInnerCone = lamp->spotsize * (1.0f - lamp->spotblend);
+            out->mAngleOuterCone = lamp->spotsize;
+            break;
         case Lamp::Type_Sun:
         case Lamp::Type_Sun:
             out->mType = aiLightSource_DIRECTIONAL;
             out->mType = aiLightSource_DIRECTIONAL;
 
 
@@ -1255,6 +1265,23 @@ aiLight* BlenderImporter::ConvertLight(const Scene& /*in*/, const Object* obj, c
     out->mColorAmbient = aiColor3D(lamp->r, lamp->g, lamp->b) * lamp->energy;
     out->mColorAmbient = aiColor3D(lamp->r, lamp->g, lamp->b) * lamp->energy;
     out->mColorSpecular = aiColor3D(lamp->r, lamp->g, lamp->b) * lamp->energy;
     out->mColorSpecular = aiColor3D(lamp->r, lamp->g, lamp->b) * lamp->energy;
     out->mColorDiffuse = aiColor3D(lamp->r, lamp->g, lamp->b) * lamp->energy;
     out->mColorDiffuse = aiColor3D(lamp->r, lamp->g, lamp->b) * lamp->energy;
+
+    // If default values are supplied, compute the coefficients from light's max distance
+    // Read this: https://imdoingitwrong.wordpress.com/2011/01/31/light-attenuation/
+    //
+    if (lamp->constant_coefficient == 1.0f && lamp->linear_coefficient == 0.0f && lamp->quadratic_coefficient == 0.0f && lamp->dist > 0.0f)
+    {
+        out->mAttenuationConstant = 1.0f;
+        out->mAttenuationLinear = 2.0f / lamp->dist;
+        out->mAttenuationQuadratic = 1.0f / (lamp->dist * lamp->dist);
+    }
+    else
+    {
+        out->mAttenuationConstant = lamp->constant_coefficient;
+        out->mAttenuationLinear = lamp->linear_coefficient;
+        out->mAttenuationQuadratic = lamp->quadratic_coefficient;
+    }
+
     return out.release();
     return out.release();
 }
 }
 
 

+ 4 - 1
code/BlenderScene.cpp

@@ -211,9 +211,12 @@ template <> void Structure :: Convert<Lamp> (
     ReadField<ErrorPolicy_Warn>(dest.b,"b",db);
     ReadField<ErrorPolicy_Warn>(dest.b,"b",db);
     ReadField<ErrorPolicy_Warn>(dest.k,"k",db);
     ReadField<ErrorPolicy_Warn>(dest.k,"k",db);
     ReadField<ErrorPolicy_Igno>(dest.energy,"energy",db);
     ReadField<ErrorPolicy_Igno>(dest.energy,"energy",db);
-    ReadField<ErrorPolicy_Igno>(dest.dist,"dist",db);
+    ReadField<ErrorPolicy_Warn>(dest.dist,"dist",db);
     ReadField<ErrorPolicy_Igno>(dest.spotsize,"spotsize",db);
     ReadField<ErrorPolicy_Igno>(dest.spotsize,"spotsize",db);
     ReadField<ErrorPolicy_Igno>(dest.spotblend,"spotblend",db);
     ReadField<ErrorPolicy_Igno>(dest.spotblend,"spotblend",db);
+    ReadField<ErrorPolicy_Warn>(dest.constant_coefficient, "coeff_const", db);
+    ReadField<ErrorPolicy_Warn>(dest.linear_coefficient, "coeff_lin", db);
+    ReadField<ErrorPolicy_Warn>(dest.quadratic_coefficient, "coeff_quad", db);
     ReadField<ErrorPolicy_Igno>(dest.att1,"att1",db);
     ReadField<ErrorPolicy_Igno>(dest.att1,"att1",db);
     ReadField<ErrorPolicy_Igno>(dest.att2,"att2",db);
     ReadField<ErrorPolicy_Igno>(dest.att2,"att2",db);
     ReadField<ErrorPolicy_Igno>(temp,"falloff_type",db);
     ReadField<ErrorPolicy_Igno>(temp,"falloff_type",db);

+ 4 - 0
code/BlenderScene.h

@@ -538,6 +538,10 @@ struct Lamp : ElemBase {
       float energy, dist, spotsize, spotblend;
       float energy, dist, spotsize, spotblend;
       //float haint;
       //float haint;
 
 
+      float constant_coefficient;
+      float linear_coefficient;
+      float quadratic_coefficient;
+
       float att1, att2;
       float att1, att2;
       //struct CurveMapping *curfalloff;
       //struct CurveMapping *curfalloff;
       FalloffType falloff_type;
       FalloffType falloff_type;

+ 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

+ 156 - 34
code/CMakeLists.txt

@@ -47,6 +47,11 @@
 cmake_minimum_required( VERSION 2.6 )
 cmake_minimum_required( VERSION 2.6 )
 SET( HEADER_PATH ../include/assimp )
 SET( HEADER_PATH ../include/assimp )
 
 
+if(NOT ANDROID AND ASSIMP_ANDROID_JNIIOSYSTEM)
+    message(WARNING "Requesting Android JNI I/O-System in non-Android toolchain. Resetting ASSIMP_ANDROID_JNIIOSYSTEM to OFF.")
+    set(ASSIMP_ANDROID_JNIIOSYSTEM OFF)
+endif(NOT ANDROID AND ASSIMP_ANDROID_JNIIOSYSTEM)
+
 SET( COMPILER_HEADERS
 SET( COMPILER_HEADERS
   ${HEADER_PATH}/Compiler/pushpack1.h
   ${HEADER_PATH}/Compiler/pushpack1.h
   ${HEADER_PATH}/Compiler/poppack1.h
   ${HEADER_PATH}/Compiler/poppack1.h
@@ -140,6 +145,10 @@ SET( Core_SRCS
   Assimp.cpp
   Assimp.cpp
 )
 )
 
 
+IF(MSVC)
+  list(APPEND Core_SRCS "res/assimp.rc")
+ENDIF(MSVC)
+
 SET( Logging_SRCS
 SET( Logging_SRCS
   ${HEADER_PATH}/DefaultLogger.hpp
   ${HEADER_PATH}/DefaultLogger.hpp
   ${HEADER_PATH}/LogStream.hpp
   ${HEADER_PATH}/LogStream.hpp
@@ -211,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)
@@ -227,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
@@ -256,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
 )
 )
@@ -273,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
@@ -300,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
 )
 )
@@ -416,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
 )
 )
@@ -434,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
 )
 )
@@ -536,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
@@ -642,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
 )
 )
@@ -662,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
@@ -688,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
@@ -695,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
@@ -727,6 +804,18 @@ ADD_ASSIMP_IMPORTER( MMD
   MMDVmdParser.h
   MMDVmdParser.h
 )
 )
 
 
+# Workaround for issue #2406 - force problematic large file to be optimized to prevent string table overflow error
+# Used -Os instead of -O2 as previous issues had mentioned, since -Os is roughly speaking -O2, excluding any
+# optimizations that take up extra space. Given that the issue is a string table overflowing, -Os seemed appropriate
+# Also, I'm not positive if both link & compile flags are needed, but this hopefully ensures that the issue should not
+# recur for edge cases such as static builds.
+if ((CMAKE_COMPILER_IS_MINGW) AND (CMAKE_BUILD_TYPE MATCHES Debug))
+  message("-- Applying MinGW StepFileGen1.cpp Debug Workaround")
+  SET_SOURCE_FILES_PROPERTIES(Importer/StepFile/StepFileGen1.cpp PROPERTIES COMPILE_FLAGS -Os )
+  SET_SOURCE_FILES_PROPERTIES(Importer/StepFile/StepFileGen1.cpp PROPERTIES LINK_FLAGS -Os )
+  SET_SOURCE_FILES_PROPERTIES(Importer/StepFile/StepFileGen1.cpp PROPERTIES STATIC_LIBRARY_FLAGS -Os )
+endif()
+
 ADD_ASSIMP_IMPORTER( STEP
 ADD_ASSIMP_IMPORTER( STEP
     STEPFile.h
     STEPFile.h
     Importer/StepFile/StepFileImporter.h
     Importer/StepFile/StepFileImporter.h
@@ -735,28 +824,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
@@ -771,7 +864,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
@@ -780,7 +873,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
@@ -788,6 +881,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
@@ -805,7 +905,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
@@ -838,7 +938,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
@@ -878,8 +978,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
@@ -894,6 +997,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}
@@ -907,7 +1011,6 @@ SET( assimp_src
 
 
   ${PUBLIC_HEADERS}
   ${PUBLIC_HEADERS}
   ${COMPILER_HEADERS}
   ${COMPILER_HEADERS}
-
 )
 )
 ADD_DEFINITIONS( -DOPENDDLPARSER_BUILD )
 ADD_DEFINITIONS( -DOPENDDLPARSER_BUILD )
 
 
@@ -932,11 +1035,11 @@ TARGET_INCLUDE_DIRECTORIES ( assimp PUBLIC
 
 
 TARGET_LINK_LIBRARIES(assimp ${ZLIB_LIBRARIES} ${OPENDDL_PARSER_LIBRARIES} ${IRRXML_LIBRARY} )
 TARGET_LINK_LIBRARIES(assimp ${ZLIB_LIBRARIES} ${OPENDDL_PARSER_LIBRARIES} ${IRRXML_LIBRARY} )
 
 
-if(ANDROID AND ASSIMP_ANDROID_JNIIOSYSTEM)
+if(ASSIMP_ANDROID_JNIIOSYSTEM)
   set(ASSIMP_ANDROID_JNIIOSYSTEM_PATH port/AndroidJNI)
   set(ASSIMP_ANDROID_JNIIOSYSTEM_PATH port/AndroidJNI)
   add_subdirectory(../${ASSIMP_ANDROID_JNIIOSYSTEM_PATH}/ ../${ASSIMP_ANDROID_JNIIOSYSTEM_PATH}/)
   add_subdirectory(../${ASSIMP_ANDROID_JNIIOSYSTEM_PATH}/ ../${ASSIMP_ANDROID_JNIIOSYSTEM_PATH}/)
   target_link_libraries(assimp android_jniiosystem)
   target_link_libraries(assimp android_jniiosystem)
-endif(ANDROID AND ASSIMP_ANDROID_JNIIOSYSTEM)
+endif(ASSIMP_ANDROID_JNIIOSYSTEM)
 
 
 IF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
 IF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
   TARGET_LINK_LIBRARIES(assimp optimized ${C4D_RELEASE_LIBRARIES})
   TARGET_LINK_LIBRARIES(assimp optimized ${C4D_RELEASE_LIBRARIES})
@@ -946,6 +1049,10 @@ ENDIF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER)
 
 
 if( MSVC )
 if( MSVC )
   # in order to prevent DLL hell, each of the DLLs have to be suffixed with the major version and msvc prefix
   # in order to prevent DLL hell, each of the DLLs have to be suffixed with the major version and msvc prefix
+  # CMake 3.12 added a variable for this
+  if(MSVC_TOOLSET_VERSION)
+    set(MSVC_PREFIX "vc${MSVC_TOOLSET_VERSION}")
+  else()
   if( MSVC70 OR MSVC71 )
   if( MSVC70 OR MSVC71 )
     set(MSVC_PREFIX "vc70")
     set(MSVC_PREFIX "vc70")
   elseif( MSVC80 )
   elseif( MSVC80 )
@@ -960,14 +1067,19 @@ if( MSVC )
     set(MSVC_PREFIX "vc120")
     set(MSVC_PREFIX "vc120")
   elseif( MSVC14 )
   elseif( MSVC14 )
     set(MSVC_PREFIX "vc140")
     set(MSVC_PREFIX "vc140")
+  elseif( MSVC15 )
+    set(MSVC_PREFIX "vc141")
   else()
   else()
     set(MSVC_PREFIX "vc150")
     set(MSVC_PREFIX "vc150")
   endif()
   endif()
+  endif()
   set(LIBRARY_SUFFIX "${ASSIMP_LIBRARY_SUFFIX}-${MSVC_PREFIX}-mt" CACHE STRING "the suffix for the assimp windows library")
   set(LIBRARY_SUFFIX "${ASSIMP_LIBRARY_SUFFIX}-${MSVC_PREFIX}-mt" CACHE STRING "the suffix for the assimp windows library")
 endif()
 endif()
 
 
 if (${CMAKE_SYSTEM_NAME} MATCHES "WindowsStore")
 if (${CMAKE_SYSTEM_NAME} MATCHES "WindowsStore")
-    set(WindowsStore TRUE)
+    target_compile_definitions(assimp PUBLIC WindowsStore)
+    TARGET_LINK_LIBRARIES(assimp advapi32)
+    #set(WindowsStore TRUE)
 endif()
 endif()
 SET_TARGET_PROPERTIES( assimp PROPERTIES
 SET_TARGET_PROPERTIES( assimp PROPERTIES
   VERSION ${ASSIMP_VERSION}
   VERSION ${ASSIMP_VERSION}
@@ -1028,6 +1140,16 @@ if (ASSIMP_ANDROID_JNIIOSYSTEM)
 ENDIF(ASSIMP_ANDROID_JNIIOSYSTEM)
 ENDIF(ASSIMP_ANDROID_JNIIOSYSTEM)
 
 
 if(MSVC AND ASSIMP_INSTALL_PDB)
 if(MSVC AND ASSIMP_INSTALL_PDB)
+  # When only the static library is built, these properties must
+  # be set to ensure the static lib .pdb is staged for installation.
+  IF(NOT BUILD_SHARED_LIBS)
+    SET_TARGET_PROPERTIES( assimp PROPERTIES
+      COMPILE_PDB_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+      COMPILE_PDB_NAME assimp${LIBRARY_SUFFIX}
+      COMPILE_PDB_NAME_DEBUG assimp${LIBRARY_SUFFIX}${CMAKE_DEBUG_POSTFIX}
+    )
+  ENDIF()
+
   IF(CMAKE_GENERATOR MATCHES "^Visual Studio")
   IF(CMAKE_GENERATOR MATCHES "^Visual Studio")
     install(FILES ${Assimp_BINARY_DIR}/code/Debug/assimp${LIBRARY_SUFFIX}${CMAKE_DEBUG_POSTFIX}.pdb
     install(FILES ${Assimp_BINARY_DIR}/code/Debug/assimp${LIBRARY_SUFFIX}${CMAKE_DEBUG_POSTFIX}.pdb
       DESTINATION ${ASSIMP_LIB_INSTALL_DIR}
       DESTINATION ${ASSIMP_LIB_INSTALL_DIR}

+ 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;
 
 

+ 73 - 39
code/ColladaExporter.cpp

@@ -64,13 +64,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 using namespace Assimp;
 using namespace Assimp;
 
 
-namespace Assimp
-{
+namespace Assimp {
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Worker function for exporting a scene to Collada. Prototyped and registered in Exporter.cpp
 // Worker function for exporting a scene to Collada. Prototyped and registered in Exporter.cpp
-void ExportSceneCollada(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/)
-{
+void ExportSceneCollada(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/) {
     std::string path = DefaultIOSystem::absolutePath(std::string(pFile));
     std::string path = DefaultIOSystem::absolutePath(std::string(pFile));
     std::string file = DefaultIOSystem::completeBaseName(std::string(pFile));
     std::string file = DefaultIOSystem::completeBaseName(std::string(pFile));
 
 
@@ -93,12 +91,12 @@ void ExportSceneCollada(const char* pFile, IOSystem* pIOSystem, const aiScene* p
 
 
 } // end of namespace Assimp
 } // end of namespace Assimp
 
 
-
-
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Constructor for a specific scene to export
 // Constructor for a specific scene to export
-ColladaExporter::ColladaExporter( const aiScene* pScene, IOSystem* pIOSystem, const std::string& path, const std::string& file) : mIOSystem(pIOSystem), mPath(path), mFile(file)
-{
+ColladaExporter::ColladaExporter( const aiScene* pScene, IOSystem* pIOSystem, const std::string& path, const std::string& file) 
+: mIOSystem(pIOSystem)
+, mPath(path)
+, mFile(file) {
     // make sure that all formatting happens using the standard, C locale and not the user's current locale
     // make sure that all formatting happens using the standard, C locale and not the user's current locale
     mOutput.imbue( std::locale("C") );
     mOutput.imbue( std::locale("C") );
     mOutput.precision(16);
     mOutput.precision(16);
@@ -115,17 +113,15 @@ ColladaExporter::ColladaExporter( const aiScene* pScene, IOSystem* pIOSystem, co
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Destructor
 // Destructor
-ColladaExporter::~ColladaExporter()
-{
-    if(mSceneOwned) {
+ColladaExporter::~ColladaExporter() {
+    if ( mSceneOwned ) {
         delete mScene;
         delete mScene;
     }
     }
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Starts writing the contents
 // Starts writing the contents
-void ColladaExporter::WriteFile()
-{
+void ColladaExporter::WriteFile() {
     // write the DTD
     // write the DTD
     mOutput << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>" << endstr;
     mOutput << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>" << endstr;
     // COLLADA element start
     // COLLADA element start
@@ -158,8 +154,7 @@ void ColladaExporter::WriteFile()
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Writes the asset header
 // Writes the asset header
-void ColladaExporter::WriteHeader()
-{
+void ColladaExporter::WriteHeader() {
     static const ai_real epsilon = ai_real( 0.00001 );
     static const ai_real epsilon = ai_real( 0.00001 );
     static const aiQuaternion x_rot(aiMatrix3x3(
     static const aiQuaternion x_rot(aiMatrix3x3(
         0, -1,  0,
         0, -1,  0,
@@ -238,25 +233,64 @@ void ColladaExporter::WriteHeader()
     mOutput << startstr << "<contributor>" << endstr;
     mOutput << startstr << "<contributor>" << endstr;
     PushTag();
     PushTag();
 
 
-    aiMetadata* meta = mScene->mRootNode->mMetaData;
+    // If no Scene metadata, use root node metadata
+    aiMetadata* meta = mScene->mMetaData;
+    if (nullptr == meta) {
+        meta = mScene->mRootNode->mMetaData;
+    }
+
     aiString value;
     aiString value;
-    if (!meta || !meta->Get("Author", value))
+    if (!meta || !meta->Get("Author", value)) {
         mOutput << startstr << "<author>" << "Assimp" << "</author>" << endstr;
         mOutput << startstr << "<author>" << "Assimp" << "</author>" << endstr;
-    else
+    } else {
         mOutput << startstr << "<author>" << XMLEscape(value.C_Str()) << "</author>" << endstr;
         mOutput << startstr << "<author>" << XMLEscape(value.C_Str()) << "</author>" << endstr;
+    }
 
 
-    if (!meta || !meta->Get("AuthoringTool", value))
+    if (nullptr == meta || !meta->Get("AuthoringTool", value)) {
         mOutput << startstr << "<authoring_tool>" << "Assimp Exporter" << "</authoring_tool>" << endstr;
         mOutput << startstr << "<authoring_tool>" << "Assimp Exporter" << "</authoring_tool>" << endstr;
-    else
+    } else {
         mOutput << startstr << "<authoring_tool>" << XMLEscape(value.C_Str()) << "</authoring_tool>" << endstr;
         mOutput << startstr << "<authoring_tool>" << XMLEscape(value.C_Str()) << "</authoring_tool>" << endstr;
+    }
 
 
-    //mOutput << startstr << "<author>" << mScene->author.C_Str() << "</author>" << endstr;
-    //mOutput << startstr << "<authoring_tool>" << mScene->authoringTool.C_Str() << "</authoring_tool>" << endstr;
+    if (meta) {
+        if (meta->Get("Comments", value)) {
+            mOutput << startstr << "<comments>" << XMLEscape(value.C_Str()) << "</comments>" << endstr;
+        }
+        if (meta->Get("Copyright", value)) {
+            mOutput << startstr << "<copyright>" << XMLEscape(value.C_Str()) << "</copyright>" << endstr;
+        }
+        if (meta->Get("SourceData", value)) {
+            mOutput << startstr << "<source_data>" << XMLEscape(value.C_Str()) << "</source_data>" << endstr;
+        }
+    }
 
 
     PopTag();
     PopTag();
     mOutput << startstr << "</contributor>" << endstr;
     mOutput << startstr << "</contributor>" << endstr;
-    mOutput << startstr << "<created>" << date_str << "</created>" << endstr;
+
+    if (nullptr == meta || !meta->Get("Created", value)) {
+        mOutput << startstr << "<created>" << date_str << "</created>" << endstr;
+    } else {
+        mOutput << startstr << "<created>" << XMLEscape(value.C_Str()) << "</created>" << endstr;
+    }
+
+    // Modified date is always the date saved
     mOutput << startstr << "<modified>" << date_str << "</modified>" << endstr;
     mOutput << startstr << "<modified>" << date_str << "</modified>" << endstr;
+
+    if (meta) {
+        if (meta->Get("Keywords", value)) {
+            mOutput << startstr << "<keywords>" << XMLEscape(value.C_Str()) << "</keywords>" << endstr;
+        }
+        if (meta->Get("Revision", value)) {
+            mOutput << startstr << "<revision>" << XMLEscape(value.C_Str()) << "</revision>" << endstr;
+        }
+        if (meta->Get("Subject", value)) {
+            mOutput << startstr << "<subject>" << XMLEscape(value.C_Str()) << "</subject>" << endstr;
+        }
+        if (meta->Get("Title", value)) {
+            mOutput << startstr << "<title>" << XMLEscape(value.C_Str()) << "</title>" << endstr;
+        }
+    }
+
     mOutput << startstr << "<unit name=\"meter\" meter=\"" << scale << "\" />" << endstr;
     mOutput << startstr << "<unit name=\"meter\" meter=\"" << scale << "\" />" << endstr;
     mOutput << startstr << "<up_axis>" << up_axis << "</up_axis>" << endstr;
     mOutput << startstr << "<up_axis>" << up_axis << "</up_axis>" << endstr;
     PopTag();
     PopTag();
@@ -269,12 +303,15 @@ void ColladaExporter::WriteTextures() {
     static const unsigned int buffer_size = 1024;
     static const unsigned int buffer_size = 1024;
     char str[buffer_size];
     char str[buffer_size];
 
 
-    if(mScene->HasTextures()) {
+    if (mScene->HasTextures()) {
         for(unsigned int i = 0; i < mScene->mNumTextures; i++) {
         for(unsigned int i = 0; i < mScene->mNumTextures; i++) {
             // It would be great to be able to create a directory in portable standard C++, but it's not the case,
             // It would be great to be able to create a directory in portable standard C++, but it's not the case,
             // so we just write the textures in the current directory.
             // so we just write the textures in the current directory.
 
 
             aiTexture* texture = mScene->mTextures[i];
             aiTexture* texture = mScene->mTextures[i];
+            if ( nullptr == texture ) {
+                continue;
+            }
 
 
             ASSIMP_itoa10(str, buffer_size, i + 1);
             ASSIMP_itoa10(str, buffer_size, i + 1);
 
 
@@ -428,6 +465,7 @@ void ColladaExporter::WritePointLight(const aiLight *const light){
     mOutput << startstr << "</point>" << endstr;
     mOutput << startstr << "</point>" << endstr;
 
 
 }
 }
+
 void ColladaExporter::WriteDirectionalLight(const aiLight *const light){
 void ColladaExporter::WriteDirectionalLight(const aiLight *const light){
     const aiColor3D &color=  light->mColorDiffuse;
     const aiColor3D &color=  light->mColorDiffuse;
     mOutput << startstr << "<directional>" << endstr;
     mOutput << startstr << "<directional>" << endstr;
@@ -440,6 +478,7 @@ void ColladaExporter::WriteDirectionalLight(const aiLight *const light){
     mOutput << startstr << "</directional>" << endstr;
     mOutput << startstr << "</directional>" << endstr;
 
 
 }
 }
+
 void ColladaExporter::WriteSpotLight(const aiLight *const light){
 void ColladaExporter::WriteSpotLight(const aiLight *const light){
 
 
     const aiColor3D &color=  light->mColorDiffuse;
     const aiColor3D &color=  light->mColorDiffuse;
@@ -496,18 +535,16 @@ void ColladaExporter::WriteAmbienttLight(const aiLight *const light){
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Reads a single surface entry from the given material keys
 // Reads a single surface entry from the given material keys
-void ColladaExporter::ReadMaterialSurface( Surface& poSurface, const aiMaterial* pSrcMat, aiTextureType pTexture, const char* pKey, size_t pType, size_t pIndex)
-{
-  if( pSrcMat->GetTextureCount( pTexture) > 0 )
-  {
+void ColladaExporter::ReadMaterialSurface( Surface& poSurface, const aiMaterial* pSrcMat, 
+                                          aiTextureType pTexture, const char* pKey, size_t pType, size_t pIndex) {
+  if( pSrcMat->GetTextureCount( pTexture) > 0 ) {
     aiString texfile;
     aiString texfile;
     unsigned int uvChannel = 0;
     unsigned int uvChannel = 0;
     pSrcMat->GetTexture( pTexture, 0, &texfile, NULL, &uvChannel);
     pSrcMat->GetTexture( pTexture, 0, &texfile, NULL, &uvChannel);
 
 
     std::string index_str(texfile.C_Str());
     std::string index_str(texfile.C_Str());
 
 
-    if(index_str.size() != 0 && index_str[0] == '*')
-    {
+    if(index_str.size() != 0 && index_str[0] == '*') {
         unsigned int index;
         unsigned int index;
 
 
         index_str = index_str.substr(1, std::string::npos);
         index_str = index_str.substr(1, std::string::npos);
@@ -525,15 +562,13 @@ void ColladaExporter::ReadMaterialSurface( Surface& poSurface, const aiMaterial*
         } else {
         } else {
             throw DeadlyExportError("could not find embedded texture at index " + index_str);
             throw DeadlyExportError("could not find embedded texture at index " + index_str);
         }
         }
-    } else
-    {
+    } else {
         poSurface.texture = texfile.C_Str();
         poSurface.texture = texfile.C_Str();
     }
     }
 
 
     poSurface.channel = uvChannel;
     poSurface.channel = uvChannel;
     poSurface.exist = true;
     poSurface.exist = true;
-  } else
-  {
+  } else {
     if( pKey )
     if( pKey )
       poSurface.exist = pSrcMat->Get( pKey, static_cast<unsigned int>(pType), static_cast<unsigned int>(pIndex), poSurface.color) == aiReturn_SUCCESS;
       poSurface.exist = pSrcMat->Get( pKey, static_cast<unsigned int>(pType), static_cast<unsigned int>(pIndex), poSurface.color) == aiReturn_SUCCESS;
   }
   }
@@ -541,15 +576,13 @@ void ColladaExporter::ReadMaterialSurface( Surface& poSurface, const aiMaterial*
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Reimplementation of isalnum(,C locale), because AppVeyor does not see standard version.
 // Reimplementation of isalnum(,C locale), because AppVeyor does not see standard version.
-static bool isalnum_C(char c)
-{
+static bool isalnum_C(char c) {
   return ( nullptr != strchr("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",c) );
   return ( nullptr != strchr("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",c) );
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Writes an image entry for the given surface
 // Writes an image entry for the given surface
-void ColladaExporter::WriteImageEntry( const Surface& pSurface, const std::string& pNameAdd)
-{
+void ColladaExporter::WriteImageEntry( const Surface& pSurface, const std::string& pNameAdd) {
   if( !pSurface.texture.empty() )
   if( !pSurface.texture.empty() )
   {
   {
     mOutput << startstr << "<image id=\"" << XMLEscape(pNameAdd) << "\">" << endstr;
     mOutput << startstr << "<image id=\"" << XMLEscape(pNameAdd) << "\">" << endstr;
@@ -803,8 +836,9 @@ void ColladaExporter::WriteControllerLibrary()
     mOutput << startstr << "<library_controllers>" << endstr;
     mOutput << startstr << "<library_controllers>" << endstr;
     PushTag();
     PushTag();
     
     
-    for( size_t a = 0; a < mScene->mNumMeshes; ++a)
+    for( size_t a = 0; a < mScene->mNumMeshes; ++a) {
         WriteController( a);
         WriteController( a);
+    }
 
 
     PopTag();
     PopTag();
     mOutput << startstr << "</library_controllers>" << endstr;
     mOutput << startstr << "</library_controllers>" << endstr;

+ 40 - 20
code/ColladaLoader.cpp

@@ -109,13 +109,13 @@ ColladaLoader::~ColladaLoader() {
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Returns whether the class can handle the format of the given file.
 // Returns whether the class can handle the format of the given file.
-bool ColladaLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
-{
+bool ColladaLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const {
     // check file extension
     // check file extension
     std::string extension = GetExtension(pFile);
     std::string extension = GetExtension(pFile);
 
 
-    if( extension == "dae")
+    if (extension == "dae") {
         return true;
         return true;
+    }
 
 
     // XML - too generic, we need to open the file and search for typical keywords
     // XML - too generic, we need to open the file and search for typical keywords
     if( extension == "xml" || !extension.length() || checkSig)  {
     if( extension == "xml" || !extension.length() || checkSig)  {
@@ -123,10 +123,13 @@ bool ColladaLoader::CanRead( const std::string& pFile, IOSystem* pIOHandler, boo
          *  support a specific file extension in general pIOHandler
          *  support a specific file extension in general pIOHandler
          *  might be NULL and it's our duty to return true here.
          *  might be NULL and it's our duty to return true here.
          */
          */
-        if (!pIOHandler)return true;
+        if (!pIOHandler) {
+            return true;
+        }
         const char* tokens[] = {"<collada"};
         const char* tokens[] = {"<collada"};
         return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
         return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
     }
     }
+
     return false;
     return false;
 }
 }
 
 
@@ -147,8 +150,7 @@ const aiImporterDesc* ColladaLoader::GetInfo () const
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Imports the given file into the given scene structure.
 // Imports the given file into the given scene structure.
-void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
-{
+void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) {
     mFileName = pFile;
     mFileName = pFile;
 
 
     // clean all member arrays - just for safety, it should work even if we did not
     // clean all member arrays - just for safety, it should work even if we did not
@@ -184,7 +186,7 @@ void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, I
     // ... then fill the materials with the now adjusted settings
     // ... then fill the materials with the now adjusted settings
     FillMaterials(parser, pScene);
     FillMaterials(parser, pScene);
 
 
-    // Apply unitsize scale calculation
+    // Apply unit-size scale calculation
     pScene->mRootNode->mTransformation *= aiMatrix4x4(parser.mUnitSize, 0,  0,  0,
     pScene->mRootNode->mTransformation *= aiMatrix4x4(parser.mUnitSize, 0,  0,  0,
                                                         0,  parser.mUnitSize,  0,  0,
                                                         0,  parser.mUnitSize,  0,  0,
                                                         0,  0,  parser.mUnitSize,  0,
                                                         0,  0,  parser.mUnitSize,  0,
@@ -205,6 +207,17 @@ void ColladaLoader::InternReadFile( const std::string& pFile, aiScene* pScene, I
                 0,  0,  0,  1);
                 0,  0,  0,  1);
     }
     }
 
 
+    // Store scene metadata
+    if (!parser.mAssetMetaData.empty()) {
+        const size_t numMeta(parser.mAssetMetaData.size());
+        pScene->mMetaData = aiMetadata::Alloc(static_cast<unsigned int>(numMeta));
+        size_t i = 0;
+        for (auto it = parser.mAssetMetaData.cbegin(); it != parser.mAssetMetaData.cend(); ++it, ++i)
+        {
+            pScene->mMetaData->Set(static_cast<unsigned int>(i), (*it).first, (*it).second);
+        }
+    }
+
     // store all meshes
     // store all meshes
     StoreSceneMeshes( pScene);
     StoreSceneMeshes( pScene);
 
 
@@ -1924,21 +1937,28 @@ const Collada::Node* ColladaLoader::FindNodeBySID( const Collada::Node* pNode, c
 std::string ColladaLoader::FindNameForNode( const Collada::Node* pNode)
 std::string ColladaLoader::FindNameForNode( const Collada::Node* pNode)
 {
 {
     // If explicitly requested, just use the collada name.
     // If explicitly requested, just use the collada name.
-    if (useColladaName) {
-        return pNode->mName;
+    if (useColladaName)
+    {
+        if (!pNode->mName.empty()) {
+            return pNode->mName;
+        } else {
+            return format() << "$ColladaAutoName$_" << mNodeNameCounter++;
+        }
     }
     }
-
-    // Now setup the name of the assimp node. The collada name might not be
-    // unique, so we use the collada ID.
-    if (!pNode->mID.empty())
-        return pNode->mID;
-    else if (!pNode->mSID.empty())
-    return pNode->mSID;
-  else
+    else
     {
     {
-        // No need to worry. Unnamed nodes are no problem at all, except
-        // if cameras or lights need to be assigned to them.
-    return format() << "$ColladaAutoName$_" << mNodeNameCounter++;
+        // Now setup the name of the assimp node. The collada name might not be
+        // unique, so we use the collada ID.
+        if (!pNode->mID.empty())
+            return pNode->mID;
+        else if (!pNode->mSID.empty())
+            return pNode->mSID;
+        else
+        {
+            // No need to worry. Unnamed nodes are no problem at all, except
+            // if cameras or lights need to be assigned to them.
+            return format() << "$ColladaAutoName$_" << mNodeNameCounter++;
+        }
     }
     }
 }
 }
 
 

+ 77 - 3
code/ColladaParser.cpp

@@ -264,14 +264,19 @@ void ColladaParser::ReadAssetInfo()
 
 
                 // check element end
                 // check element end
                 TestClosing( "up_axis");
                 TestClosing( "up_axis");
-            } else
+            }
+            else if(IsElement("contributor"))
             {
             {
-                SkipElement();
+                ReadContributorInfo();
+            }
+            else
+            {
+                ReadMetaDataItem(mAssetMetaData);
             }
             }
         }
         }
         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
         else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
         {
         {
-            if( strcmp( mReader->getNodeName(), "asset") != 0)
+            if (strcmp( mReader->getNodeName(), "asset") != 0)
                 ThrowException( "Expected end of <asset> element.");
                 ThrowException( "Expected end of <asset> element.");
 
 
             break;
             break;
@@ -279,6 +284,75 @@ void ColladaParser::ReadAssetInfo()
     }
     }
 }
 }
 
 
+// ------------------------------------------------------------------------------------------------
+// Reads the contributor info
+void ColladaParser::ReadContributorInfo()
+{
+    if (mReader->isEmptyElement())
+        return;
+
+    while (mReader->read())
+    {
+        if (mReader->getNodeType() == irr::io::EXN_ELEMENT)
+        {
+            ReadMetaDataItem(mAssetMetaData);
+        }
+        else if (mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+        {
+            if (strcmp(mReader->getNodeName(), "contributor") != 0)
+                ThrowException("Expected end of <contributor> element.");
+            break;
+        }
+    }
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a single string metadata item
+void ColladaParser::ReadMetaDataItem(StringMetaData &metadata)
+{
+    // Metadata such as created, keywords, subject etc
+    const char* key_char = mReader->getNodeName();
+    if (key_char != nullptr)
+    {
+        const std::string key_str(key_char);
+        const char* value_char = TestTextContent();
+        if (value_char != nullptr)
+        {
+            std::string camel_key_str = key_str;
+            ToCamelCase(camel_key_str);
+            aiString aistr;
+            aistr.Set(value_char);
+            metadata.emplace(camel_key_str, aistr);
+            TestClosing(key_str.c_str());
+        }
+        else
+            SkipElement();
+    }
+    else
+        SkipElement();
+}
+
+// ------------------------------------------------------------------------------------------------
+// Convert underscore_seperated to CamelCase: "authoring_tool" becomes "AuthoringTool"
+void ColladaParser::ToCamelCase(std::string &text)
+{
+    if (text.empty())
+        return;
+    // Capitalise first character
+    text[0] = ToUpper(text[0]);
+    for (auto it = text.begin(); it != text.end(); /*iterated below*/)
+    {
+        if ((*it) == '_')
+        {
+            it = text.erase(it);
+            if (it != text.end())
+                (*it) = ToUpper(*it);
+        }
+        else
+            ++it;
+    }
+}
+
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Reads the animation clips
 // Reads the animation clips
 void ColladaParser::ReadAnimationClipLibrary()
 void ColladaParser::ReadAnimationClipLibrary()

+ 15 - 0
code/ColladaParser.h

@@ -66,6 +66,9 @@ namespace Assimp
         friend class ColladaLoader;
         friend class ColladaLoader;
 
 
     protected:
     protected:
+        /** Map for generic metadata as aiString */
+        typedef std::map<std::string, aiString> StringMetaData;
+
         /** Constructor from XML file */
         /** Constructor from XML file */
         ColladaParser( IOSystem* pIOHandler, const std::string& pFile);
         ColladaParser( IOSystem* pIOHandler, const std::string& pFile);
 
 
@@ -81,6 +84,15 @@ namespace Assimp
         /** Reads asset information such as coordinate system information and legal blah */
         /** Reads asset information such as coordinate system information and legal blah */
         void ReadAssetInfo();
         void ReadAssetInfo();
 
 
+        /** Reads contributor information such as author and legal blah */
+        void ReadContributorInfo();
+
+        /** Reads generic metadata into provided map */
+        void ReadMetaDataItem(StringMetaData &metadata);
+
+        /** Convert underscore_seperated to CamelCase "authoring_tool" becomes "AuthoringTool" */
+        static void ToCamelCase(std::string &text);
+
         /** Reads the animation library */
         /** Reads the animation library */
         void ReadAnimationLibrary();
         void ReadAnimationLibrary();
 
 
@@ -343,6 +355,9 @@ namespace Assimp
         /** Which is the up vector */
         /** Which is the up vector */
         enum { UP_X, UP_Y, UP_Z } mUpDirection;
         enum { UP_X, UP_Y, UP_Z } mUpDirection;
 
 
+        /** Asset metadata (global for scene) */
+        StringMetaData mAssetMetaData;
+
         /** Collada file format version */
         /** Collada file format version */
         Collada::FormatVersion mFormat;
         Collada::FormatVersion mFormat;
     };
     };

+ 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);
         }
         }
+
     }
     }
 }
 }
 
 

+ 2 - 2
code/Exporter.cpp

@@ -288,7 +288,7 @@ void Exporter::SetProgressHandler(ProgressHandler* pHandler) {
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const char* pFormatId,
 const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const char* pFormatId,
-                                                unsigned int, const ExportProperties* /*pProperties*/ ) {
+                                                unsigned int pPreprocessing, const ExportProperties* pProperties) {
     if (pimpl->blob) {
     if (pimpl->blob) {
         delete pimpl->blob;
         delete pimpl->blob;
         pimpl->blob = nullptr;
         pimpl->blob = nullptr;
@@ -298,7 +298,7 @@ const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const cha
     BlobIOSystem* blobio = new BlobIOSystem();
     BlobIOSystem* blobio = new BlobIOSystem();
     pimpl->mIOSystem = std::shared_ptr<IOSystem>( blobio );
     pimpl->mIOSystem = std::shared_ptr<IOSystem>( blobio );
 
 
-    if (AI_SUCCESS != Export(pScene,pFormatId,blobio->GetMagicFileName())) {
+    if (AI_SUCCESS != Export(pScene,pFormatId,blobio->GetMagicFileName(), pPreprocessing, pProperties)) {
         pimpl->mIOSystem = old;
         pimpl->mIOSystem = old;
         return nullptr;
         return nullptr;
     }
     }

+ 13 - 9
code/FBXBinaryTokenizer.cpp

@@ -83,7 +83,7 @@ namespace FBX {
 //   e_unknown_21 = 1 << 21,
 //   e_unknown_21 = 1 << 21,
 //   e_unknown_22 = 1 << 22,
 //   e_unknown_22 = 1 << 22,
 //   e_unknown_23 = 1 << 23,
 //   e_unknown_23 = 1 << 23,
-//   e_flag_field_size_64_bit = 1 << 24, // Not sure what is 
+//   e_flag_field_size_64_bit = 1 << 24, // Not sure what is
 //   e_unknown_25 = 1 << 25,
 //   e_unknown_25 = 1 << 25,
 //   e_unknown_26 = 1 << 26,
 //   e_unknown_26 = 1 << 26,
 //   e_unknown_27 = 1 << 27,
 //   e_unknown_27 = 1 << 27,
@@ -98,7 +98,7 @@ namespace FBX {
 //	return (flags & to_check) != 0;
 //	return (flags & to_check) != 0;
 //}
 //}
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-Token::Token(const char* sbegin, const char* send, TokenType type, unsigned int offset)
+Token::Token(const char* sbegin, const char* send, TokenType type, size_t offset)
     :
     :
     #ifdef DEBUG
     #ifdef DEBUG
     contents(sbegin, static_cast<size_t>(send-sbegin)),
     contents(sbegin, static_cast<size_t>(send-sbegin)),
@@ -122,18 +122,18 @@ namespace {
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // signal tokenization error, this is always unrecoverable. Throws DeadlyImportError.
 // signal tokenization error, this is always unrecoverable. Throws DeadlyImportError.
-AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int offset) AI_WONT_RETURN_SUFFIX;
-AI_WONT_RETURN void TokenizeError(const std::string& message, unsigned int offset)
+AI_WONT_RETURN void TokenizeError(const std::string& message, size_t offset) AI_WONT_RETURN_SUFFIX;
+AI_WONT_RETURN void TokenizeError(const std::string& message, size_t offset)
 {
 {
     throw DeadlyImportError(Util::AddOffset("FBX-Tokenize",message,offset));
     throw DeadlyImportError(Util::AddOffset("FBX-Tokenize",message,offset));
 }
 }
 
 
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-uint32_t Offset(const char* begin, const char* cursor) {
+size_t Offset(const char* begin, const char* cursor) {
     ai_assert(begin <= cursor);
     ai_assert(begin <= cursor);
 
 
-    return static_cast<unsigned int>(cursor - begin);
+    return cursor - begin;
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
@@ -276,8 +276,8 @@ void ReadData(const char*& sbegin_out, const char*& send_out, const char* input,
     case 'f':
     case 'f':
     case 'd':
     case 'd':
     case 'l':
     case 'l':
-    case 'i':   {
-
+    case 'i':
+    case 'c':   {
         const uint32_t length = ReadWord(input, cursor, end);
         const uint32_t length = ReadWord(input, cursor, end);
         const uint32_t encoding = ReadWord(input, cursor, end);
         const uint32_t encoding = ReadWord(input, cursor, end);
 
 
@@ -298,6 +298,10 @@ void ReadData(const char*& sbegin_out, const char*& send_out, const char* input,
                 stride = 8;
                 stride = 8;
                 break;
                 break;
 
 
+            case 'c':
+                stride = 1;
+                break;
+
             default:
             default:
                 ai_assert(false);
                 ai_assert(false);
             };
             };
@@ -420,7 +424,7 @@ bool ReadScope(TokenList& output_tokens, const char* input, const char*& cursor,
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // TODO: Test FBX Binary files newer than the 7500 version to check if the 64 bits address behaviour is consistent
 // TODO: Test FBX Binary files newer than the 7500 version to check if the 64 bits address behaviour is consistent
-void TokenizeBinary(TokenList& output_tokens, const char* input, unsigned int length)
+void TokenizeBinary(TokenList& output_tokens, const char* input, size_t length)
 {
 {
     ai_assert(input);
     ai_assert(input);
 
 

+ 2 - 2
code/FBXCommon.h

@@ -47,7 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 #ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
 #ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
 
 
-
+namespace Assimp {
 namespace FBX
 namespace FBX
 {
 {
     const std::string NULL_RECORD = { // 13 null bytes
     const std::string NULL_RECORD = { // 13 null bytes
@@ -80,7 +80,7 @@ namespace FBX
         TransformInheritance_MAX // end-of-enum sentinel
         TransformInheritance_MAX // end-of-enum sentinel
     };
     };
 }
 }
-
+}
 #endif // ASSIMP_BUILD_NO_FBX_EXPORTER
 #endif // ASSIMP_BUILD_NO_FBX_EXPORTER
 
 
 #endif // AI_FBXCOMMON_H_INC
 #endif // AI_FBXCOMMON_H_INC

+ 164 - 73
code/FBXConverter.cpp

@@ -67,6 +67,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <sstream>
 #include <sstream>
 #include <iomanip>
 #include <iomanip>
 
 
+
 namespace Assimp {
 namespace Assimp {
     namespace FBX {
     namespace FBX {
 
 
@@ -76,10 +77,21 @@ namespace Assimp {
 
 
 #define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000L
 #define CONVERT_FBX_TIME(time) static_cast<double>(time) / 46186158000L
 
 
-        FBXConverter::FBXConverter(aiScene* out, const Document& doc)
-            : defaultMaterialIndex()
-            , out(out)
-            , doc(doc) {
+        FBXConverter::FBXConverter(aiScene* out, const Document& doc, bool removeEmptyBones, FbxUnit unit )
+        : defaultMaterialIndex()
+        , lights()
+        , cameras()
+        , textures()
+        , materials_converted()
+        , textures_converted()
+        , meshes_converted()
+        , node_anim_chain_bits()
+        , mNodeNames()
+        , anim_fps()
+        , out(out)
+        , doc(doc)
+        , mRemoveEmptyBones( removeEmptyBones )
+        , mCurrentUnit(FbxUnit::cm) {
             // animations need to be converted first since this will
             // animations need to be converted first since this will
             // populate the node_anim_chain_bits map, which is needed
             // populate the node_anim_chain_bits map, which is needed
             // to determine which nodes need to be generated.
             // to determine which nodes need to be generated.
@@ -107,6 +119,7 @@ namespace Assimp {
 
 
             ConvertGlobalSettings();
             ConvertGlobalSettings();
             TransferDataToScene();
             TransferDataToScene();
+            ConvertToUnitScale(unit);
 
 
             // if we didn't read any meshes set the AI_SCENE_FLAGS_INCOMPLETE
             // if we didn't read any meshes set the AI_SCENE_FLAGS_INCOMPLETE
             // to make sure the scene passes assimp's validation. FBX files
             // to make sure the scene passes assimp's validation. FBX files
@@ -128,12 +141,46 @@ namespace Assimp {
 
 
         void FBXConverter::ConvertRootNode() {
         void FBXConverter::ConvertRootNode() {
             out->mRootNode = new aiNode();
             out->mRootNode = new aiNode();
-            out->mRootNode->mName.Set("RootNode");
+            std::string unique_name;
+            GetUniqueName("RootNode", unique_name);
+            out->mRootNode->mName.Set(unique_name);
 
 
             // root has ID 0
             // root has ID 0
             ConvertNodes(0L, *out->mRootNode);
             ConvertNodes(0L, *out->mRootNode);
         }
         }
 
 
+        static std::string getAncestorBaseName(const aiNode* node)
+        {
+            const char* nodeName = nullptr;
+            size_t length = 0;
+            while (node && (!nodeName || length == 0))
+            {
+                nodeName = node->mName.C_Str();
+                length = node->mName.length;
+                node = node->mParent;
+            }
+
+            if (!nodeName || length == 0)
+            {
+                return {};
+            }
+            // could be std::string_view if c++17 available
+            return std::string(nodeName, length);
+        }
+
+        // Make unique name
+        std::string FBXConverter::MakeUniqueNodeName(const Model* const model, const aiNode& parent)
+        {
+            std::string original_name = FixNodeName(model->Name());
+            if (original_name.empty())
+            {
+                original_name = getAncestorBaseName(&parent);
+            }
+            std::string unique_name;
+            GetUniqueName(original_name, unique_name);
+            return unique_name;
+        }
+
         void FBXConverter::ConvertNodes(uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform) {
         void FBXConverter::ConvertNodes(uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform) {
             const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(id, "Model");
             const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(id, "Model");
 
 
@@ -165,35 +212,18 @@ namespace Assimp {
 
 
                         aiMatrix4x4 new_abs_transform = parent_transform;
                         aiMatrix4x4 new_abs_transform = parent_transform;
 
 
+                        std::string unique_name = MakeUniqueNodeName(model, parent);
+
                         // even though there is only a single input node, the design of
                         // even though there is only a single input node, the design of
                         // assimp (or rather: the complicated transformation chain that
                         // assimp (or rather: the complicated transformation chain that
                         // is employed by fbx) means that we may need multiple aiNode's
                         // is employed by fbx) means that we may need multiple aiNode's
                         // to represent a fbx node's transformation.
                         // to represent a fbx node's transformation.
-                        GenerateTransformationNodeChain(*model, nodes_chain, post_nodes_chain);
+                        const bool need_additional_node = GenerateTransformationNodeChain(*model, unique_name, nodes_chain, post_nodes_chain);
 
 
                         ai_assert(nodes_chain.size());
                         ai_assert(nodes_chain.size());
 
 
-                        std::string original_name = FixNodeName(model->Name());
-
-                        // check if any of the nodes in the chain has the name the fbx node
-                        // is supposed to have. If there is none, add another node to
-                        // preserve the name - people might have scripts etc. that rely
-                        // on specific node names.
-                        aiNode* name_carrier = NULL;
-                        for (aiNode* prenode : nodes_chain) {
-                            if (!strcmp(prenode->mName.C_Str(), original_name.c_str())) {
-                                name_carrier = prenode;
-                                break;
-                            }
-                        }
-
-                        if (!name_carrier) {
-                            std::string old_original_name = original_name;
-                            GetUniqueName(old_original_name, original_name);
-                            nodes_chain.push_back(new aiNode(original_name));
-                        }
-                        else {
-                            original_name = nodes_chain.back()->mName.C_Str();
+                        if (need_additional_node) {
+                            nodes_chain.push_back(new aiNode(unique_name));
                         }
                         }
 
 
                         //setup metadata on newest node
                         //setup metadata on newest node
@@ -255,11 +285,11 @@ namespace Assimp {
                         ConvertNodes(model->ID(), *last_parent, new_abs_transform);
                         ConvertNodes(model->ID(), *last_parent, new_abs_transform);
 
 
                         if (doc.Settings().readLights) {
                         if (doc.Settings().readLights) {
-                            ConvertLights(*model, original_name);
+                            ConvertLights(*model, unique_name);
                         }
                         }
 
 
                         if (doc.Settings().readCameras) {
                         if (doc.Settings().readCameras) {
-                            ConvertCameras(*model, original_name);
+                            ConvertCameras(*model, unique_name);
                         }
                         }
 
 
                         nodes.push_back(nodes_chain.front());
                         nodes.push_back(nodes_chain.front());
@@ -377,6 +407,7 @@ namespace Assimp {
                 break;
                 break;
             default:
             default:
                 ai_assert(false);
                 ai_assert(false);
+                break;
             }
             }
         }
         }
 
 
@@ -390,9 +421,18 @@ namespace Assimp {
             out_camera->mAspect = cam.AspectWidth() / cam.AspectHeight();
             out_camera->mAspect = cam.AspectWidth() / cam.AspectHeight();
 
 
             //cameras are defined along positive x direction
             //cameras are defined along positive x direction
-            out_camera->mPosition = cam.Position();
+            /*out_camera->mPosition = cam.Position();
             out_camera->mLookAt = (cam.InterestPosition() - out_camera->mPosition).Normalize();
             out_camera->mLookAt = (cam.InterestPosition() - out_camera->mPosition).Normalize();
-            out_camera->mUp = cam.UpVector();
+            out_camera->mUp = cam.UpVector();*/
+
+            out_camera->mPosition = aiVector3D(0.0f);
+            out_camera->mLookAt = aiVector3D(1.0f, 0.0f, 0.0f);
+            out_camera->mUp = aiVector3D(0.0f, 1.0f, 0.0f);
+
+            out_camera->mHorizontalFOV = AI_DEG_TO_RAD(cam.FieldOfView());
+
+            out_camera->mClipPlaneNear = cam.NearPlane();
+            out_camera->mClipPlaneFar = cam.FarPlane();
 
 
             out_camera->mHorizontalFOV = AI_DEG_TO_RAD(cam.FieldOfView());
             out_camera->mHorizontalFOV = AI_DEG_TO_RAD(cam.FieldOfView());
             out_camera->mClipPlaneNear = cam.NearPlane();
             out_camera->mClipPlaneNear = cam.NearPlane();
@@ -401,19 +441,19 @@ 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())
+            auto it_pair = mNodeNames.insert({ name, 0 }); // duplicate node name instance count
+            unsigned int& i = it_pair.first->second;
+            while (!it_pair.second)
             {
             {
-                ++i;
-                std::stringstream ext;
+                i++;
+                std::ostringstream ext;
                 ext << name << std::setfill('0') << std::setw(3) << i;
                 ext << name << std::setfill('0') << std::setw(3) << i;
                 uniqueName = ext.str();
                 uniqueName = ext.str();
+                it_pair = mNodeNames.insert({ uniqueName, 0 });
             }
             }
-            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:
@@ -643,7 +683,7 @@ namespace Assimp {
             return name + std::string(MAGIC_NODE_TAG) + "_" + NameTransformationComp(comp);
             return name + std::string(MAGIC_NODE_TAG) + "_" + NameTransformationComp(comp);
         }
         }
 
 
-        void FBXConverter::GenerateTransformationNodeChain(const Model& model, std::vector<aiNode*>& output_nodes,
+        bool FBXConverter::GenerateTransformationNodeChain(const Model& model, const std::string& name, std::vector<aiNode*>& output_nodes,
             std::vector<aiNode*>& post_output_nodes) {
             std::vector<aiNode*>& post_output_nodes) {
             const PropertyTable& props = model.Props();
             const PropertyTable& props = model.Props();
             const Model::RotOrder rot = model.RotationOrder();
             const Model::RotOrder rot = model.RotationOrder();
@@ -758,8 +798,6 @@ namespace Assimp {
             // not be guaranteed.
             // not be guaranteed.
             ai_assert(NeedsComplexTransformationChain(model) == is_complex);
             ai_assert(NeedsComplexTransformationChain(model) == is_complex);
 
 
-            std::string name = FixNodeName(model.Name());
-
             // now, if we have more than just Translation, Scaling and Rotation,
             // now, if we have more than just Translation, Scaling and Rotation,
             // we need to generate a full node chain to accommodate for assimp's
             // we need to generate a full node chain to accommodate for assimp's
             // lack to express pivots and offsets.
             // lack to express pivots and offsets.
@@ -801,20 +839,20 @@ namespace Assimp {
                 }
                 }
 
 
                 ai_assert(output_nodes.size());
                 ai_assert(output_nodes.size());
-                return;
+                return true;
             }
             }
 
 
             // else, we can just multiply the matrices together
             // else, we can just multiply the matrices together
             aiNode* nd = new aiNode();
             aiNode* nd = new aiNode();
             output_nodes.push_back(nd);
             output_nodes.push_back(nd);
-            std::string uniqueName;
-            GetUniqueName(name, uniqueName);
 
 
-            nd->mName.Set(uniqueName);
+            // name passed to the method is already unique
+            nd->mName.Set(name);
 
 
             for (const auto &transform : chain) {
             for (const auto &transform : chain) {
                 nd->mTransformation = nd->mTransformation * transform;
                 nd->mTransformation = nd->mTransformation * transform;
             }
             }
+            return false;
         }
         }
 
 
         void FBXConverter::SetupNodeMetadata(const Model& model, aiNode& nd)
         void FBXConverter::SetupNodeMetadata(const Model& model, aiNode& nd)
@@ -953,9 +991,11 @@ namespace Assimp {
             unsigned int epcount = 0;
             unsigned int epcount = 0;
             for (unsigned i = 0; i < indices.size(); i++)
             for (unsigned i = 0; i < indices.size(); i++)
             {
             {
-                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]();
@@ -1151,7 +1191,7 @@ namespace Assimp {
                     }
                     }
                 }
                 }
             }
             }
-            size_t numAnimMeshes = animMeshes.size();
+            const size_t numAnimMeshes = animMeshes.size();
             if (numAnimMeshes > 0) {
             if (numAnimMeshes > 0) {
                 out_mesh->mNumAnimMeshes = static_cast<unsigned int>(numAnimMeshes);
                 out_mesh->mNumAnimMeshes = static_cast<unsigned int>(numAnimMeshes);
                 out_mesh->mAnimMeshes = new aiAnimMesh*[numAnimMeshes];
                 out_mesh->mAnimMeshes = new aiAnimMesh*[numAnimMeshes];
@@ -1291,8 +1331,7 @@ namespace Assimp {
             unsigned int cursor = 0, in_cursor = 0;
             unsigned int cursor = 0, in_cursor = 0;
 
 
             itf = faces.begin();
             itf = faces.begin();
-            for (MatIndexArray::const_iterator it = mindices.begin(),
-                end = mindices.end(); it != end; ++it, ++itf)
+            for (MatIndexArray::const_iterator it = mindices.begin(), end = mindices.end(); it != end; ++it, ++itf)
             {
             {
                 const unsigned int pcount = *itf;
                 const unsigned int pcount = *itf;
                 if ((*it) != index) {
                 if ((*it) != index) {
@@ -1384,7 +1423,7 @@ namespace Assimp {
 
 
                     const WeightIndexArray& indices = cluster->GetIndices();
                     const WeightIndexArray& indices = cluster->GetIndices();
 
 
-                    if (indices.empty()) {
+                    if (indices.empty() && mRemoveEmptyBones ) {
                         continue;
                         continue;
                     }
                     }
 
 
@@ -1416,13 +1455,11 @@ namespace Assimp {
 
 
                                 if (index_out_indices.back() == no_index_sentinel) {
                                 if (index_out_indices.back() == no_index_sentinel) {
                                     index_out_indices.back() = out_indices.size();
                                     index_out_indices.back() = out_indices.size();
-
                                 }
                                 }
 
 
                                 if (no_mat_check) {
                                 if (no_mat_check) {
                                     out_indices.push_back(out_idx[i]);
                                     out_indices.push_back(out_idx[i]);
-                                }
-                                else {
+                                } else {
                                     // this extra lookup is in O(logn), so the entire algorithm becomes O(nlogn)
                                     // this extra lookup is in O(logn), so the entire algorithm becomes O(nlogn)
                                     const std::vector<unsigned int>::iterator it = std::lower_bound(
                                     const std::vector<unsigned int>::iterator it = std::lower_bound(
                                         outputVertStartIndices->begin(),
                                         outputVertStartIndices->begin(),
@@ -1438,11 +1475,11 @@ namespace Assimp {
                             }
                             }
                         }
                         }
                     }
                     }
-
+                    
                     // if we found at least one, generate the output bones
                     // if we found at least one, generate the output bones
                     // XXX this could be heavily simplified by collecting the bone
                     // XXX this could be heavily simplified by collecting the bone
                     // data in a single step.
                     // data in a single step.
-                    if (ok) {
+                    if (ok && mRemoveEmptyBones) {
                         ConvertCluster(bones, model, *cluster, out_indices, index_out_indices,
                         ConvertCluster(bones, model, *cluster, out_indices, index_out_indices,
                             count_out_indices, node_global_transform);
                             count_out_indices, node_global_transform);
                     }
                     }
@@ -1573,6 +1610,13 @@ namespace Assimp {
                 out_mat->AddProperty(&str, AI_MATKEY_NAME);
                 out_mat->AddProperty(&str, AI_MATKEY_NAME);
             }
             }
 
 
+            // Set the shading mode as best we can: The FBX specification only mentions Lambert and Phong, and only Phong is mentioned in Assimp's aiShadingMode enum.
+            if (material.GetShadingModel() == "phong")
+            {
+                aiShadingMode shadingMode = aiShadingMode_Phong;
+                out_mat->AddProperty<aiShadingMode>(&shadingMode, 1, AI_MATKEY_SHADING_MODEL);               
+            }
+
             // shading stuff and colors
             // shading stuff and colors
             SetShadingPropertiesCommon(out_mat, props);
             SetShadingPropertiesCommon(out_mat, props);
             SetShadingPropertiesRaw( out_mat, props, material.Textures(), mesh );
             SetShadingPropertiesRaw( out_mat, props, material.Textures(), mesh );
@@ -1704,22 +1748,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;
@@ -1827,22 +1871,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;
@@ -2033,6 +2077,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.
@@ -2183,22 +2233,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;
@@ -3378,8 +3428,9 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
 
 
             na->mNumScalingKeys = static_cast<unsigned int>(keys.size());
             na->mNumScalingKeys = static_cast<unsigned int>(keys.size());
             na->mScalingKeys = new aiVectorKey[keys.size()];
             na->mScalingKeys = new aiVectorKey[keys.size()];
-            if (keys.size() > 0)
+            if (keys.size() > 0) {
                 InterpolateKeys(na->mScalingKeys, keys, inputs, aiVector3D(1.0f, 1.0f, 1.0f), maxTime, minTime);
                 InterpolateKeys(na->mScalingKeys, keys, inputs, aiVector3D(1.0f, 1.0f, 1.0f), maxTime, minTime);
+            }
         }
         }
 
 
         void FBXConverter::ConvertTranslationKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes,
         void FBXConverter::ConvertTranslationKeys(aiNodeAnim* na, const std::vector<const AnimationCurveNode*>& nodes,
@@ -3443,6 +3494,46 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
             out->mMetaData->Set(14, "CustomFrameRate", doc.GlobalSettings().CustomFrameRate());
             out->mMetaData->Set(14, "CustomFrameRate", doc.GlobalSettings().CustomFrameRate());
         }
         }
 
 
+        void FBXConverter::ConvertToUnitScale( FbxUnit unit ) {
+            if (mCurrentUnit == unit) {
+                return;
+            }
+
+            ai_real scale = 1.0;
+            if (mCurrentUnit == FbxUnit::cm) {
+                if (unit == FbxUnit::m) {
+                    scale = (ai_real)0.01;
+                } else if (unit == FbxUnit::km) {
+                    scale = (ai_real)0.00001;
+                }
+            } else if (mCurrentUnit == FbxUnit::m) {
+                if (unit == FbxUnit::cm) {
+                    scale = (ai_real)100.0;
+                } else if (unit == FbxUnit::km) {
+                    scale = (ai_real)0.001;
+                }
+            } else if (mCurrentUnit == FbxUnit::km) {
+                if (unit == FbxUnit::cm) {
+                    scale = (ai_real)100000.0;
+                } else if (unit == FbxUnit::m) {
+                    scale = (ai_real)1000.0;
+                }
+            }
+            
+            for (auto mesh : meshes) {
+                if (nullptr == mesh) {
+                    continue;
+                }
+
+                if (mesh->HasPositions()) {
+                    for (unsigned int i = 0; i < mesh->mNumVertices; ++i) {
+                        aiVector3D &pos = mesh->mVertices[i];
+                        pos *= scale;
+                    }
+                }
+            }
+        }
+
         void FBXConverter::TransferDataToScene()
         void FBXConverter::TransferDataToScene()
         {
         {
             ai_assert(!out->mMeshes);
             ai_assert(!out->mMeshes);
@@ -3496,9 +3587,9 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
         }
         }
 
 
         // ------------------------------------------------------------------------------------------------
         // ------------------------------------------------------------------------------------------------
-        void ConvertToAssimpScene(aiScene* out, const Document& doc)
+        void ConvertToAssimpScene(aiScene* out, const Document& doc, bool removeEmptyBones, FbxUnit unit)
         {
         {
-            FBXConverter converter(out, doc);
+            FBXConverter converter(out, doc, removeEmptyBones, unit);
         }
         }
 
 
     } // !FBX
     } // !FBX

+ 35 - 11
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,14 +76,22 @@ namespace FBX {
 
 
 class Document;
 class Document;
 
 
-using NodeNameCache = std::set<std::string>;
+enum class FbxUnit {
+    cm = 0,
+    m,
+    km,
+    NumUnits,
+
+    Undefined
+};
 
 
 /** 
 /** 
  *  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
- *  @param doc Parsed FBX document 
+ *  @param doc Parsed FBX document
+ *  @param removeEmptyBones Will remove bones, which do not have any references to vertices.
  */
  */
-void ConvertToAssimpScene(aiScene* out, const Document& doc);
+void ConvertToAssimpScene(aiScene* out, const Document& doc, bool removeEmptyBones, FbxUnit unit);
 
 
 /** Dummy class to encapsulate the conversion process */
 /** Dummy class to encapsulate the conversion process */
 class FBXConverter {
 class FBXConverter {
@@ -112,7 +122,7 @@ public:
     };
     };
 
 
 public:
 public:
-    FBXConverter(aiScene* out, const Document& doc);
+    FBXConverter(aiScene* out, const Document& doc, bool removeEmptyBones, FbxUnit unit);
     ~FBXConverter();
     ~FBXConverter();
 
 
 private:
 private:
@@ -144,6 +154,11 @@ private:
     // while these would be allowed, they are a potential trouble spot so better not use them).
     // while these would be allowed, they are a potential trouble spot so better not use them).
     const char* NameTransformationComp(TransformationComp comp);
     const char* NameTransformationComp(TransformationComp comp);
 
 
+    // ------------------------------------------------------------------------------------------------
+    // Returns an unique name for a node or traverses up a hierarchy until a non-empty name is found and
+    // then makes this name unique
+    std::string MakeUniqueNodeName(const Model* const model, const aiNode& parent);
+
     // ------------------------------------------------------------------------------------------------
     // ------------------------------------------------------------------------------------------------
     // note: this returns the REAL fbx property names
     // note: this returns the REAL fbx property names
     const char* NameTransformationCompProperty(TransformationComp comp);
     const char* NameTransformationCompProperty(TransformationComp comp);
@@ -167,7 +182,7 @@ private:
     /**
     /**
     *  note: memory for output_nodes will be managed by the caller
     *  note: memory for output_nodes will be managed by the caller
     */
     */
-    void GenerateTransformationNodeChain(const Model& model, std::vector<aiNode*>& output_nodes, std::vector<aiNode*>& post_output_nodes);
+    bool GenerateTransformationNodeChain(const Model& model, const std::string& name, std::vector<aiNode*>& output_nodes, std::vector<aiNode*>& post_output_nodes);
 
 
     // ------------------------------------------------------------------------------------------------
     // ------------------------------------------------------------------------------------------------
     void SetupNodeMetadata(const Model& model, aiNode& nd);
     void SetupNodeMetadata(const Model& model, aiNode& nd);
@@ -414,12 +429,15 @@ private:
 
 
     void ConvertGlobalSettings();
     void ConvertGlobalSettings();
 
 
+    // ------------------------------------------------------------------------------------------------
+    //  Will perform the conversion from a given unit to the requested unit.
+    void ConvertToUnitScale(FbxUnit unit);
+
     // ------------------------------------------------------------------------------------------------
     // ------------------------------------------------------------------------------------------------
     // copy generated meshes, animations, lights, cameras and textures to the output scene
     // copy generated meshes, animations, lights, cameras and textures to the output scene
     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,26 +447,32 @@ 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 NodeNameCache = std::unordered_map<std::string, unsigned int>;
     NodeNameCache mNodeNames;
     NodeNameCache mNodeNames;
+
     double anim_fps;
     double anim_fps;
 
 
     aiScene* const out;
     aiScene* const out;
     const FBX::Document& doc;
     const FBX::Document& doc;
+
+    bool mRemoveEmptyBones;
+
+    FbxUnit mCurrentUnit;
 };
 };
 
 
 }
 }

+ 4 - 5
code/FBXDocument.cpp

@@ -69,8 +69,7 @@ LazyObject::LazyObject(uint64_t id, const Element& element, const Document& doc)
 : doc(doc)
 : doc(doc)
 , element(element)
 , element(element)
 , id(id)
 , id(id)
-, flags()
-{
+, flags() {
     // empty
     // empty
 }
 }
 
 
@@ -84,7 +83,7 @@ LazyObject::~LazyObject()
 const Object* LazyObject::Get(bool dieOnError)
 const Object* LazyObject::Get(bool dieOnError)
 {
 {
     if(IsBeingConstructed() || FailedToConstruct()) {
     if(IsBeingConstructed() || FailedToConstruct()) {
-        return NULL;
+        return nullptr;
     }
     }
 
 
     if (object.get()) {
     if (object.get()) {
@@ -553,7 +552,7 @@ const std::vector<const AnimationStack*>& Document::AnimationStacks() const
 LazyObject* Document::GetObject(uint64_t id) const
 LazyObject* Document::GetObject(uint64_t id) const
 {
 {
     ObjectMap::const_iterator it = objects.find(id);
     ObjectMap::const_iterator it = objects.find(id);
-    return it == objects.end() ? NULL : (*it).second;
+    return it == objects.end() ? nullptr : (*it).second;
 }
 }
 
 
 #define MAX_CLASSNAMES 6
 #define MAX_CLASSNAMES 6
@@ -610,7 +609,7 @@ std::vector<const Connection*> Document::GetConnectionsSequenced(uint64_t id, bo
         for (size_t i = 0; i < c; ++i) {
         for (size_t i = 0; i < c; ++i) {
             ai_assert(classnames[i]);
             ai_assert(classnames[i]);
             if(static_cast<size_t>(std::distance(key.begin(),key.end())) == lengths[i] && !strncmp(classnames[i],obtype,lengths[i])) {
             if(static_cast<size_t>(std::distance(key.begin(),key.end())) == lengths[i] && !strncmp(classnames[i],obtype,lengths[i])) {
-                obtype = NULL;
+                obtype = nullptr;
                 break;
                 break;
             }
             }
         }
         }

+ 46 - 77
code/FBXDocument.h

@@ -85,13 +85,11 @@ class Cluster;
 /** Represents a delay-parsed FBX objects. Many objects in the scene
 /** Represents a delay-parsed FBX objects. Many objects in the scene
  *  are not needed by assimp, so it makes no sense to parse them
  *  are not needed by assimp, so it makes no sense to parse them
  *  upfront. */
  *  upfront. */
-class LazyObject
-{
+class LazyObject {
 public:
 public:
     LazyObject(uint64_t id, const Element& element, const Document& doc);
     LazyObject(uint64_t id, const Element& element, const Document& doc);
-    ~LazyObject();
 
 
-public:
+    ~LazyObject();
 
 
     const Object* Get(bool dieOnError = false);
     const Object* Get(bool dieOnError = false);
 
 
@@ -136,11 +134,8 @@ private:
     unsigned int flags;
     unsigned int flags;
 };
 };
 
 
-
-
 /** Base class for in-memory (DOM) representations of FBX objects */
 /** Base class for in-memory (DOM) representations of FBX objects */
-class Object
-{
+class Object {
 public:
 public:
     Object(uint64_t id, const Element& element, const std::string& name);
     Object(uint64_t id, const Element& element, const std::string& name);
 
 
@@ -164,14 +159,12 @@ protected:
     const uint64_t id;
     const uint64_t id;
 };
 };
 
 
-
-
 /** DOM class for generic FBX NoteAttribute blocks. NoteAttribute's just hold a property table,
 /** DOM class for generic FBX NoteAttribute blocks. NoteAttribute's just hold a property table,
  *  fixed members are added by deriving classes. */
  *  fixed members are added by deriving classes. */
-class NodeAttribute : public Object
-{
+class NodeAttribute : public Object {
 public:
 public:
     NodeAttribute(uint64_t id, const Element& element, const Document& doc, const std::string& name);
     NodeAttribute(uint64_t id, const Element& element, const Document& doc, const std::string& name);
+
     virtual ~NodeAttribute();
     virtual ~NodeAttribute();
 
 
     const PropertyTable& Props() const {
     const PropertyTable& Props() const {
@@ -183,12 +176,11 @@ private:
     std::shared_ptr<const PropertyTable> props;
     std::shared_ptr<const PropertyTable> props;
 };
 };
 
 
-
 /** DOM base class for FBX camera settings attached to a node */
 /** DOM base class for FBX camera settings attached to a node */
-class CameraSwitcher : public NodeAttribute
-{
+class CameraSwitcher : public NodeAttribute {
 public:
 public:
     CameraSwitcher(uint64_t id, const Element& element, const Document& doc, const std::string& name);
     CameraSwitcher(uint64_t id, const Element& element, const Document& doc, const std::string& name);
+
     virtual ~CameraSwitcher();
     virtual ~CameraSwitcher();
 
 
     int CameraID() const {
     int CameraID() const {
@@ -209,7 +201,6 @@ private:
     std::string cameraIndexName;
     std::string cameraIndexName;
 };
 };
 
 
-
 #define fbx_stringize(a) #a
 #define fbx_stringize(a) #a
 
 
 #define fbx_simple_property(name, type, default_value) \
 #define fbx_simple_property(name, type, default_value) \
@@ -230,13 +221,12 @@ private:
 
 
 
 
 /** DOM base class for FBX cameras attached to a node */
 /** DOM base class for FBX cameras attached to a node */
-class Camera : public NodeAttribute
-{
+class Camera : public NodeAttribute {
 public:
 public:
     Camera(uint64_t id, const Element& element, const Document& doc, const std::string& name);
     Camera(uint64_t id, const Element& element, const Document& doc, const std::string& name);
+
     virtual  ~Camera();
     virtual  ~Camera();
 
 
-public:
     fbx_simple_property(Position, aiVector3D, aiVector3D(0,0,0))
     fbx_simple_property(Position, aiVector3D, aiVector3D(0,0,0))
     fbx_simple_property(UpVector, aiVector3D, aiVector3D(0,1,0))
     fbx_simple_property(UpVector, aiVector3D, aiVector3D(0,1,0))
     fbx_simple_property(InterestPosition, aiVector3D, aiVector3D(0,0,0))
     fbx_simple_property(InterestPosition, aiVector3D, aiVector3D(0,0,0))
@@ -256,33 +246,26 @@ public:
     fbx_simple_property(FocalLength, float, 1.0f)
     fbx_simple_property(FocalLength, float, 1.0f)
 };
 };
 
 
-
 /** DOM base class for FBX null markers attached to a node */
 /** DOM base class for FBX null markers attached to a node */
-class Null : public NodeAttribute
-{
+class Null : public NodeAttribute {
 public:
 public:
     Null(uint64_t id, const Element& element, const Document& doc, const std::string& name);
     Null(uint64_t id, const Element& element, const Document& doc, const std::string& name);
     virtual ~Null();
     virtual ~Null();
 };
 };
 
 
-
 /** DOM base class for FBX limb node markers attached to a node */
 /** DOM base class for FBX limb node markers attached to a node */
-class LimbNode : public NodeAttribute
-{
+class LimbNode : public NodeAttribute {
 public:
 public:
     LimbNode(uint64_t id, const Element& element, const Document& doc, const std::string& name);
     LimbNode(uint64_t id, const Element& element, const Document& doc, const std::string& name);
     virtual ~LimbNode();
     virtual ~LimbNode();
 };
 };
 
 
-
 /** DOM base class for FBX lights attached to a node */
 /** DOM base class for FBX lights attached to a node */
-class Light : public NodeAttribute
-{
+class Light : public NodeAttribute {
 public:
 public:
     Light(uint64_t id, const Element& element, const Document& doc, const std::string& name);
     Light(uint64_t id, const Element& element, const Document& doc, const std::string& name);
     virtual ~Light();
     virtual ~Light();
 
 
-public:
     enum Type
     enum Type
     {
     {
         Type_Point,
         Type_Point,
@@ -304,7 +287,6 @@ public:
         Decay_MAX // end-of-enum sentinel
         Decay_MAX // end-of-enum sentinel
     };
     };
 
 
-public:
     fbx_simple_property(Color, aiVector3D, aiVector3D(1,1,1))
     fbx_simple_property(Color, aiVector3D, aiVector3D(1,1,1))
     fbx_simple_enum_property(LightType, Type, 0)
     fbx_simple_enum_property(LightType, Type, 0)
     fbx_simple_property(CastLightOnObject, bool, false)
     fbx_simple_property(CastLightOnObject, bool, false)
@@ -338,10 +320,8 @@ public:
     fbx_simple_property(EnableBarnDoor, bool, true)
     fbx_simple_property(EnableBarnDoor, bool, true)
 };
 };
 
 
-
 /** DOM base class for FBX models (even though its semantics are more "node" than "model" */
 /** DOM base class for FBX models (even though its semantics are more "node" than "model" */
-class Model : public Object
-{
+class Model : public Object {
 public:
 public:
     enum RotOrder {
     enum RotOrder {
         RotOrder_EulerXYZ = 0,
         RotOrder_EulerXYZ = 0,
@@ -356,7 +336,6 @@ public:
         RotOrder_MAX // end-of-enum sentinel
         RotOrder_MAX // end-of-enum sentinel
     };
     };
 
 
-
     enum TransformInheritance {
     enum TransformInheritance {
         TransformInheritance_RrSs = 0,
         TransformInheritance_RrSs = 0,
         TransformInheritance_RSrs,
         TransformInheritance_RSrs,
@@ -490,13 +469,12 @@ private:
 };
 };
 
 
 /** DOM class for generic FBX textures */
 /** DOM class for generic FBX textures */
-class Texture : public Object
-{
+class Texture : public Object {
 public:
 public:
     Texture(uint64_t id, const Element& element, const Document& doc, const std::string& name);
     Texture(uint64_t id, const Element& element, const Document& doc, const std::string& name);
+
     virtual ~Texture();
     virtual ~Texture();
 
 
-public:
     const std::string& Type() const {
     const std::string& Type() const {
         return type;
         return type;
     }
     }
@@ -551,17 +529,15 @@ private:
 };
 };
 
 
 /** DOM class for layered FBX textures */
 /** DOM class for layered FBX textures */
-class LayeredTexture : public Object
-{
+class LayeredTexture : public Object {
 public:
 public:
     LayeredTexture(uint64_t id, const Element& element, const Document& doc, const std::string& name);
     LayeredTexture(uint64_t id, const Element& element, const Document& doc, const std::string& name);
     virtual ~LayeredTexture();
     virtual ~LayeredTexture();
 
 
-    //Can only be called after construction of the layered texture object due to construction flag.
+    // Can only be called after construction of the layered texture object due to construction flag.
     void fillTexture(const Document& doc);
     void fillTexture(const Document& doc);
 
 
-    enum BlendMode
-    {
+    enum BlendMode {
         BlendMode_Translucent,
         BlendMode_Translucent,
         BlendMode_Additive,
         BlendMode_Additive,
         BlendMode_Modulate,
         BlendMode_Modulate,
@@ -623,13 +599,12 @@ typedef std::fbx_unordered_map<std::string, const LayeredTexture*> LayeredTextur
 
 
 
 
 /** DOM class for generic FBX videos */
 /** DOM class for generic FBX videos */
-class Video : public Object
-{
+class Video : public Object {
 public:
 public:
     Video(uint64_t id, const Element& element, const Document& doc, const std::string& name);
     Video(uint64_t id, const Element& element, const Document& doc, const std::string& name);
+
     virtual ~Video();
     virtual ~Video();
 
 
-public:
     const std::string& Type() const {
     const std::string& Type() const {
         return type;
         return type;
     }
     }
@@ -668,15 +643,15 @@ private:
     std::string fileName;
     std::string fileName;
     std::shared_ptr<const PropertyTable> props;
     std::shared_ptr<const PropertyTable> props;
 
 
-    uint32_t contentLength;
+    uint64_t contentLength;
     uint8_t* content;
     uint8_t* content;
 };
 };
 
 
 /** DOM class for generic FBX materials */
 /** DOM class for generic FBX materials */
-class Material : public Object
-{
+class Material : public Object {
 public:
 public:
     Material(uint64_t id, const Element& element, const Document& doc, const std::string& name);
     Material(uint64_t id, const Element& element, const Document& doc, const std::string& name);
+
     virtual ~Material();
     virtual ~Material();
 
 
     const std::string& GetShadingModel() const {
     const std::string& GetShadingModel() const {
@@ -713,8 +688,7 @@ typedef std::vector<int64_t> KeyTimeList;
 typedef std::vector<float> KeyValueList;
 typedef std::vector<float> KeyValueList;
 
 
 /** Represents a FBX animation curve (i.e. a 1-dimensional set of keyframes and values therefor) */
 /** Represents a FBX animation curve (i.e. a 1-dimensional set of keyframes and values therefor) */
-class AnimationCurve : public Object
-{
+class AnimationCurve : public Object {
 public:
 public:
     AnimationCurve(uint64_t id, const Element& element, const std::string& name, const Document& doc);
     AnimationCurve(uint64_t id, const Element& element, const std::string& name, const Document& doc);
     virtual ~AnimationCurve();
     virtual ~AnimationCurve();
@@ -725,14 +699,12 @@ public:
         return keys;
         return keys;
     }
     }
 
 
-
     /** get list of keyframe values.
     /** get list of keyframe values.
       * Invariant: |GetKeys()| == |GetValues()| && |GetKeys()| > 0*/
       * Invariant: |GetKeys()| == |GetValues()| && |GetKeys()| > 0*/
     const KeyValueList& GetValues() const {
     const KeyValueList& GetValues() const {
         return values;
         return values;
     }
     }
 
 
-
     const std::vector<float>& GetAttributes() const {
     const std::vector<float>& GetAttributes() const {
         return attributes;
         return attributes;
     }
     }
@@ -751,10 +723,8 @@ private:
 // property-name -> animation curve
 // property-name -> animation curve
 typedef std::map<std::string, const AnimationCurve*> AnimationCurveMap;
 typedef std::map<std::string, const AnimationCurve*> AnimationCurveMap;
 
 
-
 /** Represents a FBX animation curve (i.e. a mapping from single animation curves to nodes) */
 /** Represents a FBX animation curve (i.e. a mapping from single animation curves to nodes) */
-class AnimationCurveNode : public Object
-{
+class AnimationCurveNode : public Object {
 public:
 public:
     /* the optional white list specifies a list of property names for which the caller
     /* the optional white list specifies a list of property names for which the caller
     wants animations for. If the curve node does not match one of these, std::range_error
     wants animations for. If the curve node does not match one of these, std::range_error
@@ -804,8 +774,7 @@ private:
 typedef std::vector<const AnimationCurveNode*> AnimationCurveNodeList;
 typedef std::vector<const AnimationCurveNode*> AnimationCurveNodeList;
 
 
 /** Represents a FBX animation layer (i.e. a list of node animations) */
 /** Represents a FBX animation layer (i.e. a list of node animations) */
-class AnimationLayer : public Object
-{
+class AnimationLayer : public Object {
 public:
 public:
     AnimationLayer(uint64_t id, const Element& element, const std::string& name, const Document& doc);
     AnimationLayer(uint64_t id, const Element& element, const std::string& name, const Document& doc);
     virtual ~AnimationLayer();
     virtual ~AnimationLayer();
@@ -818,7 +787,7 @@ public:
     /* the optional white list specifies a list of property names for which the caller
     /* the optional white list specifies a list of property names for which the caller
     wants animations for. Curves not matching this list will not be added to the
     wants animations for. Curves not matching this list will not be added to the
     animation layer. */
     animation layer. */
-    AnimationCurveNodeList Nodes(const char* const * target_prop_whitelist = NULL, size_t whitelist_size = 0) const;
+    AnimationCurveNodeList Nodes(const char* const * target_prop_whitelist = nullptr, size_t whitelist_size = 0) const;
 
 
 private:
 private:
     std::shared_ptr<const PropertyTable> props;
     std::shared_ptr<const PropertyTable> props;
@@ -828,8 +797,7 @@ private:
 typedef std::vector<const AnimationLayer*> AnimationLayerList;
 typedef std::vector<const AnimationLayer*> AnimationLayerList;
 
 
 /** Represents a FBX animation stack (i.e. a list of animation layers) */
 /** Represents a FBX animation stack (i.e. a list of animation layers) */
-class AnimationStack : public Object
-{
+class AnimationStack : public Object {
 public:
 public:
     AnimationStack(uint64_t id, const Element& element, const std::string& name, const Document& doc);
     AnimationStack(uint64_t id, const Element& element, const std::string& name, const Document& doc);
     virtual ~AnimationStack();
     virtual ~AnimationStack();
@@ -855,8 +823,7 @@ private:
 
 
 
 
 /** DOM class for deformers */
 /** DOM class for deformers */
-class Deformer : public Object
-{
+class Deformer : public Object {
 public:
 public:
     Deformer(uint64_t id, const Element& element, const Document& doc, const std::string& name);
     Deformer(uint64_t id, const Element& element, const Document& doc, const std::string& name);
     virtual ~Deformer();
     virtual ~Deformer();
@@ -875,10 +842,10 @@ typedef std::vector<unsigned int> WeightIndexArray;
 
 
 
 
 /** DOM class for BlendShapeChannel deformers */
 /** DOM class for BlendShapeChannel deformers */
-class BlendShapeChannel : public Deformer
-{
+class BlendShapeChannel : public Deformer {
 public:
 public:
     BlendShapeChannel(uint64_t id, const Element& element, const Document& doc, const std::string& name);
     BlendShapeChannel(uint64_t id, const Element& element, const Document& doc, const std::string& name);
+
     virtual ~BlendShapeChannel();
     virtual ~BlendShapeChannel();
 
 
     float DeformPercent() const {
     float DeformPercent() const {
@@ -892,6 +859,7 @@ public:
     const std::vector<const ShapeGeometry*>& GetShapeGeometries() const {
     const std::vector<const ShapeGeometry*>& GetShapeGeometries() const {
         return shapeGeometries;
         return shapeGeometries;
     }
     }
+
 private:
 private:
     float percent;
     float percent;
     WeightArray fullWeights;
     WeightArray fullWeights;
@@ -899,10 +867,10 @@ private:
 };
 };
 
 
 /** DOM class for BlendShape deformers */
 /** DOM class for BlendShape deformers */
-class BlendShape : public Deformer
-{
+class BlendShape : public Deformer {
 public:
 public:
     BlendShape(uint64_t id, const Element& element, const Document& doc, const std::string& name);
     BlendShape(uint64_t id, const Element& element, const Document& doc, const std::string& name);
+
     virtual ~BlendShape();
     virtual ~BlendShape();
 
 
     const std::vector<const BlendShapeChannel*>& BlendShapeChannels() const {
     const std::vector<const BlendShapeChannel*>& BlendShapeChannels() const {
@@ -913,11 +881,11 @@ private:
     std::vector<const BlendShapeChannel*> blendShapeChannels;
     std::vector<const BlendShapeChannel*> blendShapeChannels;
 };
 };
 
 
-/** DOM class for skin deformer clusters (aka subdeformers) */
-class Cluster : public Deformer
-{
+/** DOM class for skin deformer clusters (aka sub-deformers) */
+class Cluster : public Deformer {
 public:
 public:
     Cluster(uint64_t id, const Element& element, const Document& doc, const std::string& name);
     Cluster(uint64_t id, const Element& element, const Document& doc, const std::string& name);
+
     virtual ~Cluster();
     virtual ~Cluster();
 
 
     /** get the list of deformer weights associated with this cluster.
     /** get the list of deformer weights associated with this cluster.
@@ -958,10 +926,10 @@ private:
 };
 };
 
 
 /** DOM class for skin deformers */
 /** DOM class for skin deformers */
-class Skin : public Deformer
-{
+class Skin : public Deformer {
 public:
 public:
     Skin(uint64_t id, const Element& element, const Document& doc, const std::string& name);
     Skin(uint64_t id, const Element& element, const Document& doc, const std::string& name);
+
     virtual ~Skin();
     virtual ~Skin();
 
 
     float DeformAccuracy() const {
     float DeformAccuracy() const {
@@ -978,10 +946,10 @@ private:
 };
 };
 
 
 /** Represents a link between two FBX objects. */
 /** Represents a link between two FBX objects. */
-class Connection
-{
+class Connection {
 public:
 public:
     Connection(uint64_t insertionOrder,  uint64_t src, uint64_t dest, const std::string& prop, const Document& doc);
     Connection(uint64_t insertionOrder,  uint64_t src, uint64_t dest, const std::string& prop, const Document& doc);
+
     ~Connection();
     ~Connection();
 
 
     // note: a connection ensures that the source and dest objects exist, but
     // note: a connection ensures that the source and dest objects exist, but
@@ -1006,7 +974,7 @@ public:
     }
     }
 
 
     int CompareTo(const Connection* c) const {
     int CompareTo(const Connection* c) const {
-        ai_assert( NULL != c );
+        ai_assert( nullptr != c );
 
 
         // note: can't subtract because this would overflow uint64_t
         // note: can't subtract because this would overflow uint64_t
         if(InsertionOrder() > c->InsertionOrder()) {
         if(InsertionOrder() > c->InsertionOrder()) {
@@ -1019,7 +987,7 @@ public:
     }
     }
 
 
     bool Compare(const Connection* c) const {
     bool Compare(const Connection* c) const {
-        ai_assert( NULL != c );
+        ai_assert( nullptr != c );
 
 
         return InsertionOrder() < c->InsertionOrder();
         return InsertionOrder() < c->InsertionOrder();
     }
     }
@@ -1047,6 +1015,7 @@ typedef std::multimap<uint64_t, const Connection*> ConnectionMap;
 class FileGlobalSettings {
 class FileGlobalSettings {
 public:
 public:
     FileGlobalSettings(const Document& doc, std::shared_ptr<const PropertyTable> props);
     FileGlobalSettings(const Document& doc, std::shared_ptr<const PropertyTable> props);
+
     ~FileGlobalSettings();
     ~FileGlobalSettings();
 
 
     const PropertyTable& Props() const {
     const PropertyTable& Props() const {
@@ -1103,10 +1072,10 @@ private:
 };
 };
 
 
 /** DOM root for a FBX file */
 /** DOM root for a FBX file */
-class Document
-{
+class Document {
 public:
 public:
     Document(const Parser& parser, const ImportSettings& settings);
     Document(const Parser& parser, const ImportSettings& settings);
+
     ~Document();
     ~Document();
 
 
     LazyObject* GetObject(uint64_t id) const;
     LazyObject* GetObject(uint64_t id) const;

+ 6 - 8
code/FBXDocumentUtil.h

@@ -56,7 +56,6 @@ namespace Assimp {
 namespace FBX {
 namespace FBX {
 namespace Util {
 namespace Util {
 
 
-
 /* DOM/Parse error reporting - does not return */
 /* DOM/Parse error reporting - does not return */
 AI_WONT_RETURN void DOMError(const std::string& message, const Token& token) AI_WONT_RETURN_SUFFIX;
 AI_WONT_RETURN void DOMError(const std::string& message, const Token& token) AI_WONT_RETURN_SUFFIX;
 AI_WONT_RETURN void DOMError(const std::string& message, const Element* element = NULL) AI_WONT_RETURN_SUFFIX;
 AI_WONT_RETURN void DOMError(const std::string& message, const Element* element = NULL) AI_WONT_RETURN_SUFFIX;
@@ -73,28 +72,28 @@ std::shared_ptr<const PropertyTable> GetPropertyTable(const Document& doc,
     const Scope& sc,
     const Scope& sc,
     bool no_warn = false);
     bool no_warn = false);
 
 
-
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 template <typename T>
 template <typename T>
-inline const T* ProcessSimpleConnection(const Connection& con,
+inline
+const T* ProcessSimpleConnection(const Connection& con,
     bool is_object_property_conn,
     bool is_object_property_conn,
     const char* name,
     const char* name,
     const Element& element,
     const Element& element,
-    const char** propNameOut = NULL)
+    const char** propNameOut = nullptr)
 {
 {
     if (is_object_property_conn && !con.PropertyName().length()) {
     if (is_object_property_conn && !con.PropertyName().length()) {
         DOMWarning("expected incoming " + std::string(name) +
         DOMWarning("expected incoming " + std::string(name) +
             " link to be an object-object connection, ignoring",
             " link to be an object-object connection, ignoring",
             &element
             &element
             );
             );
-        return NULL;
+        return nullptr;
     }
     }
     else if (!is_object_property_conn && con.PropertyName().length()) {
     else if (!is_object_property_conn && con.PropertyName().length()) {
         DOMWarning("expected incoming " + std::string(name) +
         DOMWarning("expected incoming " + std::string(name) +
             " link to be an object-property connection, ignoring",
             " link to be an object-property connection, ignoring",
             &element
             &element
             );
             );
-        return NULL;
+        return nullptr;
     }
     }
 
 
     if(is_object_property_conn && propNameOut) {
     if(is_object_property_conn && propNameOut) {
@@ -108,13 +107,12 @@ inline const T* ProcessSimpleConnection(const Connection& con,
         DOMWarning("failed to read source object for incoming " + std::string(name) +
         DOMWarning("failed to read source object for incoming " + std::string(name) +
             " link, ignoring",
             " link, ignoring",
             &element);
             &element);
-        return NULL;
+        return nullptr;
     }
     }
 
 
     return dynamic_cast<const T*>(ob);
     return dynamic_cast<const T*>(ob);
 }
 }
 
 
-
 } //!Util
 } //!Util
 } //!FBX
 } //!FBX
 } //!Assimp
 } //!Assimp

+ 9 - 6
code/FBXExportNode.cpp

@@ -54,6 +54,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <sstream> // ostringstream
 #include <sstream> // ostringstream
 #include <memory> // shared_ptr
 #include <memory> // shared_ptr
 
 
+namespace Assimp {
 // AddP70<type> helpers... there's no usable pattern here,
 // AddP70<type> helpers... there's no usable pattern here,
 // so all are defined as separate functions.
 // so all are defined as separate functions.
 // Even "animatable" properties are often completely different
 // Even "animatable" properties are often completely different
@@ -252,7 +253,8 @@ void FBX::Node::DumpChildren(
     } else {
     } else {
         std::ostringstream ss;
         std::ostringstream ss;
         DumpChildrenAscii(ss, indent);
         DumpChildrenAscii(ss, indent);
-        s.PutString(ss.str());
+        if (ss.tellp() > 0)
+            s.PutString(ss.str());
     }
     }
 }
 }
 
 
@@ -266,7 +268,8 @@ void FBX::Node::End(
     } else {
     } else {
         std::ostringstream ss;
         std::ostringstream ss;
         EndAscii(ss, indent, has_children);
         EndAscii(ss, indent, has_children);
-        s.PutString(ss.str());
+        if (ss.tellp() > 0)
+            s.PutString(ss.str());
     }
     }
 }
 }
 
 
@@ -367,7 +370,7 @@ void FBX::Node::EndBinary(
     bool has_children
     bool has_children
 ) {
 ) {
     // if there were children, add a null record
     // if there were children, add a null record
-    if (has_children) { s.PutString(FBX::NULL_RECORD); }
+    if (has_children) { s.PutString(Assimp::FBX::NULL_RECORD); }
 
 
     // now go back and write initial pos
     // now go back and write initial pos
     this->end_pos = s.Tell();
     this->end_pos = s.Tell();
@@ -432,7 +435,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 +471,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
@@ -563,6 +566,6 @@ void FBX::Node::WritePropertyNode(
         FBX::Node::WritePropertyNodeAscii(name, v, s, indent);
         FBX::Node::WritePropertyNodeAscii(name, v, s, indent);
     }
     }
 }
 }
-
+}
 #endif // ASSIMP_BUILD_NO_FBX_EXPORTER
 #endif // ASSIMP_BUILD_NO_FBX_EXPORTER
 #endif // ASSIMP_BUILD_NO_EXPORT
 #endif // ASSIMP_BUILD_NO_EXPORT

+ 6 - 6
code/FBXExportNode.h

@@ -54,16 +54,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <string>
 #include <string>
 #include <vector>
 #include <vector>
 
 
+namespace Assimp {
 namespace FBX {
 namespace FBX {
     class Node;
     class Node;
 }
 }
 
 
-class FBX::Node
-{
-public: // public data members
+class FBX::Node {
+public: 
     // TODO: accessors
     // TODO: accessors
     std::string name; // node name
     std::string name; // node name
-    std::vector<FBX::Property> properties; // node properties
+    std::vector<FBX::FBXExportProperty> properties; // node properties
     std::vector<FBX::Node> children; // child nodes
     std::vector<FBX::Node> children; // child nodes
 
 
     // some nodes always pretend they have children...
     // some nodes always pretend they have children...
@@ -214,7 +214,7 @@ public: // static member functions
         Assimp::StreamWriterLE& s,
         Assimp::StreamWriterLE& s,
         bool binary, int indent
         bool binary, int indent
     ) {
     ) {
-        FBX::Property p(value);
+        FBX::FBXExportProperty p(value);
         FBX::Node node(name, p);
         FBX::Node node(name, p);
         node.Dump(s, binary, indent);
         node.Dump(s, binary, indent);
     }
     }
@@ -264,7 +264,7 @@ private: // static helper functions
     );
     );
 
 
 };
 };
-
+}
 
 
 #endif // ASSIMP_BUILD_NO_FBX_EXPORTER
 #endif // ASSIMP_BUILD_NO_FBX_EXPORTER
 
 

+ 141 - 116
code/FBXExportProperty.cpp

@@ -52,187 +52,210 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <locale>
 #include <locale>
 #include <sstream> // ostringstream
 #include <sstream> // ostringstream
 
 
+namespace Assimp {
+namespace FBX {
 
 
 // constructors for single element properties
 // constructors for single element properties
 
 
-FBX::Property::Property(bool v)
-    : type('C'), data(1)
-{
-    data = {uint8_t(v)};
+FBXExportProperty::FBXExportProperty(bool v)
+: type('C')
+, data(1) {
+    data = {
+        uint8_t(v)
+    };
 }
 }
 
 
-FBX::Property::Property(int16_t v) : type('Y'), data(2)
-{
+FBXExportProperty::FBXExportProperty(int16_t v)
+: type('Y')
+, data(2) {
     uint8_t* d = data.data();
     uint8_t* d = data.data();
     (reinterpret_cast<int16_t*>(d))[0] = v;
     (reinterpret_cast<int16_t*>(d))[0] = v;
 }
 }
 
 
-FBX::Property::Property(int32_t v) : type('I'), data(4)
-{
+FBXExportProperty::FBXExportProperty(int32_t v)
+: type('I')
+, data(4) {
     uint8_t* d = data.data();
     uint8_t* d = data.data();
     (reinterpret_cast<int32_t*>(d))[0] = v;
     (reinterpret_cast<int32_t*>(d))[0] = v;
 }
 }
 
 
-FBX::Property::Property(float v) : type('F'), data(4)
-{
+FBXExportProperty::FBXExportProperty(float v)
+: type('F')
+, data(4) {
     uint8_t* d = data.data();
     uint8_t* d = data.data();
     (reinterpret_cast<float*>(d))[0] = v;
     (reinterpret_cast<float*>(d))[0] = v;
 }
 }
 
 
-FBX::Property::Property(double v) : type('D'), data(8)
-{
+FBXExportProperty::FBXExportProperty(double v)
+: type('D')
+, data(8) {
     uint8_t* d = data.data();
     uint8_t* d = data.data();
     (reinterpret_cast<double*>(d))[0] = v;
     (reinterpret_cast<double*>(d))[0] = v;
 }
 }
 
 
-FBX::Property::Property(int64_t v) : type('L'), data(8)
-{
+FBXExportProperty::FBXExportProperty(int64_t v)
+: type('L')
+, data(8) {
     uint8_t* d = data.data();
     uint8_t* d = data.data();
     (reinterpret_cast<int64_t*>(d))[0] = v;
     (reinterpret_cast<int64_t*>(d))[0] = v;
 }
 }
 
 
-
 // constructors for array-type properties
 // constructors for array-type properties
 
 
-FBX::Property::Property(const char* c, bool raw)
-    : Property(std::string(c), raw)
-{}
+FBXExportProperty::FBXExportProperty(const char* c, bool raw)
+: FBXExportProperty(std::string(c), raw) {
+    // empty
+}
 
 
 // strings can either be saved as "raw" (R) data, or "string" (S) data
 // strings can either be saved as "raw" (R) data, or "string" (S) data
-FBX::Property::Property(const std::string& s, bool raw)
-    : type(raw ? 'R' : 'S'), data(s.size())
-{
+FBXExportProperty::FBXExportProperty(const std::string& s, bool raw)
+: type(raw ? 'R' : 'S')
+, data(s.size()) {
     for (size_t i = 0; i < s.size(); ++i) {
     for (size_t i = 0; i < s.size(); ++i) {
         data[i] = uint8_t(s[i]);
         data[i] = uint8_t(s[i]);
     }
     }
 }
 }
 
 
-FBX::Property::Property(const std::vector<uint8_t>& r)
-    : type('R'), data(r)
-{}
+FBXExportProperty::FBXExportProperty(const std::vector<uint8_t>& r)
+: type('R')
+, data(r) {
+    // empty
+}
 
 
-FBX::Property::Property(const std::vector<int32_t>& va)
-    : type('i'), data(4*va.size())
-{
+FBXExportProperty::FBXExportProperty(const std::vector<int32_t>& va)
+: type('i')
+, data(4 * va.size() ) {
     int32_t* d = reinterpret_cast<int32_t*>(data.data());
     int32_t* d = reinterpret_cast<int32_t*>(data.data());
-    for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; }
+    for (size_t i = 0; i < va.size(); ++i) {
+        d[i] = va[i];
+    }
 }
 }
 
 
-FBX::Property::Property(const std::vector<int64_t>& va)
-    : type('l'), data(8*va.size())
-{
+FBXExportProperty::FBXExportProperty(const std::vector<int64_t>& va)
+: type('l')
+, data(8 * va.size()) {
     int64_t* d = reinterpret_cast<int64_t*>(data.data());
     int64_t* d = reinterpret_cast<int64_t*>(data.data());
-    for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; }
+    for (size_t i = 0; i < va.size(); ++i) {
+        d[i] = va[i];
+    }
 }
 }
 
 
-FBX::Property::Property(const std::vector<float>& va)
-    : type('f'), data(4*va.size())
-{
+FBXExportProperty::FBXExportProperty(const std::vector<float>& va)
+: type('f')
+, data(4 * va.size()) {
     float* d = reinterpret_cast<float*>(data.data());
     float* d = reinterpret_cast<float*>(data.data());
-    for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; }
+    for (size_t i = 0; i < va.size(); ++i) {
+        d[i] = va[i];
+    }
 }
 }
 
 
-FBX::Property::Property(const std::vector<double>& va)
-    : type('d'), data(8*va.size())
-{
+FBXExportProperty::FBXExportProperty(const std::vector<double>& va)
+: type('d')
+, data(8 * va.size()) {
     double* d = reinterpret_cast<double*>(data.data());
     double* d = reinterpret_cast<double*>(data.data());
-    for (size_t i = 0; i < va.size(); ++i) { d[i] = va[i]; }
+    for (size_t i = 0; i < va.size(); ++i) {
+        d[i] = va[i];
+    }
 }
 }
 
 
-FBX::Property::Property(const aiMatrix4x4& vm)
-    : type('d'), data(8*16)
-{
+FBXExportProperty::FBXExportProperty(const aiMatrix4x4& vm)
+: type('d')
+, data(8 * 16) {
     double* d = reinterpret_cast<double*>(data.data());
     double* d = reinterpret_cast<double*>(data.data());
     for (unsigned int c = 0; c < 4; ++c) {
     for (unsigned int c = 0; c < 4; ++c) {
         for (unsigned int r = 0; r < 4; ++r) {
         for (unsigned int r = 0; r < 4; ++r) {
-            d[4*c+r] = vm[r][c];
+            d[4 * c + r] = vm[r][c];
         }
         }
     }
     }
 }
 }
 
 
 // public member functions
 // public member functions
 
 
-size_t FBX::Property::size()
-{
+size_t FBXExportProperty::size() {
     switch (type) {
     switch (type) {
-    case 'C': case 'Y': case 'I': case 'F': case 'D': case 'L':
-        return data.size() + 1;
-    case 'S': case 'R':
-        return data.size() + 5;
-    case 'i': case 'd':
-        return data.size() + 13;
-    default:
-        throw DeadlyExportError("Requested size on property of unknown type");
+        case 'C':
+        case 'Y':
+        case 'I':
+        case 'F':
+        case 'D':
+        case 'L':
+            return data.size() + 1;
+        case 'S':
+        case 'R':
+            return data.size() + 5;
+        case 'i':
+        case 'd':
+            return data.size() + 13;
+        default:
+            throw DeadlyExportError("Requested size on property of unknown type");
     }
     }
 }
 }
 
 
-void FBX::Property::DumpBinary(Assimp::StreamWriterLE &s)
-{
+void FBXExportProperty::DumpBinary(Assimp::StreamWriterLE& s) {
     s.PutU1(type);
     s.PutU1(type);
     uint8_t* d = data.data();
     uint8_t* d = data.data();
     size_t N;
     size_t N;
     switch (type) {
     switch (type) {
-    case 'C': s.PutU1(*(reinterpret_cast<uint8_t*>(d))); return;
-    case 'Y': s.PutI2(*(reinterpret_cast<int16_t*>(d))); return;
-    case 'I': s.PutI4(*(reinterpret_cast<int32_t*>(d))); return;
-    case 'F': s.PutF4(*(reinterpret_cast<float*>(d))); return;
-    case 'D': s.PutF8(*(reinterpret_cast<double*>(d))); return;
-    case 'L': s.PutI8(*(reinterpret_cast<int64_t*>(d))); return;
-    case 'S':
-    case 'R':
-        s.PutU4(uint32_t(data.size()));
-        for (size_t i = 0; i < data.size(); ++i) { s.PutU1(data[i]); }
-        return;
-    case 'i':
-        N = data.size() / 4;
-        s.PutU4(uint32_t(N)); // number of elements
-        s.PutU4(0); // no encoding (1 would be zip-compressed)
-        // TODO: compress if large?
-        s.PutU4(uint32_t(data.size())); // data size
-        for (size_t i = 0; i < N; ++i) {
-            s.PutI4((reinterpret_cast<int32_t*>(d))[i]);
-        }
-        return;
-    case 'l':
-        N = data.size() / 8;
-        s.PutU4(uint32_t(N)); // number of elements
-        s.PutU4(0); // no encoding (1 would be zip-compressed)
-        // TODO: compress if large?
-        s.PutU4(uint32_t(data.size())); // data size
-        for (size_t i = 0; i < N; ++i) {
-            s.PutI8((reinterpret_cast<int64_t*>(d))[i]);
-        }
-        return;
-    case 'f':
-        N = data.size() / 4;
-        s.PutU4(uint32_t(N)); // number of elements
-        s.PutU4(0); // no encoding (1 would be zip-compressed)
-        // TODO: compress if large?
-        s.PutU4(uint32_t(data.size())); // data size
-        for (size_t i = 0; i < N; ++i) {
-            s.PutF4((reinterpret_cast<float*>(d))[i]);
-        }
-        return;
-    case 'd':
-        N = data.size() / 8;
-        s.PutU4(uint32_t(N)); // number of elements
-        s.PutU4(0); // no encoding (1 would be zip-compressed)
-        // TODO: compress if large?
-        s.PutU4(uint32_t(data.size())); // data size
-        for (size_t i = 0; i < N; ++i) {
-            s.PutF8((reinterpret_cast<double*>(d))[i]);
-        }
-        return;
-    default:
-        std::ostringstream err;
-        err << "Tried to dump property with invalid type '";
-        err << type << "'!";
-        throw DeadlyExportError(err.str());
+        case 'C': s.PutU1(*(reinterpret_cast<uint8_t*>(d))); return;
+        case 'Y': s.PutI2(*(reinterpret_cast<int16_t*>(d))); return;
+        case 'I': s.PutI4(*(reinterpret_cast<int32_t*>(d))); return;
+        case 'F': s.PutF4(*(reinterpret_cast<float*>(d))); return;
+        case 'D': s.PutF8(*(reinterpret_cast<double*>(d))); return;
+        case 'L': s.PutI8(*(reinterpret_cast<int64_t*>(d))); return;
+        case 'S':
+        case 'R':
+            s.PutU4(uint32_t(data.size()));
+            for (size_t i = 0; i < data.size(); ++i) { s.PutU1(data[i]); }
+            return;
+        case 'i':
+            N = data.size() / 4;
+            s.PutU4(uint32_t(N)); // number of elements
+            s.PutU4(0); // no encoding (1 would be zip-compressed)
+            // TODO: compress if large?
+            s.PutU4(uint32_t(data.size())); // data size
+            for (size_t i = 0; i < N; ++i) {
+                s.PutI4((reinterpret_cast<int32_t*>(d))[i]);
+            }
+            return;
+        case 'l':
+            N = data.size() / 8;
+            s.PutU4(uint32_t(N)); // number of elements
+            s.PutU4(0); // no encoding (1 would be zip-compressed)
+            // TODO: compress if large?
+            s.PutU4(uint32_t(data.size())); // data size
+            for (size_t i = 0; i < N; ++i) {
+                s.PutI8((reinterpret_cast<int64_t*>(d))[i]);
+            }
+            return;
+        case 'f':
+            N = data.size() / 4;
+            s.PutU4(uint32_t(N)); // number of elements
+            s.PutU4(0); // no encoding (1 would be zip-compressed)
+            // TODO: compress if large?
+            s.PutU4(uint32_t(data.size())); // data size
+            for (size_t i = 0; i < N; ++i) {
+                s.PutF4((reinterpret_cast<float*>(d))[i]);
+            }
+            return;
+        case 'd':
+            N = data.size() / 8;
+            s.PutU4(uint32_t(N)); // number of elements
+            s.PutU4(0); // no encoding (1 would be zip-compressed)
+            // TODO: compress if large?
+            s.PutU4(uint32_t(data.size())); // data size
+            for (size_t i = 0; i < N; ++i) {
+                s.PutF8((reinterpret_cast<double*>(d))[i]);
+            }
+            return;
+        default:
+            std::ostringstream err;
+            err << "Tried to dump property with invalid type '";
+            err << type << "'!";
+            throw DeadlyExportError(err.str());
     }
     }
 }
 }
 
 
-void FBX::Property::DumpAscii(Assimp::StreamWriterLE &outstream, int indent)
-{
+void FBXExportProperty::DumpAscii(Assimp::StreamWriterLE& outstream, int indent) {
     std::ostringstream ss;
     std::ostringstream ss;
     ss.imbue(std::locale::classic());
     ss.imbue(std::locale::classic());
     ss.precision(15); // this seems to match official FBX SDK exports
     ss.precision(15); // this seems to match official FBX SDK exports
@@ -240,8 +263,7 @@ void FBX::Property::DumpAscii(Assimp::StreamWriterLE &outstream, int indent)
     outstream.PutString(ss.str());
     outstream.PutString(ss.str());
 }
 }
 
 
-void FBX::Property::DumpAscii(std::ostream& s, int indent)
-{
+void FBXExportProperty::DumpAscii(std::ostream& s, int indent) {
     // no writing type... or anything. just shove it into the stream.
     // no writing type... or anything. just shove it into the stream.
     uint8_t* d = data.data();
     uint8_t* d = data.data();
     size_t N;
     size_t N;
@@ -360,5 +382,8 @@ void FBX::Property::DumpAscii(std::ostream& s, int indent)
     }
     }
 }
 }
 
 
+} // Namespace FBX
+} // Namespace Assimp
+
 #endif // ASSIMP_BUILD_NO_FBX_EXPORTER
 #endif // ASSIMP_BUILD_NO_FBX_EXPORTER
 #endif // ASSIMP_BUILD_NO_EXPORT
 #endif // ASSIMP_BUILD_NO_EXPORT

+ 24 - 24
code/FBXExportProperty.h

@@ -47,7 +47,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 #ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
 #ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
 
 
-
 #include <assimp/types.h> // aiMatrix4x4
 #include <assimp/types.h> // aiMatrix4x4
 #include <assimp/StreamWriter.h> // StreamWriterLE
 #include <assimp/StreamWriter.h> // StreamWriterLE
 
 
@@ -56,11 +55,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <ostream>
 #include <ostream>
 #include <type_traits> // is_void
 #include <type_traits> // is_void
 
 
+namespace Assimp {
 namespace FBX {
 namespace FBX {
-    class Property;
-}
 
 
-/** FBX::Property
+/** @brief FBX::Property
  *
  *
  *  Holds a value of any of FBX's recognized types,
  *  Holds a value of any of FBX's recognized types,
  *  each represented by a particular one-character code.
  *  each represented by a particular one-character code.
@@ -78,35 +76,34 @@ namespace FBX {
  *  S : string (array of 1-byte char)
  *  S : string (array of 1-byte char)
  *  R : raw data (array of bytes)
  *  R : raw data (array of bytes)
  */
  */
-class FBX::Property
-{
+class FBXExportProperty {
 public:
 public:
     // constructors for basic types.
     // constructors for basic types.
     // all explicit to avoid accidental typecasting
     // all explicit to avoid accidental typecasting
-    explicit Property(bool v);
+    explicit FBXExportProperty(bool v);
     // TODO: determine if there is actually a byte type,
     // TODO: determine if there is actually a byte type,
     // or if this always means <bool>. 'C' seems to imply <char>,
     // or if this always means <bool>. 'C' seems to imply <char>,
     // so possibly the above was intended to represent both.
     // so possibly the above was intended to represent both.
-    explicit Property(int16_t v);
-    explicit Property(int32_t v);
-    explicit Property(float v);
-    explicit Property(double v);
-    explicit Property(int64_t v);
+    explicit FBXExportProperty(int16_t v);
+    explicit FBXExportProperty(int32_t v);
+    explicit FBXExportProperty(float v);
+    explicit FBXExportProperty(double v);
+    explicit FBXExportProperty(int64_t v);
     // strings can either be stored as 'R' (raw) or 'S' (string) type
     // strings can either be stored as 'R' (raw) or 'S' (string) type
-    explicit Property(const char* c, bool raw=false);
-    explicit Property(const std::string& s, bool raw=false);
-    explicit Property(const std::vector<uint8_t>& r);
-    explicit Property(const std::vector<int32_t>& va);
-    explicit Property(const std::vector<int64_t>& va);
-    explicit Property(const std::vector<double>& va);
-    explicit Property(const std::vector<float>& va);
-    explicit Property(const aiMatrix4x4& vm);
+    explicit FBXExportProperty(const char* c, bool raw = false);
+    explicit FBXExportProperty(const std::string& s, bool raw = false);
+    explicit FBXExportProperty(const std::vector<uint8_t>& r);
+    explicit FBXExportProperty(const std::vector<int32_t>& va);
+    explicit FBXExportProperty(const std::vector<int64_t>& va);
+    explicit FBXExportProperty(const std::vector<double>& va);
+    explicit FBXExportProperty(const std::vector<float>& va);
+    explicit FBXExportProperty(const aiMatrix4x4& vm);
 
 
     // this will catch any type not defined above,
     // this will catch any type not defined above,
     // so that we don't accidentally convert something we don't want.
     // so that we don't accidentally convert something we don't want.
     // for example (const char*) --> (bool)... seriously wtf C++
     // for example (const char*) --> (bool)... seriously wtf C++
     template <class T>
     template <class T>
-    explicit Property(T v) : type('X') {
+    explicit FBXExportProperty(T v) : type('X') {
         static_assert(std::is_void<T>::value, "TRIED TO CREATE FBX PROPERTY WITH UNSUPPORTED TYPE, CHECK YOUR PROPERTY INSTANTIATION");
         static_assert(std::is_void<T>::value, "TRIED TO CREATE FBX PROPERTY WITH UNSUPPORTED TYPE, CHECK YOUR PROPERTY INSTANTIATION");
     } // note: no line wrap so it appears verbatim on the compiler error
     } // note: no line wrap so it appears verbatim on the compiler error
 
 
@@ -114,9 +111,9 @@ public:
     size_t size();
     size_t size();
 
 
     // write this property node as binary data to the given stream
     // write this property node as binary data to the given stream
-    void DumpBinary(Assimp::StreamWriterLE &s);
-    void DumpAscii(Assimp::StreamWriterLE &s, int indent=0);
-    void DumpAscii(std::ostream &s, int indent=0);
+    void DumpBinary(Assimp::StreamWriterLE& s);
+    void DumpAscii(Assimp::StreamWriterLE& s, int indent = 0);
+    void DumpAscii(std::ostream& s, int indent = 0);
     // note: make sure the ostream is in classic "C" locale
     // note: make sure the ostream is in classic "C" locale
 
 
 private:
 private:
@@ -124,6 +121,9 @@ private:
     std::vector<uint8_t> data;
     std::vector<uint8_t> data;
 };
 };
 
 
+} // Namespace FBX
+} // Namespace Assimp
+
 #endif // ASSIMP_BUILD_NO_FBX_EXPORTER
 #endif // ASSIMP_BUILD_NO_FBX_EXPORTER
 
 
 #endif // AI_FBXEXPORTPROPERTY_H_INC
 #endif // AI_FBXEXPORTPROPERTY_H_INC

+ 59 - 25
code/FBXExporter.cpp

@@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "FBXExportNode.h"
 #include "FBXExportNode.h"
 #include "FBXExportProperty.h"
 #include "FBXExportProperty.h"
 #include "FBXCommon.h"
 #include "FBXCommon.h"
+#include "FBXUtil.h"
 
 
 #include <assimp/version.h> // aiGetVersion
 #include <assimp/version.h> // aiGetVersion
 #include <assimp/IOSystem.hpp>
 #include <assimp/IOSystem.hpp>
@@ -73,7 +74,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 const ai_real DEG = ai_real( 57.29577951308232087679815481 ); // degrees per radian
 const ai_real DEG = ai_real( 57.29577951308232087679815481 ); // degrees per radian
 
 
+using namespace Assimp;
+using namespace Assimp::FBX;
+
 // some constants that we'll use for writing metadata
 // some constants that we'll use for writing metadata
+namespace Assimp {
 namespace FBX {
 namespace FBX {
     const std::string EXPORT_VERSION_STR = "7.4.0";
     const std::string EXPORT_VERSION_STR = "7.4.0";
     const uint32_t EXPORT_VERSION_INT = 7400; // 7.4 == 2014/2015
     const uint32_t EXPORT_VERSION_INT = 7400; // 7.4 == 2014/2015
@@ -92,11 +97,6 @@ namespace FBX {
         ";------------------------------------------------------------------";
         ";------------------------------------------------------------------";
 }
 }
 
 
-using namespace Assimp;
-using namespace FBX;
-
-namespace Assimp {
-
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // Worker function for exporting a scene to binary FBX.
     // Worker function for exporting a scene to binary FBX.
     // Prototyped and registered in Exporter.cpp
     // Prototyped and registered in Exporter.cpp
@@ -121,6 +121,7 @@ namespace Assimp {
         IOSystem* pIOSystem,
         IOSystem* pIOSystem,
         const aiScene* pScene,
         const aiScene* pScene,
         const ExportProperties* pProperties
         const ExportProperties* pProperties
+
     ){
     ){
         // initialize the exporter
         // initialize the exporter
         FBXExporter exporter(pScene, pProperties);
         FBXExporter exporter(pScene, pProperties);
@@ -1393,10 +1394,6 @@ void FBXExporter::WriteObjects ()
 
 
     // FbxVideo - stores images used by textures.
     // FbxVideo - stores images used by textures.
     for (const auto &it : uid_by_image) {
     for (const auto &it : uid_by_image) {
-        if (it.first.compare(0, 1, "*") == 0) {
-            // TODO: embedded textures
-            continue;
-        }
         FBX::Node n("Video");
         FBX::Node n("Video");
         const int64_t& uid = it.second;
         const int64_t& uid = it.second;
         const std::string name = ""; // TODO: ... name???
         const std::string name = ""; // TODO: ... name???
@@ -1406,7 +1403,33 @@ void FBXExporter::WriteObjects ()
         // TODO: get full path... relative path... etc... ugh...
         // TODO: get full path... relative path... etc... ugh...
         // for now just use the same path for everything,
         // for now just use the same path for everything,
         // and hopefully one of them will work out.
         // and hopefully one of them will work out.
-        const std::string& path = it.first;
+        std::string path = it.first;
+        // try get embedded texture
+        const aiTexture* embedded_texture = mScene->GetEmbeddedTexture(it.first.c_str());
+        if (embedded_texture != nullptr) {
+            // change the path (use original filename, if available. If name is empty, concatenate texture index with file extension)
+            std::stringstream newPath;
+            if (embedded_texture->mFilename.length > 0) {
+                newPath << embedded_texture->mFilename.C_Str();
+            } else if (embedded_texture->achFormatHint[0]) {
+                int texture_index = std::stoi(path.substr(1, path.size() - 1));
+                newPath << texture_index << "." << embedded_texture->achFormatHint;
+            }
+            path = newPath.str();
+            // embed the texture
+            size_t texture_size = static_cast<size_t>(embedded_texture->mWidth * std::max(embedded_texture->mHeight, 1u));
+            if (binary) {
+                // embed texture as binary data
+                std::vector<uint8_t> tex_data;
+                tex_data.resize(texture_size);
+                memcpy(&tex_data[0], (char*)embedded_texture->pcData, texture_size);
+                n.AddChild("Content", tex_data);
+            } else {
+                // embed texture in base64 encoding
+                std::string encoded_texture = FBX::Util::EncodeBase64((char*)embedded_texture->pcData, texture_size);
+                n.AddChild("Content", encoded_texture);
+            }
+        }
         p.AddP70("Path", "KString", "XRefUrl", "", path);
         p.AddP70("Path", "KString", "XRefUrl", "", path);
         n.AddChild(p);
         n.AddChild(p);
         n.AddChild("UseMipMap", int32_t(0));
         n.AddChild("UseMipMap", int32_t(0));
@@ -1419,17 +1442,17 @@ void FBXExporter::WriteObjects ()
     // referenced by material_index/texture_type pairs.
     // referenced by material_index/texture_type pairs.
     std::map<std::pair<size_t,size_t>,int64_t> texture_uids;
     std::map<std::pair<size_t,size_t>,int64_t> texture_uids;
     const std::map<aiTextureType,std::string> prop_name_by_tt = {
     const std::map<aiTextureType,std::string> prop_name_by_tt = {
-        {aiTextureType_DIFFUSE, "DiffuseColor"},
-        {aiTextureType_SPECULAR, "SpecularColor"},
-        {aiTextureType_AMBIENT, "AmbientColor"},
-        {aiTextureType_EMISSIVE, "EmissiveColor"},
-        {aiTextureType_HEIGHT, "Bump"},
-        {aiTextureType_NORMALS, "NormalMap"},
-        {aiTextureType_SHININESS, "ShininessExponent"},
-        {aiTextureType_OPACITY, "TransparentColor"},
+        {aiTextureType_DIFFUSE,      "DiffuseColor"},
+        {aiTextureType_SPECULAR,     "SpecularColor"},
+        {aiTextureType_AMBIENT,      "AmbientColor"},
+        {aiTextureType_EMISSIVE,     "EmissiveColor"},
+        {aiTextureType_HEIGHT,       "Bump"},
+        {aiTextureType_NORMALS,      "NormalMap"},
+        {aiTextureType_SHININESS,    "ShininessExponent"},
+        {aiTextureType_OPACITY,      "TransparentColor"},
         {aiTextureType_DISPLACEMENT, "DisplacementColor"},
         {aiTextureType_DISPLACEMENT, "DisplacementColor"},
         //{aiTextureType_LIGHTMAP, "???"},
         //{aiTextureType_LIGHTMAP, "???"},
-        {aiTextureType_REFLECTION, "ReflectionColor"}
+        {aiTextureType_REFLECTION,   "ReflectionColor"}
         //{aiTextureType_UNKNOWN, ""}
         //{aiTextureType_UNKNOWN, ""}
     };
     };
     for (size_t i = 0; i < mScene->mNumMaterials; ++i) {
     for (size_t i = 0; i < mScene->mNumMaterials; ++i) {
@@ -1575,11 +1598,22 @@ void FBXExporter::WriteObjects ()
     // one sticky point is that the number of vertices may not match,
     // one sticky point is that the number of vertices may not match,
     // because assimp splits vertices by normal, uv, etc.
     // because assimp splits vertices by normal, uv, etc.
 
 
+    // functor for aiNode sorting
+    struct SortNodeByName
+    {
+        bool operator()(const aiNode *lhs, const aiNode *rhs) const
+        {
+            return strcmp(lhs->mName.C_Str(), rhs->mName.C_Str()) < 0;
+        }
+    };
+
     // first we should mark the skeleton for each mesh.
     // first we should mark the skeleton for each mesh.
     // the skeleton must include not only the aiBones,
     // the skeleton must include not only the aiBones,
     // but also all their parent nodes.
     // but also all their parent nodes.
     // anything that affects the position of any bone node must be included.
     // anything that affects the position of any bone node must be included.
-    std::vector<std::set<const aiNode*>> skeleton_by_mesh(mScene->mNumMeshes);
+    // Use SorNodeByName to make sure the exported result will be the same across all systems
+    // Otherwise the aiNodes of the skeleton would be sorted based on the pointer address, which isn't consistent
+    std::vector<std::set<const aiNode*, SortNodeByName>> skeleton_by_mesh(mScene->mNumMeshes);
     // at the same time we can build a list of all the skeleton nodes,
     // at the same time we can build a list of all the skeleton nodes,
     // which will be used later to mark them as type "limbNode".
     // which will be used later to mark them as type "limbNode".
     std::unordered_set<const aiNode*> limbnodes;
     std::unordered_set<const aiNode*> limbnodes;
@@ -1587,7 +1621,7 @@ void FBXExporter::WriteObjects ()
     std::map<std::string,aiNode*> node_by_bone;
     std::map<std::string,aiNode*> node_by_bone;
     for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) {
     for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) {
         const aiMesh* m = mScene->mMeshes[mi];
         const aiMesh* m = mScene->mMeshes[mi];
-        std::set<const aiNode*> skeleton;
+        std::set<const aiNode*, SortNodeByName> skeleton;
         for (size_t bi =0; bi < m->mNumBones; ++bi) {
         for (size_t bi =0; bi < m->mNumBones; ++bi) {
             const aiBone* b = m->mBones[bi];
             const aiBone* b = m->mBones[bi];
             const std::string name(b->mName.C_Str());
             const std::string name(b->mName.C_Str());
@@ -1728,7 +1762,7 @@ void FBXExporter::WriteObjects ()
         aiMatrix4x4 mesh_xform = get_world_transform(mesh_node, mScene);
         aiMatrix4x4 mesh_xform = get_world_transform(mesh_node, mScene);
 
 
         // now make a subdeformer for each bone in the skeleton
         // now make a subdeformer for each bone in the skeleton
-        const std::set<const aiNode*> &skeleton = skeleton_by_mesh[mi];
+        const std::set<const aiNode*, SortNodeByName> skeleton= skeleton_by_mesh[mi];
         for (const aiNode* bone_node : skeleton) {
         for (const aiNode* bone_node : skeleton) {
             // if there's a bone for this node, find it
             // if there's a bone for this node, find it
             const aiBone* b = nullptr;
             const aiBone* b = nullptr;
@@ -2237,8 +2271,8 @@ void FBXExporter::WriteModelNode(
 
 
     // not sure what these are for,
     // not sure what these are for,
     // but they seem to be omnipresent
     // but they seem to be omnipresent
-    m.AddChild("Shading", Property(true));
-    m.AddChild("Culling", Property("CullingOff"));
+    m.AddChild("Shading", FBXExportProperty(true));
+    m.AddChild("Culling", FBXExportProperty("CullingOff"));
 
 
     m.Dump(outstream, binary, 1);
     m.Dump(outstream, binary, 1);
 }
 }
@@ -2351,7 +2385,7 @@ void FBXExporter::WriteModelNodes(
         na.AddProperties(
         na.AddProperties(
             node_attribute_uid, FBX::SEPARATOR + "NodeAttribute", "LimbNode"
             node_attribute_uid, FBX::SEPARATOR + "NodeAttribute", "LimbNode"
         );
         );
-        na.AddChild("TypeFlags", Property("Skeleton"));
+        na.AddChild("TypeFlags", FBXExportProperty("Skeleton"));
         na.Dump(outstream, binary, 1);
         na.Dump(outstream, binary, 1);
         // and connect them
         // and connect them
         connections.emplace_back("C", "OO", node_attribute_uid, node_uid);
         connections.emplace_back("C", "OO", node_attribute_uid, node_uid);

+ 25 - 14
code/FBXImportSettings.h

@@ -53,19 +53,22 @@ namespace FBX {
 struct ImportSettings
 struct ImportSettings
 {
 {
     ImportSettings()
     ImportSettings()
-        : strictMode(true)
-        , readAllLayers(true)
-        , readAllMaterials(false)
-        , readMaterials(true)
-        , readTextures(true)
-        , readCameras(true)
-        , readLights(true)
-        , readAnimations(true)
-        , readWeights(true)
-        , preservePivots(true)
-        , optimizeEmptyAnimationCurves(true)
-        , useLegacyEmbeddedTextureNaming(false)
-    {}
+    : strictMode(true)
+    , readAllLayers(true)
+    , readAllMaterials(false)
+    , readMaterials(true)
+    , readTextures(true)
+    , readCameras(true)
+    , readLights(true)
+    , readAnimations(true)
+    , readWeights(true)
+    , preservePivots(true)
+    , optimizeEmptyAnimationCurves(true)
+    , useLegacyEmbeddedTextureNaming(false)
+    , removeEmptyBones( true )
+    , convertToMeters( false ) {
+        // empty
+    }
 
 
 
 
     /** enable strict mode:
     /** enable strict mode:
@@ -141,8 +144,16 @@ struct ImportSettings
     bool optimizeEmptyAnimationCurves;
     bool optimizeEmptyAnimationCurves;
 
 
     /** use legacy naming for embedded textures eg: (*0, *1, *2)
     /** use legacy naming for embedded textures eg: (*0, *1, *2)
-    **/
+    */
     bool useLegacyEmbeddedTextureNaming;
     bool useLegacyEmbeddedTextureNaming;
+
+    /** Empty bones shall be removed
+    */
+    bool removeEmptyBones;
+
+    /** Set to true to perform a conversion from cm to meter after the import
+    */
+    bool convertToMeters;
 };
 };
 
 
 
 

+ 16 - 7
code/FBXImporter.cpp

@@ -60,11 +60,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/importerdesc.h>
 #include <assimp/importerdesc.h>
 
 
 namespace Assimp {
 namespace Assimp {
-    template<> const char* LogFunctions<FBXImporter>::Prefix()
-    {
-        static auto prefix = "FBX: ";
-        return prefix;
-    }
+
+template<>
+const char* LogFunctions<FBXImporter>::Prefix() {
+    static auto prefix = "FBX: ";
+    return prefix;
+}
+
 }
 }
 
 
 using namespace Assimp;
 using namespace Assimp;
@@ -72,6 +74,7 @@ using namespace Assimp::Formatter;
 using namespace Assimp::FBX;
 using namespace Assimp::FBX;
 
 
 namespace {
 namespace {
+
 static const aiImporterDesc desc = {
 static const aiImporterDesc desc = {
     "Autodesk FBX Importer",
     "Autodesk FBX Importer",
     "",
     "",
@@ -137,6 +140,8 @@ void FBXImporter::SetupProperties(const Importer* pImp)
     settings.preservePivots = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true);
     settings.preservePivots = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true);
     settings.optimizeEmptyAnimationCurves = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, true);
     settings.optimizeEmptyAnimationCurves = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, true);
     settings.useLegacyEmbeddedTextureNaming = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING, false);
     settings.useLegacyEmbeddedTextureNaming = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING, false);
+    settings.removeEmptyBones = pImp->GetPropertyBool(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, true);
+    settings.convertToMeters = pImp->GetPropertyBool(AI_CONFIG_FBX_CONVERT_TO_M, false);
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
@@ -167,7 +172,7 @@ void FBXImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
         bool is_binary = false;
         bool is_binary = false;
         if (!strncmp(begin,"Kaydara FBX Binary",18)) {
         if (!strncmp(begin,"Kaydara FBX Binary",18)) {
             is_binary = true;
             is_binary = true;
-            TokenizeBinary(tokens,begin,static_cast<unsigned int>(contents.size()));
+            TokenizeBinary(tokens,begin,contents.size());
         }
         }
         else {
         else {
             Tokenize(tokens,begin);
             Tokenize(tokens,begin);
@@ -180,8 +185,12 @@ void FBXImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
         // take the raw parse-tree and convert it to a FBX DOM
         // take the raw parse-tree and convert it to a FBX DOM
         Document doc(parser,settings);
         Document doc(parser,settings);
 
 
+        FbxUnit unit(FbxUnit::cm);
+        if (settings.convertToMeters) {
+            unit = FbxUnit::m;
+        }
         // convert the FBX DOM to aiScene
         // convert the FBX DOM to aiScene
-        ConvertToAssimpScene(pScene,doc);
+        ConvertToAssimpScene(pScene,doc, settings.removeEmptyBones, unit);
 
 
         std::for_each(tokens.begin(),tokens.end(),Util::delete_fun<Token>());
         std::for_each(tokens.begin(),tokens.end(),Util::delete_fun<Token>());
     }
     }

+ 60 - 3
code/FBXMaterial.cpp

@@ -55,6 +55,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/ByteSwapper.h>
 #include <assimp/ByteSwapper.h>
 
 
 #include <algorithm> // std::transform
 #include <algorithm> // std::transform
+#include "FBXUtil.h"
 
 
 namespace Assimp {
 namespace Assimp {
 namespace FBX {
 namespace FBX {
@@ -206,6 +207,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());
@@ -301,13 +316,52 @@ Video::Video(uint64_t id, const Element& element, const Document& doc, const std
         relativeFileName = ParseTokenAsString(GetRequiredToken(*RelativeFilename,0));
         relativeFileName = ParseTokenAsString(GetRequiredToken(*RelativeFilename,0));
     }
     }
 
 
-    if(Content) {
+    if(Content && !Content->Tokens().empty()) {
         //this field is omitted when the embedded texture is already loaded, let's ignore if it's not found
         //this field is omitted when the embedded texture is already loaded, let's ignore if it's not found
         try {
         try {
             const Token& token = GetRequiredToken(*Content, 0);
             const Token& token = GetRequiredToken(*Content, 0);
             const char* data = token.begin();
             const char* data = token.begin();
             if (!token.IsBinary()) {
             if (!token.IsBinary()) {
-                DOMWarning("video content is not binary data, ignoring", &element);
+                if (*data != '"') {
+                    DOMError("embedded content is not surrounded by quotation marks", &element);
+                }
+                else {
+                    size_t targetLength = 0;
+                    auto numTokens = Content->Tokens().size();
+                    // First time compute size (it could be large like 64Gb and it is good to allocate it once)
+                    for (uint32_t tokenIdx = 0; tokenIdx < numTokens; ++tokenIdx)
+                    {
+                        const Token& dataToken = GetRequiredToken(*Content, tokenIdx);
+                        size_t tokenLength = dataToken.end() - dataToken.begin() - 2; // ignore double quotes
+                        const char* base64data = dataToken.begin() + 1;
+                        const size_t outLength = Util::ComputeDecodedSizeBase64(base64data, tokenLength);
+                        if (outLength == 0)
+                        {
+                            DOMError("Corrupted embedded content found", &element);
+                        }
+                        targetLength += outLength;
+                    }
+                    if (targetLength == 0)
+                    {
+                        DOMError("Corrupted embedded content found", &element);
+                    }
+                    content = new uint8_t[targetLength];
+                    contentLength = static_cast<uint64_t>(targetLength);
+                    size_t dst_offset = 0;
+                    for (uint32_t tokenIdx = 0; tokenIdx < numTokens; ++tokenIdx)
+                    {
+                        const Token& dataToken = GetRequiredToken(*Content, tokenIdx);
+                        size_t tokenLength = dataToken.end() - dataToken.begin() - 2; // ignore double quotes
+                        const char* base64data = dataToken.begin() + 1;
+                        dst_offset += Util::DecodeBase64(base64data, tokenLength, content + dst_offset, targetLength - dst_offset);
+                    }
+                    if (targetLength != dst_offset)
+                    {
+                        delete[] content;
+                        contentLength = 0;
+                        DOMError("Corrupted embedded content found", &element);
+                    }
+                }
             }
             }
             else if (static_cast<size_t>(token.end() - data) < 5) {
             else if (static_cast<size_t>(token.end() - data) < 5) {
                 DOMError("binary data array is too short, need five (5) bytes for type signature and element count", &element);
                 DOMError("binary data array is too short, need five (5) bytes for type signature and element count", &element);
@@ -326,8 +380,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());
         }
         }
     }
     }
 
 

+ 6 - 5
code/FBXMeshGeometry.cpp

@@ -568,15 +568,15 @@ void MeshGeometry::ReadVertexDataColors(std::vector<aiColor4D>& colors_out, cons
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-static const std::string TangentIndexToken = "TangentIndex";
-static const std::string TangentsIndexToken = "TangentsIndex";
+static const char *TangentIndexToken = "TangentIndex";
+static const char *TangentsIndexToken = "TangentsIndex";
 
 
 void MeshGeometry::ReadVertexDataTangents(std::vector<aiVector3D>& tangents_out, const Scope& source,
 void MeshGeometry::ReadVertexDataTangents(std::vector<aiVector3D>& tangents_out, const Scope& source,
     const std::string& MappingInformationType,
     const std::string& MappingInformationType,
     const std::string& ReferenceInformationType)
     const std::string& ReferenceInformationType)
 {
 {
     const char * str = source.Elements().count( "Tangents" ) > 0 ? "Tangents" : "Tangent";
     const char * str = source.Elements().count( "Tangents" ) > 0 ? "Tangents" : "Tangent";
-    const char * strIdx = source.Elements().count( "Tangents" ) > 0 ? TangentsIndexToken.c_str() : TangentIndexToken.c_str();
+    const char * strIdx = source.Elements().count( "Tangents" ) > 0 ? TangentsIndexToken : TangentIndexToken;
     ResolveVertexDataArray(tangents_out,source,MappingInformationType,ReferenceInformationType,
     ResolveVertexDataArray(tangents_out,source,MappingInformationType,ReferenceInformationType,
         str,
         str,
         strIdx,
         strIdx,
@@ -630,10 +630,11 @@ void MeshGeometry::ReadVertexDataMaterials(std::vector<int>& materials_out, cons
             materials_out.clear();
             materials_out.clear();
         }
         }
 
 
-        m_materials.assign(m_vertices.size(),materials_out[0]);
+        materials_out.resize(m_vertices.size());
+        std::fill(materials_out.begin(), materials_out.end(), materials_out.at(0));
     }
     }
     else if (MappingInformationType == "ByPolygon" && ReferenceInformationType == "IndexToDirect") {
     else if (MappingInformationType == "ByPolygon" && ReferenceInformationType == "IndexToDirect") {
-        m_materials.resize(face_count);
+        materials_out.resize(face_count);
 
 
         if(materials_out.size() != face_count) {
         if(materials_out.size() != face_count) {
             FBXImporter::LogError(Formatter::format("length of input data unexpected for ByPolygon mapping: ")
             FBXImporter::LogError(Formatter::format("length of input data unexpected for ByPolygon mapping: ")

+ 4 - 4
code/FBXParser.cpp

@@ -117,7 +117,7 @@ namespace FBX {
 Element::Element(const Token& key_token, Parser& parser)
 Element::Element(const Token& key_token, Parser& parser)
 : key_token(key_token)
 : key_token(key_token)
 {
 {
-    TokenPtr n = NULL;
+    TokenPtr n = nullptr;
     do {
     do {
         n = parser.AdvanceToNextToken();
         n = parser.AdvanceToNextToken();
         if(!n) {
         if(!n) {
@@ -643,9 +643,9 @@ void ParseVectorDataArray(std::vector<aiVector3D>& out, const Element& el)
         if (type == 'd') {
         if (type == 'd') {
             const double* d = reinterpret_cast<const double*>(&buff[0]);
             const double* d = reinterpret_cast<const double*>(&buff[0]);
             for (unsigned int i = 0; i < count3; ++i, d += 3) {
             for (unsigned int i = 0; i < count3; ++i, d += 3) {
-                out.push_back(aiVector3D(static_cast<float>(d[0]),
-                    static_cast<float>(d[1]),
-                    static_cast<float>(d[2])));
+                out.push_back(aiVector3D(static_cast<ai_real>(d[0]),
+                    static_cast<ai_real>(d[1]),
+                    static_cast<ai_real>(d[2])));
             }
             }
             // for debugging
             // for debugging
             /*for ( size_t i = 0; i < out.size(); i++ ) {
             /*for ( size_t i = 0; i < out.size(); i++ ) {

+ 6 - 6
code/FBXTokenizer.h

@@ -93,7 +93,7 @@ public:
     Token(const char* sbegin, const char* send, TokenType type, unsigned int line, unsigned int column);
     Token(const char* sbegin, const char* send, TokenType type, unsigned int line, unsigned int column);
 
 
     /** construct a binary token */
     /** construct a binary token */
-    Token(const char* sbegin, const char* send, TokenType type, unsigned int offset);
+    Token(const char* sbegin, const char* send, TokenType type, size_t offset);
 
 
     ~Token();
     ~Token();
 
 
@@ -118,14 +118,14 @@ public:
         return type;
         return type;
     }
     }
 
 
-    unsigned int Offset() const {
+    size_t Offset() const {
         ai_assert(IsBinary());
         ai_assert(IsBinary());
         return offset;
         return offset;
     }
     }
 
 
     unsigned int Line() const {
     unsigned int Line() const {
         ai_assert(!IsBinary());
         ai_assert(!IsBinary());
-        return line;
+        return static_cast<unsigned int>(line);
     }
     }
 
 
     unsigned int Column() const {
     unsigned int Column() const {
@@ -147,8 +147,8 @@ private:
     const TokenType type;
     const TokenType type;
 
 
     union {
     union {
-        const unsigned int line;
-        unsigned int offset;
+        size_t line;
+        size_t offset;
     };
     };
     const unsigned int column;
     const unsigned int column;
 };
 };
@@ -178,7 +178,7 @@ void Tokenize(TokenList& output_tokens, const char* input);
  * @param input_buffer Binary input buffer to be processed.
  * @param input_buffer Binary input buffer to be processed.
  * @param length Length of input buffer, in bytes. There is no 0-terminal.
  * @param length Length of input buffer, in bytes. There is no 0-terminal.
  * @throw DeadlyImportError if something goes wrong */
  * @throw DeadlyImportError if something goes wrong */
-void TokenizeBinary(TokenList& output_tokens, const char* input, unsigned int length);
+void TokenizeBinary(TokenList& output_tokens, const char* input, size_t length);
 
 
 
 
 } // ! FBX
 } // ! FBX

+ 124 - 1
code/FBXUtil.cpp

@@ -49,6 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 #include <assimp/TinyFormatter.h>
 #include <assimp/TinyFormatter.h>
 #include <string>
 #include <string>
+#include <cstring>
 
 
 #ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
 #ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
 
 
@@ -85,7 +86,7 @@ const char* TokenTypeString(TokenType t)
 
 
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-std::string AddOffset(const std::string& prefix, const std::string& text, unsigned int offset)
+std::string AddOffset(const std::string& prefix, const std::string& text, size_t offset)
 {
 {
     return static_cast<std::string>( (Formatter::format() << prefix << " (offset 0x" << std::hex << offset << ") " << text) );
     return static_cast<std::string>( (Formatter::format() << prefix << " (offset 0x" << std::hex << offset << ") " << text) );
 }
 }
@@ -113,6 +114,128 @@ std::string AddTokenText(const std::string& prefix, const std::string& text, con
         text) );
         text) );
 }
 }
 
 
+// Generated by this formula: T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i;
+static const uint8_t base64DecodeTable[128] = {
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,
+    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 255, 255, 255,
+    255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255,
+    255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255
+};
+
+uint8_t DecodeBase64(char ch)
+{
+    const auto idx = static_cast<uint8_t>(ch);
+    if (idx > 127)
+        return 255;
+    return base64DecodeTable[idx];
+}
+
+size_t ComputeDecodedSizeBase64(const char* in, size_t inLength)
+{
+    if (inLength < 2)
+    {
+        return 0;
+    }
+    const size_t equals = size_t(in[inLength - 1] == '=') + size_t(in[inLength - 2] == '=');
+    const size_t full_length = (inLength * 3) >> 2; // div by 4
+    if (full_length < equals)
+    {
+        return 0;
+    }
+    return full_length - equals;
+}
+
+size_t DecodeBase64(const char* in, size_t inLength, uint8_t* out, size_t maxOutLength)
+{
+    if (maxOutLength == 0 || inLength < 2) {
+        return 0;
+    }
+    const size_t realLength = inLength - size_t(in[inLength - 1] == '=') - size_t(in[inLength - 2] == '=');
+    size_t dst_offset = 0;
+    int val = 0, valb = -8;
+    for (size_t src_offset = 0; src_offset < realLength; ++src_offset)
+    {
+        const uint8_t table_value = Util::DecodeBase64(in[src_offset]);
+        if (table_value == 255)
+        {
+            return 0;
+        }
+        val = (val << 6) + table_value;
+        valb += 6;
+        if (valb >= 0)
+        {
+            out[dst_offset++] = static_cast<uint8_t>((val >> valb) & 0xFF);
+            valb -= 8;
+            val &= 0xFFF;
+        }
+    }
+    return dst_offset;
+}
+
+static const char to_base64_string[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+char EncodeBase64(char byte)
+{
+    return to_base64_string[(size_t)byte];
+}
+
+/** Encodes a block of 4 bytes to base64 encoding
+*
+*  @param bytes Bytes to encode.
+*  @param out_string String to write encoded values to.
+*  @param string_pos Position in out_string.*/
+void EncodeByteBlock(const char* bytes, std::string& out_string, size_t string_pos)
+{
+    char b0 = (bytes[0] & 0xFC) >> 2;
+    char b1 = (bytes[0] & 0x03) << 4 | ((bytes[1] & 0xF0) >> 4);
+    char b2 = (bytes[1] & 0x0F) << 2 | ((bytes[2] & 0xC0) >> 6);
+    char b3 = (bytes[2] & 0x3F);
+
+    out_string[string_pos + 0] = EncodeBase64(b0);
+    out_string[string_pos + 1] = EncodeBase64(b1);
+    out_string[string_pos + 2] = EncodeBase64(b2);
+    out_string[string_pos + 3] = EncodeBase64(b3);
+}
+
+std::string EncodeBase64(const char* data, size_t length)
+{
+    // calculate extra bytes needed to get a multiple of 3
+    size_t extraBytes = 3 - length % 3;
+
+    // number of base64 bytes
+    size_t encodedBytes = 4 * (length + extraBytes) / 3;
+
+    std::string encoded_string(encodedBytes, '=');
+
+    // read blocks of 3 bytes
+    for (size_t ib3 = 0; ib3 < length / 3; ib3++)
+    {
+        const size_t iByte = ib3 * 3;
+        const size_t iEncodedByte = ib3 * 4;
+        const char* currData = &data[iByte];
+
+        EncodeByteBlock(currData, encoded_string, iEncodedByte);
+    }
+
+    // if size of data is not a multiple of 3, also encode the final bytes (and add zeros where needed)
+    if (extraBytes > 0)
+    {
+        char finalBytes[4] = { 0,0,0,0 };
+        memcpy(&finalBytes[0], &data[length - length % 3], length % 3);
+
+        const size_t iEncodedByte = encodedBytes - 4;
+        EncodeByteBlock(&finalBytes[0], encoded_string, iEncodedByte);
+
+        // add '=' at the end
+        for (size_t i = 0; i < 4 * extraBytes / 3; i++)
+            encoded_string[encodedBytes - i - 1] = '=';
+    }
+    return encoded_string;
+}
+
 } // !Util
 } // !Util
 } // !FBX
 } // !FBX
 } // !Assimp
 } // !Assimp

+ 33 - 1
code/FBXUtil.h

@@ -48,6 +48,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 #include "FBXCompileConfig.h"
 #include "FBXCompileConfig.h"
 #include "FBXTokenizer.h"
 #include "FBXTokenizer.h"
+#include <stdint.h>
 
 
 namespace Assimp {
 namespace Assimp {
 namespace FBX {
 namespace FBX {
@@ -77,7 +78,7 @@ const char* TokenTypeString(TokenType t);
  *  @param line Line index, 1-based
  *  @param line Line index, 1-based
  *  @param column Column index, 1-based
  *  @param column Column index, 1-based
  *  @return A string of the following format: {prefix} (offset 0x{offset}) {text}*/
  *  @return A string of the following format: {prefix} (offset 0x{offset}) {text}*/
-std::string AddOffset(const std::string& prefix, const std::string& text, unsigned int offset);
+std::string AddOffset(const std::string& prefix, const std::string& text, size_t offset);
 
 
 
 
 /** Format log/error messages using a given line location in the source file.
 /** Format log/error messages using a given line location in the source file.
@@ -98,6 +99,37 @@ std::string AddLineAndColumn(const std::string& prefix, const std::string& text,
  *  @return A string of the following format: {prefix} ({token-type}, line {line}, col {column}) {text}*/
  *  @return A string of the following format: {prefix} ({token-type}, line {line}, col {column}) {text}*/
 std::string AddTokenText(const std::string& prefix, const std::string& text, const Token* tok);
 std::string AddTokenText(const std::string& prefix, const std::string& text, const Token* tok);
 
 
+/** Decode a single Base64-encoded character.
+*
+*  @param ch Character to decode (from base64 to binary).
+*  @return decoded byte value*/
+uint8_t DecodeBase64(char ch);
+
+/** Compute decoded size of a Base64-encoded string
+*
+*  @param in Characters to decode.
+*  @param inLength Number of characters to decode.
+*  @return size of the decoded data (number of bytes)*/
+size_t ComputeDecodedSizeBase64(const char* in, size_t inLength);
+
+/** Decode a Base64-encoded string
+*
+*  @param in Characters to decode.
+*  @param inLength Number of characters to decode.
+*  @param out Pointer where we will store the decoded data.
+*  @param maxOutLength Size of output buffer.
+*  @return size of the decoded data (number of bytes)*/
+size_t DecodeBase64(const char* in, size_t inLength, uint8_t* out, size_t maxOutLength);
+
+char EncodeBase64(char byte);
+
+/** Encode bytes in base64-encoding
+*
+*  @param data Binary data to encode.
+*  @param inLength Number of bytes to encode.
+*  @return base64-encoded string*/
+std::string EncodeBase64(const char* data, size_t length);
+
 }
 }
 }
 }
 }
 }

+ 1 - 0
code/FIReader.cpp

@@ -228,6 +228,7 @@ bool FindDegeneratesProcess::ExecuteOnMesh( aiMesh* mesh) {
                     if ( area < 1e-6 ) {
                     if ( area < 1e-6 ) {
                         if ( mConfigRemoveDegenerates ) {
                         if ( mConfigRemoveDegenerates ) {
                             remove_me[ a ] = true;
                             remove_me[ a ] = true;
+                            ++deg;
                             goto evil_jump_outside;
                             goto evil_jump_outside;
                         }
                         }
 
 

+ 16 - 16
code/FindInstancesProcess.cpp

@@ -137,6 +137,11 @@ void FindInstancesProcess::Execute( aiScene* pScene)
             aiMesh* inst = pScene->mMeshes[i];
             aiMesh* inst = pScene->mMeshes[i];
             hashes[i] = GetMeshHash(inst);
             hashes[i] = GetMeshHash(inst);
 
 
+            // Find an appropriate epsilon 
+            // to compare position differences against
+            float epsilon = ComputePositionEpsilon(inst);
+            epsilon *= epsilon;
+
             for (int a = i-1; a >= 0; --a) {
             for (int a = i-1; a >= 0; --a) {
                 if (hashes[i] == hashes[a])
                 if (hashes[i] == hashes[a])
                 {
                 {
@@ -154,12 +159,7 @@ void FindInstancesProcess::Execute( aiScene* pScene)
                         orig->mPrimitiveTypes != inst->mPrimitiveTypes)
                         orig->mPrimitiveTypes != inst->mPrimitiveTypes)
                         continue;
                         continue;
 
 
-                    // up to now the meshes are equal. find an appropriate
-                    // epsilon to compare position differences against
-                    float epsilon = ComputePositionEpsilon(inst);
-                    epsilon *= epsilon;
-
-                    // now compare vertex positions, normals,
+                    // up to now the meshes are equal. Now compare vertex positions, normals,
                     // tangents and bitangents using this epsilon.
                     // tangents and bitangents using this epsilon.
                     if (orig->HasPositions()) {
                     if (orig->HasPositions()) {
                         if(!CompareArrays(orig->mVertices,inst->mVertices,orig->mNumVertices,epsilon))
                         if(!CompareArrays(orig->mVertices,inst->mVertices,orig->mNumVertices,epsilon))
@@ -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;
                         }
                         }
                     }
                     }

+ 6 - 4
code/FindInvalidDataProcess.cpp

@@ -282,9 +282,11 @@ void FindInvalidDataProcess::ProcessAnimation (aiAnimation* anim) {
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 void FindInvalidDataProcess::ProcessAnimationChannel (aiNodeAnim* anim) {
 void FindInvalidDataProcess::ProcessAnimationChannel (aiNodeAnim* anim) {
-    ai_assert( 0 != anim->mPositionKeys );
-    ai_assert( 0 != anim->mRotationKeys );
-    ai_assert( 0 != anim->mScalingKeys );
+    ai_assert( nullptr != anim );
+    if (anim->mNumPositionKeys == 0 && anim->mNumRotationKeys == 0 && anim->mNumScalingKeys == 0) {
+        ai_assert_entry();
+        return;
+    }
 
 
     // Check whether all values in a tracks are identical - in this case
     // Check whether all values in a tracks are identical - in this case
     // we can remove al keys except one.
     // we can remove al keys except one.
@@ -328,7 +330,7 @@ void FindInvalidDataProcess::ProcessAnimationChannel (aiNodeAnim* anim) {
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Search a mesh for invalid contents
 // Search a mesh for invalid contents
-int FindInvalidDataProcess::ProcessMesh (aiMesh* pMesh)
+int FindInvalidDataProcess::ProcessMesh(aiMesh* pMesh)
 {
 {
     bool ret = false;
     bool ret = false;
     std::vector<bool> dirtyMask(pMesh->mNumVertices, pMesh->mNumFaces != 0);
     std::vector<bool> dirtyMask(pMesh->mNumVertices, pMesh->mNumFaces != 0);

+ 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

+ 1 - 1
code/Importer/IFC/IFCBoolean.cpp

@@ -256,7 +256,7 @@ bool IntersectsBoundaryProfile(const IfcVector3& e0, const IfcVector3& e1, const
     for( size_t i = 0, bcount = boundary.size(); i < bcount; ++i ) {
     for( size_t i = 0, bcount = boundary.size(); i < bcount; ++i ) {
         IfcVector3 b01 = boundary[(i + 1) % bcount] - boundary[i];
         IfcVector3 b01 = boundary[(i + 1) % bcount] - boundary[i];
         IfcVector3 b12 = boundary[(i + 2) % bcount] - boundary[(i + 1) % bcount];
         IfcVector3 b12 = boundary[(i + 2) % bcount] - boundary[(i + 1) % bcount];
-        IfcVector3 b1_side = IfcVector3(b01.y, -b01.x, 0.0); // rotated 90° clockwise in Z plane
+        IfcVector3 b1_side = IfcVector3(b01.y, -b01.x, 0.0); // rotated 90° clockwise in Z plane
         // Warning: rough estimate only. A concave poly with lots of small segments each featuring a small counter rotation
         // Warning: rough estimate only. A concave poly with lots of small segments each featuring a small counter rotation
         // could fool the accumulation. Correct implementation would be sum( acos( b01 * b2) * sign( b12 * b1_side))
         // could fool the accumulation. Correct implementation would be sum( acos( b01 * b2) * sign( b12 * b1_side))
         windingOrder += (b1_side.x*b12.x + b1_side.y*b12.y);
         windingOrder += (b1_side.x*b12.x + b1_side.y*b12.y);

+ 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

+ 31 - 10
code/ObjFileImporter.cpp

@@ -442,11 +442,12 @@ 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 ];
     }
     }
 
 
     // Copy vertices, normals and textures into aiMesh instance
     // Copy vertices, normals and textures into aiMesh instance
+    bool normalsok = true, uvok = true;
     unsigned int newIndex = 0, outIndex = 0;
     unsigned int newIndex = 0, outIndex = 0;
     for ( size_t index=0; index < pObjMesh->m_Faces.size(); index++ ) {
     for ( size_t index=0; index < pObjMesh->m_Faces.size(); index++ ) {
         // Get source face
         // Get source face
@@ -466,12 +467,16 @@ void ObjFileImporter::createVertexArray(const ObjFile::Model* pModel,
             pMesh->mVertices[ newIndex ] = pModel->m_Vertices[ vertex ];
             pMesh->mVertices[ newIndex ] = pModel->m_Vertices[ vertex ];
 
 
             // Copy all normals
             // Copy all normals
-            if ( !pModel->m_Normals.empty() && vertexIndex < pSourceFace->m_normals.size()) {
+            if ( normalsok && !pModel->m_Normals.empty() && vertexIndex < pSourceFace->m_normals.size()) {
                 const unsigned int normal = pSourceFace->m_normals.at( vertexIndex );
                 const unsigned int normal = pSourceFace->m_normals.at( vertexIndex );
-                if ( normal >= pModel->m_Normals.size() ) {
-                    throw DeadlyImportError( "OBJ: vertex normal index out of range" );
+                if ( normal >= pModel->m_Normals.size() )
+                {
+                    normalsok = false;
+                }
+                else
+                {
+                    pMesh->mNormals[ newIndex ] = pModel->m_Normals[ normal ];
                 }
                 }
-                pMesh->mNormals[ newIndex ] = pModel->m_Normals[ normal ];
             }
             }
 
 
             // Copy all vertex colors
             // Copy all vertex colors
@@ -482,15 +487,19 @@ void ObjFileImporter::createVertexArray(const ObjFile::Model* pModel,
             }
             }
 
 
             // Copy all texture coordinates
             // Copy all texture coordinates
-            if ( !pModel->m_TextureCoord.empty() && vertexIndex < pSourceFace->m_texturCoords.size())
+            if ( uvok && !pModel->m_TextureCoord.empty() && vertexIndex < pSourceFace->m_texturCoords.size())
             {
             {
                 const unsigned int tex = pSourceFace->m_texturCoords.at( vertexIndex );
                 const unsigned int tex = pSourceFace->m_texturCoords.at( vertexIndex );
 
 
                 if ( tex >= pModel->m_TextureCoord.size() )
                 if ( tex >= pModel->m_TextureCoord.size() )
-                    throw DeadlyImportError("OBJ: texture coordinate index out of range");
-
-                const aiVector3D &coord3d = pModel->m_TextureCoord[ tex ];
-                pMesh->mTextureCoords[ 0 ][ newIndex ] = aiVector3D( coord3d.x, coord3d.y, coord3d.z );
+                {
+                    uvok = false;
+                }
+                else
+                {
+                    const aiVector3D &coord3d = pModel->m_TextureCoord[ tex ];
+                    pMesh->mTextureCoords[ 0 ][ newIndex ] = aiVector3D( coord3d.x, coord3d.y, coord3d.z );
+                }
             }
             }
 
 
             // Get destination face
             // Get destination face
@@ -534,6 +543,18 @@ void ObjFileImporter::createVertexArray(const ObjFile::Model* pModel,
             ++newIndex;
             ++newIndex;
         }
         }
     }
     }
+
+    if (!normalsok)
+    {
+        delete [] pMesh->mNormals;
+        pMesh->mNormals = nullptr;
+    }
+
+    if (!uvok)
+    {
+        delete [] pMesh->mTextureCoords[0];
+        pMesh->mTextureCoords[0] = nullptr;
+    }
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------

+ 30 - 10
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 = getTexCoordVector(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;
@@ -271,6 +272,17 @@ static bool isDataDefinitionEnd( const char *tmp ) {
     return false;
     return false;
 }
 }
 
 
+static bool isNanOrInf(const char * in) {
+    // Look for "nan" or "inf", case insensitive
+    if ((in[0] == 'N' || in[0] == 'n') && ASSIMP_strincmp(in, "nan", 3) == 0) {
+        return true;
+    }
+    else if ((in[0] == 'I' || in[0] == 'i') && ASSIMP_strincmp(in, "inf", 3) == 0) {
+        return true;
+    }
+    return false;
+}
+
 size_t ObjFileParser::getNumComponentsInDataDefinition() {
 size_t ObjFileParser::getNumComponentsInDataDefinition() {
     size_t numComponents( 0 );
     size_t numComponents( 0 );
     const char* tmp( &m_DataIt[0] );
     const char* tmp( &m_DataIt[0] );
@@ -284,7 +296,7 @@ size_t ObjFileParser::getNumComponentsInDataDefinition() {
         if ( !SkipSpaces( &tmp ) ) {
         if ( !SkipSpaces( &tmp ) ) {
             break;
             break;
         }
         }
-        const bool isNum( IsNumeric( *tmp ) );
+        const bool isNum( IsNumeric( *tmp ) || isNanOrInf(tmp));
         SkipToken( tmp );
         SkipToken( tmp );
         if ( isNum ) {
         if ( isNum ) {
             ++numComponents;
             ++numComponents;
@@ -296,7 +308,7 @@ size_t ObjFileParser::getNumComponentsInDataDefinition() {
     return numComponents;
     return numComponents;
 }
 }
 
 
-void ObjFileParser::getVector( std::vector<aiVector3D> &point3d_array ) {
+size_t ObjFileParser::getTexCoordVector( 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 ) {
@@ -318,8 +330,20 @@ void ObjFileParser::getVector( std::vector<aiVector3D> &point3d_array ) {
     } else {
     } else {
         throw DeadlyImportError( "OBJ: Invalid number of components" );
         throw DeadlyImportError( "OBJ: Invalid number of components" );
     }
     }
+
+    // Coerce nan and inf to 0 as is the OBJ default value
+    if (!std::isfinite(x))
+        x = 0;
+
+    if (!std::isfinite(y))
+        y = 0;
+
+    if (!std::isfinite(z))
+        z = 0;
+
     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 ) {
@@ -427,13 +451,6 @@ void ObjFileParser::getFace( aiPrimitiveType type ) {
             if (type == aiPrimitiveType_POINT) {
             if (type == aiPrimitiveType_POINT) {
                 ASSIMP_LOG_ERROR("Obj: Separator unexpected in point statement");
                 ASSIMP_LOG_ERROR("Obj: Separator unexpected in point statement");
             }
             }
-            if (iPos == 0) {
-                //if there are no texture coordinates in the file, but normals
-                if (!vt && vn) {
-                    iPos = 1;
-                    iStep++;
-                }
-            }
             iPos++;
             iPos++;
         } else if( IsSpaceOrNewLine( *m_DataIt ) ) {
         } else if( IsSpaceOrNewLine( *m_DataIt ) ) {
             iPos = 0;
             iPos = 0;
@@ -450,6 +467,9 @@ void ObjFileParser::getFace( aiPrimitiveType type ) {
                 ++iStep;
                 ++iStep;
             }
             }
 
 
+            if (iPos == 1 && !vt && vn)
+                iPos = 2; // skip texture coords for normals if there are no tex coords
+
             if ( iVal > 0 ) {
             if ( iVal > 0 ) {
                 // Store parsed index
                 // Store parsed index
                 if ( 0 == iPos ) {
                 if ( 0 == iPos ) {

+ 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 getTexCoordVector( 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) {

+ 1 - 0
code/STLLoader.cpp

@@ -278,6 +278,7 @@ void STLImporter::LoadASCIIFile( aiNode *root ) {
             }
             }
             std::string name( szMe, temp );
             std::string name( szMe, temp );
             node->mName.Set( name.c_str() );
             node->mName.Set( name.c_str() );
+            pMesh->mName.Set( name.c_str() );
             //pScene->mRootNode->mName.length = temp;
             //pScene->mRootNode->mName.length = temp;
             //memcpy(pScene->mRootNode->mName.data,szMe,temp);
             //memcpy(pScene->mRootNode->mName.data,szMe,temp);
             //pScene->mRootNode->mName.data[temp] = '\0';
             //pScene->mRootNode->mName.data[temp] = '\0';

+ 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]);
         }
         }
     }
     }
 }
 }

+ 59 - 52
code/ValidateDataStructure.cpp

@@ -46,8 +46,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *    the data structure returned by Assimp.
  *    the data structure returned by Assimp.
  */
  */
 
 
-
-
 // internal headers
 // internal headers
 #include "ValidateDataStructure.h"
 #include "ValidateDataStructure.h"
 #include <assimp/BaseImporter.h>
 #include <assimp/BaseImporter.h>
@@ -110,8 +108,8 @@ void ValidateDSProcess::ReportWarning(const char* msg,...)
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-inline int HasNameMatch(const aiString& in, aiNode* node)
-{
+inline
+int HasNameMatch(const aiString& in, aiNode* node) {
     int result = (node->mName == in ? 1 : 0 );
     int result = (node->mName == in ? 1 : 0 );
     for (unsigned int i = 0; i < node->mNumChildren;++i)    {
     for (unsigned int i = 0; i < node->mNumChildren;++i)    {
         result += HasNameMatch(in,node->mChildren[i]);
         result += HasNameMatch(in,node->mChildren[i]);
@@ -121,9 +119,8 @@ inline int HasNameMatch(const aiString& in, aiNode* node)
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 template <typename T>
 template <typename T>
-inline void ValidateDSProcess::DoValidation(T** parray, unsigned int size,
-    const char* firstName, const char* secondName)
-{
+inline
+void ValidateDSProcess::DoValidation(T** parray, unsigned int size, const char* firstName, const char* secondName) {
     // validate all entries
     // validate all entries
     if (size)
     if (size)
     {
     {
@@ -160,7 +157,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 +167,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);
                 }
                 }
             }
             }
         }
         }
@@ -181,7 +178,8 @@ inline void ValidateDSProcess::DoValidationEx(T** parray, unsigned int size,
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 template <typename T>
 template <typename T>
 inline
 inline
-void ValidateDSProcess::DoValidationWithNameCheck(T** array, unsigned int size, const char* firstName, const char* secondName) {
+void ValidateDSProcess::DoValidationWithNameCheck(T** array, unsigned int size, const char* firstName,
+        const char* secondName) {
     // validate all entries
     // validate all entries
     DoValidationEx(array,size,firstName,secondName);
     DoValidationEx(array,size,firstName,secondName);
 
 
@@ -201,9 +199,8 @@ void ValidateDSProcess::DoValidationWithNameCheck(T** array, unsigned int size,
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Executes the post processing step on the given imported data.
 // Executes the post processing step on the given imported data.
-void ValidateDSProcess::Execute( aiScene* pScene)
-{
-    this->mScene = pScene;
+void ValidateDSProcess::Execute( aiScene* pScene) {
+    mScene = pScene;
     ASSIMP_LOG_DEBUG("ValidateDataStructureProcess begin");
     ASSIMP_LOG_DEBUG("ValidateDataStructureProcess begin");
 
 
     // validate the node graph of the scene
     // validate the node graph of the scene
@@ -330,6 +327,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 +420,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 ...
     {
     {
@@ -513,13 +513,11 @@ void ValidateDSProcess::Validate( const aiMesh* pMesh)
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
-void ValidateDSProcess::Validate( const aiMesh* pMesh,
-    const aiBone* pBone,float* afSum)
-{
+void ValidateDSProcess::Validate( const aiMesh* pMesh, const aiBone* pBone,float* afSum) {
     this->Validate(&pBone->mName);
     this->Validate(&pBone->mName);
 
 
     if (!pBone->mNumWeights)    {
     if (!pBone->mNumWeights)    {
-        ReportError("aiBone::mNumWeights is zero");
+        //ReportError("aiBone::mNumWeights is zero");
     }
     }
 
 
     // check whether all vertices affected by this bone are valid
     // check whether all vertices affected by this bone are valid
@@ -557,10 +555,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.");
-
-    // 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");
+    else {
+    	ReportError("aiAnimation::mNumChannels is 0. At least one node animation channel must be there.");
+    }
 }
 }
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
@@ -577,15 +574,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)   {
@@ -740,8 +738,9 @@ void ValidateDSProcess::Validate( const aiMaterial* pMaterial)
                     "AI_MATKEY_SHININESS_STRENGTH key is 0.0");
                     "AI_MATKEY_SHININESS_STRENGTH key is 0.0");
             }
             }
             break;
             break;
-        default: ;
-        };
+        default:
+            break;
+        }
     }
     }
 
 
     if (AI_SUCCESS == aiGetMaterialFloat( pMaterial,AI_MATKEY_OPACITY,&fTemp) && (!fTemp || fTemp > 1.01)) {
     if (AI_SUCCESS == aiGetMaterialFloat( pMaterial,AI_MATKEY_OPACITY,&fTemp) && (!fTemp || fTemp > 1.01)) {
@@ -773,8 +772,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 +806,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 +845,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 +906,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 +930,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 +944,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 +958,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 +966,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,

+ 7 - 4
code/glTF2Asset.inl

@@ -789,13 +789,16 @@ static void BuildVertexWeightMapping(Mesh::Primitive& primitive, std::vector<std
     delete[] indices16;
     delete[] indices16;
 }
 }
 
 
+static std::string GetNodeName(const Node& node)
+{
+    return node.name.empty() ? node.id : node.name;
+}
+
 aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector<unsigned int>& meshOffsets, glTF2::Ref<glTF2::Node>& ptr)
 aiNode* ImportNode(aiScene* pScene, glTF2::Asset& r, std::vector<unsigned int>& meshOffsets, glTF2::Ref<glTF2::Node>& ptr)
 {
 {
     Node& node = *ptr;
     Node& node = *ptr;
 
 
-    std::string nameOrId = node.name.empty() ? node.id : node.name;
-
-    aiNode* ainode = new aiNode(nameOrId);
+    aiNode* ainode = new aiNode(GetNodeName(node));
 
 
     if (!node.children.empty()) {
     if (!node.children.empty()) {
         ainode->mNumChildren = unsigned(node.children.size());
         ainode->mNumChildren = unsigned(node.children.size());
@@ -921,7 +924,7 @@ struct AnimationSamplers {
 aiNodeAnim* CreateNodeAnim(glTF2::Asset& r, Node& node, AnimationSamplers& samplers)
 aiNodeAnim* CreateNodeAnim(glTF2::Asset& r, Node& node, AnimationSamplers& samplers)
 {
 {
     aiNodeAnim* anim = new aiNodeAnim();
     aiNodeAnim* anim = new aiNodeAnim();
-    anim->mNodeName = node.name;
+    anim->mNodeName = GetNodeName(node);
 
 
     static const float kMillisecondsFromSeconds = 1000.f;
     static const float kMillisecondsFromSeconds = 1000.f;
 
 

+ 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);
     }
     }
 }
 }
 
 

+ 0 - 105
code/glTFImporter.cpp

@@ -1,105 +0,0 @@
-### USE OF THIS MAKEFILE IS NOT RECOMMENDED.
-### It is no longer maintained. Use CMAKE instead.
-
-# ---------------------------------------------------------------------------
-# Makefile for Open Asset Import Library (MinGW32-make)
-# [email protected]
-#   - just a quick'n'dirty one, could be buggy ...
-#
-# Usage: mingw32-make -f makefile.mingw <target> <macros>
-
-# TARGETS:
-#   all                  Build a shared so from the whole library
-#   clean                Cleanup object files, prepare for rebuild
-#   static               Build a static library (*.a)
-
-# MACROS: (make clean before you change one)
-#   NOBOOST=1            Build against boost workaround
-#   SINGLETHREADED=1     Build single-threaded library
-#   DEBUG=1              Build debug build of library
-# 
-# ---------------------------------------------------------------------------
-
-# C++ object files
-OBJECTS   := $(patsubst %.cpp,%.o,  $(wildcard *.cpp)) 
-OBJECTS   += $(patsubst %.cpp,%.o,  $(wildcard extra/*.cpp)) 
-OBJECTS   += $(patsubst %.cpp,%.o,  $(wildcard ./../contrib/irrXML/*.cpp)) 
-
-# C object files 
-OBJECTSC  := $(patsubst %.c,%.oc,   $(wildcard ./../contrib/zlib/*.c))
-OBJECTSC  += $(patsubst %.c,%.oc,   $(wildcard ./../contrib/ConvertUTF/*.c))
-OBJECTSC  += $(patsubst %.c,%.oc,   $(wildcard ./../contrib/unzip/*.c))
-
-# Include flags for gcc
-INCLUDEFLAGS =
-
-# Preprocessor defines for gcc
-DEFINEFLAGS = 
-
-# Suffix for the output binary, represents build type
-NAMESUFFIX = 
-
-# Output path for binaries
-BINPATH = ../bin/mingw/
-
-# GCC compiler flags 
-CPPFLAGS=-Wall 
-
-# Setup environment for noboost build
-ifeq ($(NOBOOST),1)
-	SINGLETHREADED = 1
-	INCLUDEFLAGS  += -I./BoostWorkaround/
-	DEFINEFLAGS   += -DASSIMP_BUILD_BOOST_WORKAROUND 
-#	NAMESUFFIX    += -noboost
-else
-	# adjust this manually if your boost is stored elsewhere
-	INCLUDEFLAGS  += -I"C:/Program Files/boost/boost_1_38"
-	#INCLUDEFLAGS  += -I"$(BOOST_DIR)"
-
-endif
-
-# Setup environment for st build
-ifeq ($(SINGLETHREADED),1)
-	DEFINEFLAGS   += -DASSIMP_BUILD_SINGLETHREADED
-#	NAMESUFFIX    += -st
-endif
-
-# Setup environment for debug build
-ifeq ($(DEBUG),1)
-	DEFINEFLAGS   += -D_DEBUG -DDEBUG
-	CPPFLAGS      += -g
-#	NAMESUFFIX    += -debug
-else
-	CPPFLAGS      += -O2 -s
-	DEFINEFLAGS   += -DNDEBUG -D_NDEBUG
-endif
-
-# Output name of shared library
-SHARED_TARGET = $(BINPATH)/libassimp$(NAMESUFFIX).so
-
-# Output name of static library
-STATIC = $(BINPATH)/libassimp$(NAMESUFFIX).a
-
-# target: all
-# usage : build a shared library (*.so)
-all:	$(SHARED_TARGET)
-
-$(SHARED_TARGET):  $(OBJECTS)  $(OBJECTSC)
-	gcc -o $@ $(OBJECTS) $(OBJECTSC) -shared -lstdc++ 
-%.o:%.cpp
-	$(CXX) -c  $(CPPFLAGS) $? -o $@ $(INCLUDEFLAGS) $(DEFINEFLAGS)
-%.oc:%.c
-	$(CXX) -x c -c -ansi $(CPPFLAGS) $? -o $@ 
-
-# target: clean
-# usage : cleanup all object files, prepare for a rebuild
-.PHONY: clean
-clean:
-	-del *.o .\..\contrib\irrXML\*.o .\..\contrib\zlib\*.oc .\..\contrib\unzip\*.oc .\..\contrib\ConvertUTF\*.oc
-
-# target: static
-# usage : build a static library (*.a)
-static:    $(STATIC) 
-$(STATIC):    $(OBJECTS) $(OBJECTSC)
-	ar rcs $@ $(OBJECTS) $(OBJECTSC)
-

+ 5 - 5
code/res/assimp.rc

@@ -1,7 +1,7 @@
 // Microsoft Visual C++ generated resource script.
 // Microsoft Visual C++ generated resource script.
 //
 //
 #include "resource.h"
 #include "resource.h"
-#include "..\..\revision.h"
+#include "revision.h"
 
 
 #define APSTUDIO_READONLY_SYMBOLS
 #define APSTUDIO_READONLY_SYMBOLS
 /////////////////////////////////////////////////////////////////////////////
 /////////////////////////////////////////////////////////////////////////////
@@ -31,8 +31,8 @@ LANGUAGE LANG_GERMAN, SUBLANG_GERMAN
 //
 //
 
 
 VS_VERSION_INFO VERSIONINFO
 VS_VERSION_INFO VERSIONINFO
- FILEVERSION 1,1,SVNRevision, 0
- PRODUCTVERSION 1,1,SVNRevision,0
+ FILEVERSION VER_FILEVERSION
+ PRODUCTVERSION VER_FILEVERSION
  FILEFLAGSMASK 0x17L
  FILEFLAGSMASK 0x17L
 #ifdef _DEBUG
 #ifdef _DEBUG
  FILEFLAGS 0x1L
  FILEFLAGS 0x1L
@@ -50,12 +50,12 @@ BEGIN
             VALUE "Comments", "Licensed under a 3-clause BSD license"
             VALUE "Comments", "Licensed under a 3-clause BSD license"
             VALUE "CompanyName", "assimp team"
             VALUE "CompanyName", "assimp team"
             VALUE "FileDescription", "Open Asset Import Library"
             VALUE "FileDescription", "Open Asset Import Library"
-            VALUE "FileVersion", 1,1,SVNRevision,0
+            VALUE "FileVersion", VER_FILEVERSION
             VALUE "InternalName", "assimp "
             VALUE "InternalName", "assimp "
             VALUE "LegalCopyright", "Copyright (C) 2006-2010"
             VALUE "LegalCopyright", "Copyright (C) 2006-2010"
             VALUE "OriginalFilename", "assimpNN.dll"
             VALUE "OriginalFilename", "assimpNN.dll"
             VALUE "ProductName", "Open Asset Import Library"
             VALUE "ProductName", "Open Asset Import Library"
-            VALUE "ProductVersion", 1,1,SVNRevision,0
+            VALUE "ProductVersion", VER_FILEVERSION_STR
 		,0
 		,0
         END
         END
     END
     END

+ 2 - 2
code/res/resource.h

@@ -2,8 +2,8 @@
 // Microsoft Visual C++ generated include file.
 // Microsoft Visual C++ generated include file.
 // Used by assimp.rc
 // Used by assimp.rc
 
 
-// Nächste Standardwerte für neue Objekte
-// 
+// Next standard values for new objects
+//
 #ifdef APSTUDIO_INVOKED
 #ifdef APSTUDIO_INVOKED
 #ifndef APSTUDIO_READONLY_SYMBOLS
 #ifndef APSTUDIO_READONLY_SYMBOLS
 #define _APS_NEXT_RESOURCE_VALUE        101
 #define _APS_NEXT_RESOURCE_VALUE        101

+ 8 - 1
contrib/irrXML/CMakeLists.txt

@@ -13,10 +13,17 @@ 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})
+IF(CMAKE_SYSTEM_NAME MATCHES "(Darwin|FreeBSD)")
+  add_library(IrrXML ${IrrXML_SRCS})
+ELSE()
+  add_library(IrrXML STATIC ${IrrXML_SRCS})
+ENDIF()
 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));

+ 1 - 1
contrib/irrXML/irrString.h

@@ -19,7 +19,7 @@ so you can assign unicode to string<c8> and ascii to string<wchar_t>
 Note that the conversation between both is not done using an encoding.
 Note that the conversation between both is not done using an encoding.
 
 
 Known bugs:
 Known bugs:
-Special characters like 'Ä', 'Ü' and 'Ö' are ignored in the
+Special characters like 'Ä', 'Ü' and 'Ö' are ignored in the
 methods make_upper, make_lower and equals_ignore_case.
 methods make_upper, make_lower and equals_ignore_case.
 */
 */
 template <class T>
 template <class T>

+ 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

+ 1 - 1
contrib/poly2tri/AUTHORS

@@ -1,7 +1,7 @@
 Primary Contributors:
 Primary Contributors:
 
 
   Mason Green <[email protected]> (C++, Python)
   Mason Green <[email protected]> (C++, Python)
-  Thomas Åhlén <[email protected]>    (Java)
+  Thomas Ã…hlén <[email protected]>    (Java)
 
 
 Other Contributors:
 Other Contributors:
 
 

+ 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)
+

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


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

@@ -7,29 +7,40 @@
  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  * 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;
+}

+ 7 - 2
contrib/zlib/CMakeLists.txt

@@ -5,12 +5,17 @@ set(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS ON)
 # See http://www.cmake.org/cmake/help/v3.0/policy/CMP0048.html
 # See http://www.cmake.org/cmake/help/v3.0/policy/CMP0048.html
 cmake_policy(PUSH)
 cmake_policy(PUSH)
 if(CMAKE_MAJOR_VERSION GREATER 2)
 if(CMAKE_MAJOR_VERSION GREATER 2)
-	cmake_policy(SET CMP0048 OLD)
+	cmake_policy(SET CMP0048 NEW)
 endif()
 endif()
 project(zlib C)
 project(zlib C)
+SET (ZLIB_VERSION_MAJOR 1)
+SET (ZLIB_VERSION_MINOR 2)
+SET (ZLIB_VERSION_PATCH 11)
+SET (ZLIB_VERSION ${ZLIB_VERSION_MAJOR}.${ZLIB_VERSION_MINOR}.${ZLIB_VERSION_PATCH})
+SET (ZLIB_SOVERSION 1)
+SET (PROJECT_VERSION "${ZLIB_VERSION}")
 cmake_policy(POP)
 cmake_policy(POP)
 
 
-set(VERSION "1.2.11.1")
 
 
 option(ASM686 "Enable building i686 assembly implementation")
 option(ASM686 "Enable building i686 assembly implementation")
 option(AMD64 "Enable building amd64 assembly implementation")
 option(AMD64 "Enable building amd64 assembly implementation")

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