From b3177a2a9a8c3f1455d3894490f7afc7b933db72 Mon Sep 17 00:00:00 2001 From: Matias Lavik Date: Sat, 6 Apr 2019 19:31:56 +0200 Subject: [PATCH] issue 1957: Added support for reading embedded content (textures) in ASCII FBX. In ASCII FBX embedded content is encoded in Base64 and is surrounded by double quotation marks. The decoding functions are based on those used in the glTF importer. --- code/FBXMaterial.cpp | 18 +++++++++++++++++- code/FBXUtil.cpp | 43 +++++++++++++++++++++++++++++++++++++++++++ code/FBXUtil.h | 14 ++++++++++++++ 3 files changed, 74 insertions(+), 1 deletion(-) diff --git a/code/FBXMaterial.cpp b/code/FBXMaterial.cpp index f5f6fda03..1b05673f4 100644 --- a/code/FBXMaterial.cpp +++ b/code/FBXMaterial.cpp @@ -55,6 +55,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include // std::transform +#include "FBXUtil.h" namespace Assimp { namespace FBX { @@ -307,7 +308,22 @@ Video::Video(uint64_t id, const Element& element, const Document& doc, const std const Token& token = GetRequiredToken(*Content, 0); const char* data = token.begin(); if (!token.IsBinary()) { - DOMWarning("video content is not binary data, ignoring", &element); + if (*data != '"') { + DOMError("embedded content is not surrounded by quotation marks", &element); + } + else { + const char* encodedData = data + 1; + size_t encodedDataLen = static_cast(token.end() - token.begin()); + // search for last quotation mark + while (encodedDataLen > 1 && encodedData[encodedDataLen] != '"') + encodedDataLen--; + if (encodedDataLen % 4 != 0) { + DOMError("embedded content is invalid, needs to be in base64", &element); + } + else { + contentLength = Util::DecodeBase64(encodedData, encodedDataLen, content); + } + } } else if (static_cast(token.end() - data) < 5) { DOMError("binary data array is too short, need five (5) bytes for type signature and element count", &element); diff --git a/code/FBXUtil.cpp b/code/FBXUtil.cpp index c184c4a00..1a9bea914 100644 --- a/code/FBXUtil.cpp +++ b/code/FBXUtil.cpp @@ -113,6 +113,49 @@ std::string AddTokenText(const std::string& prefix, const std::string& text, con text) ); } +static const uint8_t base64DecodeTable[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 64, 0, 0, + 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, + 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0 +}; + +uint8_t DecodeBase64(char ch) +{ + return base64DecodeTable[ch]; +} + +size_t DecodeBase64(const char* in, size_t inLength, uint8_t*& out) +{ + if (inLength < 4) { + out = 0; + return 0; + } + + const size_t outLength = (inLength * 3) / 4; + out = new uint8_t[outLength]; + memset(out, 0, outLength); + + size_t i = 0; + size_t j = 0; + for (i = 0; i < inLength - 4; i += 4) + { + uint8_t b0 = Util::DecodeBase64(in[i]); + uint8_t b1 = Util::DecodeBase64(in[i + 1]); + uint8_t b2 = Util::DecodeBase64(in[i + 2]); + uint8_t b3 = Util::DecodeBase64(in[i + 3]); + + out[j++] = (uint8_t)((b0 << 2) | (b1 >> 4)); + out[j++] = (uint8_t)((b1 << 4) | (b2 >> 2)); + out[j++] = (uint8_t)((b2 << 6) | b3); + } + return outLength; +} + } // !Util } // !FBX } // !Assimp diff --git a/code/FBXUtil.h b/code/FBXUtil.h index 1a37d346b..2b19b9e4f 100644 --- a/code/FBXUtil.h +++ b/code/FBXUtil.h @@ -98,6 +98,20 @@ std::string AddLineAndColumn(const std::string& prefix, const std::string& text, * @return A string of the following format: {prefix} ({token-type}, line {line}, col {column}) {text}*/ std::string AddTokenText(const std::string& prefix, const std::string& text, const Token* tok); +/** Decode a single Base64-encoded character. +* +* @param ch Character to decode (from base64 to binary). +* @return decoded byte value*/ +uint8_t DecodeBase64(char ch); + +/** Decode a Base64-encoded string +* +* @param in Characters to decode. +* @param inLength Number of characters to decode. +* @param out Reference to pointer where we will store the decoded data. +* @return size of the decoded data (number of bytes)*/ +size_t DecodeBase64(const char* in, size_t inLength, uint8_t*& out); + } } }