فهرست منبع

Merge pull request #4381 from assimp/kimkulling/introduce_compression

Kimkulling/introduce compression
Kim Kulling 3 سال پیش
والد
کامیت
884bb39391

+ 10 - 40
code/AssetLib/Blender/BlenderLoader.cpp

@@ -66,11 +66,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 // zlib is needed for compressed blend files
 #ifndef ASSIMP_BUILD_NO_COMPRESSED_BLEND
-#  ifdef ASSIMP_BUILD_NO_OWN_ZLIB
+#include "Common/Compression.h"
+/* #ifdef ASSIMP_BUILD_NO_OWN_ZLIB
 #    include <zlib.h>
 #  else
 #    include "../contrib/zlib/zlib.h"
-#  endif
+#  endif*/
 #endif
 
 namespace Assimp {
@@ -141,7 +142,7 @@ void BlenderImporter::SetupProperties(const Importer * /*pImp*/) {
 void BlenderImporter::InternReadFile(const std::string &pFile,
         aiScene *pScene, IOSystem *pIOHandler) {
 #ifndef ASSIMP_BUILD_NO_COMPRESSED_BLEND
-    std::vector<Bytef> uncompressed;
+    std::vector<char> uncompressed;
 #endif
 
     FileDatabase file;
@@ -159,7 +160,6 @@ void BlenderImporter::InternReadFile(const std::string &pFile,
 #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");
         }
@@ -173,42 +173,12 @@ void BlenderImporter::InternReadFile(const std::string &pFile,
         stream->Seek(0L, aiOrigin_SET);
         std::shared_ptr<StreamReaderLE> reader = std::shared_ptr<StreamReaderLE>(new StreamReaderLE(stream));
 
-        // build a zlib stream
-        z_stream zstream;
-        zstream.opaque = Z_NULL;
-        zstream.zalloc = Z_NULL;
-        zstream.zfree = Z_NULL;
-        zstream.data_type = Z_BINARY;
-
-        // http://hewgill.com/journal/entries/349-how-to-decompress-gzip-stream-with-zlib
-        inflateInit2(&zstream, 16 + MAX_WBITS);
-
-        zstream.next_in = reinterpret_cast<Bytef *>(reader->GetPtr());
-        zstream.avail_in = (uInt)reader->GetRemainingSize();
-
-        size_t total = 0l;
-
-        // TODO: be smarter about this, decompress directly into heap buffer
-        // and decompress the data .... do 1k chunks in the hope that we won't kill the stack
-#define MYBLOCK 1024
-        Bytef block[MYBLOCK];
-        int ret;
-        do {
-            zstream.avail_out = MYBLOCK;
-            zstream.next_out = block;
-            ret = inflate(&zstream, Z_NO_FLUSH);
-
-            if (ret != Z_STREAM_END && ret != Z_OK) {
-                ThrowException("Failure decompressing this file using gzip, seemingly it is NOT a compressed .BLEND file");
-            }
-            const size_t have = MYBLOCK - zstream.avail_out;
-            total += have;
-            uncompressed.resize(total);
-            memcpy(uncompressed.data() + total - have, block, have);
-        } while (ret != Z_STREAM_END);
-
-        // terminate zlib
-        inflateEnd(&zstream);
+        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.reset(new MemoryIOStream(reinterpret_cast<uint8_t *>(uncompressed.data()), total));

+ 16 - 45
code/AssetLib/FBX/FBXParser.cpp

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2022, assimp team
 
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -46,11 +45,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
 
-#ifdef ASSIMP_BUILD_NO_OWN_ZLIB
-#   include <zlib.h>
-#else
-#   include "../contrib/zlib/zlib.h"
-#endif
+//#ifdef ASSIMP_BUILD_NO_OWN_ZLIB
+#include "Common/Compression.h"
+//#   include <zlib.h>
+//#else
+//#   include "../contrib/zlib/zlib.h"
+//#endif
 
 #include "FBXTokenizer.h"
 #include "FBXParser.h"
@@ -115,9 +115,7 @@ namespace Assimp {
 namespace FBX {
 
 // ------------------------------------------------------------------------------------------------
-Element::Element(const Token& key_token, Parser& parser)
-: key_token(key_token)
-{
+Element::Element(const Token& key_token, Parser& parser) : key_token(key_token) {
     TokenPtr n = nullptr;
     do {
         n = parser.AdvanceToNextToken();
@@ -210,8 +208,7 @@ Scope::Scope(Parser& parser,bool topLevel)
 }
 
 // ------------------------------------------------------------------------------------------------
-Scope::~Scope()
-{
+Scope::~Scope() {
     for(ElementMap::value_type& v : elements) {
         delete v.second;
     }
@@ -527,9 +524,7 @@ void ReadBinaryDataArrayHead(const char*& data, const char* end, char& type, uin
 // ------------------------------------------------------------------------------------------------
 // read binary data array, assume cursor points to the 'compression mode' field (i.e. behind the header)
 void ReadBinaryDataArray(char type, uint32_t count, const char*& data, const char* end,
-    std::vector<char>& buff,
-    const Element& /*el*/)
-{
+        std::vector<char>& buff, const Element& /*el*/) {
     BE_NCONST uint32_t encmode = SafeParse<uint32_t>(data, end);
     AI_SWAP4(encmode);
     data += 4;
@@ -571,31 +566,11 @@ void ReadBinaryDataArray(char type, uint32_t count, const char*& data, const cha
     else if(encmode == 1) {
         // zlib/deflate, next comes ZIP head (0x78 0x01)
         // see http://www.ietf.org/rfc/rfc1950.txt
-
-        z_stream zstream;
-        zstream.opaque = Z_NULL;
-        zstream.zalloc = Z_NULL;
-        zstream.zfree  = Z_NULL;
-        zstream.data_type = Z_BINARY;
-
-        // http://hewgill.com/journal/entries/349-how-to-decompress-gzip-stream-with-zlib
-        if(Z_OK != inflateInit(&zstream)) {
-            ParseError("failure initializing zlib");
+         Compression compress;
+        if (compress.open(Compression::Format::Binary, Compression::FlushMode::Finish, 0)) {
+            compress.decompress(data, comp_len, buff);
+            compress.close();
         }
-
-        zstream.next_in   = reinterpret_cast<Bytef*>( const_cast<char*>(data) );
-        zstream.avail_in  = comp_len;
-
-        zstream.avail_out = static_cast<uInt>(buff.size());
-        zstream.next_out = reinterpret_cast<Bytef*>(&*buff.begin());
-        const int ret = inflate(&zstream, Z_FINISH);
-
-        if (ret != Z_STREAM_END && ret != Z_OK) {
-            ParseError("failure decompressing compressed data section");
-        }
-
-        // terminate zlib
-        inflateEnd(&zstream);
     }
 #ifdef ASSIMP_BUILD_DEBUG
     else {
@@ -701,7 +676,6 @@ void ParseVectorDataArray(std::vector<aiVector3D>& out, const Element& el)
     }
 }
 
-
 // ------------------------------------------------------------------------------------------------
 // read an array of color4 tuples
 void ParseVectorDataArray(std::vector<aiColor4D>& out, const Element& el)
@@ -786,8 +760,7 @@ void ParseVectorDataArray(std::vector<aiColor4D>& out, const Element& el)
 
 // ------------------------------------------------------------------------------------------------
 // read an array of float2 tuples
-void ParseVectorDataArray(std::vector<aiVector2D>& out, const Element& el)
-{
+void ParseVectorDataArray(std::vector<aiVector2D>& out, const Element& el) {
     out.resize( 0 );
     const TokenList& tok = el.Tokens();
     if(tok.empty()) {
@@ -831,8 +804,7 @@ void ParseVectorDataArray(std::vector<aiVector2D>& out, const Element& el)
                 out.push_back(aiVector2D(static_cast<float>(d[0]),
                     static_cast<float>(d[1])));
             }
-        }
-        else if (type == 'f') {
+        } else if (type == 'f') {
             const float* f = reinterpret_cast<const float*>(&buff[0]);
             for (unsigned int i = 0; i < count2; ++i, f += 2) {
                 out.push_back(aiVector2D(f[0],f[1]));
@@ -865,8 +837,7 @@ void ParseVectorDataArray(std::vector<aiVector2D>& out, const Element& el)
 
 // ------------------------------------------------------------------------------------------------
 // read an array of ints
-void ParseVectorDataArray(std::vector<int>& out, const Element& el)
-{
+void ParseVectorDataArray(std::vector<int>& out, const Element& el) {
     out.resize( 0 );
     const TokenList& tok = el.Tokens();
     if(tok.empty()) {

+ 30 - 67
code/AssetLib/X/XFileParser.cpp

@@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2022, assimp team
 
-
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -60,25 +58,11 @@ using namespace Assimp::Formatter;
 
 #ifndef ASSIMP_BUILD_NO_COMPRESSED_X
 
-#ifdef ASSIMP_BUILD_NO_OWN_ZLIB
-#include <zlib.h>
-#else
-#include "../contrib/zlib/zlib.h"
-#endif
+#include "Common/Compression.h"
 
 // Magic identifier for MSZIP compressed data
-#define MSZIP_MAGIC 0x4B43
-#define MSZIP_BLOCK 32786
-
-// ------------------------------------------------------------------------------------------------
-// Dummy memory wrappers for use with zlib
-static void *dummy_alloc(void * /*opaque*/, unsigned int items, unsigned int size) {
-    return ::operator new(items *size);
-}
-
-static void dummy_free(void * /*opaque*/, void *address) {
-    return ::operator delete(address);
-}
+constexpr unsigned int MSZIP_MAGIC = 0x4B43;
+constexpr size_t MSZIP_BLOCK = 32786l;
 
 #endif // !! ASSIMP_BUILD_NO_COMPRESSED_X
 
@@ -133,13 +117,13 @@ XFileParser::XFileParser(const std::vector<char> &pBuffer) :
         mIsBinaryFormat = true;
         compressed = true;
     } else
-        ThrowException("Unsupported xfile format '", mP[8], mP[9], mP[10], mP[11], "'");
+        ThrowException("Unsupported x-file format '", mP[8], mP[9], mP[10], mP[11], "'");
 
     // float size
     mBinaryFloatSize = (unsigned int)(mP[12] - 48) * 1000 + (unsigned int)(mP[13] - 48) * 100 + (unsigned int)(mP[14] - 48) * 10 + (unsigned int)(mP[15] - 48);
 
     if (mBinaryFloatSize != 32 && mBinaryFloatSize != 64)
-        ThrowException("Unknown float size ", mBinaryFloatSize, " specified in xfile header.");
+        ThrowException("Unknown float size ", mBinaryFloatSize, " specified in x-file header.");
 
     // The x format specifies size in bits, but we work in bytes
     mBinaryFloatSize /= 8;
@@ -171,16 +155,6 @@ XFileParser::XFileParser(const std::vector<char> &pBuffer) :
          * ///////////////////////////////////////////////////////////////////////
          */
 
-        // build a zlib stream
-        z_stream stream;
-        stream.opaque = nullptr;
-        stream.zalloc = &dummy_alloc;
-        stream.zfree = &dummy_free;
-        stream.data_type = (mIsBinaryFormat ? Z_BINARY : Z_ASCII);
-
-        // initialize the inflation algorithm
-        ::inflateInit2(&stream, -MAX_WBITS);
-
         // skip unknown data (checksum, flags?)
         mP += 6;
 
@@ -207,43 +181,29 @@ XFileParser::XFileParser(const std::vector<char> &pBuffer) :
 
             // and advance to the next offset
             P1 += ofs;
-            est_out += MSZIP_BLOCK; // one decompressed block is 32786 in size
+            est_out += MSZIP_BLOCK; // one decompressed block is 327861 in size
         }
-
+        
         // Allocate storage and terminating zero and do the actual uncompressing
+        Compression compression;
         uncompressed.resize(est_out + 1);
         char *out = &uncompressed.front();
-        while (mP + 3 < mEnd) {
-            uint16_t ofs = *((uint16_t *)mP);
-            AI_SWAP2(ofs);
-            mP += 4;
+        if (compression.open(mIsBinaryFormat ? Compression::Format::Binary : Compression::Format::ASCII,
+                Compression::FlushMode::SyncFlush, -Compression::MaxWBits)) {
+            while (mP + 3 < mEnd) {
+                uint16_t ofs = *((uint16_t *)mP);
+                AI_SWAP2(ofs);
+                mP += 4;
 
-            if (mP + ofs > mEnd + 2) {
-                throw DeadlyImportError("X: Unexpected EOF in compressed chunk");
+                if (mP + ofs > mEnd + 2) {
+                    throw DeadlyImportError("X: Unexpected EOF in compressed chunk");
+                }
+                out += compression.decompressBlock(mP, ofs, out, MSZIP_BLOCK);
+                mP += ofs;
             }
-
-            // push data to the stream
-            stream.next_in = (Bytef *)mP;
-            stream.avail_in = ofs;
-            stream.next_out = (Bytef *)out;
-            stream.avail_out = MSZIP_BLOCK;
-
-            // and decompress the data ....
-            int ret = ::inflate(&stream, Z_SYNC_FLUSH);
-            if (ret != Z_OK && ret != Z_STREAM_END)
-                throw DeadlyImportError("X: Failed to decompress MSZIP-compressed data");
-
-            ::inflateReset(&stream);
-            ::inflateSetDictionary(&stream, (const Bytef *)out, MSZIP_BLOCK - stream.avail_out);
-
-            // and advance to the next offset
-            out += MSZIP_BLOCK - stream.avail_out;
-            mP += ofs;
+            compression.close();
         }
 
-        // terminate zlib
-        ::inflateEnd(&stream);
-
         // ok, update pointers to point to the uncompressed file data
         mP = &uncompressed[0];
         mEnd = out;
@@ -279,15 +239,16 @@ void XFileParser::ParseFile() {
     while (running) {
         // read name of next object
         std::string objectName = GetNextToken();
-        if (objectName.length() == 0)
+        if (objectName.length() == 0) {
             break;
+        }
 
         // parse specific object
-        if (objectName == "template")
+        if (objectName == "template") {
             ParseDataObjectTemplate();
-        else if (objectName == "Frame")
+        } else if (objectName == "Frame") {
             ParseDataObjectFrame(nullptr);
-        else if (objectName == "Mesh") {
+        } else if (objectName == "Mesh") {
             // some meshes have no frames at all
             Mesh *mesh = new Mesh;
             ParseDataObjectMesh(mesh);
@@ -326,11 +287,13 @@ void XFileParser::ParseDataObjectTemplate() {
     while (running) {
         std::string s = GetNextToken();
 
-        if (s == "}")
+        if (s == "}") {
             break;
+        }
 
-        if (s.length() == 0)
+        if (s.length() == 0) {
             ThrowException("Unexpected end of file reached while parsing template definition");
+        }
     }
 }
 
@@ -500,7 +463,7 @@ void XFileParser::ParseDataObjectSkinWeights(Mesh *pMesh) {
     bone.mWeights.reserve(numWeights);
 
     for (unsigned int a = 0; a < numWeights; a++) {
-        BoneWeight weight;
+        BoneWeight weight = {};
         weight.mVertex = ReadInt();
         bone.mWeights.push_back(weight);
     }

+ 10 - 10
code/AssetLib/XGL/XGLLoader.cpp

@@ -53,8 +53,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/importerdesc.h>
 #include <assimp/mesh.h>
 #include <assimp/scene.h>
-#include <cctype>
-#include <memory>
+//#include <cctype>
+//#include <memory>
 
 using namespace Assimp;
 
@@ -112,7 +112,7 @@ const aiImporterDesc *XGLImporter::GetInfo() const {
 // Imports the given file into the given scene structure.
 void XGLImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
  #ifndef ASSIMP_BUILD_NO_COMPRESSED_XGL
-	std::vector<unsigned char> uncompressed;
+	std::vector<char> uncompressed;
 #endif
 
 	m_scene = pScene;
@@ -130,16 +130,16 @@ void XGLImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy
 #else
 		std::unique_ptr<StreamReaderLE> raw_reader(new StreamReaderLE(stream));
 
-        Compression c;
+        Compression compression;
         size_t total = 0l;
-        if (c.open()) {
+        if (compression.open(Compression::Format::Binary, Compression::FlushMode::NoFlush, -Compression::MaxWBits)) {
             // skip two extra bytes, zgl files do carry a crc16 upfront (I think)
             raw_reader->IncPtr(2);
-            total = c.decompress((unsigned char *)raw_reader->GetPtr(), raw_reader->GetRemainingSize(), uncompressed);
-            c.close();
+            total = compression.decompress((unsigned char *)raw_reader->GetPtr(), raw_reader->GetRemainingSize(), uncompressed);
+            compression.close();
         }
 		// replace the input stream with a memory stream
-		stream.reset(new MemoryIOStream(reinterpret_cast<uint8_t *>(uncompressed.data()), total));
+		stream.reset(new MemoryIOStream(reinterpret_cast<uint8_t*>(uncompressed.data()), total));
 #endif
 	}
 
@@ -200,7 +200,7 @@ void XGLImporter::ReadWorld(XmlNode &node, TempScope &scope) {
 	if (!nd) {
 		ThrowException("failure reading <world>");
 	}
-	if (!nd->mName.length) {
+	if (nd->mName.length == 0) {
 		nd->mName.Set("WORLD");
 	}
 
@@ -784,4 +784,4 @@ aiColor3D XGLImporter::ReadCol3(XmlNode &node) {
 	return aiColor3D(v.x, v.y, v.z);
 }
 
-#endif
+#endif // ASSIMP_BUILD_NO_XGL_IMPORTER

+ 97 - 24
code/Common/Compression.cpp

@@ -43,20 +43,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/ai_assert.h>
 #include <assimp/Exceptional.h>
 
-#ifdef ASSIMP_BUILD_NO_OWN_ZLIB
-#include <zlib.h>
-#else
-#include "../contrib/zlib/zlib.h"
-#endif
-
 namespace Assimp {
 
 struct Compression::impl {
     bool mOpen;
     z_stream mZSstream;
+    FlushMode mFlushMode;
 
     impl() :
-            mOpen(false) {}
+            mOpen(false),
+            mZSstream(),
+            mFlushMode(Compression::FlushMode::NoFlush) {
+        // empty
+    }
 };
 
 Compression::Compression() :
@@ -70,7 +69,7 @@ Compression::~Compression() {
     delete mImpl;
 }
 
-bool Compression::open() {
+bool Compression::open(Format format, FlushMode flush, int windowBits) {
     ai_assert(mImpl != nullptr);
 
     if (mImpl->mOpen) {
@@ -81,44 +80,118 @@ bool Compression::open() {
     mImpl->mZSstream.opaque = Z_NULL;
     mImpl->mZSstream.zalloc = Z_NULL;
     mImpl->mZSstream.zfree = Z_NULL;
-    mImpl->mZSstream.data_type = Z_BINARY;
+    mImpl->mFlushMode = flush;
+    if (format == Format::Binary) {
+        mImpl->mZSstream.data_type = Z_BINARY;
+    } else {
+        mImpl->mZSstream.data_type = Z_ASCII;
+    }
 
     // raw decompression without a zlib or gzip header
-    inflateInit2(&mImpl->mZSstream, -MAX_WBITS);
+    if (windowBits == 0) {
+        inflateInit(&mImpl->mZSstream);
+    } else {
+        inflateInit2(&mImpl->mZSstream, windowBits);
+    }
     mImpl->mOpen = true;
 
     return mImpl->mOpen;
 }
 
-constexpr size_t MYBLOCK = 1024;
+static int getFlushMode(Compression::FlushMode flush) {
+    int z_flush = 0;
+    switch (flush) {
+        case Compression::FlushMode::NoFlush:
+            z_flush = Z_NO_FLUSH;
+            break;
+        case Compression::FlushMode::Block:
+            z_flush = Z_BLOCK;
+            break;
+        case Compression::FlushMode::Tree:
+            z_flush = Z_TREES;
+            break;
+        case Compression::FlushMode::SyncFlush:
+            z_flush = Z_SYNC_FLUSH;
+            break;
+        case Compression::FlushMode::Finish:
+            z_flush = Z_FINISH;
+            break;
+        default:
+            ai_assert(false);
+            break;
+    }
 
-size_t Compression::decompress(unsigned char *data, size_t in, std::vector<unsigned char> &uncompressed) {
+    return z_flush;
+}
+
+constexpr size_t MYBLOCK = 32786;
+
+size_t Compression::decompress(const void *data, size_t in, std::vector<char> &uncompressed) {
     ai_assert(mImpl != nullptr);
+    if (data == nullptr || in == 0) {
+        return 0l;
+    }
 
-    mImpl->mZSstream.next_in = reinterpret_cast<Bytef *>(data);
+    mImpl->mZSstream.next_in = (Bytef*)(data);
     mImpl->mZSstream.avail_in = (uInt)in;
 
-    Bytef block[MYBLOCK] = {};
     int ret = 0;
     size_t total = 0l;
-    do {
-        mImpl->mZSstream.avail_out = MYBLOCK;
-        mImpl->mZSstream.next_out = block;
-        ret = inflate(&mImpl->mZSstream, Z_NO_FLUSH);
+    const int flushMode = getFlushMode(mImpl->mFlushMode);
+    if (flushMode == Z_FINISH) {
+        mImpl->mZSstream.avail_out = static_cast<uInt>(uncompressed.size());
+        mImpl->mZSstream.next_out = reinterpret_cast<Bytef *>(&*uncompressed.begin());
+        ret = inflate(&mImpl->mZSstream, Z_FINISH);
 
         if (ret != Z_STREAM_END && ret != Z_OK) {
             throw DeadlyImportError("Compression", "Failure decompressing this file using gzip.");
-
         }
-        const size_t have = MYBLOCK - mImpl->mZSstream.avail_out;
-        total += have;
-        uncompressed.resize(total);
-        ::memcpy(uncompressed.data() + total - have, block, have);
-    } while (ret != Z_STREAM_END);
+        total = mImpl->mZSstream.avail_out;
+    } else {
+        do {
+            Bytef block[MYBLOCK] = {};
+            mImpl->mZSstream.avail_out = MYBLOCK;
+            mImpl->mZSstream.next_out = block;
+
+            ret = inflate(&mImpl->mZSstream, flushMode);
+
+            if (ret != Z_STREAM_END && ret != Z_OK) {
+                throw DeadlyImportError("Compression", "Failure decompressing this file using gzip.");
+            }
+            const size_t have = MYBLOCK - mImpl->mZSstream.avail_out;
+            total += have;
+            uncompressed.resize(total);
+            ::memcpy(uncompressed.data() + total - have, block, have);
+        } while (ret != Z_STREAM_END);
+    }
 
     return total;
 }
 
+size_t Compression::decompressBlock(const void *data, size_t in, char *out, size_t availableOut) {
+    ai_assert(mImpl != nullptr);
+    if (data == nullptr || in == 0 || out == nullptr || availableOut == 0) {
+        return 0l;
+    }
+
+    // push data to the stream
+    mImpl->mZSstream.next_in = (Bytef *)data;
+    mImpl->mZSstream.avail_in = (uInt)in;
+    mImpl->mZSstream.next_out = (Bytef *)out;
+    mImpl->mZSstream.avail_out = (uInt)availableOut;
+
+    // and decompress the data ....
+    int ret = ::inflate(&mImpl->mZSstream, Z_SYNC_FLUSH);
+    if (ret != Z_OK && ret != Z_STREAM_END) {
+        throw DeadlyImportError("X: Failed to decompress MSZIP-compressed data");
+    }
+
+    ::inflateReset(&mImpl->mZSstream);
+    ::inflateSetDictionary(&mImpl->mZSstream, (const Bytef *)out, (uInt)availableOut - mImpl->mZSstream.avail_out);
+
+    return availableOut - (size_t)mImpl->mZSstream.avail_out;
+}
+
 bool Compression::isOpen() const {
     ai_assert(mImpl != nullptr);
 

+ 43 - 3
code/Common/Compression.h

@@ -41,6 +41,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #pragma once
 
+#ifdef ASSIMP_BUILD_NO_OWN_ZLIB
+#include <zlib.h>
+#else
+#include "../contrib/zlib/zlib.h"
+#endif
+
 #include <vector>
 #include <cstddef> // size_t
 
@@ -49,6 +55,29 @@ namespace Assimp {
 /// @brief This class provides the decompression of zlib-compressed data.
 class Compression {
 public:
+    static const int MaxWBits = MAX_WBITS;
+
+    /// @brief Describes the format data type
+    enum class Format {
+        InvalidFormat = -1, ///< Invalid enum type.
+        Binary = 0,         ///< Binary format.
+        ASCII,              ///< ASCII format.
+
+        NumFormats          ///< The number of supported formats.
+    };
+
+    /// @brief The supported flush mode, used for blocked access.
+    enum class FlushMode {
+        InvalidFormat = -1, ///< Invalid enum type.
+        NoFlush = 0,        ///< No flush, will be done on inflate end.
+        Block,              ///< Assists in combination of compress.
+        Tree,               ///< Assists in combination of compress and returns if stream is finish.
+        SyncFlush,          ///< Synced flush mode.
+        Finish,             ///< Finish mode, all in once, no block access.
+
+        NumModes            ///< The number of supported modes.
+    };
+
     /// @brief  The class constructor.
     Compression();
 
@@ -56,8 +85,11 @@ public:
     ~Compression();
 
     /// @brief  Will open the access to the compression.
+    /// @param[in] format       The format type
+    /// @param[in] flush        The flush mode.
+    /// @param[in] windowBits   The windows history working size, shall be between 8 and 15.
     /// @return true if close was successful, false if not.
-    bool open();
+    bool open(Format format, FlushMode flush, int windowBits);
 
     /// @brief  Will return the open state.
     /// @return true if the access is opened, false if not.
@@ -67,11 +99,19 @@ public:
     /// @return true if close was successful, false if not.
     bool close();
 
-    /// @brief Will decompress the data buffer.
+    /// @brief Will decompress the data buffer in one step.
     /// @param[in] data         The data to decompress
     /// @param[in] in           The size of the data.
     /// @param[out uncompressed A std::vector containing the decompressed data.
-    size_t decompress(unsigned char *data, size_t in, std::vector<unsigned char> &uncompressed);
+    size_t decompress(const void *data, size_t in, std::vector<char> &uncompressed);
+
+    /// @brief Will decompress the data buffer block-wise.
+    /// @param[in]  data         The compressed data
+    /// @param[in]  in           The size of the data buffer
+    /// @param[out] out          The output buffer
+    /// @param[out] availableOut The upper limit of the output buffer.
+    /// @return The size of the decompressed data buffer.
+    size_t decompressBlock(const void *data, size_t in, char *out, size_t availableOut);
 
 private:
     struct impl;

+ 1 - 1
test/unit/utFBXImporterExporter.cpp

@@ -53,7 +53,7 @@ using namespace Assimp;
 
 class utFBXImporterExporter : public AbstractImportExportBase {
 public:
-    virtual bool importerTest() {
+    bool importerTest() override {
         Assimp::Importer importer;
         const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/FBX/spider.fbx", aiProcess_ValidateDataStructure);
         return nullptr != scene;