1838 lines
68 KiB
C++
1838 lines
68 KiB
C++
|
/*
|
||
|
Open Asset Import Library (assimp)
|
||
|
----------------------------------------------------------------------
|
||
|
|
||
|
Copyright (c) 2006-2020, assimp team
|
||
|
|
||
|
|
||
|
All rights reserved.
|
||
|
|
||
|
Redistribution and use of this software in source and binary forms,
|
||
|
with or without modification, are permitted provided that the
|
||
|
following conditions are met:
|
||
|
|
||
|
* Redistributions of source code must retain the above
|
||
|
copyright notice, this list of conditions and the
|
||
|
following disclaimer.
|
||
|
|
||
|
* Redistributions in binary form must reproduce the above
|
||
|
copyright notice, this list of conditions and the
|
||
|
following disclaimer in the documentation and/or other
|
||
|
materials provided with the distribution.
|
||
|
|
||
|
* Neither the name of the assimp team, nor the names of its
|
||
|
contributors may be used to endorse or promote products
|
||
|
derived from this software without specific prior
|
||
|
written permission of the assimp team.
|
||
|
|
||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
|
||
|
----------------------------------------------------------------------
|
||
|
*/
|
||
|
/// \file FIReader.cpp
|
||
|
/// \brief Reader for Fast Infoset encoded binary XML files.
|
||
|
/// \date 2017
|
||
|
/// \author Patrick Daehne
|
||
|
|
||
|
#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
|
||
|
|
||
|
#include "FIReader.hpp"
|
||
|
#include <assimp/StringUtils.h>
|
||
|
|
||
|
// Workaround for issue #1361
|
||
|
// https://github.com/assimp/assimp/issues/1361
|
||
|
#ifdef __ANDROID__
|
||
|
# define _GLIBCXX_USE_C99 1
|
||
|
#endif
|
||
|
|
||
|
#include <assimp/Exceptional.h>
|
||
|
#include <assimp/IOStream.hpp>
|
||
|
#include <assimp/types.h>
|
||
|
#include <assimp/MemoryIOWrapper.h>
|
||
|
#include <assimp/irrXMLWrapper.h>
|
||
|
#ifdef ASSIMP_USE_HUNTER
|
||
|
# include <utf8/utf8.h>
|
||
|
#else
|
||
|
# include "../contrib/utf8cpp/source/utf8.h"
|
||
|
#endif
|
||
|
#include <assimp/fast_atof.h>
|
||
|
#include <stack>
|
||
|
#include <map>
|
||
|
#include <iostream>
|
||
|
#include <sstream>
|
||
|
#include <iomanip>
|
||
|
|
||
|
namespace Assimp {
|
||
|
|
||
|
static const std::string parseErrorMessage = "Fast Infoset parse error";
|
||
|
|
||
|
static const char *xmlDeclarations[] = {
|
||
|
"<?xml encoding='finf'?>",
|
||
|
"<?xml encoding='finf' standalone='yes'?>",
|
||
|
"<?xml encoding='finf' standalone='no'?>",
|
||
|
"<?xml version='1.0' encoding='finf'?>",
|
||
|
"<?xml version='1.0' encoding='finf' standalone='yes'?>",
|
||
|
"<?xml version='1.0' encoding='finf' standalone='no'?>",
|
||
|
"<?xml version='1.1' encoding='finf'?>",
|
||
|
"<?xml version='1.1' encoding='finf' standalone='yes'?>",
|
||
|
"<?xml version='1.1' encoding='finf' standalone='no'?>"
|
||
|
};
|
||
|
|
||
|
static size_t parseMagic(const uint8_t *data, const uint8_t *dataEnd) {
|
||
|
if (dataEnd - data < 4) {
|
||
|
return 0;
|
||
|
}
|
||
|
uint32_t magic = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
|
||
|
switch (magic) {
|
||
|
case 0xe0000001:
|
||
|
return 4;
|
||
|
case 0x3c3f786d: // "<?xm"
|
||
|
{
|
||
|
size_t xmlDeclarationsLength = sizeof(xmlDeclarations) / sizeof(xmlDeclarations[0]);
|
||
|
for (size_t i = 0; i < xmlDeclarationsLength; ++i) {
|
||
|
auto xmlDeclaration = xmlDeclarations[i];
|
||
|
ptrdiff_t xmlDeclarationLength = strlen(xmlDeclaration);
|
||
|
if ((dataEnd - data >= xmlDeclarationLength) && (memcmp(xmlDeclaration, data, xmlDeclarationLength) == 0)) {
|
||
|
data += xmlDeclarationLength;
|
||
|
if (dataEnd - data < 4) {
|
||
|
return 0;
|
||
|
}
|
||
|
magic = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
|
||
|
return magic == 0xe0000001 ? xmlDeclarationLength + 4 : 0;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
default:
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static std::string parseUTF8String(const uint8_t *data, size_t len) {
|
||
|
return std::string((char*)data, len);
|
||
|
}
|
||
|
|
||
|
static std::string parseUTF16String(const uint8_t *data, size_t len) {
|
||
|
if (len & 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
size_t numShorts = len / 2;
|
||
|
std::vector<short> utf16;
|
||
|
utf16.reserve(numShorts);
|
||
|
for (size_t i = 0; i < numShorts; ++i) {
|
||
|
short v = (data[0] << 8) | data[1];
|
||
|
utf16.push_back(v);
|
||
|
data += 2;
|
||
|
}
|
||
|
std::string result;
|
||
|
utf8::utf16to8(utf16.begin(), utf16.end(), back_inserter(result));
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
struct FIStringValueImpl: public FIStringValue {
|
||
|
inline FIStringValueImpl(std::string &&value_) { value = std::move(value_); }
|
||
|
virtual const std::string &toString() const /*override*/ { return value; }
|
||
|
};
|
||
|
|
||
|
std::shared_ptr<FIStringValue> FIStringValue::create(std::string &&value) {
|
||
|
return std::make_shared<FIStringValueImpl>(std::move(value));
|
||
|
}
|
||
|
|
||
|
struct FIHexValueImpl: public FIHexValue {
|
||
|
mutable std::string strValue;
|
||
|
mutable bool strValueValid;
|
||
|
inline FIHexValueImpl(std::vector<uint8_t> &&value_): strValueValid(false) { value = std::move(value_); }
|
||
|
virtual const std::string &toString() const /*override*/ {
|
||
|
if (!strValueValid) {
|
||
|
strValueValid = true;
|
||
|
std::ostringstream os;
|
||
|
os << std::hex << std::uppercase << std::setfill('0');
|
||
|
std::for_each(value.begin(), value.end(), [&](uint8_t c) { os << std::setw(2) << static_cast<int>(c); });
|
||
|
strValue = os.str();
|
||
|
}
|
||
|
return strValue;
|
||
|
};
|
||
|
};
|
||
|
|
||
|
std::shared_ptr<FIHexValue> FIHexValue::create(std::vector<uint8_t> &&value) {
|
||
|
return std::make_shared<FIHexValueImpl>(std::move(value));
|
||
|
}
|
||
|
|
||
|
struct FIBase64ValueImpl: public FIBase64Value {
|
||
|
mutable std::string strValue;
|
||
|
mutable bool strValueValid;
|
||
|
inline FIBase64ValueImpl(std::vector<uint8_t> &&value_): strValueValid(false) { value = std::move(value_); }
|
||
|
virtual const std::string &toString() const /*override*/ {
|
||
|
if (!strValueValid) {
|
||
|
strValueValid = true;
|
||
|
std::ostringstream os;
|
||
|
uint8_t c1 = 0, c2;
|
||
|
int imod3 = 0;
|
||
|
std::vector<uint8_t>::size_type valueSize = value.size();
|
||
|
for (std::vector<uint8_t>::size_type i = 0; i < valueSize; ++i) {
|
||
|
c2 = value[i];
|
||
|
switch (imod3) {
|
||
|
case 0:
|
||
|
os << basis_64[c2 >> 2];
|
||
|
imod3 = 1;
|
||
|
break;
|
||
|
case 1:
|
||
|
os << basis_64[((c1 & 0x03) << 4) | ((c2 & 0xf0) >> 4)];
|
||
|
imod3 = 2;
|
||
|
break;
|
||
|
case 2:
|
||
|
os << basis_64[((c1 & 0x0f) << 2) | ((c2 & 0xc0) >> 6)] << basis_64[c2 & 0x3f];
|
||
|
imod3 = 0;
|
||
|
break;
|
||
|
}
|
||
|
c1 = c2;
|
||
|
}
|
||
|
switch (imod3) {
|
||
|
case 1:
|
||
|
os << basis_64[(c1 & 0x03) << 4] << "==";
|
||
|
break;
|
||
|
case 2:
|
||
|
os << basis_64[(c1 & 0x0f) << 2] << '=';
|
||
|
break;
|
||
|
}
|
||
|
strValue = os.str();
|
||
|
}
|
||
|
return strValue;
|
||
|
};
|
||
|
static const char basis_64[];
|
||
|
};
|
||
|
|
||
|
const char FIBase64ValueImpl::basis_64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||
|
|
||
|
std::shared_ptr<FIBase64Value> FIBase64Value::create(std::vector<uint8_t> &&value) {
|
||
|
return std::make_shared<FIBase64ValueImpl>(std::move(value));
|
||
|
}
|
||
|
|
||
|
struct FIShortValueImpl: public FIShortValue {
|
||
|
mutable std::string strValue;
|
||
|
mutable bool strValueValid;
|
||
|
inline FIShortValueImpl(std::vector<int16_t> &&value_): strValueValid(false) { value = std::move(value_); }
|
||
|
virtual const std::string &toString() const /*override*/ {
|
||
|
if (!strValueValid) {
|
||
|
strValueValid = true;
|
||
|
std::ostringstream os;
|
||
|
int n = 0;
|
||
|
std::for_each(value.begin(), value.end(), [&](int16_t s) { if (++n > 1) os << ' '; os << s; });
|
||
|
strValue = os.str();
|
||
|
}
|
||
|
return strValue;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
std::shared_ptr<FIShortValue> FIShortValue::create(std::vector<int16_t> &&value) {
|
||
|
return std::make_shared<FIShortValueImpl>(std::move(value));
|
||
|
}
|
||
|
|
||
|
struct FIIntValueImpl: public FIIntValue {
|
||
|
mutable std::string strValue;
|
||
|
mutable bool strValueValid;
|
||
|
inline FIIntValueImpl(std::vector<int32_t> &&value_): strValueValid(false) { value = std::move(value_); }
|
||
|
virtual const std::string &toString() const /*override*/ {
|
||
|
if (!strValueValid) {
|
||
|
strValueValid = true;
|
||
|
std::ostringstream os;
|
||
|
int n = 0;
|
||
|
std::for_each(value.begin(), value.end(), [&](int32_t i) { if (++n > 1) os << ' '; os << i; });
|
||
|
strValue = os.str();
|
||
|
}
|
||
|
return strValue;
|
||
|
};
|
||
|
};
|
||
|
|
||
|
std::shared_ptr<FIIntValue> FIIntValue::create(std::vector<int32_t> &&value) {
|
||
|
return std::make_shared<FIIntValueImpl>(std::move(value));
|
||
|
}
|
||
|
|
||
|
struct FILongValueImpl: public FILongValue {
|
||
|
mutable std::string strValue;
|
||
|
mutable bool strValueValid;
|
||
|
inline FILongValueImpl(std::vector<int64_t> &&value_): strValueValid(false) { value = std::move(value_); }
|
||
|
virtual const std::string &toString() const /*override*/ {
|
||
|
if (!strValueValid) {
|
||
|
strValueValid = true;
|
||
|
std::ostringstream os;
|
||
|
int n = 0;
|
||
|
std::for_each(value.begin(), value.end(), [&](int64_t l) { if (++n > 1) os << ' '; os << l; });
|
||
|
strValue = os.str();
|
||
|
}
|
||
|
return strValue;
|
||
|
};
|
||
|
};
|
||
|
|
||
|
std::shared_ptr<FILongValue> FILongValue::create(std::vector<int64_t> &&value) {
|
||
|
return std::make_shared<FILongValueImpl>(std::move(value));
|
||
|
}
|
||
|
|
||
|
struct FIBoolValueImpl: public FIBoolValue {
|
||
|
mutable std::string strValue;
|
||
|
mutable bool strValueValid;
|
||
|
inline FIBoolValueImpl(std::vector<bool> &&value_): strValueValid(false) { value = std::move(value_); }
|
||
|
virtual const std::string &toString() const /*override*/ {
|
||
|
if (!strValueValid) {
|
||
|
strValueValid = true;
|
||
|
std::ostringstream os;
|
||
|
os << std::boolalpha;
|
||
|
int n = 0;
|
||
|
std::for_each(value.begin(), value.end(), [&](bool b) { if (++n > 1) os << ' '; os << b; });
|
||
|
strValue = os.str();
|
||
|
}
|
||
|
return strValue;
|
||
|
};
|
||
|
};
|
||
|
|
||
|
std::shared_ptr<FIBoolValue> FIBoolValue::create(std::vector<bool> &&value) {
|
||
|
return std::make_shared<FIBoolValueImpl>(std::move(value));
|
||
|
}
|
||
|
|
||
|
struct FIFloatValueImpl: public FIFloatValue {
|
||
|
mutable std::string strValue;
|
||
|
mutable bool strValueValid;
|
||
|
inline FIFloatValueImpl(std::vector<float> &&value_): strValueValid(false) { value = std::move(value_); }
|
||
|
virtual const std::string &toString() const /*override*/ {
|
||
|
if (!strValueValid) {
|
||
|
strValueValid = true;
|
||
|
std::ostringstream os;
|
||
|
int n = 0;
|
||
|
std::for_each(value.begin(), value.end(), [&](float f) { if (++n > 1) os << ' '; os << f; });
|
||
|
strValue = os.str();
|
||
|
}
|
||
|
return strValue;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
std::shared_ptr<FIFloatValue> FIFloatValue::create(std::vector<float> &&value) {
|
||
|
return std::make_shared<FIFloatValueImpl>(std::move(value));
|
||
|
}
|
||
|
|
||
|
struct FIDoubleValueImpl: public FIDoubleValue {
|
||
|
mutable std::string strValue;
|
||
|
mutable bool strValueValid;
|
||
|
inline FIDoubleValueImpl(std::vector<double> &&value_): strValueValid(false) { value = std::move(value_); }
|
||
|
virtual const std::string &toString() const /*override*/ {
|
||
|
if (!strValueValid) {
|
||
|
strValueValid = true;
|
||
|
std::ostringstream os;
|
||
|
int n = 0;
|
||
|
std::for_each(value.begin(), value.end(), [&](double d) { if (++n > 1) os << ' '; os << d; });
|
||
|
strValue = os.str();
|
||
|
}
|
||
|
return strValue;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
std::shared_ptr<FIDoubleValue> FIDoubleValue::create(std::vector<double> &&value) {
|
||
|
return std::make_shared<FIDoubleValueImpl>(std::move(value));
|
||
|
}
|
||
|
|
||
|
struct FIUUIDValueImpl: public FIUUIDValue {
|
||
|
mutable std::string strValue;
|
||
|
mutable bool strValueValid;
|
||
|
inline FIUUIDValueImpl(std::vector<uint8_t> &&value_): strValueValid(false) { value = std::move(value_); }
|
||
|
virtual const std::string &toString() const /*override*/ {
|
||
|
if (!strValueValid) {
|
||
|
strValueValid = true;
|
||
|
std::ostringstream os;
|
||
|
os << std::hex << std::uppercase << std::setfill('0');
|
||
|
std::vector<uint8_t>::size_type valueSize = value.size();
|
||
|
for (std::vector<uint8_t>::size_type i = 0; i < valueSize; ++i) {
|
||
|
switch (i & 15) {
|
||
|
case 0:
|
||
|
if (i > 0) {
|
||
|
os << ' ';
|
||
|
}
|
||
|
os << std::setw(2) << static_cast<int>(value[i]);
|
||
|
break;
|
||
|
case 4:
|
||
|
case 6:
|
||
|
case 8:
|
||
|
case 10:
|
||
|
os << '-';
|
||
|
// intentionally fall through!
|
||
|
case 1:
|
||
|
case 2:
|
||
|
case 3:
|
||
|
case 5:
|
||
|
case 7:
|
||
|
case 9:
|
||
|
case 11:
|
||
|
case 12:
|
||
|
case 13:
|
||
|
case 14:
|
||
|
case 15:
|
||
|
os << std::setw(2) << static_cast<int>(value[i]);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
strValue = os.str();
|
||
|
}
|
||
|
return strValue;
|
||
|
};
|
||
|
};
|
||
|
|
||
|
std::shared_ptr<FIUUIDValue> FIUUIDValue::create(std::vector<uint8_t> &&value) {
|
||
|
return std::make_shared<FIUUIDValueImpl>(std::move(value));
|
||
|
}
|
||
|
|
||
|
struct FICDATAValueImpl: public FICDATAValue {
|
||
|
inline FICDATAValueImpl(std::string &&value_) { value = std::move(value_); }
|
||
|
virtual const std::string &toString() const /*override*/ { return value; }
|
||
|
};
|
||
|
|
||
|
std::shared_ptr<FICDATAValue> FICDATAValue::create(std::string &&value) {
|
||
|
return std::make_shared<FICDATAValueImpl>(std::move(value));
|
||
|
}
|
||
|
|
||
|
struct FIHexDecoder: public FIDecoder {
|
||
|
virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
|
||
|
return FIHexValue::create(std::vector<uint8_t>(data, data + len));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
struct FIBase64Decoder: public FIDecoder {
|
||
|
virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
|
||
|
return FIBase64Value::create(std::vector<uint8_t>(data, data + len));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
struct FIShortDecoder: public FIDecoder {
|
||
|
virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
|
||
|
if (len & 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
std::vector<int16_t> value;
|
||
|
size_t numShorts = len / 2;
|
||
|
value.reserve(numShorts);
|
||
|
for (size_t i = 0; i < numShorts; ++i) {
|
||
|
int16_t v = (data[0] << 8) | data[1];
|
||
|
value.push_back(v);
|
||
|
data += 2;
|
||
|
}
|
||
|
return FIShortValue::create(std::move(value));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
struct FIIntDecoder: public FIDecoder {
|
||
|
virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
|
||
|
if (len & 3) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
std::vector<int32_t> value;
|
||
|
size_t numInts = len / 4;
|
||
|
value.reserve(numInts);
|
||
|
for (size_t i = 0; i < numInts; ++i) {
|
||
|
int32_t v = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
|
||
|
value.push_back(v);
|
||
|
data += 4;
|
||
|
}
|
||
|
return FIIntValue::create(std::move(value));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
struct FILongDecoder: public FIDecoder {
|
||
|
virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
|
||
|
if (len & 7) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
std::vector<int64_t> value;
|
||
|
size_t numLongs = len / 8;
|
||
|
value.reserve(numLongs);
|
||
|
for (size_t i = 0; i < numLongs; ++i) {
|
||
|
int64_t b0 = data[0], b1 = data[1], b2 = data[2], b3 = data[3], b4 = data[4], b5 = data[5], b6 = data[6], b7 = data[7];
|
||
|
int64_t v = (b0 << 56) | (b1 << 48) | (b2 << 40) | (b3 << 32) | (b4 << 24) | (b5 << 16) | (b6 << 8) | b7;
|
||
|
value.push_back(v);
|
||
|
data += 8;
|
||
|
}
|
||
|
return FILongValue::create(std::move(value));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
struct FIBoolDecoder: public FIDecoder {
|
||
|
virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
|
||
|
if (len < 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
std::vector<bool> value;
|
||
|
uint8_t b = *data++;
|
||
|
size_t unusedBits = b >> 4;
|
||
|
size_t numBools = (len * 8) - 4 - unusedBits;
|
||
|
value.reserve(numBools);
|
||
|
uint8_t mask = 1 << 3;
|
||
|
for (size_t i = 0; i < numBools; ++i) {
|
||
|
if (!mask) {
|
||
|
mask = 1 << 7;
|
||
|
b = *data++;
|
||
|
}
|
||
|
value.push_back((b & mask) != 0);
|
||
|
}
|
||
|
return FIBoolValue::create(std::move(value));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
struct FIFloatDecoder: public FIDecoder {
|
||
|
virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
|
||
|
if (len & 3) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
std::vector<float> value;
|
||
|
size_t numFloats = len / 4;
|
||
|
value.reserve(numFloats);
|
||
|
for (size_t i = 0; i < numFloats; ++i) {
|
||
|
int v = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
|
||
|
float f;
|
||
|
memcpy(&f, &v, 4);
|
||
|
value.push_back(f);
|
||
|
data += 4;
|
||
|
}
|
||
|
return FIFloatValue::create(std::move(value));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
struct FIDoubleDecoder: public FIDecoder {
|
||
|
virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
|
||
|
if (len & 7) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
std::vector<double> value;
|
||
|
size_t numDoubles = len / 8;
|
||
|
value.reserve(numDoubles);
|
||
|
for (size_t i = 0; i < numDoubles; ++i) {
|
||
|
long long b0 = data[0], b1 = data[1], b2 = data[2], b3 = data[3], b4 = data[4], b5 = data[5], b6 = data[6], b7 = data[7];
|
||
|
long long v = (b0 << 56) | (b1 << 48) | (b2 << 40) | (b3 << 32) | (b4 << 24) | (b5 << 16) | (b6 << 8) | b7;
|
||
|
double f;
|
||
|
memcpy(&f, &v, 8);
|
||
|
value.push_back(f);
|
||
|
data += 8;
|
||
|
}
|
||
|
return FIDoubleValue::create(std::move(value));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
struct FIUUIDDecoder: public FIDecoder {
|
||
|
virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
|
||
|
if (len & 15) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
return FIUUIDValue::create(std::vector<uint8_t>(data, data + len));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
struct FICDATADecoder: public FIDecoder {
|
||
|
virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
|
||
|
return FICDATAValue::create(parseUTF8String(data, len));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
class CFIReaderImpl: public FIReader {
|
||
|
public:
|
||
|
|
||
|
CFIReaderImpl(std::unique_ptr<uint8_t[]> data_, size_t size):
|
||
|
data(std::move(data_)), dataP(data.get()), dataEnd(data.get() + size), currentNodeType(irr::io::EXN_NONE),
|
||
|
emptyElement(false), headerPending(true), terminatorPending(false)
|
||
|
{}
|
||
|
|
||
|
virtual ~CFIReaderImpl() {}
|
||
|
|
||
|
virtual bool read() /*override*/ {
|
||
|
if (headerPending) {
|
||
|
headerPending = false;
|
||
|
parseHeader();
|
||
|
}
|
||
|
if (terminatorPending) {
|
||
|
terminatorPending = false;
|
||
|
if (elementStack.empty()) {
|
||
|
return false;
|
||
|
}
|
||
|
else {
|
||
|
nodeName = elementStack.top();
|
||
|
elementStack.pop();
|
||
|
currentNodeType = nodeName.empty() ? irr::io::EXN_UNKNOWN : irr::io::EXN_ELEMENT_END;
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
if (dataP >= dataEnd) {
|
||
|
return false;
|
||
|
}
|
||
|
uint8_t b = *dataP;
|
||
|
if (b < 0x80) { // Element (C.2.11.2, C.3.7.2)
|
||
|
// C.3
|
||
|
parseElement();
|
||
|
return true;
|
||
|
}
|
||
|
else if (b < 0xc0) { // Characters (C.3.7.5)
|
||
|
// C.7
|
||
|
auto chars = parseNonIdentifyingStringOrIndex3(vocabulary.charactersTable);
|
||
|
nodeName = chars->toString();
|
||
|
currentNodeType = irr::io::EXN_TEXT;
|
||
|
return true;
|
||
|
}
|
||
|
else if (b < 0xe0) {
|
||
|
if ((b & 0xfc) == 0xc4) { // DTD (C.2.11.5)
|
||
|
// C.9
|
||
|
++dataP;
|
||
|
if (b & 0x02) {
|
||
|
/*const std::string &systemID =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
|
||
|
}
|
||
|
if (b & 0x01) {
|
||
|
/*const std::string &publicID =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
|
||
|
}
|
||
|
elementStack.push(EmptyString);
|
||
|
currentNodeType = irr::io::EXN_UNKNOWN;
|
||
|
return true;
|
||
|
}
|
||
|
else if ((b & 0xfc) == 0xc8) { // Unexpanded entity reference (C.3.7.4)
|
||
|
// C.6
|
||
|
++dataP;
|
||
|
/*const std::string &name =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable);
|
||
|
if (b & 0x02) {
|
||
|
/*const std::string &systemID =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
|
||
|
}
|
||
|
if (b & 0x01) {
|
||
|
/*const std::string &publicID =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
|
||
|
}
|
||
|
currentNodeType = irr::io::EXN_UNKNOWN;
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
else if (b < 0xf0) {
|
||
|
if (b == 0xe1) { // Processing instruction (C.2.11.3, C.3.7.3)
|
||
|
// C.5
|
||
|
++dataP;
|
||
|
/*const std::string &target =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable);
|
||
|
if (dataEnd - dataP < 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
/*std::shared_ptr<const FIValue> data =*/ parseNonIdentifyingStringOrIndex1(vocabulary.otherStringTable);
|
||
|
currentNodeType = irr::io::EXN_UNKNOWN;
|
||
|
return true;
|
||
|
}
|
||
|
else if (b == 0xe2) { // Comment (C.2.11.4, C.3.7.6)
|
||
|
// C.8
|
||
|
++dataP;
|
||
|
if (dataEnd - dataP < 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
std::shared_ptr<const FIValue> comment = parseNonIdentifyingStringOrIndex1(vocabulary.otherStringTable);
|
||
|
nodeName = comment->toString();
|
||
|
currentNodeType = irr::io::EXN_COMMENT;
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
else { // Terminator (C.2.12, C.3.8)
|
||
|
++dataP;
|
||
|
if (b == 0xff) {
|
||
|
terminatorPending = true;
|
||
|
}
|
||
|
if (elementStack.empty()) {
|
||
|
return false;
|
||
|
}
|
||
|
else {
|
||
|
nodeName = elementStack.top();
|
||
|
elementStack.pop();
|
||
|
currentNodeType = nodeName.empty() ? irr::io::EXN_UNKNOWN : irr::io::EXN_ELEMENT_END;
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
|
||
|
virtual irr::io::EXML_NODE getNodeType() const /*override*/ {
|
||
|
return currentNodeType;
|
||
|
}
|
||
|
|
||
|
virtual int getAttributeCount() const /*override*/ {
|
||
|
return static_cast<int>(attributes.size());
|
||
|
}
|
||
|
|
||
|
virtual const char* getAttributeName(int idx) const /*override*/ {
|
||
|
if (idx < 0 || idx >= (int)attributes.size()) {
|
||
|
return nullptr;
|
||
|
}
|
||
|
return attributes[idx].name.c_str();
|
||
|
}
|
||
|
|
||
|
virtual const char* getAttributeValue(int idx) const /*override*/ {
|
||
|
if (idx < 0 || idx >= (int)attributes.size()) {
|
||
|
return nullptr;
|
||
|
}
|
||
|
return attributes[idx].value->toString().c_str();
|
||
|
}
|
||
|
|
||
|
virtual const char* getAttributeValue(const char* name) const /*override*/ {
|
||
|
const Attribute* attr = getAttributeByName(name);
|
||
|
if (!attr) {
|
||
|
return nullptr;
|
||
|
}
|
||
|
return attr->value->toString().c_str();
|
||
|
}
|
||
|
|
||
|
virtual const char* getAttributeValueSafe(const char* name) const /*override*/ {
|
||
|
const Attribute* attr = getAttributeByName(name);
|
||
|
if (!attr) {
|
||
|
return EmptyString.c_str();
|
||
|
}
|
||
|
return attr->value->toString().c_str();
|
||
|
}
|
||
|
|
||
|
virtual int getAttributeValueAsInt(const char* name) const /*override*/ {
|
||
|
const Attribute* attr = getAttributeByName(name);
|
||
|
if (!attr) {
|
||
|
return 0;
|
||
|
}
|
||
|
std::shared_ptr<const FIIntValue> intValue = std::dynamic_pointer_cast<const FIIntValue>(attr->value);
|
||
|
if (intValue) {
|
||
|
return intValue->value.size() == 1 ? intValue->value.front() : 0;
|
||
|
}
|
||
|
return atoi(attr->value->toString().c_str());
|
||
|
}
|
||
|
|
||
|
virtual int getAttributeValueAsInt(int idx) const /*override*/ {
|
||
|
if (idx < 0 || idx >= (int)attributes.size()) {
|
||
|
return 0;
|
||
|
}
|
||
|
std::shared_ptr<const FIIntValue> intValue = std::dynamic_pointer_cast<const FIIntValue>(attributes[idx].value);
|
||
|
if (intValue) {
|
||
|
return intValue->value.size() == 1 ? intValue->value.front() : 0;
|
||
|
}
|
||
|
return atoi(attributes[idx].value->toString().c_str());
|
||
|
}
|
||
|
|
||
|
virtual float getAttributeValueAsFloat(const char* name) const /*override*/ {
|
||
|
const Attribute* attr = getAttributeByName(name);
|
||
|
if (!attr) {
|
||
|
return 0;
|
||
|
}
|
||
|
std::shared_ptr<const FIFloatValue> floatValue = std::dynamic_pointer_cast<const FIFloatValue>(attr->value);
|
||
|
if (floatValue) {
|
||
|
return floatValue->value.size() == 1 ? floatValue->value.front() : 0;
|
||
|
}
|
||
|
|
||
|
return fast_atof(attr->value->toString().c_str());
|
||
|
}
|
||
|
|
||
|
virtual float getAttributeValueAsFloat(int idx) const /*override*/ {
|
||
|
if (idx < 0 || idx >= (int)attributes.size()) {
|
||
|
return 0;
|
||
|
}
|
||
|
std::shared_ptr<const FIFloatValue> floatValue = std::dynamic_pointer_cast<const FIFloatValue>(attributes[idx].value);
|
||
|
if (floatValue) {
|
||
|
return floatValue->value.size() == 1 ? floatValue->value.front() : 0;
|
||
|
}
|
||
|
return fast_atof(attributes[idx].value->toString().c_str());
|
||
|
}
|
||
|
|
||
|
virtual const char* getNodeName() const /*override*/ {
|
||
|
return nodeName.c_str();
|
||
|
}
|
||
|
|
||
|
virtual const char* getNodeData() const /*override*/ {
|
||
|
return nodeName.c_str();
|
||
|
}
|
||
|
|
||
|
virtual bool isEmptyElement() const /*override*/ {
|
||
|
return emptyElement;
|
||
|
}
|
||
|
|
||
|
virtual irr::io::ETEXT_FORMAT getSourceFormat() const /*override*/ {
|
||
|
return irr::io::ETF_UTF8;
|
||
|
}
|
||
|
|
||
|
virtual irr::io::ETEXT_FORMAT getParserFormat() const /*override*/ {
|
||
|
return irr::io::ETF_UTF8;
|
||
|
}
|
||
|
|
||
|
virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(int idx) const /*override*/ {
|
||
|
if (idx < 0 || idx >= (int)attributes.size()) {
|
||
|
return nullptr;
|
||
|
}
|
||
|
return attributes[idx].value;
|
||
|
}
|
||
|
|
||
|
virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(const char* name) const /*override*/ {
|
||
|
const Attribute* attr = getAttributeByName(name);
|
||
|
if (!attr) {
|
||
|
return nullptr;
|
||
|
}
|
||
|
return attr->value;
|
||
|
}
|
||
|
|
||
|
virtual void registerDecoder(const std::string &algorithmUri, std::unique_ptr<FIDecoder> decoder) /*override*/ {
|
||
|
decoderMap[algorithmUri] = std::move(decoder);
|
||
|
}
|
||
|
|
||
|
virtual void registerVocabulary(const std::string &vocabularyUri, const FIVocabulary *_vocabulary) /*override*/ {
|
||
|
vocabularyMap[vocabularyUri] = _vocabulary;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
|
||
|
struct QName {
|
||
|
std::string prefix;
|
||
|
std::string uri;
|
||
|
std::string name;
|
||
|
inline QName() {}
|
||
|
inline QName(const FIQName &qname): prefix(qname.prefix ? qname.prefix : ""), uri(qname.uri ? qname.uri : ""), name(qname.name) {}
|
||
|
};
|
||
|
|
||
|
struct Attribute {
|
||
|
QName qname;
|
||
|
std::string name;
|
||
|
std::shared_ptr<const FIValue> value;
|
||
|
};
|
||
|
|
||
|
struct Vocabulary {
|
||
|
std::vector<std::string> restrictedAlphabetTable;
|
||
|
std::vector<std::string> encodingAlgorithmTable;
|
||
|
std::vector<std::string> prefixTable;
|
||
|
std::vector<std::string> namespaceNameTable;
|
||
|
std::vector<std::string> localNameTable;
|
||
|
std::vector<std::string> otherNCNameTable;
|
||
|
std::vector<std::string> otherURITable;
|
||
|
std::vector<std::shared_ptr<const FIValue>> attributeValueTable;
|
||
|
std::vector<std::shared_ptr<const FIValue>> charactersTable;
|
||
|
std::vector<std::shared_ptr<const FIValue>> otherStringTable;
|
||
|
std::vector<QName> elementNameTable;
|
||
|
std::vector<QName> attributeNameTable;
|
||
|
Vocabulary() {
|
||
|
prefixTable.push_back("xml");
|
||
|
namespaceNameTable.push_back("http://www.w3.org/XML/1998/namespace");
|
||
|
}
|
||
|
};
|
||
|
|
||
|
const Attribute* getAttributeByName(const char* name) const {
|
||
|
if (!name) {
|
||
|
return 0;
|
||
|
}
|
||
|
std::string n = name;
|
||
|
for (int i=0; i<(int)attributes.size(); ++i) {
|
||
|
if (attributes[i].name == n) {
|
||
|
return &attributes[i];
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
size_t parseInt2() { // C.25
|
||
|
uint8_t b = *dataP++;
|
||
|
if (!(b & 0x40)) { // x0...... (C.25.2)
|
||
|
return b & 0x3f;
|
||
|
}
|
||
|
else if ((b & 0x60) == 0x40) { // x10..... ........ (C.25.3)
|
||
|
if (dataEnd - dataP > 0) {
|
||
|
return (((b & 0x1f) << 8) | *dataP++) + 0x40;
|
||
|
}
|
||
|
}
|
||
|
else if ((b & 0x70) == 0x60) { // x110.... ........ ........ (C.25.4)
|
||
|
if (dataEnd - dataP > 1) {
|
||
|
size_t result = (((b & 0x0f) << 16) | (dataP[0] << 8) | dataP[1]) + 0x2040;
|
||
|
dataP += 2;
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
|
||
|
size_t parseInt3() { // C.27
|
||
|
uint8_t b = *dataP++;
|
||
|
if (!(b & 0x20)) { // xx0..... (C.27.2)
|
||
|
return b & 0x1f;
|
||
|
}
|
||
|
else if ((b & 0x38) == 0x20) { // xx100... ........ (C.27.3)
|
||
|
if (dataEnd - dataP > 0) {
|
||
|
return (((b & 0x07) << 8) | *dataP++) + 0x20;
|
||
|
}
|
||
|
}
|
||
|
else if ((b & 0x38) == 0x28) { // xx101... ........ ........ (C.27.4)
|
||
|
if (dataEnd - dataP > 1) {
|
||
|
size_t result = (((b & 0x07) << 16) | (dataP[0] << 8) | dataP[1]) + 0x820;
|
||
|
dataP += 2;
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
else if ((b & 0x3f) == 0x30) { // xx110000 0000.... ........ ........ (C.27.5)
|
||
|
if ((dataEnd - dataP > 2) && !(dataP[0] & 0xf0)) {
|
||
|
size_t result = (((dataP[0] & 0x0f) << 16) | (dataP[1] << 8) | dataP[2]) + 0x80820;
|
||
|
dataP += 3;
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
|
||
|
size_t parseInt4() { // C.28
|
||
|
uint8_t b = *dataP++;
|
||
|
if (!(b & 0x10)) { // xxx0.... (C.28.2)
|
||
|
return b & 0x0f;
|
||
|
}
|
||
|
else if ((b & 0x1c) == 0x10) { // xxx100.. ........ (C.28.3)
|
||
|
if (dataEnd - dataP > 0) {
|
||
|
return (((b & 0x03) << 8) | *dataP++) + 0x10;
|
||
|
}
|
||
|
}
|
||
|
else if ((b & 0x1c) == 0x14) { // xxx101.. ........ ........ (C.28.4)
|
||
|
if (dataEnd - dataP > 1) {
|
||
|
size_t result = (((b & 0x03) << 16) | (dataP[0] << 8) | dataP[1]) + 0x410;
|
||
|
dataP += 2;
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
else if ((b & 0x1f) == 0x18) { // xxx11000 0000.... ........ ........ (C.28.5)
|
||
|
if ((dataEnd - dataP > 2) && !(dataP[0] & 0xf0)) {
|
||
|
size_t result = (((dataP[0] & 0x0f) << 16) | (dataP[1] << 8) | dataP[2]) + 0x40410;
|
||
|
dataP += 3;
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
|
||
|
size_t parseSequenceLen() { // C.21
|
||
|
if (dataEnd - dataP > 0) {
|
||
|
uint8_t b = *dataP++;
|
||
|
if (b < 0x80) { // 0....... (C.21.2)
|
||
|
return b;
|
||
|
}
|
||
|
else if ((b & 0xf0) == 0x80) { // 1000.... ........ ........ (C.21.3)
|
||
|
if (dataEnd - dataP > 1) {
|
||
|
size_t result = (((b & 0x0f) << 16) | (dataP[0] << 8) | dataP[1]) + 0x80;
|
||
|
dataP += 2;
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
|
||
|
std::string parseNonEmptyOctetString2() { // C.22
|
||
|
// Parse the length of the string
|
||
|
uint8_t b = *dataP++ & 0x7f;
|
||
|
size_t len;
|
||
|
if (!(b & 0x40)) { // x0...... (C.22.3.1)
|
||
|
len = b + 1;
|
||
|
}
|
||
|
else if (b == 0x40) { // x1000000 ........ (C.22.3.2)
|
||
|
if (dataEnd - dataP < 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
len = *dataP++ + 0x41;
|
||
|
}
|
||
|
else if (b == 0x60) { // x1100000 ........ ........ ........ ........ (C.22.3.3)
|
||
|
if (dataEnd - dataP < 4) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
len = ((dataP[0] << 24) | (dataP[1] << 16) | (dataP[2] << 8) | dataP[3]) + 0x141;
|
||
|
dataP += 4;
|
||
|
}
|
||
|
else {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
|
||
|
// Parse the string (C.22.4)
|
||
|
if (dataEnd - dataP < static_cast<ptrdiff_t>(len)) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
std::string s = parseUTF8String(dataP, len);
|
||
|
dataP += len;
|
||
|
|
||
|
return s;
|
||
|
}
|
||
|
|
||
|
size_t parseNonEmptyOctetString5Length() { // C.23
|
||
|
// Parse the length of the string
|
||
|
size_t b = *dataP++ & 0x0f;
|
||
|
if (!(b & 0x08)) { // xxxx0... (C.23.3.1)
|
||
|
return b + 1;
|
||
|
}
|
||
|
else if (b == 0x08) { // xxxx1000 ........ (C.23.3.2)
|
||
|
if (dataEnd - dataP > 0) {
|
||
|
return *dataP++ + 0x09;
|
||
|
}
|
||
|
}
|
||
|
else if (b == 0x0c) { // xxxx1100 ........ ........ ........ ........ (C.23.3.3)
|
||
|
if (dataEnd - dataP > 3) {
|
||
|
size_t result = ((dataP[0] << 24) | (dataP[1] << 16) | (dataP[2] << 8) | dataP[3]) + 0x109;
|
||
|
dataP += 4;
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
|
||
|
size_t parseNonEmptyOctetString7Length() { // C.24
|
||
|
// Parse the length of the string
|
||
|
size_t b = *dataP++ & 0x03;
|
||
|
if (!(b & 0x02)) { // xxxxxx0. (C.24.3.1)
|
||
|
return b + 1;
|
||
|
}
|
||
|
else if (b == 0x02) { // xxxxxx10 ........ (C.24.3.2)
|
||
|
if (dataEnd - dataP > 0) {
|
||
|
return *dataP++ + 0x3;
|
||
|
}
|
||
|
}
|
||
|
else if (b == 0x03) { // xxxxxx11 ........ ........ ........ ........ (C.24.3.3)
|
||
|
if (dataEnd - dataP > 3) {
|
||
|
size_t result = ((dataP[0] << 24) | (dataP[1] << 16) | (dataP[2] << 8) | dataP[3]) + 0x103;
|
||
|
dataP += 4;
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
|
||
|
std::shared_ptr<const FIValue> parseEncodedData(size_t index, size_t len) {
|
||
|
if (index < 32) {
|
||
|
FIDecoder *decoder = defaultDecoder[index];
|
||
|
if (!decoder) {
|
||
|
throw DeadlyImportError("Invalid encoding algorithm index " + to_string(index));
|
||
|
}
|
||
|
return decoder->decode(dataP, len);
|
||
|
}
|
||
|
else {
|
||
|
if (index - 32 >= vocabulary.encodingAlgorithmTable.size()) {
|
||
|
throw DeadlyImportError("Invalid encoding algorithm index " + to_string(index));
|
||
|
}
|
||
|
std::string uri = vocabulary.encodingAlgorithmTable[index - 32];
|
||
|
auto it = decoderMap.find(uri);
|
||
|
if (it == decoderMap.end()) {
|
||
|
throw DeadlyImportError("Unsupported encoding algorithm " + uri);
|
||
|
}
|
||
|
else {
|
||
|
return it->second->decode(dataP, len);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
std::shared_ptr<const FIValue> parseRestrictedAlphabet(size_t index, size_t len) {
|
||
|
std::string alphabet;
|
||
|
if (index < 16) {
|
||
|
switch (index) {
|
||
|
case 0: // numeric
|
||
|
alphabet = "0123456789-+.e ";
|
||
|
break;
|
||
|
case 1: // date and time
|
||
|
alphabet = "0123456789-:TZ ";
|
||
|
break;
|
||
|
default:
|
||
|
throw DeadlyImportError("Invalid restricted alphabet index " + to_string(index));
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if (index - 16 >= vocabulary.restrictedAlphabetTable.size()) {
|
||
|
throw DeadlyImportError("Invalid restricted alphabet index " + to_string(index));
|
||
|
}
|
||
|
alphabet = vocabulary.restrictedAlphabetTable[index - 16];
|
||
|
}
|
||
|
std::vector<uint32_t> alphabetUTF32;
|
||
|
utf8::utf8to32(alphabet.begin(), alphabet.end(), back_inserter(alphabetUTF32));
|
||
|
std::string::size_type alphabetLength = alphabetUTF32.size();
|
||
|
if (alphabetLength < 2) {
|
||
|
throw DeadlyImportError("Invalid restricted alphabet length " + to_string(alphabetLength));
|
||
|
}
|
||
|
std::string::size_type bitsPerCharacter = 1;
|
||
|
while ((1ull << bitsPerCharacter) <= alphabetLength) {
|
||
|
++bitsPerCharacter;
|
||
|
}
|
||
|
size_t bitsAvail = 0;
|
||
|
uint8_t mask = (1 << bitsPerCharacter) - 1;
|
||
|
uint32_t bits = 0;
|
||
|
std::string s;
|
||
|
for (size_t i = 0; i < len; ++i) {
|
||
|
bits = (bits << 8) | dataP[i];
|
||
|
bitsAvail += 8;
|
||
|
while (bitsAvail >= bitsPerCharacter) {
|
||
|
bitsAvail -= bitsPerCharacter;
|
||
|
const size_t charIndex = (bits >> bitsAvail) & mask;
|
||
|
if (charIndex < alphabetLength) {
|
||
|
s += (char) alphabetUTF32[charIndex];
|
||
|
} else if (charIndex != mask) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return FIStringValue::create(std::move(s));
|
||
|
}
|
||
|
|
||
|
std::shared_ptr<const FIValue> parseEncodedCharacterString3() { // C.19
|
||
|
std::shared_ptr<const FIValue> result;
|
||
|
size_t len;
|
||
|
uint8_t b = *dataP;
|
||
|
if (b & 0x20) {
|
||
|
++dataP;
|
||
|
if (dataEnd - dataP < 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
size_t index = ((b & 0x0f) << 4) | ((*dataP & 0xf0) >> 4); // C.29
|
||
|
len = parseNonEmptyOctetString5Length();
|
||
|
if (dataEnd - dataP < static_cast<ptrdiff_t>(len)) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
if (b & 0x10) {
|
||
|
// encoding algorithm (C.19.3.4)
|
||
|
result = parseEncodedData(index, len);
|
||
|
}
|
||
|
else {
|
||
|
// Restricted alphabet (C.19.3.3)
|
||
|
result = parseRestrictedAlphabet(index, len);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
len = parseNonEmptyOctetString5Length();
|
||
|
if (dataEnd - dataP < static_cast<ptrdiff_t>(len)) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
if (b & 0x10) {
|
||
|
// UTF-16 (C.19.3.2)
|
||
|
if (len & 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
result = FIStringValue::create(parseUTF16String(dataP, len));
|
||
|
}
|
||
|
else {
|
||
|
// UTF-8 (C.19.3.1)
|
||
|
result = FIStringValue::create(parseUTF8String(dataP, len));
|
||
|
}
|
||
|
}
|
||
|
dataP += len;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
std::shared_ptr<const FIValue> parseEncodedCharacterString5() { // C.20
|
||
|
std::shared_ptr<const FIValue> result;
|
||
|
size_t len;
|
||
|
uint8_t b = *dataP;
|
||
|
if (b & 0x08) {
|
||
|
++dataP;
|
||
|
if (dataEnd - dataP < 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
size_t index = ((b & 0x03) << 6) | ((*dataP & 0xfc) >> 2); /* C.29 */
|
||
|
len = parseNonEmptyOctetString7Length();
|
||
|
if (dataEnd - dataP < static_cast<ptrdiff_t>(len)) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
if (b & 0x04) {
|
||
|
// encoding algorithm (C.20.3.4)
|
||
|
result = parseEncodedData(index, len);
|
||
|
}
|
||
|
else {
|
||
|
// Restricted alphabet (C.20.3.3)
|
||
|
result = parseRestrictedAlphabet(index, len);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
len = parseNonEmptyOctetString7Length();
|
||
|
if (dataEnd - dataP < static_cast<ptrdiff_t>(len)) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
if (b & 0x04) {
|
||
|
// UTF-16 (C.20.3.2)
|
||
|
if (len & 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
result = FIStringValue::create(parseUTF16String(dataP, len));
|
||
|
}
|
||
|
else {
|
||
|
// UTF-8 (C.20.3.1)
|
||
|
result = FIStringValue::create(parseUTF8String(dataP, len));
|
||
|
}
|
||
|
}
|
||
|
dataP += len;
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
const std::string &parseIdentifyingStringOrIndex(std::vector<std::string> &stringTable) { // C.13
|
||
|
if (dataEnd - dataP < 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
uint8_t b = *dataP;
|
||
|
if (b & 0x80) {
|
||
|
// We have an index (C.13.4)
|
||
|
size_t index = parseInt2();
|
||
|
if (index >= stringTable.size()) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
return stringTable[index];
|
||
|
}
|
||
|
else {
|
||
|
// We have a string (C.13.3)
|
||
|
stringTable.push_back(parseNonEmptyOctetString2());
|
||
|
return stringTable.back();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
QName parseNameSurrogate() { // C.16
|
||
|
if (dataEnd - dataP < 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
uint8_t b = *dataP++;
|
||
|
if (b & 0xfc) { // Padding '000000' C.2.5.5
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
QName result;
|
||
|
size_t index;
|
||
|
if (b & 0x02) { // prefix (C.16.3)
|
||
|
if ((dataEnd - dataP < 1) || (*dataP & 0x80)) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
index = parseInt2();
|
||
|
if (index >= vocabulary.prefixTable.size()) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
result.prefix = vocabulary.prefixTable[index];
|
||
|
}
|
||
|
if (b & 0x01) { // namespace-name (C.16.4)
|
||
|
if ((dataEnd - dataP < 1) || (*dataP & 0x80)) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
index = parseInt2();
|
||
|
if (index >= vocabulary.namespaceNameTable.size()) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
result.uri = vocabulary.namespaceNameTable[index];
|
||
|
}
|
||
|
// local-name
|
||
|
if ((dataEnd - dataP < 1) || (*dataP & 0x80)) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
index = parseInt2();
|
||
|
if (index >= vocabulary.localNameTable.size()) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
result.name = vocabulary.localNameTable[index];
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
const QName &parseQualifiedNameOrIndex2(std::vector<QName> &qNameTable) { // C.17
|
||
|
uint8_t b = *dataP;
|
||
|
if ((b & 0x7c) == 0x78) { // x11110..
|
||
|
// We have a literal (C.17.3)
|
||
|
++dataP;
|
||
|
QName result;
|
||
|
// prefix (C.17.3.1)
|
||
|
result.prefix = b & 0x02 ? parseIdentifyingStringOrIndex(vocabulary.prefixTable) : std::string();
|
||
|
// namespace-name (C.17.3.1)
|
||
|
result.uri = b & 0x01 ? parseIdentifyingStringOrIndex(vocabulary.namespaceNameTable) : std::string();
|
||
|
// local-name
|
||
|
result.name = parseIdentifyingStringOrIndex(vocabulary.localNameTable);
|
||
|
qNameTable.push_back(result);
|
||
|
return qNameTable.back();
|
||
|
}
|
||
|
else {
|
||
|
// We have an index (C.17.4)
|
||
|
size_t index = parseInt2();
|
||
|
if (index >= qNameTable.size()) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
return qNameTable[index];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const QName &parseQualifiedNameOrIndex3(std::vector<QName> &qNameTable) { // C.18
|
||
|
uint8_t b = *dataP;
|
||
|
if ((b & 0x3c) == 0x3c) { // xx1111..
|
||
|
// We have a literal (C.18.3)
|
||
|
++dataP;
|
||
|
QName result;
|
||
|
// prefix (C.18.3.1)
|
||
|
result.prefix = b & 0x02 ? parseIdentifyingStringOrIndex(vocabulary.prefixTable) : std::string();
|
||
|
// namespace-name (C.18.3.1)
|
||
|
result.uri = b & 0x01 ? parseIdentifyingStringOrIndex(vocabulary.namespaceNameTable) : std::string();
|
||
|
// local-name
|
||
|
result.name = parseIdentifyingStringOrIndex(vocabulary.localNameTable);
|
||
|
qNameTable.push_back(result);
|
||
|
return qNameTable.back();
|
||
|
}
|
||
|
else {
|
||
|
// We have an index (C.18.4)
|
||
|
size_t index = parseInt3();
|
||
|
if (index >= qNameTable.size()) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
return qNameTable[index];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
std::shared_ptr<const FIValue> parseNonIdentifyingStringOrIndex1(std::vector<std::shared_ptr<const FIValue>> &valueTable) { // C.14
|
||
|
uint8_t b = *dataP;
|
||
|
if (b == 0xff) { // C.26.2
|
||
|
// empty string
|
||
|
++dataP;
|
||
|
return EmptyFIString;
|
||
|
}
|
||
|
else if (b & 0x80) { // C.14.4
|
||
|
// We have an index
|
||
|
size_t index = parseInt2();
|
||
|
if (index >= valueTable.size()) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
return valueTable[index];
|
||
|
}
|
||
|
else { // C.14.3
|
||
|
// We have a literal
|
||
|
std::shared_ptr<const FIValue> result = parseEncodedCharacterString3();
|
||
|
if (b & 0x40) { // C.14.3.1
|
||
|
valueTable.push_back(result);
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
std::shared_ptr<const FIValue> parseNonIdentifyingStringOrIndex3(std::vector<std::shared_ptr<const FIValue>> &valueTable) { // C.15
|
||
|
uint8_t b = *dataP;
|
||
|
if (b & 0x20) { // C.15.4
|
||
|
// We have an index
|
||
|
size_t index = parseInt4();
|
||
|
if (index >= valueTable.size()) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
return valueTable[index];
|
||
|
}
|
||
|
else { // C.15.3
|
||
|
// We have a literal
|
||
|
std::shared_ptr<const FIValue> result = parseEncodedCharacterString5();
|
||
|
if (b & 0x10) { // C.15.3.1
|
||
|
valueTable.push_back(result);
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void parseElement() {
|
||
|
// C.3
|
||
|
|
||
|
attributes.clear();
|
||
|
|
||
|
uint8_t b = *dataP;
|
||
|
bool hasAttributes = (b & 0x40) != 0; // C.3.3
|
||
|
if ((b & 0x3f) == 0x38) { // C.3.4.1
|
||
|
// Parse namespaces
|
||
|
++dataP;
|
||
|
for (;;) {
|
||
|
if (dataEnd - dataP < 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
b = *dataP++;
|
||
|
if (b == 0xf0) { // C.3.4.3
|
||
|
break;
|
||
|
}
|
||
|
if ((b & 0xfc) != 0xcc) { // C.3.4.2
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
// C.12
|
||
|
Attribute attr;
|
||
|
attr.qname.prefix = "xmlns";
|
||
|
attr.qname.name = b & 0x02 ? parseIdentifyingStringOrIndex(vocabulary.prefixTable) : std::string();
|
||
|
attr.qname.uri = b & 0x01 ? parseIdentifyingStringOrIndex(vocabulary.namespaceNameTable) : std::string();
|
||
|
attr.name = attr.qname.name.empty() ? "xmlns" : "xmlns:" + attr.qname.name;
|
||
|
attr.value = FIStringValue::create(std::string(attr.qname.uri));
|
||
|
attributes.push_back(attr);
|
||
|
}
|
||
|
if ((dataEnd - dataP < 1) || (*dataP & 0xc0)) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Parse Element name (C.3.5)
|
||
|
const QName &elemName = parseQualifiedNameOrIndex3(vocabulary.elementNameTable);
|
||
|
nodeName = elemName.prefix.empty() ? elemName.name : elemName.prefix + ':' + elemName.name;
|
||
|
|
||
|
if (hasAttributes) {
|
||
|
for (;;) {
|
||
|
if (dataEnd - dataP < 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
b = *dataP;
|
||
|
if (b < 0x80) { // C.3.6.1
|
||
|
// C.4
|
||
|
Attribute attr;
|
||
|
attr.qname = parseQualifiedNameOrIndex2(vocabulary.attributeNameTable);
|
||
|
attr.name = attr.qname.prefix.empty() ? attr.qname.name : attr.qname.prefix + ':' + attr.qname.name;
|
||
|
if (dataEnd - dataP < 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
attr.value = parseNonIdentifyingStringOrIndex1(vocabulary.attributeValueTable);
|
||
|
attributes.push_back(attr);
|
||
|
}
|
||
|
else {
|
||
|
if ((b & 0xf0) != 0xf0) { // C.3.6.2
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
emptyElement = b == 0xff; // C.3.6.2, C.3.8
|
||
|
++dataP;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if (dataEnd - dataP < 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
b = *dataP;
|
||
|
switch (b) {
|
||
|
case 0xff:
|
||
|
terminatorPending = true;
|
||
|
// Intentionally fall through
|
||
|
case 0xf0:
|
||
|
emptyElement = true;
|
||
|
++dataP;
|
||
|
break;
|
||
|
default:
|
||
|
emptyElement = false;
|
||
|
}
|
||
|
}
|
||
|
if (!emptyElement) {
|
||
|
elementStack.push(nodeName);
|
||
|
}
|
||
|
|
||
|
currentNodeType = irr::io::EXN_ELEMENT;
|
||
|
}
|
||
|
|
||
|
void parseHeader() {
|
||
|
// Parse header (C.1.3)
|
||
|
size_t magicSize = parseMagic(dataP, dataEnd);
|
||
|
if (!magicSize) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
dataP += magicSize;
|
||
|
// C.2.3
|
||
|
if (dataEnd - dataP < 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
uint8_t b = *dataP++;
|
||
|
if (b & 0x40) {
|
||
|
// Parse additional data (C.2.4)
|
||
|
size_t len = parseSequenceLen();
|
||
|
for (size_t i = 0; i < len; ++i) {
|
||
|
if (dataEnd - dataP < 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
/*std::string id =*/ parseNonEmptyOctetString2();
|
||
|
if (dataEnd - dataP < 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
/*std::string data =*/ parseNonEmptyOctetString2();
|
||
|
}
|
||
|
}
|
||
|
if (b & 0x20) {
|
||
|
// Parse initial vocabulary (C.2.5)
|
||
|
if (dataEnd - dataP < 2) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
uint16_t b1 = (dataP[0] << 8) | dataP[1];
|
||
|
dataP += 2;
|
||
|
if (b1 & 0x1000) {
|
||
|
// External vocabulary (C.2.5.2)
|
||
|
if (dataEnd - dataP < 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
std::string uri = parseNonEmptyOctetString2();
|
||
|
auto it = vocabularyMap.find(uri);
|
||
|
if (it == vocabularyMap.end()) {
|
||
|
throw DeadlyImportError("Unknown vocabulary " + uri);
|
||
|
}
|
||
|
const FIVocabulary *externalVocabulary = it->second;
|
||
|
if (externalVocabulary->restrictedAlphabetTable) {
|
||
|
std::copy(externalVocabulary->restrictedAlphabetTable, externalVocabulary->restrictedAlphabetTable + externalVocabulary->restrictedAlphabetTableSize, std::back_inserter(vocabulary.restrictedAlphabetTable));
|
||
|
}
|
||
|
if (externalVocabulary->encodingAlgorithmTable) {
|
||
|
std::copy(externalVocabulary->encodingAlgorithmTable, externalVocabulary->encodingAlgorithmTable + externalVocabulary->encodingAlgorithmTableSize, std::back_inserter(vocabulary.encodingAlgorithmTable));
|
||
|
}
|
||
|
if (externalVocabulary->prefixTable) {
|
||
|
std::copy(externalVocabulary->prefixTable, externalVocabulary->prefixTable + externalVocabulary->prefixTableSize, std::back_inserter(vocabulary.prefixTable));
|
||
|
}
|
||
|
if (externalVocabulary->namespaceNameTable) {
|
||
|
std::copy(externalVocabulary->namespaceNameTable, externalVocabulary->namespaceNameTable + externalVocabulary->namespaceNameTableSize, std::back_inserter(vocabulary.namespaceNameTable));
|
||
|
}
|
||
|
if (externalVocabulary->localNameTable) {
|
||
|
std::copy(externalVocabulary->localNameTable, externalVocabulary->localNameTable + externalVocabulary->localNameTableSize, std::back_inserter(vocabulary.localNameTable));
|
||
|
}
|
||
|
if (externalVocabulary->otherNCNameTable) {
|
||
|
std::copy(externalVocabulary->otherNCNameTable, externalVocabulary->otherNCNameTable + externalVocabulary->otherNCNameTableSize, std::back_inserter(vocabulary.otherNCNameTable));
|
||
|
}
|
||
|
if (externalVocabulary->otherURITable) {
|
||
|
std::copy(externalVocabulary->otherURITable, externalVocabulary->otherURITable + externalVocabulary->otherURITableSize, std::back_inserter(vocabulary.otherURITable));
|
||
|
}
|
||
|
if (externalVocabulary->attributeValueTable) {
|
||
|
std::copy(externalVocabulary->attributeValueTable, externalVocabulary->attributeValueTable + externalVocabulary->attributeValueTableSize, std::back_inserter(vocabulary.attributeValueTable));
|
||
|
}
|
||
|
if (externalVocabulary->charactersTable) {
|
||
|
std::copy(externalVocabulary->charactersTable, externalVocabulary->charactersTable + externalVocabulary->charactersTableSize, std::back_inserter(vocabulary.charactersTable));
|
||
|
}
|
||
|
if (externalVocabulary->otherStringTable) {
|
||
|
std::copy(externalVocabulary->otherStringTable, externalVocabulary->otherStringTable + externalVocabulary->otherStringTableSize, std::back_inserter(vocabulary.otherStringTable));
|
||
|
}
|
||
|
if (externalVocabulary->elementNameTable) {
|
||
|
std::copy(externalVocabulary->elementNameTable, externalVocabulary->elementNameTable + externalVocabulary->elementNameTableSize, std::back_inserter(vocabulary.elementNameTable));
|
||
|
}
|
||
|
if (externalVocabulary->attributeNameTable) {
|
||
|
std::copy(externalVocabulary->attributeNameTable, externalVocabulary->attributeNameTable + externalVocabulary->attributeNameTableSize, std::back_inserter(vocabulary.attributeNameTable));
|
||
|
}
|
||
|
}
|
||
|
if (b1 & 0x0800) {
|
||
|
// Parse restricted alphabets (C.2.5.3)
|
||
|
for (size_t len = parseSequenceLen(); len > 0; --len) {
|
||
|
if (dataEnd - dataP < 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
vocabulary.restrictedAlphabetTable.push_back(parseNonEmptyOctetString2());
|
||
|
}
|
||
|
}
|
||
|
if (b1 & 0x0400) {
|
||
|
// Parse encoding algorithms (C.2.5.3)
|
||
|
for (size_t len = parseSequenceLen(); len > 0; --len) {
|
||
|
if (dataEnd - dataP < 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
vocabulary.encodingAlgorithmTable.push_back(parseNonEmptyOctetString2());
|
||
|
}
|
||
|
}
|
||
|
if (b1 & 0x0200) {
|
||
|
// Parse prefixes (C.2.5.3)
|
||
|
for (size_t len = parseSequenceLen(); len > 0; --len) {
|
||
|
if (dataEnd - dataP < 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
vocabulary.prefixTable.push_back(parseNonEmptyOctetString2());
|
||
|
}
|
||
|
}
|
||
|
if (b1 & 0x0100) {
|
||
|
// Parse namespace names (C.2.5.3)
|
||
|
for (size_t len = parseSequenceLen(); len > 0; --len) {
|
||
|
if (dataEnd - dataP < 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
vocabulary.namespaceNameTable.push_back(parseNonEmptyOctetString2());
|
||
|
}
|
||
|
}
|
||
|
if (b1 & 0x0080) {
|
||
|
// Parse local names (C.2.5.3)
|
||
|
for (size_t len = parseSequenceLen(); len > 0; --len) {
|
||
|
if (dataEnd - dataP < 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
vocabulary.localNameTable.push_back(parseNonEmptyOctetString2());
|
||
|
}
|
||
|
}
|
||
|
if (b1 & 0x0040) {
|
||
|
// Parse other ncnames (C.2.5.3)
|
||
|
for (size_t len = parseSequenceLen(); len > 0; --len) {
|
||
|
if (dataEnd - dataP < 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
vocabulary.otherNCNameTable.push_back(parseNonEmptyOctetString2());
|
||
|
}
|
||
|
}
|
||
|
if (b1 & 0x0020) {
|
||
|
// Parse other uris (C.2.5.3)
|
||
|
for (size_t len = parseSequenceLen(); len > 0; --len) {
|
||
|
if (dataEnd - dataP < 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
vocabulary.otherURITable.push_back(parseNonEmptyOctetString2());
|
||
|
}
|
||
|
}
|
||
|
if (b1 & 0x0010) {
|
||
|
// Parse attribute values (C.2.5.4)
|
||
|
for (size_t len = parseSequenceLen(); len > 0; --len) {
|
||
|
if (dataEnd - dataP < 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
vocabulary.attributeValueTable.push_back(parseEncodedCharacterString3());
|
||
|
}
|
||
|
}
|
||
|
if (b1 & 0x0008) {
|
||
|
// Parse content character chunks (C.2.5.4)
|
||
|
for (size_t len = parseSequenceLen(); len > 0; --len) {
|
||
|
if (dataEnd - dataP < 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
vocabulary.charactersTable.push_back(parseEncodedCharacterString3());
|
||
|
}
|
||
|
}
|
||
|
if (b1 & 0x0004) {
|
||
|
// Parse other strings (C.2.5.4)
|
||
|
for (size_t len = parseSequenceLen(); len > 0; --len) {
|
||
|
if (dataEnd - dataP < 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
vocabulary.otherStringTable.push_back(parseEncodedCharacterString3());
|
||
|
}
|
||
|
}
|
||
|
if (b1 & 0x0002) {
|
||
|
// Parse element name surrogates (C.2.5.5)
|
||
|
for (size_t len = parseSequenceLen(); len > 0; --len) {
|
||
|
vocabulary.elementNameTable.push_back(parseNameSurrogate());
|
||
|
}
|
||
|
}
|
||
|
if (b1 & 0x0001) {
|
||
|
// Parse attribute name surrogates (C.2.5.5)
|
||
|
for (size_t len = parseSequenceLen(); len > 0; --len) {
|
||
|
vocabulary.attributeNameTable.push_back(parseNameSurrogate());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (b & 0x10) {
|
||
|
// Parse notations (C.2.6)
|
||
|
for (;;) {
|
||
|
if (dataEnd - dataP < 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
uint8_t b1 = *dataP++;
|
||
|
if (b1 == 0xf0) {
|
||
|
break;
|
||
|
}
|
||
|
if ((b1 & 0xfc) != 0xc0) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
/* C.11 */
|
||
|
/*const std::string &name =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable);
|
||
|
if (b1 & 0x02) {
|
||
|
/*const std::string &systemId =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
|
||
|
}
|
||
|
if (b1 & 0x01) {
|
||
|
/*const std::string &publicId =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (b & 0x08) {
|
||
|
// Parse unparsed entities (C.2.7)
|
||
|
for (;;) {
|
||
|
if (dataEnd - dataP < 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
uint8_t b1 = *dataP++;
|
||
|
if (b1 == 0xf0) {
|
||
|
break;
|
||
|
}
|
||
|
if ((b1 & 0xfe) != 0xd0) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
/* C.10 */
|
||
|
/*const std::string &name =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable);
|
||
|
/*const std::string &systemId =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
|
||
|
if (b1 & 0x01) {
|
||
|
/*const std::string &publicId =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
|
||
|
}
|
||
|
/*const std::string ¬ationName =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable);
|
||
|
}
|
||
|
}
|
||
|
if (b & 0x04) {
|
||
|
// Parse character encoding scheme (C.2.8)
|
||
|
if (dataEnd - dataP < 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
/*std::string characterEncodingScheme =*/ parseNonEmptyOctetString2();
|
||
|
}
|
||
|
if (b & 0x02) {
|
||
|
// Parse standalone flag (C.2.9)
|
||
|
if (dataEnd - dataP < 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
uint8_t b1 = *dataP++;
|
||
|
if (b1 & 0xfe) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
//bool standalone = b1 & 0x01;
|
||
|
}
|
||
|
if (b & 0x01) {
|
||
|
// Parse version (C.2.10)
|
||
|
if (dataEnd - dataP < 1) {
|
||
|
throw DeadlyImportError(parseErrorMessage);
|
||
|
}
|
||
|
/*std::shared_ptr<const FIValue> version =*/ parseNonIdentifyingStringOrIndex1(vocabulary.otherStringTable);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
std::unique_ptr<uint8_t[]> data;
|
||
|
uint8_t *dataP, *dataEnd;
|
||
|
irr::io::EXML_NODE currentNodeType;
|
||
|
bool emptyElement;
|
||
|
bool headerPending;
|
||
|
bool terminatorPending;
|
||
|
Vocabulary vocabulary;
|
||
|
std::vector<Attribute> attributes;
|
||
|
std::stack<std::string> elementStack;
|
||
|
std::string nodeName;
|
||
|
std::map<std::string, std::unique_ptr<FIDecoder>> decoderMap;
|
||
|
std::map<std::string, const FIVocabulary*> vocabularyMap;
|
||
|
|
||
|
static const std::string EmptyString;
|
||
|
static std::shared_ptr<const FIValue> EmptyFIString;
|
||
|
|
||
|
static FIHexDecoder hexDecoder;
|
||
|
static FIBase64Decoder base64Decoder;
|
||
|
static FIShortDecoder shortDecoder;
|
||
|
static FIIntDecoder intDecoder;
|
||
|
static FILongDecoder longDecoder;
|
||
|
static FIBoolDecoder boolDecoder;
|
||
|
static FIFloatDecoder floatDecoder;
|
||
|
static FIDoubleDecoder doubleDecoder;
|
||
|
static FIUUIDDecoder uuidDecoder;
|
||
|
static FICDATADecoder cdataDecoder;
|
||
|
static FIDecoder *defaultDecoder[32];
|
||
|
};
|
||
|
|
||
|
const std::string CFIReaderImpl::EmptyString;
|
||
|
std::shared_ptr<const FIValue> CFIReaderImpl::EmptyFIString = FIStringValue::create(std::string());
|
||
|
|
||
|
FIHexDecoder CFIReaderImpl::hexDecoder;
|
||
|
FIBase64Decoder CFIReaderImpl::base64Decoder;
|
||
|
FIShortDecoder CFIReaderImpl::shortDecoder;
|
||
|
FIIntDecoder CFIReaderImpl::intDecoder;
|
||
|
FILongDecoder CFIReaderImpl::longDecoder;
|
||
|
FIBoolDecoder CFIReaderImpl::boolDecoder;
|
||
|
FIFloatDecoder CFIReaderImpl::floatDecoder;
|
||
|
FIDoubleDecoder CFIReaderImpl::doubleDecoder;
|
||
|
FIUUIDDecoder CFIReaderImpl::uuidDecoder;
|
||
|
FICDATADecoder CFIReaderImpl::cdataDecoder;
|
||
|
|
||
|
FIDecoder *CFIReaderImpl::defaultDecoder[32] = {
|
||
|
&hexDecoder,
|
||
|
&base64Decoder,
|
||
|
&shortDecoder,
|
||
|
&intDecoder,
|
||
|
&longDecoder,
|
||
|
&boolDecoder,
|
||
|
&floatDecoder,
|
||
|
&doubleDecoder,
|
||
|
&uuidDecoder,
|
||
|
&cdataDecoder
|
||
|
};
|
||
|
|
||
|
class CXMLReaderImpl : public FIReader
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
//! Constructor
|
||
|
CXMLReaderImpl(std::unique_ptr<irr::io::IIrrXMLReader<char, irr::io::IXMLBase>> reader_)
|
||
|
: reader(std::move(reader_))
|
||
|
{}
|
||
|
|
||
|
virtual ~CXMLReaderImpl() {}
|
||
|
|
||
|
virtual bool read() /*override*/ {
|
||
|
return reader->read();
|
||
|
}
|
||
|
|
||
|
virtual irr::io::EXML_NODE getNodeType() const /*override*/ {
|
||
|
return reader->getNodeType();
|
||
|
}
|
||
|
|
||
|
virtual int getAttributeCount() const /*override*/ {
|
||
|
return reader->getAttributeCount();
|
||
|
}
|
||
|
|
||
|
virtual const char* getAttributeName(int idx) const /*override*/ {
|
||
|
return reader->getAttributeName(idx);
|
||
|
}
|
||
|
|
||
|
virtual const char* getAttributeValue(int idx) const /*override*/ {
|
||
|
return reader->getAttributeValue(idx);
|
||
|
}
|
||
|
|
||
|
virtual const char* getAttributeValue(const char* name) const /*override*/ {
|
||
|
return reader->getAttributeValue(name);
|
||
|
}
|
||
|
|
||
|
virtual const char* getAttributeValueSafe(const char* name) const /*override*/ {
|
||
|
return reader->getAttributeValueSafe(name);
|
||
|
}
|
||
|
|
||
|
virtual int getAttributeValueAsInt(const char* name) const /*override*/ {
|
||
|
return reader->getAttributeValueAsInt(name);
|
||
|
}
|
||
|
|
||
|
virtual int getAttributeValueAsInt(int idx) const /*override*/ {
|
||
|
return reader->getAttributeValueAsInt(idx);
|
||
|
}
|
||
|
|
||
|
virtual float getAttributeValueAsFloat(const char* name) const /*override*/ {
|
||
|
return reader->getAttributeValueAsFloat(name);
|
||
|
}
|
||
|
|
||
|
virtual float getAttributeValueAsFloat(int idx) const /*override*/ {
|
||
|
return reader->getAttributeValueAsFloat(idx);
|
||
|
}
|
||
|
|
||
|
virtual const char* getNodeName() const /*override*/ {
|
||
|
return reader->getNodeName();
|
||
|
}
|
||
|
|
||
|
virtual const char* getNodeData() const /*override*/ {
|
||
|
return reader->getNodeData();
|
||
|
}
|
||
|
|
||
|
virtual bool isEmptyElement() const /*override*/ {
|
||
|
return reader->isEmptyElement();
|
||
|
}
|
||
|
|
||
|
virtual irr::io::ETEXT_FORMAT getSourceFormat() const /*override*/ {
|
||
|
return reader->getSourceFormat();
|
||
|
}
|
||
|
|
||
|
virtual irr::io::ETEXT_FORMAT getParserFormat() const /*override*/ {
|
||
|
return reader->getParserFormat();
|
||
|
}
|
||
|
|
||
|
virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(int /*idx*/) const /*override*/ {
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(const char* /*name*/) const /*override*/ {
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
virtual void registerDecoder(const std::string & /*algorithmUri*/, std::unique_ptr<FIDecoder> /*decoder*/) /*override*/ {}
|
||
|
|
||
|
|
||
|
virtual void registerVocabulary(const std::string &/*vocabularyUri*/, const FIVocabulary * /*vocabulary*/) /*override*/ {}
|
||
|
|
||
|
private:
|
||
|
|
||
|
std::unique_ptr<irr::io::IIrrXMLReader<char, irr::io::IXMLBase>> reader;
|
||
|
};
|
||
|
|
||
|
static std::unique_ptr<uint8_t[]> readFile(IOStream *stream, size_t &size, bool &isFI) {
|
||
|
size = stream->FileSize();
|
||
|
std::unique_ptr<uint8_t[]> data = std::unique_ptr<uint8_t[]>(new uint8_t[size]);
|
||
|
if (stream->Read(data.get(), size, 1) != 1) {
|
||
|
size = 0;
|
||
|
data.reset();
|
||
|
}
|
||
|
isFI = parseMagic(data.get(), data.get() + size) > 0;
|
||
|
return data;
|
||
|
}
|
||
|
|
||
|
std::unique_ptr<FIReader> FIReader::create(IOStream *stream)
|
||
|
{
|
||
|
size_t size;
|
||
|
bool isFI;
|
||
|
auto data = readFile(stream, size, isFI);
|
||
|
if (isFI) {
|
||
|
return std::unique_ptr<FIReader>(new CFIReaderImpl(std::move(data), size));
|
||
|
}
|
||
|
else {
|
||
|
auto memios = std::unique_ptr<MemoryIOStream>(new MemoryIOStream(data.release(), size, true));
|
||
|
auto callback = std::unique_ptr<CIrrXML_IOStreamReader>(new CIrrXML_IOStreamReader(memios.get()));
|
||
|
return std::unique_ptr<FIReader>(new CXMLReaderImpl(std::unique_ptr<irr::io::IIrrXMLReader<char, irr::io::IXMLBase>>(createIrrXMLReader(callback.get()))));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}// namespace Assimp
|
||
|
|
||
|
#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER
|