diff --git a/code/FBXBinaryTokenizer.cpp b/code/FBXBinaryTokenizer.cpp index c84b0dfe1..bdde35b43 100644 --- a/code/FBXBinaryTokenizer.cpp +++ b/code/FBXBinaryTokenizer.cpp @@ -216,7 +216,7 @@ void ReadData(const char*& sbegin_out, const char*& send_out, const char* input, const uint32_t length = ReadWord(input, cursor, end); const uint32_t encoding = ReadWord(input, cursor, end); - const uint32_t decomp_len = ReadWord(input, cursor, end); + const uint32_t comp_len = ReadWord(input, cursor, end); // compute length based on type and check against the stored value if(encoding == 0) { @@ -236,7 +236,7 @@ void ReadData(const char*& sbegin_out, const char*& send_out, const char* input, default: ai_assert(false); }; - if(length * stride != decomp_len) { + if(length * stride != comp_len) { TokenizeError("cannot ReadData, calculated data stride differs from what the file claims",input, cursor); } } @@ -244,7 +244,7 @@ void ReadData(const char*& sbegin_out, const char*& send_out, const char* input, else if (encoding != 1) { TokenizeError("cannot ReadData, unknown encoding",input, cursor); } - cursor += decomp_len; + cursor += comp_len; break; } diff --git a/code/FBXParser.cpp b/code/FBXParser.cpp index 8661e5531..be7292fd7 100644 --- a/code/FBXParser.cpp +++ b/code/FBXParser.cpp @@ -45,6 +45,14 @@ 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 +#else +# include "../contrib/zlib/zlib.h" +#endif + + #include "FBXTokenizer.h" #include "FBXParser.h" #include "FBXUtil.h" @@ -461,12 +469,173 @@ std::string ParseTokenAsString(const Token& t, const char*& err_out) } +namespace { + +// ------------------------------------------------------------------------------------------------ +// read the type code and element count of a binary data array and stop there +void ReadBinaryDataArrayHead(const char*& data, const char* end, char& type, uint32_t& count, + const Element& el) +{ + if (static_cast(end-data) < 5) { + ParseError("binary data array is too short, need five (5) bytes for type signature and element count",&el); + } + + // data type + type = *data; + + // read number of elements + BE_NCONST uint32_t len = *reinterpret_cast(data+1); + AI_SWAP4(len); + + count = len; + data += 5; +} + + +// ------------------------------------------------------------------------------------------------ +// 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& buff, + const Element& el) +{ + ai_assert(static_cast(end-data) >= 4); // runtime check for this happens at tokenization stage + + BE_NCONST uint32_t encmode = *reinterpret_cast(data); + AI_SWAP4(encmode); + data += 4; + + // next comes the compressed length + BE_NCONST uint32_t comp_len = *reinterpret_cast(data); + AI_SWAP4(comp_len); + data += 4; + + ai_assert(data + comp_len == end); + + // determine the length of the uncompressed data by looking at the type signature + uint32_t stride; + switch(type) + { + case 'f': + case 'i': + stride = 4; + break; + + case 'd': + case 'l': + stride = 8; + break; + + default: + ai_assert(false); + }; + + const uint32_t full_length = stride * count; + buff.resize(full_length); + + if(encmode == 0) { + ai_assert(full_length == comp_len); + + // plain data, no compression + std::copy(data, end, buff.begin()); + } + 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 + inflateInit(&zstream); + + zstream.next_in = reinterpret_cast( const_cast(data) ); + zstream.avail_in = comp_len; + + zstream.avail_out = buff.size(); + zstream.next_out = reinterpret_cast(&*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 _DEBUG + else { + // runtime check for this happens at tokenization stage + ai_assert(false); + } +#endif + + data += comp_len; + ai_assert(data == end); +} + +} // !anon + + // ------------------------------------------------------------------------------------------------ // read an array of float3 tuples void ParseVectorDataArray(std::vector& out, const Element& el) { out.clear(); + const TokenList& tok = el.Tokens(); + if(tok.empty()) { + ParseError("unexpected empty element",&el); + } + + if(tok[0]->IsBinary()) { + const char* data = tok[0]->begin(), *end = tok[0]->end(); + + char type; + uint32_t count; + ReadBinaryDataArrayHead(data, end, type, count, el); + + if(count % 3 != 0) { + ParseError("number of floats is not a multiple of three (3) (binary)",&el); + } + + if(!count) { + return; + } + + if (type != 'd' && type != 'f') { + ParseError("expected float or double array (binary)",&el); + } + + std::vector buff; + ReadBinaryDataArray(type, count, data, end, buff, el); + + ai_assert(data == end); + ai_assert(buff.size() == count * (type == 'd' ? 8 : 4)); + + const uint32_t count3 = count / 3; + out.reserve(count3); + + if (type != 'd') { + const double* d = reinterpret_cast(&buff[0]); + for (unsigned int i = 0; i < count3; ++i, d += 3) { + out.push_back(aiVector3D(static_cast(d[0]), + static_cast(d[1]), + static_cast(d[2]))); + } + } + else if (type != 'f') { + const float* f = reinterpret_cast(&buff[0]); + for (unsigned int i = 0; i < count3; ++i, f += 3) { + out.push_back(aiVector3D(f[0],f[1],f[2])); + } + } + + return; + } + const size_t dim = ParseTokenAsDim(*tok[0]); // may throw bad_alloc if the input is rubbish, but this need @@ -497,6 +666,56 @@ void ParseVectorDataArray(std::vector& out, const Element& el) { out.clear(); const TokenList& tok = el.Tokens(); + if(tok.empty()) { + ParseError("unexpected empty element",&el); + } + + if(tok[0]->IsBinary()) { + const char* data = tok[0]->begin(), *end = tok[0]->end(); + + char type; + uint32_t count; + ReadBinaryDataArrayHead(data, end, type, count, el); + + if(count % 4 != 0) { + ParseError("number of floats is not a multiple of four (4) (binary)",&el); + } + + if(!count) { + return; + } + + if (type != 'd' && type != 'f') { + ParseError("expected float or double array (binary)",&el); + } + + std::vector buff; + ReadBinaryDataArray(type, count, data, end, buff, el); + + ai_assert(data == end); + ai_assert(buff.size() == count * (type == 'd' ? 8 : 4)); + + const uint32_t count4 = count / 4; + out.reserve(count4); + + if (type != 'd') { + const double* d = reinterpret_cast(&buff[0]); + for (unsigned int i = 0; i < count4; ++i, d += 4) { + out.push_back(aiColor4D(static_cast(d[0]), + static_cast(d[1]), + static_cast(d[2]), + static_cast(d[3]))); + } + } + else if (type != 'f') { + const float* f = reinterpret_cast(&buff[0]); + for (unsigned int i = 0; i < count4; ++i, f += 4) { + out.push_back(aiColor4D(f[0],f[1],f[2],f[3])); + } + } + return; + } + const size_t dim = ParseTokenAsDim(*tok[0]); // see notes in ParseVectorDataArray() above @@ -526,6 +745,55 @@ void ParseVectorDataArray(std::vector& out, const Element& el) { out.clear(); const TokenList& tok = el.Tokens(); + if(tok.empty()) { + ParseError("unexpected empty element",&el); + } + + if(tok[0]->IsBinary()) { + const char* data = tok[0]->begin(), *end = tok[0]->end(); + + char type; + uint32_t count; + ReadBinaryDataArrayHead(data, end, type, count, el); + + if(count % 2 != 0) { + ParseError("number of floats is not a multiple of two (2) (binary)",&el); + } + + if(!count) { + return; + } + + if (type != 'd' && type != 'f') { + ParseError("expected float or double array (binary)",&el); + } + + std::vector buff; + ReadBinaryDataArray(type, count, data, end, buff, el); + + ai_assert(data == end); + ai_assert(buff.size() == count * (type == 'd' ? 8 : 4)); + + const uint32_t count2 = count / 2; + out.reserve(count2); + + if (type != 'd') { + const double* d = reinterpret_cast(&buff[0]); + for (unsigned int i = 0; i < count2; ++i, d += 2) { + out.push_back(aiVector2D(static_cast(d[0]), + static_cast(d[1]))); + } + } + else if (type != 'f') { + const float* f = reinterpret_cast(&buff[0]); + for (unsigned int i = 0; i < count2; ++i, f += 2) { + out.push_back(aiVector2D(f[0],f[1])); + } + } + + return; + } + const size_t dim = ParseTokenAsDim(*tok[0]); // see notes in ParseVectorDataArray() above @@ -553,6 +821,43 @@ void ParseVectorDataArray(std::vector& out, const Element& el) { out.clear(); const TokenList& tok = el.Tokens(); + if(tok.empty()) { + ParseError("unexpected empty element",&el); + } + + if(tok[0]->IsBinary()) { + const char* data = tok[0]->begin(), *end = tok[0]->end(); + + char type; + uint32_t count; + ReadBinaryDataArrayHead(data, end, type, count, el); + + if(!count) { + return; + } + + if (type != 'i') { + ParseError("expected int array (binary)",&el); + } + + std::vector buff; + ReadBinaryDataArray(type, count, data, end, buff, el); + + ai_assert(data == end); + ai_assert(buff.size() == count * 4); + + out.reserve(count); + + const uint32_t* ip = reinterpret_cast(&buff[0]); + for (unsigned int i = 0; i < count; ++i, ++ip) { + BE_NCONST uint32_t val = *ip; + AI_SWAP4(val); + out.push_back(val); + } + + return; + } + const size_t dim = ParseTokenAsDim(*tok[0]); // see notes in ParseVectorDataArray() @@ -574,6 +879,47 @@ void ParseVectorDataArray(std::vector& out, const Element& el) { out.clear(); const TokenList& tok = el.Tokens(); + if(tok.empty()) { + ParseError("unexpected empty element",&el); + } + + if(tok[0]->IsBinary()) { + const char* data = tok[0]->begin(), *end = tok[0]->end(); + + char type; + uint32_t count; + ReadBinaryDataArrayHead(data, end, type, count, el); + + if(!count) { + return; + } + + if (type != 'd' && type != 'f') { + ParseError("expected float or double array (binary)",&el); + } + + std::vector buff; + ReadBinaryDataArray(type, count, data, end, buff, el); + + ai_assert(data == end); + ai_assert(buff.size() == count * (type == 'd' ? 8 : 4)); + + if (type != 'd') { + const double* d = reinterpret_cast(&buff[0]); + for (unsigned int i = 0; i < count; ++i, ++d) { + out.push_back(static_cast(*d)); + } + } + else if (type != 'f') { + const float* f = reinterpret_cast(&buff[0]); + for (unsigned int i = 0; i < count; ++i, ++f) { + out.push_back(*f); + } + } + + return; + } + const size_t dim = ParseTokenAsDim(*tok[0]); // see notes in ParseVectorDataArray() @@ -595,6 +941,15 @@ void ParseVectorDataArray(std::vector& out, const Element& el) { out.clear(); const TokenList& tok = el.Tokens(); + if(tok.empty()) { + ParseError("unexpected empty element",&el); + } + + if(tok[0]->IsBinary()) { + // XXX don't think we need this - there is no special type sign for unsigned ints in the binary encoding + ParseError("feature not implemented",&el); + } + const size_t dim = ParseTokenAsDim(*tok[0]); // see notes in ParseVectorDataArray() @@ -619,6 +974,43 @@ void ParseVectorDataArray(std::vector& out, const Element& el) { out.clear(); const TokenList& tok = el.Tokens(); + if(tok.empty()) { + ParseError("unexpected empty element",&el); + } + + if(tok[0]->IsBinary()) { + const char* data = tok[0]->begin(), *end = tok[0]->end(); + + char type; + uint32_t count; + ReadBinaryDataArrayHead(data, end, type, count, el); + + if(!count) { + return; + } + + if (type != 'l') { + ParseError("expected long array (binary)",&el); + } + + std::vector buff; + ReadBinaryDataArray(type, count, data, end, buff, el); + + ai_assert(data == end); + ai_assert(buff.size() == count * 8); + + out.reserve(count); + + const uint64_t* ip = reinterpret_cast(&buff[0]); + for (unsigned int i = 0; i < count; ++i, ++ip) { + BE_NCONST uint64_t val = *ip; + AI_SWAP8(val); + out.push_back(val); + } + + return; + } + const size_t dim = ParseTokenAsDim(*tok[0]); // see notes in ParseVectorDataArray()