Merge pull request #4381 from assimp/kimkulling/introduce_compression

Kimkulling/introduce compression
pull/4391/head
Kim Kulling 2022-02-15 19:09:52 +01:00 committed by GitHub
commit 884bb39391
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 207 additions and 190 deletions

View File

@ -66,11 +66,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// zlib is needed for compressed blend files // zlib is needed for compressed blend files
#ifndef ASSIMP_BUILD_NO_COMPRESSED_BLEND #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> # include <zlib.h>
# else # else
# include "../contrib/zlib/zlib.h" # include "../contrib/zlib/zlib.h"
# endif # endif*/
#endif #endif
namespace Assimp { namespace Assimp {
@ -141,7 +142,7 @@ void BlenderImporter::SetupProperties(const Importer * /*pImp*/) {
void BlenderImporter::InternReadFile(const std::string &pFile, void BlenderImporter::InternReadFile(const std::string &pFile,
aiScene *pScene, IOSystem *pIOHandler) { aiScene *pScene, IOSystem *pIOHandler) {
#ifndef ASSIMP_BUILD_NO_COMPRESSED_BLEND #ifndef ASSIMP_BUILD_NO_COMPRESSED_BLEND
std::vector<Bytef> uncompressed; std::vector<char> uncompressed;
#endif #endif
FileDatabase file; FileDatabase file;
@ -159,7 +160,6 @@ void BlenderImporter::InternReadFile(const std::string &pFile,
#ifdef ASSIMP_BUILD_NO_COMPRESSED_BLEND #ifdef ASSIMP_BUILD_NO_COMPRESSED_BLEND
ThrowException("BLENDER magic bytes are missing, is this file compressed (Assimp was built without decompression support)?"); ThrowException("BLENDER magic bytes are missing, is this file compressed (Assimp was built without decompression support)?");
#else #else
if (magic[0] != 0x1f || static_cast<uint8_t>(magic[1]) != 0x8b) { if (magic[0] != 0x1f || static_cast<uint8_t>(magic[1]) != 0x8b) {
ThrowException("BLENDER magic bytes are missing, couldn't find GZIP header either"); 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); stream->Seek(0L, aiOrigin_SET);
std::shared_ptr<StreamReaderLE> reader = std::shared_ptr<StreamReaderLE>(new StreamReaderLE(stream)); std::shared_ptr<StreamReaderLE> reader = std::shared_ptr<StreamReaderLE>(new StreamReaderLE(stream));
// build a zlib stream size_t total = 0;
z_stream zstream; Compression compression;
zstream.opaque = Z_NULL; if (compression.open(Compression::Format::Binary, Compression::FlushMode::NoFlush, 16 + Compression::MaxWBits)) {
zstream.zalloc = Z_NULL; total = compression.decompress((unsigned char *)reader->GetPtr(), reader->GetRemainingSize(), uncompressed);
zstream.zfree = Z_NULL; compression.close();
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);
// replace the input stream with a memory stream // 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));

View File

@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2022, assimp team Copyright (c) 2006-2022, 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,
@ -46,11 +45,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER #ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
#ifdef ASSIMP_BUILD_NO_OWN_ZLIB //#ifdef ASSIMP_BUILD_NO_OWN_ZLIB
# include <zlib.h> #include "Common/Compression.h"
#else //# include <zlib.h>
# include "../contrib/zlib/zlib.h" //#else
#endif //# include "../contrib/zlib/zlib.h"
//#endif
#include "FBXTokenizer.h" #include "FBXTokenizer.h"
#include "FBXParser.h" #include "FBXParser.h"
@ -115,9 +115,7 @@ namespace Assimp {
namespace FBX { 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 = nullptr; TokenPtr n = nullptr;
do { do {
n = parser.AdvanceToNextToken(); n = parser.AdvanceToNextToken();
@ -210,8 +208,7 @@ Scope::Scope(Parser& parser,bool topLevel)
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
Scope::~Scope() Scope::~Scope() {
{
for(ElementMap::value_type& v : elements) { for(ElementMap::value_type& v : elements) {
delete v.second; 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) // 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, void ReadBinaryDataArray(char type, uint32_t count, const char*& data, const char* end,
std::vector<char>& buff, std::vector<char>& buff, const Element& /*el*/) {
const Element& /*el*/)
{
BE_NCONST uint32_t encmode = SafeParse<uint32_t>(data, end); BE_NCONST uint32_t encmode = SafeParse<uint32_t>(data, end);
AI_SWAP4(encmode); AI_SWAP4(encmode);
data += 4; data += 4;
@ -571,31 +566,11 @@ void ReadBinaryDataArray(char type, uint32_t count, const char*& data, const cha
else if(encmode == 1) { else if(encmode == 1) {
// zlib/deflate, next comes ZIP head (0x78 0x01) // zlib/deflate, next comes ZIP head (0x78 0x01)
// see http://www.ietf.org/rfc/rfc1950.txt // see http://www.ietf.org/rfc/rfc1950.txt
Compression compress;
z_stream zstream; if (compress.open(Compression::Format::Binary, Compression::FlushMode::Finish, 0)) {
zstream.opaque = Z_NULL; compress.decompress(data, comp_len, buff);
zstream.zalloc = Z_NULL; compress.close();
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");
} }
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 #ifdef ASSIMP_BUILD_DEBUG
else { else {
@ -701,7 +676,6 @@ void ParseVectorDataArray(std::vector<aiVector3D>& out, const Element& el)
} }
} }
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// read an array of color4 tuples // read an array of color4 tuples
void ParseVectorDataArray(std::vector<aiColor4D>& out, const Element& el) 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 // 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 ); out.resize( 0 );
const TokenList& tok = el.Tokens(); const TokenList& tok = el.Tokens();
if(tok.empty()) { 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]), out.push_back(aiVector2D(static_cast<float>(d[0]),
static_cast<float>(d[1]))); static_cast<float>(d[1])));
} }
} } else if (type == 'f') {
else if (type == 'f') {
const float* f = reinterpret_cast<const float*>(&buff[0]); const float* f = reinterpret_cast<const float*>(&buff[0]);
for (unsigned int i = 0; i < count2; ++i, f += 2) { for (unsigned int i = 0; i < count2; ++i, f += 2) {
out.push_back(aiVector2D(f[0],f[1])); 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 // 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 ); out.resize( 0 );
const TokenList& tok = el.Tokens(); const TokenList& tok = el.Tokens();
if(tok.empty()) { if(tok.empty()) {

View File

@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
Copyright (c) 2006-2022, assimp team Copyright (c) 2006-2022, 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,
@ -60,25 +58,11 @@ using namespace Assimp::Formatter;
#ifndef ASSIMP_BUILD_NO_COMPRESSED_X #ifndef ASSIMP_BUILD_NO_COMPRESSED_X
#ifdef ASSIMP_BUILD_NO_OWN_ZLIB #include "Common/Compression.h"
#include <zlib.h>
#else
#include "../contrib/zlib/zlib.h"
#endif
// Magic identifier for MSZIP compressed data // Magic identifier for MSZIP compressed data
#define MSZIP_MAGIC 0x4B43 constexpr unsigned int MSZIP_MAGIC = 0x4B43;
#define MSZIP_BLOCK 32786 constexpr size_t MSZIP_BLOCK = 32786l;
// ------------------------------------------------------------------------------------------------
// 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);
}
#endif // !! ASSIMP_BUILD_NO_COMPRESSED_X #endif // !! ASSIMP_BUILD_NO_COMPRESSED_X
@ -133,13 +117,13 @@ XFileParser::XFileParser(const std::vector<char> &pBuffer) :
mIsBinaryFormat = true; mIsBinaryFormat = true;
compressed = true; compressed = true;
} else } 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 // 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); 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) 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 // The x format specifies size in bits, but we work in bytes
mBinaryFloatSize /= 8; 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?) // skip unknown data (checksum, flags?)
mP += 6; mP += 6;
@ -207,43 +181,29 @@ XFileParser::XFileParser(const std::vector<char> &pBuffer) :
// and advance to the next offset // and advance to the next offset
P1 += ofs; 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 // Allocate storage and terminating zero and do the actual uncompressing
Compression compression;
uncompressed.resize(est_out + 1); uncompressed.resize(est_out + 1);
char *out = &uncompressed.front(); char *out = &uncompressed.front();
while (mP + 3 < mEnd) { if (compression.open(mIsBinaryFormat ? Compression::Format::Binary : Compression::Format::ASCII,
uint16_t ofs = *((uint16_t *)mP); Compression::FlushMode::SyncFlush, -Compression::MaxWBits)) {
AI_SWAP2(ofs); while (mP + 3 < mEnd) {
mP += 4; uint16_t ofs = *((uint16_t *)mP);
AI_SWAP2(ofs);
mP += 4;
if (mP + ofs > mEnd + 2) { if (mP + ofs > mEnd + 2) {
throw DeadlyImportError("X: Unexpected EOF in compressed chunk"); throw DeadlyImportError("X: Unexpected EOF in compressed chunk");
}
out += compression.decompressBlock(mP, ofs, out, MSZIP_BLOCK);
mP += ofs;
} }
compression.close();
// 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;
} }
// terminate zlib
::inflateEnd(&stream);
// ok, update pointers to point to the uncompressed file data // ok, update pointers to point to the uncompressed file data
mP = &uncompressed[0]; mP = &uncompressed[0];
mEnd = out; mEnd = out;
@ -279,15 +239,16 @@ void XFileParser::ParseFile() {
while (running) { while (running) {
// read name of next object // read name of next object
std::string objectName = GetNextToken(); std::string objectName = GetNextToken();
if (objectName.length() == 0) if (objectName.length() == 0) {
break; break;
}
// parse specific object // parse specific object
if (objectName == "template") if (objectName == "template") {
ParseDataObjectTemplate(); ParseDataObjectTemplate();
else if (objectName == "Frame") } else if (objectName == "Frame") {
ParseDataObjectFrame(nullptr); ParseDataObjectFrame(nullptr);
else if (objectName == "Mesh") { } else if (objectName == "Mesh") {
// some meshes have no frames at all // some meshes have no frames at all
Mesh *mesh = new Mesh; Mesh *mesh = new Mesh;
ParseDataObjectMesh(mesh); ParseDataObjectMesh(mesh);
@ -326,11 +287,13 @@ void XFileParser::ParseDataObjectTemplate() {
while (running) { while (running) {
std::string s = GetNextToken(); std::string s = GetNextToken();
if (s == "}") if (s == "}") {
break; break;
}
if (s.length() == 0) if (s.length() == 0) {
ThrowException("Unexpected end of file reached while parsing template definition"); ThrowException("Unexpected end of file reached while parsing template definition");
}
} }
} }
@ -500,7 +463,7 @@ void XFileParser::ParseDataObjectSkinWeights(Mesh *pMesh) {
bone.mWeights.reserve(numWeights); bone.mWeights.reserve(numWeights);
for (unsigned int a = 0; a < numWeights; a++) { for (unsigned int a = 0; a < numWeights; a++) {
BoneWeight weight; BoneWeight weight = {};
weight.mVertex = ReadInt(); weight.mVertex = ReadInt();
bone.mWeights.push_back(weight); bone.mWeights.push_back(weight);
} }

View File

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

View File

@ -43,20 +43,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/ai_assert.h> #include <assimp/ai_assert.h>
#include <assimp/Exceptional.h> #include <assimp/Exceptional.h>
#ifdef ASSIMP_BUILD_NO_OWN_ZLIB
#include <zlib.h>
#else
#include "../contrib/zlib/zlib.h"
#endif
namespace Assimp { namespace Assimp {
struct Compression::impl { struct Compression::impl {
bool mOpen; bool mOpen;
z_stream mZSstream; z_stream mZSstream;
FlushMode mFlushMode;
impl() : impl() :
mOpen(false) {} mOpen(false),
mZSstream(),
mFlushMode(Compression::FlushMode::NoFlush) {
// empty
}
}; };
Compression::Compression() : Compression::Compression() :
@ -70,7 +69,7 @@ Compression::~Compression() {
delete mImpl; delete mImpl;
} }
bool Compression::open() { bool Compression::open(Format format, FlushMode flush, int windowBits) {
ai_assert(mImpl != nullptr); ai_assert(mImpl != nullptr);
if (mImpl->mOpen) { if (mImpl->mOpen) {
@ -81,44 +80,118 @@ bool Compression::open() {
mImpl->mZSstream.opaque = Z_NULL; mImpl->mZSstream.opaque = Z_NULL;
mImpl->mZSstream.zalloc = Z_NULL; mImpl->mZSstream.zalloc = Z_NULL;
mImpl->mZSstream.zfree = 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 // 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; mImpl->mOpen = true;
return mImpl->mOpen; 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); 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; mImpl->mZSstream.avail_in = (uInt)in;
Bytef block[MYBLOCK] = {};
int ret = 0; int ret = 0;
size_t total = 0l; size_t total = 0l;
do { const int flushMode = getFlushMode(mImpl->mFlushMode);
mImpl->mZSstream.avail_out = MYBLOCK; if (flushMode == Z_FINISH) {
mImpl->mZSstream.next_out = block; mImpl->mZSstream.avail_out = static_cast<uInt>(uncompressed.size());
ret = inflate(&mImpl->mZSstream, Z_NO_FLUSH); mImpl->mZSstream.next_out = reinterpret_cast<Bytef *>(&*uncompressed.begin());
ret = inflate(&mImpl->mZSstream, Z_FINISH);
if (ret != Z_STREAM_END && ret != Z_OK) { if (ret != Z_STREAM_END && ret != Z_OK) {
throw DeadlyImportError("Compression", "Failure decompressing this file using gzip."); throw DeadlyImportError("Compression", "Failure decompressing this file using gzip.");
} }
const size_t have = MYBLOCK - mImpl->mZSstream.avail_out; total = mImpl->mZSstream.avail_out;
total += have; } else {
uncompressed.resize(total); do {
::memcpy(uncompressed.data() + total - have, block, have); Bytef block[MYBLOCK] = {};
} while (ret != Z_STREAM_END); 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; 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 { bool Compression::isOpen() const {
ai_assert(mImpl != nullptr); ai_assert(mImpl != nullptr);

View File

@ -41,6 +41,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#pragma once #pragma once
#ifdef ASSIMP_BUILD_NO_OWN_ZLIB
#include <zlib.h>
#else
#include "../contrib/zlib/zlib.h"
#endif
#include <vector> #include <vector>
#include <cstddef> // size_t #include <cstddef> // size_t
@ -49,6 +55,29 @@ namespace Assimp {
/// @brief This class provides the decompression of zlib-compressed data. /// @brief This class provides the decompression of zlib-compressed data.
class Compression { class Compression {
public: 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. /// @brief The class constructor.
Compression(); Compression();
@ -56,8 +85,11 @@ public:
~Compression(); ~Compression();
/// @brief Will open the access to the 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. /// @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. /// @brief Will return the open state.
/// @return true if the access is opened, false if not. /// @return true if the access is opened, false if not.
@ -67,11 +99,19 @@ public:
/// @return true if close was successful, false if not. /// @return true if close was successful, false if not.
bool close(); 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] data The data to decompress
/// @param[in] in The size of the data. /// @param[in] in The size of the data.
/// @param[out uncompressed A std::vector containing the decompressed 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: private:
struct impl; struct impl;

View File

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