Browse Source

Merge pull request #5153 from feuerste/blender_can_read

Unify way to check readable blender files.
Kim Kulling 2 years ago
parent
commit
e4cac7d00b
2 changed files with 76 additions and 57 deletions
  1. 63 57
      code/AssetLib/Blender/BlenderLoader.cpp
  2. 13 0
      code/AssetLib/Blender/BlenderLoader.h

+ 63 - 57
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[] = { "<BLENDER", "blender" };
-
-    return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens));
+    return ParseMagicToken(pFile, pIOHandler).error.empty();
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -142,63 +139,21 @@ void BlenderImporter::SetupProperties(const Importer * /*pImp*/) {
 // Imports the given file into the given scene structure.
 void BlenderImporter::InternReadFile(const std::string &pFile,
         aiScene *pScene, IOSystem *pIOHandler) {
-#ifndef ASSIMP_BUILD_NO_COMPRESSED_BLEND
-    std::vector<char> uncompressed;
-#endif
-
     FileDatabase file;
-    std::shared_ptr<IOStream> stream(pIOHandler->Open(pFile, "rb"));
-    if (!stream) {
-        ThrowException("Could not open file for reading");
-    }
-
-    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<uint8_t>(magic[1]) != 0x8b) {
-            ThrowException("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) {
-            ThrowException("Unsupported GZIP compression method");
-        }
-
-        // http://www.gzip.org/zlib/rfc-gzip.html#header-trailer
-        stream->Seek(0L, aiOrigin_SET);
-        std::shared_ptr<StreamReaderLE> reader = std::shared_ptr<StreamReaderLE>(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<MemoryIOStream>(reinterpret_cast<uint8_t *>(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
+    StreamOrError streamOrError = ParseMagicToken(pFile, pIOHandler);
+    if (!streamOrError.error.empty()) {
+        ThrowException(streamOrError.error);
     }
+    std::shared_ptr<IOStream> stream = std::move(streamOrError.stream);
 
-    file.i64bit = (stream->Read(magic, 1, 1), magic[0] == '-');
-    file.little = (stream->Read(magic, 1, 1), magic[0] == 'v');
+    char version[4] = { 0 };
+    file.i64bit = (stream->Read(version, 1, 1), version[0] == '-');
+    file.little = (stream->Read(version, 1, 1), version[0] == 'v');
 
-    stream->Read(magic, 3, 1);
-    magic[3] = '\0';
+    stream->Read(version, 3, 1);
+    version[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<IOStream> 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<uint8_t>(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<StreamReaderLE> reader = std::shared_ptr<StreamReaderLE>(new StreamReaderLE(stream));
+
+    size_t total = 0;
+    Compression compression;
+    auto uncompressed = std::make_shared<std::vector<char>>();
+    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<MemoryIOStream>(reinterpret_cast<uint8_t *>(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

+ 13 - 0
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<IOStream> stream;
+        std::shared_ptr<std::vector<char>> 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,