diff --git a/CMakeLists.txt b/CMakeLists.txt index 46c2a591c..224f6e48f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -268,6 +268,11 @@ IF ((CMAKE_C_COMPILER_ID MATCHES "GNU") AND NOT MINGW) SET(CMAKE_CXX_STANDARD 17) SET(CMAKE_POSITION_INDEPENDENT_CODE ON) ENDIF() + + IF(CMAKE_CXX_COMPILER_VERSION GREATER_EQUAL 13) + MESSAGE(STATUS "GCC13 detected disabling \"-Wdangling-reference\" in Cpp files as it appears to be a false positive") + ADD_COMPILE_OPTIONS("$<$:-Wno-dangling-reference>") + ENDIF() # hide all not-exported symbols IF(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "mips64" ) SET(CMAKE_CXX_FLAGS "-mxgot -fvisibility=hidden -fno-strict-aliasing -Wall ${CMAKE_CXX_FLAGS}") diff --git a/code/AssetLib/Blender/BlenderLoader.cpp b/code/AssetLib/Blender/BlenderLoader.cpp index 269c90b96..5c6e7bc5b 100644 --- a/code/AssetLib/Blender/BlenderLoader.cpp +++ b/code/AssetLib/Blender/BlenderLoader.cpp @@ -115,15 +115,12 @@ BlenderImporter::~BlenderImporter() { delete modifier_cache; } -static const char * const Tokens[] = { "BLENDER" }; +static const char Token[] = "BLENDER"; // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. bool BlenderImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { - // note: this won't catch compressed files - static const char *tokens[] = { " uncompressed; -#endif - FileDatabase file; - std::shared_ptr stream(pIOHandler->Open(pFile, "rb")); - if (!stream) { - ThrowException("Could not open file for reading"); + StreamOrError streamOrError = ParseMagicToken(pFile, pIOHandler); + if (!streamOrError.error.empty()) { + ThrowException(streamOrError.error); } + std::shared_ptr stream = std::move(streamOrError.stream); - char magic[8] = { 0 }; - stream->Read(magic, 7, 1); - if (strcmp(magic, Tokens[0])) { - // Check for presence of the gzip header. If yes, assume it is a - // compressed blend file and try uncompressing it, else fail. This is to - // avoid uncompressing random files which our loader might end up with. -#ifdef ASSIMP_BUILD_NO_COMPRESSED_BLEND - ThrowException("BLENDER magic bytes are missing, is this file compressed (Assimp was built without decompression support)?"); -#else - if (magic[0] != 0x1f || static_cast(magic[1]) != 0x8b) { - ThrowException("BLENDER magic bytes are missing, couldn't find GZIP header either"); - } + char version[4] = { 0 }; + file.i64bit = (stream->Read(version, 1, 1), version[0] == '-'); + file.little = (stream->Read(version, 1, 1), version[0] == 'v'); - LogDebug("Found no BLENDER magic word but a GZIP header, might be a compressed file"); - if (magic[2] != 8) { - ThrowException("Unsupported GZIP compression method"); - } + stream->Read(version, 3, 1); + version[3] = '\0'; - // http://www.gzip.org/zlib/rfc-gzip.html#header-trailer - stream->Seek(0L, aiOrigin_SET); - std::shared_ptr reader = std::shared_ptr(new StreamReaderLE(stream)); - - size_t total = 0; - Compression compression; - if (compression.open(Compression::Format::Binary, Compression::FlushMode::NoFlush, 16 + Compression::MaxWBits)) { - total = compression.decompress((unsigned char *)reader->GetPtr(), reader->GetRemainingSize(), uncompressed); - compression.close(); - } - - // replace the input stream with a memory stream - stream = std::make_shared(reinterpret_cast(uncompressed.data()), total); - - // .. and retry - stream->Read(magic, 7, 1); - if (strcmp(magic, "BLENDER")) { - ThrowException("Found no BLENDER magic word in decompressed GZIP file"); - } -#endif - } - - file.i64bit = (stream->Read(magic, 1, 1), magic[0] == '-'); - file.little = (stream->Read(magic, 1, 1), magic[0] == 'v'); - - stream->Read(magic, 3, 1); - magic[3] = '\0'; - - LogInfo("Blender version is ", magic[0], ".", magic + 1, + LogInfo("Blender version is ", version[0], ".", version + 1, " (64bit: ", file.i64bit ? "true" : "false", ", little endian: ", file.little ? "true" : "false", ")"); @@ -1338,4 +1293,55 @@ aiNode *BlenderImporter::ConvertNode(const Scene &in, const Object *obj, Convers return node.release(); } +BlenderImporter::StreamOrError BlenderImporter::ParseMagicToken(const std::string &pFile, IOSystem *pIOHandler) const { + std::shared_ptr stream(pIOHandler->Open(pFile, "rb")); + if (stream == nullptr) { + return {{}, {}, "Could not open file for reading"}; + } + + char magic[8] = { 0 }; + stream->Read(magic, 7, 1); + if (strcmp(magic, Token) == 0) { + return {stream, {}, {}}; + } + + // Check for presence of the gzip header. If yes, assume it is a + // compressed blend file and try uncompressing it, else fail. This is to + // avoid uncompressing random files which our loader might end up with. +#ifdef ASSIMP_BUILD_NO_COMPRESSED_BLEND + return {{}, {}, "BLENDER magic bytes are missing, is this file compressed (Assimp was built without decompression support)?"}; +#else + if (magic[0] != 0x1f || static_cast(magic[1]) != 0x8b) { + return {{}, {}, "BLENDER magic bytes are missing, couldn't find GZIP header either"}; + } + + LogDebug("Found no BLENDER magic word but a GZIP header, might be a compressed file"); + if (magic[2] != 8) { + return {{}, {}, "Unsupported GZIP compression method"}; + } + + // http://www.gzip.org/zlib/rfc-gzip.html#header-trailer + stream->Seek(0L, aiOrigin_SET); + std::shared_ptr reader = std::shared_ptr(new StreamReaderLE(stream)); + + size_t total = 0; + Compression compression; + auto uncompressed = std::make_shared>(); + if (compression.open(Compression::Format::Binary, Compression::FlushMode::NoFlush, 16 + Compression::MaxWBits)) { + total = compression.decompress((unsigned char *)reader->GetPtr(), reader->GetRemainingSize(), *uncompressed); + compression.close(); + } + + // replace the input stream with a memory stream + stream = std::make_shared(reinterpret_cast(uncompressed->data()), total); + + // .. and retry + stream->Read(magic, 7, 1); + if (strcmp(magic, Token) == 0) { + return {stream, uncompressed, {}}; + } + return {{}, {}, "Found no BLENDER magic word in decompressed GZIP file"}; +#endif +} + #endif // ASSIMP_BUILD_NO_BLEND_IMPORTER diff --git a/code/AssetLib/Blender/BlenderLoader.h b/code/AssetLib/Blender/BlenderLoader.h index b29ee5941..2bdc24ae2 100644 --- a/code/AssetLib/Blender/BlenderLoader.h +++ b/code/AssetLib/Blender/BlenderLoader.h @@ -180,6 +180,19 @@ private: const Blender::MTex *tex, Blender::ConversionData &conv_data); + // TODO: Move to a std::variant, once c++17 is supported. + struct StreamOrError { + std::shared_ptr stream; + std::shared_ptr> input; + std::string error; + }; + + // Returns either a stream (and optional input data for the stream) or + // an error if it can't parse the magic token. + StreamOrError ParseMagicToken( + const std::string &pFile, + IOSystem *pIOHandler) const; + private: // static stuff, mostly logging and error reporting. // -------------------- static void CheckActualType(const Blender::ElemBase *dt, diff --git a/port/PyAssimp/pyassimp/core.py b/port/PyAssimp/pyassimp/core.py index c7ceaa9cd..95159c4a1 100644 --- a/port/PyAssimp/pyassimp/core.py +++ b/port/PyAssimp/pyassimp/core.py @@ -311,6 +311,7 @@ def load(filename, Scene object with model data ''' + from ctypes import c_char_p if hasattr(filename, 'read'): # This is the case where a file object has been passed to load. # It is calling the following function: @@ -324,7 +325,7 @@ def load(filename, model = _assimp_lib.load_mem(data, len(data), processing, - file_type) + c_char_p(file_type.encode(sys.getfilesystemencoding()))) else: # a filename string has been passed model = _assimp_lib.load(filename.encode(sys.getfilesystemencoding()), processing)