X3D importer: Implemented support for binary X3D files
authorPatrick Dähne <pdaehne@gmail.com>
Tue, 11 Jul 2017 17:28:40 +0000 (19:28 +0200)
committerPatrick Dähne <pdaehne@gmail.com>
Tue, 11 Jul 2017 17:28:40 +0000 (19:28 +0200)
code/CMakeLists.txt
code/FIReader.cpp [new file with mode: 0755]
code/FIReader.hpp [new file with mode: 0644]
code/X3DImporter.cpp [changed mode: 0644->0755]
code/X3DImporter.hpp
code/X3DImporter_Geometry3D.cpp
code/X3DImporter_Metadata.cpp
code/X3DImporter_Node.hpp
code/X3DImporter_Rendering.cpp
code/X3DVocabulary.cpp [new file with mode: 0644]

index 8249746..0477f5f 100644 (file)
@@ -647,6 +647,9 @@ ADD_ASSIMP_IMPORTER(X3D
   X3DImporter_Rendering.cpp
   X3DImporter_Shape.cpp
   X3DImporter_Texturing.cpp
+  FIReader.hpp
+  FIReader.cpp
+  X3DVocabulary.cpp
 )
 
 ADD_ASSIMP_IMPORTER( GLTF
diff --git a/code/FIReader.cpp b/code/FIReader.cpp
new file mode 100755 (executable)
index 0000000..617c87b
--- /dev/null
@@ -0,0 +1,1818 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2017, 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 "Exceptional.h"
+#include <assimp/IOStream.hpp>
+#include <assimp/types.h>
+#include "MemoryIOWrapper.h"
+#include "irrXMLWrapper.h"
+#include "../contrib/utf8cpp/source/utf8.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"
+        {
+            auto xmlDeclarationsLength = sizeof(xmlDeclarations) / sizeof(xmlDeclarations[0]);
+            for (auto 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, c2;
+            int imod3 = 0;
+            auto valueSize = value.size();
+            for (auto 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');
+            auto valueSize = value.size();
+            for (auto 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];
+            value.push_back(*(float*)&v);
+            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;
+            value.push_back(*(double*)&v);
+            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 stoi(attr->value->toString());
+    }
+
+    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 stoi(attributes[idx].value->toString());
+    }
+
+    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 stof(attr->value->toString());
+    }
+
+    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 stof(attributes[idx].value->toString());
+    }
+
+    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 " + std::to_string(index));
+            }
+            return decoder->decode(dataP, len);
+        }
+        else {
+            if (index - 32 >= vocabulary.encodingAlgorithmTable.size()) {
+                throw DeadlyImportError("Invalid encoding algorithm index " + std::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 " + std::to_string(index));
+            }
+        }
+        else {
+            if (index - 16 >= vocabulary.restrictedAlphabetTable.size()) {
+                throw DeadlyImportError("Invalid restricted alphabet index " + std::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 " + std::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;
+                size_t charIndex = (bits >> bitsAvail) & mask;
+                if (charIndex < alphabetLength) {
+                    s.push_back(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 &notationName =*/ 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
diff --git a/code/FIReader.hpp b/code/FIReader.hpp
new file mode 100644 (file)
index 0000000..05f13ec
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2017, 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.hpp
+/// \brief  Reader for Fast Infoset encoded binary XML files.
+/// \date   2017
+/// \author Patrick Daehne
+
+#ifndef INCLUDED_AI_FI_READER_H
+#define INCLUDED_AI_FI_READER_H
+
+#include <irrXML.h>
+#include <string>
+#include <vector>
+#include <cstdint>
+
+namespace Assimp {
+
+struct FIValue {
+    virtual const std::string &toString() const = 0;
+};
+
+struct FIStringValue: public FIValue {
+    std::string value;
+    static std::shared_ptr<FIStringValue> create(std::string &&value);
+};
+
+struct FIByteValue: public FIValue {
+    std::vector<uint8_t> value;
+};
+
+struct FIHexValue: public FIByteValue {
+    static std::shared_ptr<FIHexValue> create(std::vector<uint8_t> &&value);
+};
+
+struct FIBase64Value: public FIByteValue {
+    static std::shared_ptr<FIBase64Value> create(std::vector<uint8_t> &&value);
+};
+
+struct FIShortValue: public FIValue {
+    std::vector<int16_t> value;
+    static std::shared_ptr<FIShortValue> create(std::vector<int16_t> &&value);
+};
+
+struct FIIntValue: public FIValue {
+    std::vector<int32_t> value;
+    static std::shared_ptr<FIIntValue> create(std::vector<int32_t> &&value);
+};
+
+struct FILongValue: public FIValue {
+    std::vector<int64_t> value;
+    static std::shared_ptr<FILongValue> create(std::vector<int64_t> &&value);
+};
+
+struct FIBoolValue: public FIValue {
+    std::vector<bool> value;
+    static std::shared_ptr<FIBoolValue> create(std::vector<bool> &&value);
+};
+
+struct FIFloatValue: public FIValue {
+    std::vector<float> value;
+    static std::shared_ptr<FIFloatValue> create(std::vector<float> &&value);
+};
+
+struct FIDoubleValue: public FIValue {
+    std::vector<double> value;
+    static std::shared_ptr<FIDoubleValue> create(std::vector<double> &&value);
+};
+
+struct FIUUIDValue: public FIByteValue {
+    static std::shared_ptr<FIUUIDValue> create(std::vector<uint8_t> &&value);
+};
+
+struct FICDATAValue: public FIStringValue {
+    static std::shared_ptr<FICDATAValue> create(std::string &&value);
+};
+
+struct FIDecoder {
+    virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) = 0;
+};
+
+struct FIQName {
+    const char *name;
+    const char *prefix;
+    const char *uri;
+};
+
+struct FIVocabulary {
+    const char **restrictedAlphabetTable;
+    size_t restrictedAlphabetTableSize;
+    const char **encodingAlgorithmTable;
+    size_t encodingAlgorithmTableSize;
+    const char **prefixTable;
+    size_t prefixTableSize;
+    const char **namespaceNameTable;
+    size_t namespaceNameTableSize;
+    const char **localNameTable;
+    size_t localNameTableSize;
+    const char **otherNCNameTable;
+    size_t otherNCNameTableSize;
+    const char **otherURITable;
+    size_t otherURITableSize;
+    const std::shared_ptr<const FIValue> *attributeValueTable;
+    size_t attributeValueTableSize;
+    const std::shared_ptr<const FIValue> *charactersTable;
+    size_t charactersTableSize;
+    const std::shared_ptr<const FIValue> *otherStringTable;
+    size_t otherStringTableSize;
+    const FIQName *elementNameTable;
+    size_t elementNameTableSize;
+    const FIQName *attributeNameTable;
+    size_t attributeNameTableSize;
+};
+
+class IOStream;
+
+class FIReader: public irr::io::IIrrXMLReader<char, irr::io::IXMLBase> {
+public:
+
+    virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(int idx) const = 0;
+
+    virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(const char *name) const = 0;
+
+    virtual void registerDecoder(const std::string &algorithmUri, std::unique_ptr<FIDecoder> decoder) = 0;
+
+    virtual void registerVocabulary(const std::string &vocabularyUri, const FIVocabulary *vocabulary) = 0;
+
+    static std::unique_ptr<FIReader> create(IOStream *stream);
+
+};// class IFIReader
+
+}// namespace Assimp
+
+#endif // INCLUDED_AI_FI_READER_H
old mode 100644 (file)
new mode 100755 (executable)
index 2edb1b0..c714532
@@ -52,6 +52,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 // Header files, Assimp.
 #include <assimp/DefaultIOSystem.h>
 #include "fast_atof.h"
+#include "FIReader.hpp"
 
 // Header files, stdlib.
 #include <memory>
@@ -66,15 +67,16 @@ const aiImporterDesc X3DImporter::Description = {
        "smalcom",
        "",
        "See documentation in source code. Chapter: Limitations.",
-       aiImporterFlags_SupportTextFlavour | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental,
+       aiImporterFlags_SupportTextFlavour | aiImporterFlags_SupportBinaryFlavour | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental,
        0,
        0,
        0,
        0,
-       "x3d"
+       "x3d x3db"
 };
 
-const std::string X3DImporter::whitespace(" ,\t\n\r");
+const std::regex X3DImporter::pattern_nws(R"([^, \t\r\n]+)");
+const std::regex X3DImporter::pattern_true(R"(^\s*(?:true|1)\s*$)", std::regex::icase);
 
 X3DImporter::X3DImporter()
 : NodeElement_Cur( nullptr )
@@ -83,7 +85,6 @@ X3DImporter::X3DImporter()
 }
 
 X3DImporter::~X3DImporter() {
-    delete mReader;
     // Clear() is accounting if data already is deleted. So, just check again if all data is deleted.
     Clear();
 }
@@ -370,38 +371,65 @@ bool X3DImporter::XML_SearchNode(const std::string& pNodeName)
 
 bool X3DImporter::XML_ReadNode_GetAttrVal_AsBool(const int pAttrIdx)
 {
-std::string val(mReader->getAttributeValue(pAttrIdx));
-
-       if(val == "false")
-               return false;
-       else if(val == "true")
-               return true;
-       else
-               throw DeadlyImportError("Bool attribute value can contain \"false\" or \"true\" not the \"" + val + "\"");
+    auto boolValue = std::dynamic_pointer_cast<const FIBoolValue>(mReader->getAttributeEncodedValue(pAttrIdx));
+    if (boolValue) {
+        if (boolValue->value.size() == 1) {
+            return boolValue->value.front();
+        }
+        throw DeadlyImportError("Invalid bool value");
+    }
+    else {
+        std::string val(mReader->getAttributeValue(pAttrIdx));
+
+        if(val == "false")
+            return false;
+        else if(val == "true")
+            return true;
+        else
+            throw DeadlyImportError("Bool attribute value can contain \"false\" or \"true\" not the \"" + val + "\"");
+    }
 }
 
 float X3DImporter::XML_ReadNode_GetAttrVal_AsFloat(const int pAttrIdx)
 {
-    std::string val;
-    float tvalf;
+    auto floatValue = std::dynamic_pointer_cast<const FIFloatValue>(mReader->getAttributeEncodedValue(pAttrIdx));
+    if (floatValue) {
+        if (floatValue->value.size() == 1) {
+            return floatValue->value.front();
+        }
+        throw DeadlyImportError("Invalid float value");
+    }
+    else {
+        std::string val;
+        float tvalf;
 
-       ParseHelper_FixTruncatedFloatString(mReader->getAttributeValue(pAttrIdx), val);
-       fast_atoreal_move(val.c_str(), tvalf, false);
+        ParseHelper_FixTruncatedFloatString(mReader->getAttributeValue(pAttrIdx), val);
+        fast_atoreal_move(val.c_str(), tvalf, false);
 
-       return tvalf;
+        return tvalf;
+    }
 }
 
 int32_t X3DImporter::XML_ReadNode_GetAttrVal_AsI32(const int pAttrIdx)
 {
-       return strtol10(mReader->getAttributeValue(pAttrIdx));
+    auto intValue = std::dynamic_pointer_cast<const FIIntValue>(mReader->getAttributeEncodedValue(pAttrIdx));
+    if (intValue) {
+        if (intValue->value.size() == 1) {
+            return intValue->value.front();
+        }
+        throw DeadlyImportError("Invalid int value");
+    }
+    else {
+        return strtol10(mReader->getAttributeValue(pAttrIdx));
+    }
 }
 
 void X3DImporter::XML_ReadNode_GetAttrVal_AsCol3f(const int pAttrIdx, aiColor3D& pValue)
 {
-    std::list<float> tlist;
-    std::list<float>::iterator it;
+    std::vector<float> tlist;
+    std::vector<float>::iterator it;
 
-       XML_ReadNode_GetAttrVal_AsListF(pAttrIdx, tlist);
+       XML_ReadNode_GetAttrVal_AsArrF(pAttrIdx, tlist);
        if(tlist.size() != 3) Throw_ConvertFail_Str2ArrF(mReader->getAttributeValue(pAttrIdx));
 
        it = tlist.begin();
@@ -412,10 +440,10 @@ void X3DImporter::XML_ReadNode_GetAttrVal_AsCol3f(const int pAttrIdx, aiColor3D&
 
 void X3DImporter::XML_ReadNode_GetAttrVal_AsVec2f(const int pAttrIdx, aiVector2D& pValue)
 {
-    std::list<float> tlist;
-    std::list<float>::iterator it;
+    std::vector<float> tlist;
+    std::vector<float>::iterator it;
 
-       XML_ReadNode_GetAttrVal_AsListF(pAttrIdx, tlist);
+       XML_ReadNode_GetAttrVal_AsArrF(pAttrIdx, tlist);
        if(tlist.size() != 2) Throw_ConvertFail_Str2ArrF(mReader->getAttributeValue(pAttrIdx));
 
        it = tlist.begin();
@@ -425,10 +453,10 @@ void X3DImporter::XML_ReadNode_GetAttrVal_AsVec2f(const int pAttrIdx, aiVector2D
 
 void X3DImporter::XML_ReadNode_GetAttrVal_AsVec3f(const int pAttrIdx, aiVector3D& pValue)
 {
-    std::list<float> tlist;
-    std::list<float>::iterator it;
+    std::vector<float> tlist;
+    std::vector<float>::iterator it;
 
-       XML_ReadNode_GetAttrVal_AsListF(pAttrIdx, tlist);
+       XML_ReadNode_GetAttrVal_AsArrF(pAttrIdx, tlist);
        if(tlist.size() != 3) Throw_ConvertFail_Str2ArrF(mReader->getAttributeValue(pAttrIdx));
 
        it = tlist.begin();
@@ -437,166 +465,75 @@ void X3DImporter::XML_ReadNode_GetAttrVal_AsVec3f(const int pAttrIdx, aiVector3D
        pValue.z = *it;
 }
 
-void X3DImporter::XML_ReadNode_GetAttrVal_AsListB(const int pAttrIdx, std::list<bool>& pValue)
-{
-       const char *tok_cur = mReader->getAttributeValue(pAttrIdx);
-       const char *tok_end = tok_cur + strlen(tok_cur);
-
-       for(;;)
-       {
-               while((tok_cur < tok_end) && (whitespace.find_first_of(*tok_cur) != std::string::npos)) tok_cur++;// skip spaces between values.
-               if (tok_cur >= tok_end)
-                       break;
-
-               if(strncmp(tok_cur, "true", 4) == 0)
-               {
-                       pValue.push_back(true);
-                       tok_cur += 4;
-               }
-               else if(strncmp(tok_cur, "false", 5) == 0)
-               {
-                       pValue.push_back(false);
-                       tok_cur += 5;
-               }
-               else
-               {
-                       Throw_IncorrectAttrValue(mReader->getAttributeName(pAttrIdx));
-               }
-       }// for(;;)
-}
-
 void X3DImporter::XML_ReadNode_GetAttrVal_AsArrB(const int pAttrIdx, std::vector<bool>& pValue)
 {
-    std::list<bool> tlist;
-
-       XML_ReadNode_GetAttrVal_AsListB(pAttrIdx, tlist);// read as list
-       // and copy to array
-       if(tlist.size() > 0)
-       {
-               pValue.reserve(tlist.size());
-               for(std::list<bool>::iterator it = tlist.begin(); it != tlist.end(); it++) pValue.push_back(*it);
-       }
-}
-
-void X3DImporter::XML_ReadNode_GetAttrVal_AsListI32(const int pAttrIdx, std::list<int32_t>& pValue)
-{
-    const char* tstr = mReader->getAttributeValue(pAttrIdx);
-    const char* tstr_end = tstr + strlen(tstr);
-
-       do
-       {
-               const char* ostr;
-
-               int32_t tval32;
-
-               while((tstr < tstr_end) && (whitespace.find_first_of(*tstr) != std::string::npos)) tstr++;// skip spaces between values.
-
-               tval32 = strtol10(tstr, &ostr);
-               if(ostr == tstr) break;
-
-               tstr = ostr;
-               pValue.push_back(tval32);
-       } while(tstr < tstr_end);
+    auto boolValue = std::dynamic_pointer_cast<const FIBoolValue>(mReader->getAttributeEncodedValue(pAttrIdx));
+    if (boolValue) {
+        pValue = boolValue->value;
+    }
+    else {
+        const char *val = mReader->getAttributeValue(pAttrIdx);
+        std::cregex_iterator wordItBegin(val, val + strlen(val), pattern_nws);
+        const std::cregex_iterator wordItEnd;
+        pValue.clear();
+        std::transform(wordItBegin, wordItEnd, std::back_inserter(pValue), [](const std::cmatch &match) { return std::regex_match(match.str(), pattern_true); });
+    }
 }
 
 void X3DImporter::XML_ReadNode_GetAttrVal_AsArrI32(const int pAttrIdx, std::vector<int32_t>& pValue)
 {
-    std::list<int32_t> tlist;
-
-       XML_ReadNode_GetAttrVal_AsListI32(pAttrIdx, tlist);// read as list
-       // and copy to array
-       if(tlist.size() > 0)
-       {
-               pValue.reserve(tlist.size());
-               for(std::list<int32_t>::iterator it = tlist.begin(); it != tlist.end(); it++) pValue.push_back(*it);
-       }
-}
-
-void X3DImporter::XML_ReadNode_GetAttrVal_AsListF(const int pAttrIdx, std::list<float>& pValue)
-{
-    std::string str_fixed;
-
-       // at first check string values like '.xxx'.
-       ParseHelper_FixTruncatedFloatString(mReader->getAttributeValue(pAttrIdx), str_fixed);
-
-       // and convert all values and place it in list.
-       const char* pstr = str_fixed.c_str();
-       const char* pstr_end = pstr + str_fixed.size();
-
-       do
-       {
-               float tvalf;
-
-               while((pstr < pstr_end) && (whitespace.find_first_of(*pstr) != std::string::npos)) pstr++;// skip spaces between values.
-
-               if(pstr < pstr_end)// additional check, because attribute value can be ended with spaces.
-               {
-                       pstr = fast_atoreal_move(pstr, tvalf, false);
-                       pValue.push_back(tvalf);
-               }
-       } while(pstr < pstr_end);
+    auto intValue = std::dynamic_pointer_cast<const FIIntValue>(mReader->getAttributeEncodedValue(pAttrIdx));
+    if (intValue) {
+        pValue = intValue->value;
+    }
+    else {
+        const char *val = mReader->getAttributeValue(pAttrIdx);
+        std::cregex_iterator wordItBegin(val, val + strlen(val), pattern_nws);
+        const std::cregex_iterator wordItEnd;
+        pValue.clear();
+        std::transform(wordItBegin, wordItEnd, std::back_inserter(pValue), [](const std::cmatch &match) { return std::stoi(match.str()); });
+    }
 }
 
 void X3DImporter::XML_ReadNode_GetAttrVal_AsArrF(const int pAttrIdx, std::vector<float>& pValue)
 {
-    std::list<float> tlist;
-
-       XML_ReadNode_GetAttrVal_AsListF(pAttrIdx, tlist);// read as list
-       // and copy to array
-       if(tlist.size() > 0)
-       {
-               pValue.reserve(tlist.size());
-               for(std::list<float>::iterator it = tlist.begin(); it != tlist.end(); it++) pValue.push_back(*it);
-       }
-}
-
-void X3DImporter::XML_ReadNode_GetAttrVal_AsListD(const int pAttrIdx, std::list<double>& pValue)
-{
-    std::string str_fixed;
-
-       // at first check string values like '.xxx'.
-       ParseHelper_FixTruncatedFloatString(mReader->getAttributeValue(pAttrIdx), str_fixed);
-
-       // and convert all values and place it in list.
-       const char* pstr = str_fixed.c_str();
-       const char* pstr_end = pstr + str_fixed.size();
-
-       do
-       {
-               double tvald;
-
-               while((pstr < pstr_end) && (whitespace.find_first_of(*pstr) != std::string::npos)) pstr++;// skip spaces between values.
-
-               if(pstr < pstr_end)// additional check, because attribute value can be ended with spaces.
-               {
-                       pstr = fast_atoreal_move(pstr, tvald, false);
-                       pValue.push_back(tvald);
-               }
-       } while(pstr < pstr_end);
+    auto floatValue = std::dynamic_pointer_cast<const FIFloatValue>(mReader->getAttributeEncodedValue(pAttrIdx));
+    if (floatValue) {
+        pValue = floatValue->value;
+    }
+    else {
+        const char *val = mReader->getAttributeValue(pAttrIdx);
+        std::cregex_iterator wordItBegin(val, val + strlen(val), pattern_nws);
+        const std::cregex_iterator wordItEnd;
+        pValue.clear();
+        std::transform(wordItBegin, wordItEnd, std::back_inserter(pValue), [](const std::cmatch &match) { return std::stof(match.str()); });
+    }
 }
 
 void X3DImporter::XML_ReadNode_GetAttrVal_AsArrD(const int pAttrIdx, std::vector<double>& pValue)
 {
-    std::list<double> tlist;
-
-       XML_ReadNode_GetAttrVal_AsListD(pAttrIdx, tlist);// read as list
-       // and copy to array
-       if(tlist.size() > 0)
-       {
-               pValue.reserve(tlist.size());
-               for(std::list<double>::iterator it = tlist.begin(); it != tlist.end(); it++) pValue.push_back(*it);
-       }
+    auto doubleValue = std::dynamic_pointer_cast<const FIDoubleValue>(mReader->getAttributeEncodedValue(pAttrIdx));
+    if (doubleValue) {
+        pValue = doubleValue->value;
+    }
+    else {
+        const char *val = mReader->getAttributeValue(pAttrIdx);
+        std::cregex_iterator wordItBegin(val, val + strlen(val), pattern_nws);
+        const std::cregex_iterator wordItEnd;
+        pValue.clear();
+        std::transform(wordItBegin, wordItEnd, std::back_inserter(pValue), [](const std::cmatch &match) { return std::stod(match.str()); });
+    }
 }
 
 void X3DImporter::XML_ReadNode_GetAttrVal_AsListCol3f(const int pAttrIdx, std::list<aiColor3D>& pValue)
 {
-    std::list<float> tlist;
+    std::vector<float> tlist;
 
-       XML_ReadNode_GetAttrVal_AsListF(pAttrIdx, tlist);// read as list
+       XML_ReadNode_GetAttrVal_AsArrF(pAttrIdx, tlist);// read as list
        if(tlist.size() % 3) Throw_ConvertFail_Str2ArrF(mReader->getAttributeValue(pAttrIdx));
 
        // copy data to array
-       for(std::list<float>::iterator it = tlist.begin(); it != tlist.end();)
+       for(std::vector<float>::iterator it = tlist.begin(); it != tlist.end();)
        {
                aiColor3D tcol;
 
@@ -622,13 +559,13 @@ void X3DImporter::XML_ReadNode_GetAttrVal_AsArrCol3f(const int pAttrIdx, std::ve
 
 void X3DImporter::XML_ReadNode_GetAttrVal_AsListCol4f(const int pAttrIdx, std::list<aiColor4D>& pValue)
 {
-    std::list<float> tlist;
+    std::vector<float> tlist;
 
-       XML_ReadNode_GetAttrVal_AsListF(pAttrIdx, tlist);// read as list
+       XML_ReadNode_GetAttrVal_AsArrF(pAttrIdx, tlist);// read as list
        if(tlist.size() % 4) Throw_ConvertFail_Str2ArrF(mReader->getAttributeValue(pAttrIdx));
 
        // copy data to array
-       for(std::list<float>::iterator it = tlist.begin(); it != tlist.end();)
+       for(std::vector<float>::iterator it = tlist.begin(); it != tlist.end();)
        {
                aiColor4D tcol;
 
@@ -658,16 +595,16 @@ void X3DImporter::XML_ReadNode_GetAttrVal_AsArrCol4f(const int pAttrIdx, std::ve
 
 void X3DImporter::XML_ReadNode_GetAttrVal_AsListVec2f(const int pAttrIdx, std::list<aiVector2D>& pValue)
 {
-    std::list<float> tlist;
+    std::vector<float> tlist;
 
-       XML_ReadNode_GetAttrVal_AsListF(pAttrIdx, tlist);// read as list
+       XML_ReadNode_GetAttrVal_AsArrF(pAttrIdx, tlist);// read as list
     if ( tlist.size() % 2 )
     {
         Throw_ConvertFail_Str2ArrF( mReader->getAttributeValue( pAttrIdx ) );
     }
 
        // copy data to array
-       for(std::list<float>::iterator it = tlist.begin(); it != tlist.end();)
+       for(std::vector<float>::iterator it = tlist.begin(); it != tlist.end();)
        {
                aiVector2D tvec;
 
@@ -695,16 +632,16 @@ void X3DImporter::XML_ReadNode_GetAttrVal_AsArrVec2f(const int pAttrIdx, std::ve
 
 void X3DImporter::XML_ReadNode_GetAttrVal_AsListVec3f(const int pAttrIdx, std::list<aiVector3D>& pValue)
 {
-    std::list<float> tlist;
+    std::vector<float> tlist;
 
-       XML_ReadNode_GetAttrVal_AsListF(pAttrIdx, tlist);// read as list
+       XML_ReadNode_GetAttrVal_AsArrF(pAttrIdx, tlist);// read as list
     if ( tlist.size() % 3 )
     {
         Throw_ConvertFail_Str2ArrF( mReader->getAttributeValue( pAttrIdx ) );
     }
 
        // copy data to array
-       for(std::list<float>::iterator it = tlist.begin(); it != tlist.end();)
+       for(std::vector<float>::iterator it = tlist.begin(); it != tlist.end();)
        {
                aiVector3D tvec;
 
@@ -894,9 +831,9 @@ void X3DImporter::GeometryHelper_MakeQL_RectParallelepiped(const aiVector3D& pSi
 
 #undef MESH_RectParallelepiped_CREATE_VERT
 
-void X3DImporter::GeometryHelper_CoordIdxStr2FacesArr(const std::list<int32_t>& pCoordIdx, std::vector<aiFace>& pFaces, unsigned int& pPrimitiveTypes) const
+void X3DImporter::GeometryHelper_CoordIdxStr2FacesArr(const std::vector<int32_t>& pCoordIdx, std::vector<aiFace>& pFaces, unsigned int& pPrimitiveTypes) const
 {
-    std::list<int32_t> f_data(pCoordIdx);
+    std::vector<int32_t> f_data(pCoordIdx);
     std::vector<unsigned int> inds;
     unsigned int prim_type = 0;
 
@@ -909,7 +846,7 @@ void X3DImporter::GeometryHelper_CoordIdxStr2FacesArr(const std::list<int32_t>&
        pFaces.reserve(f_data.size() / 3);
        inds.reserve(4);
     //PrintVectorSet("build. ci", pCoordIdx);
-       for(std::list<int32_t>::iterator it = f_data.begin(); it != f_data.end(); it++)
+       for(std::vector<int32_t>::iterator it = f_data.begin(); it != f_data.end(); it++)
        {
                // when face is got count how many indices in it.
                if(*it == (-1))
@@ -1001,7 +938,7 @@ void X3DImporter::MeshGeometry_AddColor(aiMesh& pMesh, const std::list<aiColor4D
        }// if(pColorPerVertex) else
 }
 
-void X3DImporter::MeshGeometry_AddColor(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<int32_t>& pColorIdx,
+void X3DImporter::MeshGeometry_AddColor(aiMesh& pMesh, const std::vector<int32_t>& pCoordIdx, const std::vector<int32_t>& pColorIdx,
                                                                                const std::list<aiColor3D>& pColors, const bool pColorPerVertex) const
 {
     std::list<aiColor4D> tcol;
@@ -1016,7 +953,7 @@ void X3DImporter::MeshGeometry_AddColor(aiMesh& pMesh, const std::list<int32_t>&
        MeshGeometry_AddColor(pMesh, pCoordIdx, pColorIdx, tcol, pColorPerVertex);
 }
 
-void X3DImporter::MeshGeometry_AddColor(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<int32_t>& pColorIdx,
+void X3DImporter::MeshGeometry_AddColor(aiMesh& pMesh, const std::vector<int32_t>& pCoordIdx, const std::vector<int32_t>& pColorIdx,
                                                                                const std::list<aiColor4D>& pColors, const bool pColorPerVertex) const
 {
     std::vector<aiColor4D> col_tgt_arr;
@@ -1047,7 +984,7 @@ void X3DImporter::MeshGeometry_AddColor(aiMesh& pMesh, const std::list<int32_t>&
                        }
                        // create list with colors for every vertex.
                        col_tgt_arr.resize(pMesh.mNumVertices);
-                       for(std::list<int32_t>::const_iterator colidx_it = pColorIdx.begin(), coordidx_it = pCoordIdx.begin(); colidx_it != pColorIdx.end(); colidx_it++, coordidx_it++)
+                       for(std::vector<int32_t>::const_iterator colidx_it = pColorIdx.begin(), coordidx_it = pCoordIdx.begin(); colidx_it != pColorIdx.end(); colidx_it++, coordidx_it++)
                        {
                 if ( *colidx_it == ( -1 ) )
                 {
@@ -1095,7 +1032,7 @@ void X3DImporter::MeshGeometry_AddColor(aiMesh& pMesh, const std::list<int32_t>&
                        // create list with colors for every vertex using faces indices.
                        col_tgt_arr.resize(pMesh.mNumFaces);
 
-                       std::list<int32_t>::const_iterator colidx_it = pColorIdx.begin();
+                       std::vector<int32_t>::const_iterator colidx_it = pColorIdx.begin();
                        for(size_t fi = 0; fi < pMesh.mNumFaces; fi++)
                        {
                                if((unsigned int)*colidx_it > pMesh.mNumFaces) throw DeadlyImportError("MeshGeometry_AddColor2. Face idx is out of range.");
@@ -1125,7 +1062,7 @@ void X3DImporter::MeshGeometry_AddColor(aiMesh& pMesh, const std::list<int32_t>&
        MeshGeometry_AddColor(pMesh, col_tgt_list, pColorPerVertex);
 }
 
-void X3DImporter::MeshGeometry_AddNormal(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<int32_t>& pNormalIdx,
+void X3DImporter::MeshGeometry_AddNormal(aiMesh& pMesh, const std::vector<int32_t>& pCoordIdx, const std::vector<int32_t>& pNormalIdx,
                                                                const std::list<aiVector3D>& pNormals, const bool pNormalPerVertex) const
 {
     std::vector<size_t> tind;
@@ -1146,7 +1083,7 @@ void X3DImporter::MeshGeometry_AddNormal(aiMesh& pMesh, const std::list<int32_t>
                        if(pNormalIdx.size() != pCoordIdx.size()) throw DeadlyImportError("Normals and Coords inidces count must be equal.");
 
                        tind.reserve(pNormalIdx.size());
-                       for(std::list<int32_t>::const_iterator it = pNormalIdx.begin(); it != pNormalIdx.end(); it++)
+                       for(std::vector<int32_t>::const_iterator it = pNormalIdx.begin(); it != pNormalIdx.end(); it++)
                        {
                                if(*it != (-1)) tind.push_back(*it);
                        }
@@ -1178,7 +1115,7 @@ void X3DImporter::MeshGeometry_AddNormal(aiMesh& pMesh, const std::list<int32_t>
                {
                        if(pMesh.mNumFaces != pNormalIdx.size()) throw DeadlyImportError("Normals faces count must be equal to mesh faces count.");
 
-                       std::list<int32_t>::const_iterator normidx_it = pNormalIdx.begin();
+                       std::vector<int32_t>::const_iterator normidx_it = pNormalIdx.begin();
 
                        tind.reserve(pNormalIdx.size());
                        for(size_t i = 0, i_e = pNormalIdx.size(); i < i_e; i++) tind.push_back(*normidx_it++);
@@ -1231,7 +1168,7 @@ void X3DImporter::MeshGeometry_AddNormal(aiMesh& pMesh, const std::list<aiVector
        }// if(pNormalPerVertex) else
 }
 
-void X3DImporter::MeshGeometry_AddTexCoord(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<int32_t>& pTexCoordIdx,
+void X3DImporter::MeshGeometry_AddTexCoord(aiMesh& pMesh, const std::vector<int32_t>& pCoordIdx, const std::vector<int32_t>& pTexCoordIdx,
                                                                const std::list<aiVector2D>& pTexCoords) const
 {
     std::vector<aiVector3D> texcoord_arr_copy;
@@ -1304,7 +1241,7 @@ void X3DImporter::MeshGeometry_AddTexCoord(aiMesh& pMesh, const std::list<aiVect
     }
 }
 
-aiMesh* X3DImporter::GeometryHelper_MakeMesh(const std::list<int32_t>& pCoordIdx, const std::list<aiVector3D>& pVertices) const
+aiMesh* X3DImporter::GeometryHelper_MakeMesh(const std::vector<int32_t>& pCoordIdx, const std::list<aiVector3D>& pVertices) const
 {
     std::vector<aiFace> faces;
     unsigned int prim_type = 0;
@@ -1407,9 +1344,12 @@ void X3DImporter::ParseHelper_FixTruncatedFloatString(const char* pInStr, std::s
        }
 }
 
+extern FIVocabulary X3D_vocabulary_3_2;
+extern FIVocabulary X3D_vocabulary_3_3;
+
 void X3DImporter::ParseFile(const std::string& pFile, IOSystem* pIOHandler)
 {
-    irr::io::IrrXMLReader* OldReader = mReader;// store current XMLreader.
+    std::unique_ptr<FIReader> OldReader = std::move(mReader);// store current XMLreader.
     std::unique_ptr<IOStream> file(pIOHandler->Open(pFile, "rb"));
 
        // Check whether we can read from the file
@@ -1417,19 +1357,18 @@ void X3DImporter::ParseFile(const std::string& pFile, IOSystem* pIOHandler)
     {
         throw DeadlyImportError( "Failed to open X3D file " + pFile + "." );
     }
-       // generate a XML reader for it
-       std::unique_ptr<CIrrXML_IOStreamReader> mIOWrapper(new CIrrXML_IOStreamReader(file.get()));
-       mReader = irr::io::createIrrXMLReader(mIOWrapper.get());
+       mReader = FIReader::create(file.get());
     if ( !mReader )
     {
         throw DeadlyImportError( "Failed to create XML reader for file" + pFile + "." );
     }
+    mReader->registerVocabulary("urn:web3d:x3d:fi-vocabulary-3.2", &X3D_vocabulary_3_2);
+    mReader->registerVocabulary("urn:web3d:x3d:fi-vocabulary-3.3", &X3D_vocabulary_3_3);
        // start reading
        ParseNode_Root();
 
-       delete mReader;
        // restore old XMLreader
-       mReader = OldReader;
+       mReader = std::move(OldReader);
 }
 
 void X3DImporter::ParseNode_Root()
@@ -1643,7 +1582,7 @@ bool X3DImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool p
 {
     const std::string extension = GetExtension(pFile);
 
-       if(extension == "x3d") return true;
+       if((extension == "x3d") || (extension == "x3db")) return true;
 
        if(!extension.length() || pCheckSig)
        {
@@ -1658,6 +1597,7 @@ bool X3DImporter::CanRead(const std::string& pFile, IOSystem* pIOHandler, bool p
 void X3DImporter::GetExtensionList(std::set<std::string>& pExtensionList)
 {
        pExtensionList.insert("x3d");
+       pExtensionList.insert("x3db");
 }
 
 const aiImporterDesc* X3DImporter::GetInfo () const
index e02d1ab..58faefb 100644 (file)
@@ -56,6 +56,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/types.h>
 #include "BaseImporter.h"
 #include "irrXMLWrapper.h"
+#include "FIReader.hpp"
+#include <regex>
 
 namespace Assimp {
 
@@ -449,33 +451,21 @@ private:
        /// Read attribute value.
        /// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
        /// \param [out] pValue - read data.
-       void XML_ReadNode_GetAttrVal_AsListB(const int pAttrIdx, std::list<bool>& pValue);
-
-       /// \overload void XML_ReadNode_GetAttrVal_AsListBool(const int pAttrIdx, std::list<bool>& pValue)
        void XML_ReadNode_GetAttrVal_AsArrB(const int pAttrIdx, std::vector<bool>& pValue);
 
        /// Read attribute value.
        /// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
        /// \param [out] pValue - read data.
-       void XML_ReadNode_GetAttrVal_AsListI32(const int pAttrIdx, std::list<int32_t>& pValue);
-
-       /// \overload void XML_ReadNode_GetAttrVal_AsListI32(const int pAttrIdx, std::list<int32_t>& pValue)
        void XML_ReadNode_GetAttrVal_AsArrI32(const int pAttrIdx, std::vector<int32_t>& pValue);
 
        /// Read attribute value.
        /// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
        /// \param [out] pValue - read data.
-       void XML_ReadNode_GetAttrVal_AsListF(const int pAttrIdx, std::list<float>& pValue);
-
-    /// \overload void XML_ReadNode_GetAttrVal_AsListF(const int pAttrIdx, std::list<float>& pValue)
        void XML_ReadNode_GetAttrVal_AsArrF(const int pAttrIdx, std::vector<float>& pValue);
 
     /// Read attribute value.
        /// \param [in] pAttrIdx - attribute index (\ref mReader->getAttribute* set).
        /// \param [out] pValue - read data.
-       void XML_ReadNode_GetAttrVal_AsListD(const int pAttrIdx, std::list<double>& pValue);
-
-       /// \overload void XML_ReadNode_GetAttrVal_AsListD(const int pAttrIdx, std::list<double>& pValue)
        void XML_ReadNode_GetAttrVal_AsArrD(const int pAttrIdx, std::vector<double>& pValue);
 
        /// Read attribute value.
@@ -554,7 +544,7 @@ private:
        /// \param [in] pCoordIdx - vertices indices divided by delimiter "-1".
        /// \param [in] pFaces - created faces array.
        /// \param [in] pPrimitiveTypes - type of primitives in faces.
-       void GeometryHelper_CoordIdxStr2FacesArr(const std::list<int32_t>& pCoordIdx, std::vector<aiFace>& pFaces, unsigned int& pPrimitiveTypes) const;
+       void GeometryHelper_CoordIdxStr2FacesArr(const std::vector<int32_t>& pCoordIdx, std::vector<aiFace>& pFaces, unsigned int& pPrimitiveTypes) const;
 
        /// Add colors to mesh.
        /// a. If colorPerVertex is FALSE, colours are applied to each face, as follows:
@@ -573,11 +563,11 @@ private:
        /// then pColorIdx contain color indices for every faces and must not contain delimiter "-1".
        /// \param [in] pColors - defined colors.
        /// \param [in] pColorPerVertex - if \ref pColorPerVertex is true then color in \ref pColors defined for every vertex, if false - for every face.
-       void MeshGeometry_AddColor(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<int32_t>& pColorIdx,
+       void MeshGeometry_AddColor(aiMesh& pMesh, const std::vector<int32_t>& pCoordIdx, const std::vector<int32_t>& pColorIdx,
                                                                const std::list<aiColor4D>& pColors, const bool pColorPerVertex) const;
 
        /// \overload void MeshGeometry_AddColor(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<int32_t>& pColorIdx, const std::list<aiColor4D>& pColors, const bool pColorPerVertex) const;
-       void MeshGeometry_AddColor(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<int32_t>& pColorIdx,
+       void MeshGeometry_AddColor(aiMesh& pMesh, const std::vector<int32_t>& pCoordIdx, const std::vector<int32_t>& pColorIdx,
                                                                const std::list<aiColor3D>& pColors, const bool pColorPerVertex) const;
 
        /// Add colors to mesh.
@@ -590,14 +580,14 @@ private:
        void MeshGeometry_AddColor(aiMesh& pMesh, const std::list<aiColor3D>& pColors, const bool pColorPerVertex) const;
 
        /// Add normals to mesh. Function work similar to \ref MeshGeometry_AddColor;
-       void MeshGeometry_AddNormal(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<int32_t>& pNormalIdx,
+       void MeshGeometry_AddNormal(aiMesh& pMesh, const std::vector<int32_t>& pCoordIdx, const std::vector<int32_t>& pNormalIdx,
                                                                const std::list<aiVector3D>& pNormals, const bool pNormalPerVertex) const;
 
        /// Add normals to mesh. Function work similar to \ref MeshGeometry_AddColor;
        void MeshGeometry_AddNormal(aiMesh& pMesh, const std::list<aiVector3D>& pNormals, const bool pNormalPerVertex) const;
 
     /// Add texture coordinates to mesh. Function work similar to \ref MeshGeometry_AddColor;
-       void MeshGeometry_AddTexCoord(aiMesh& pMesh, const std::list<int32_t>& pCoordIdx, const std::list<int32_t>& pTexCoordIdx,
+       void MeshGeometry_AddTexCoord(aiMesh& pMesh, const std::vector<int32_t>& pCoordIdx, const std::vector<int32_t>& pTexCoordIdx,
                                                                const std::list<aiVector2D>& pTexCoords) const;
 
     /// Add texture coordinates to mesh. Function work similar to \ref MeshGeometry_AddColor;
@@ -607,7 +597,7 @@ private:
        /// \param [in] pCoordIdx - vertices indices divided by delimiter "-1".
        /// \param [in] pVertices - vertices of mesh.
        /// \return created mesh.
-       aiMesh* GeometryHelper_MakeMesh(const std::list<int32_t>& pCoordIdx, const std::list<aiVector3D>& pVertices) const;
+       aiMesh* GeometryHelper_MakeMesh(const std::vector<int32_t>& pCoordIdx, const std::list<aiVector3D>& pVertices) const;
 
        /***********************************************/
        /******** Functions: parse set private *********/
@@ -826,13 +816,15 @@ private:
     /****************** Constants ******************/
     /***********************************************/
     static const aiImporterDesc Description;
-    static const std::string whitespace;
+    static const std::regex pattern_nws;
+    static const std::regex pattern_true;
+
 
     /***********************************************/
     /****************** Variables ******************/
     /***********************************************/
     CX3DImporter_NodeElement* NodeElement_Cur;///< Current element.
-    irr::io::IrrXMLReader* mReader;///< Pointer to XML-reader object
+    std::unique_ptr<FIReader> mReader;///< Pointer to XML-reader object
     IOSystem *mpIOHandler;
 };// class X3DImporter
 
index 10dde65..721221f 100644 (file)
@@ -285,7 +285,7 @@ void X3DImporter::ParseNode_Geometry3D_ElevationGrid()
     bool ccw = true;
     bool colorPerVertex = true;
     float creaseAngle = 0;
-    std::list<float> height;
+    std::vector<float> height;
     bool normalPerVertex = true;
     bool solid = true;
     int32_t xDimension = 0;
@@ -301,7 +301,7 @@ void X3DImporter::ParseNode_Geometry3D_ElevationGrid()
                MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool);
                MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool);
                MACRO_ATTRREAD_CHECK_RET("creaseAngle", creaseAngle, XML_ReadNode_GetAttrVal_AsFloat);
-               MACRO_ATTRREAD_CHECK_REF("height", height, XML_ReadNode_GetAttrVal_AsListF);
+               MACRO_ATTRREAD_CHECK_REF("height", height, XML_ReadNode_GetAttrVal_AsArrF);
                MACRO_ATTRREAD_CHECK_RET("xDimension", xDimension, XML_ReadNode_GetAttrVal_AsI32);
                MACRO_ATTRREAD_CHECK_RET("xSpacing", xSpacing, XML_ReadNode_GetAttrVal_AsFloat);
                MACRO_ATTRREAD_CHECK_RET("zDimension", zDimension, XML_ReadNode_GetAttrVal_AsI32);
@@ -326,7 +326,7 @@ void X3DImporter::ParseNode_Geometry3D_ElevationGrid()
                CX3DImporter_NodeElement_ElevationGrid& grid_alias = *((CX3DImporter_NodeElement_ElevationGrid*)ne);// create alias for conveience
 
                {// create grid vertices list
-                       std::list<float>::const_iterator he_it = height.begin();
+                       std::vector<float>::const_iterator he_it = height.begin();
 
                        for(int32_t zi = 0; zi < zDimension; zi++)// rows
                        {
@@ -863,29 +863,29 @@ void X3DImporter::ParseNode_Geometry3D_IndexedFaceSet()
 {
     std::string use, def;
     bool ccw = true;
-    std::list<int32_t> colorIndex;
+    std::vector<int32_t> colorIndex;
     bool colorPerVertex = true;
     bool convex = true;
-    std::list<int32_t> coordIndex;
+    std::vector<int32_t> coordIndex;
     float creaseAngle = 0;
-    std::list<int32_t> normalIndex;
+    std::vector<int32_t> normalIndex;
     bool normalPerVertex = true;
     bool solid = true;
-    std::list<int32_t> texCoordIndex;
+    std::vector<int32_t> texCoordIndex;
     CX3DImporter_NodeElement* ne( nullptr );
 
        MACRO_ATTRREAD_LOOPBEG;
                MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
                MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool);
-               MACRO_ATTRREAD_CHECK_REF("colorIndex", colorIndex, XML_ReadNode_GetAttrVal_AsListI32);
+               MACRO_ATTRREAD_CHECK_REF("colorIndex", colorIndex, XML_ReadNode_GetAttrVal_AsArrI32);
                MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool);
                MACRO_ATTRREAD_CHECK_RET("convex", convex, XML_ReadNode_GetAttrVal_AsBool);
-               MACRO_ATTRREAD_CHECK_REF("coordIndex", coordIndex, XML_ReadNode_GetAttrVal_AsListI32);
+               MACRO_ATTRREAD_CHECK_REF("coordIndex", coordIndex, XML_ReadNode_GetAttrVal_AsArrI32);
                MACRO_ATTRREAD_CHECK_RET("creaseAngle", creaseAngle, XML_ReadNode_GetAttrVal_AsFloat);
-               MACRO_ATTRREAD_CHECK_REF("normalIndex", normalIndex, XML_ReadNode_GetAttrVal_AsListI32);
+               MACRO_ATTRREAD_CHECK_REF("normalIndex", normalIndex, XML_ReadNode_GetAttrVal_AsArrI32);
                MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool);
                MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
-               MACRO_ATTRREAD_CHECK_REF("texCoordIndex", texCoordIndex, XML_ReadNode_GetAttrVal_AsListI32);
+               MACRO_ATTRREAD_CHECK_REF("texCoordIndex", texCoordIndex, XML_ReadNode_GetAttrVal_AsArrI32);
        MACRO_ATTRREAD_LOOPEND;
 
        // if "USE" defined then find already defined element.
index febc9cc..9d7147a 100644 (file)
@@ -123,14 +123,14 @@ void X3DImporter::ParseNode_MetadataBoolean()
 {
     std::string def, use;
     std::string name, reference;
-    std::list<bool> value;
+    std::vector<bool> value;
     CX3DImporter_NodeElement* ne( nullptr );
 
        MACRO_ATTRREAD_LOOPBEG;
                MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
                MACRO_ATTRREAD_CHECK_RET("name", name, mReader->getAttributeValue);
                MACRO_ATTRREAD_CHECK_RET("reference", reference, mReader->getAttributeValue);
-               MACRO_ATTRREAD_CHECK_REF("value", value, XML_ReadNode_GetAttrVal_AsListB);
+               MACRO_ATTRREAD_CHECK_REF("value", value, XML_ReadNode_GetAttrVal_AsArrB);
        MACRO_ATTRREAD_LOOPEND;
 
        MACRO_METADATA_FINDCREATE(def, use, reference, value, ne, CX3DImporter_NodeElement_MetaBoolean, "MetadataBoolean", ENET_MetaBoolean);
@@ -147,14 +147,14 @@ void X3DImporter::ParseNode_MetadataDouble()
 {
     std::string def, use;
     std::string name, reference;
-    std::list<double> value;
+    std::vector<double> value;
     CX3DImporter_NodeElement* ne( nullptr );
 
        MACRO_ATTRREAD_LOOPBEG;
                MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
                MACRO_ATTRREAD_CHECK_RET("name", name, mReader->getAttributeValue);
                MACRO_ATTRREAD_CHECK_RET("reference", reference, mReader->getAttributeValue);
-               MACRO_ATTRREAD_CHECK_REF("value", value, XML_ReadNode_GetAttrVal_AsListD);
+               MACRO_ATTRREAD_CHECK_REF("value", value, XML_ReadNode_GetAttrVal_AsArrD);
        MACRO_ATTRREAD_LOOPEND;
 
        MACRO_METADATA_FINDCREATE(def, use, reference, value, ne, CX3DImporter_NodeElement_MetaDouble, "MetadataDouble", ENET_MetaDouble);
@@ -171,14 +171,14 @@ void X3DImporter::ParseNode_MetadataFloat()
 {
     std::string def, use;
     std::string name, reference;
-    std::list<float> value;
+    std::vector<float> value;
     CX3DImporter_NodeElement* ne( nullptr );
 
        MACRO_ATTRREAD_LOOPBEG;
                MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
                MACRO_ATTRREAD_CHECK_RET("name", name, mReader->getAttributeValue);
                MACRO_ATTRREAD_CHECK_RET("reference", reference, mReader->getAttributeValue);
-               MACRO_ATTRREAD_CHECK_REF("value", value, XML_ReadNode_GetAttrVal_AsListF);
+               MACRO_ATTRREAD_CHECK_REF("value", value, XML_ReadNode_GetAttrVal_AsArrF);
        MACRO_ATTRREAD_LOOPEND;
 
        MACRO_METADATA_FINDCREATE(def, use, reference, value, ne, CX3DImporter_NodeElement_MetaFloat, "MetadataFloat", ENET_MetaFloat);
@@ -195,14 +195,14 @@ void X3DImporter::ParseNode_MetadataInteger()
 {
     std::string def, use;
     std::string name, reference;
-    std::list<int32_t> value;
+    std::vector<int32_t> value;
     CX3DImporter_NodeElement* ne( nullptr );
 
        MACRO_ATTRREAD_LOOPBEG;
                MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
                MACRO_ATTRREAD_CHECK_RET("name", name, mReader->getAttributeValue);
                MACRO_ATTRREAD_CHECK_RET("reference", reference, mReader->getAttributeValue);
-               MACRO_ATTRREAD_CHECK_REF("value", value, XML_ReadNode_GetAttrVal_AsListI32);
+               MACRO_ATTRREAD_CHECK_REF("value", value, XML_ReadNode_GetAttrVal_AsArrI32);
        MACRO_ATTRREAD_LOOPEND;
 
        MACRO_METADATA_FINDCREATE(def, use, reference, value, ne, CX3DImporter_NodeElement_MetaInteger, "MetadataInteger", ENET_MetaInteger);
index 7061d06..6ed96c2 100644 (file)
@@ -52,6 +52,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 // Header files, stdlib.
 #include <list>
+#include <vector>
 #include <string>
 
 /// \class CX3DImporter_NodeElement
@@ -264,7 +265,7 @@ public:
 /// This struct describe metavalue of type boolean.
 struct CX3DImporter_NodeElement_MetaBoolean : public CX3DImporter_NodeElement_Meta
 {
-       std::list<bool> Value;///< Stored value.
+       std::vector<bool> Value;///< Stored value.
 
        /// \fn CX3DImporter_NodeElement_MetaBoolean(CX3DImporter_NodeElement* pParent)
        /// Constructor
@@ -279,7 +280,7 @@ struct CX3DImporter_NodeElement_MetaBoolean : public CX3DImporter_NodeElement_Me
 /// This struct describe metavalue of type double.
 struct CX3DImporter_NodeElement_MetaDouble : public CX3DImporter_NodeElement_Meta
 {
-       std::list<double> Value;///< Stored value.
+       std::vector<double> Value;///< Stored value.
 
        /// \fn CX3DImporter_NodeElement_MetaDouble(CX3DImporter_NodeElement* pParent)
        /// Constructor
@@ -294,7 +295,7 @@ struct CX3DImporter_NodeElement_MetaDouble : public CX3DImporter_NodeElement_Met
 /// This struct describe metavalue of type float.
 struct CX3DImporter_NodeElement_MetaFloat : public CX3DImporter_NodeElement_Meta
 {
-       std::list<float> Value;///< Stored value.
+       std::vector<float> Value;///< Stored value.
 
        /// \fn CX3DImporter_NodeElement_MetaFloat(CX3DImporter_NodeElement* pParent)
        /// Constructor
@@ -309,7 +310,7 @@ struct CX3DImporter_NodeElement_MetaFloat : public CX3DImporter_NodeElement_Meta
 /// This struct describe metavalue of type integer.
 struct CX3DImporter_NodeElement_MetaInteger : public CX3DImporter_NodeElement_Meta
 {
-       std::list<int32_t> Value;///< Stored value.
+       std::vector<int32_t> Value;///< Stored value.
 
        /// \fn CX3DImporter_NodeElement_MetaInteger(CX3DImporter_NodeElement* pParent)
        /// Constructor
@@ -508,7 +509,7 @@ public:
        /// If the angle between the geometric normals of two adjacent faces is less than the crease angle, normals shall be calculated so that the faces are
        /// shaded smoothly across the edge; otherwise, normals shall be calculated so that a lighting discontinuity across the edge is produced.
        float CreaseAngle;
-       std::list<int32_t> CoordIdx;///< Coordinates list by faces. In X3D format: "-1" - delimiter for faces.
+       std::vector<int32_t> CoordIdx;///< Coordinates list by faces. In X3D format: "-1" - delimiter for faces.
 
        /***********************************************/
        /****************** Functions ******************/
@@ -554,21 +555,21 @@ public:
        /// direction. If normals are not generated but are supplied using a Normal node, and the orientation of the normals does not match the setting of the
        /// ccw field, results are undefined.
        bool CCW;
-       std::list<int32_t> ColorIndex;///< Field to specify the polygonal faces by indexing into the <Color> or <ColorRGBA>.
+       std::vector<int32_t> ColorIndex;///< Field to specify the polygonal faces by indexing into the <Color> or <ColorRGBA>.
        bool ColorPerVertex;///< If true then colors are defined for every vertex, else for every face(line).
        /// \var Convex
        /// The convex field indicates whether all polygons in the shape are convex (TRUE). A polygon is convex if it is planar, does not intersect itself,
        /// and all of the interior angles at its vertices are less than 180 degrees. Non planar and self intersecting polygons may produce undefined results
        /// even if the convex field is FALSE.
        bool Convex;
-       std::list<int32_t> CoordIndex;///< Field to specify the polygonal faces by indexing into the <Coordinate>.
+       std::vector<int32_t> CoordIndex;///< Field to specify the polygonal faces by indexing into the <Coordinate>.
        /// \var CreaseAngle
        /// If the angle between the geometric normals of two adjacent faces is less than the crease angle, normals shall be calculated so that the faces are
        /// shaded smoothly across the edge; otherwise, normals shall be calculated so that a lighting discontinuity across the edge is produced.
        float CreaseAngle;
-       std::list<int32_t> NormalIndex;///< Field to specify the polygonal faces by indexing into the <Normal>.
+       std::vector<int32_t> NormalIndex;///< Field to specify the polygonal faces by indexing into the <Normal>.
        bool NormalPerVertex;///< If true then normals are defined for every vertex, else for every face(line).
-       std::list<int32_t> TexCoordIndex;///< Field to specify the polygonal faces by indexing into the <TextureCoordinate>.
+       std::vector<int32_t> TexCoordIndex;///< Field to specify the polygonal faces by indexing into the <TextureCoordinate>.
 
        /***********************************************/
        /****************** Functions ******************/
@@ -616,10 +617,10 @@ public:
        bool CCW;
        bool ColorPerVertex;///< If true then colors are defined for every vertex, else for every face(line).
        bool NormalPerVertex;///< If true then normals are defined for every vertex, else for every face(line).
-       std::list<int32_t> CoordIndex;///< Field to specify the polygonal faces by indexing into the <Coordinate>.
-       std::list<int32_t> NormalIndex;///< Field to specify the polygonal faces by indexing into the <Normal>.
-       std::list<int32_t> TexCoordIndex;///< Field to specify the polygonal faces by indexing into the <TextureCoordinate>.
-       std::list<int32_t> VertexCount;///< Field describes how many vertices are to be used in each polyline(polygon) from the <Coordinate> field.
+       std::vector<int32_t> CoordIndex;///< Field to specify the polygonal faces by indexing into the <Coordinate>.
+       std::vector<int32_t> NormalIndex;///< Field to specify the polygonal faces by indexing into the <Normal>.
+       std::vector<int32_t> TexCoordIndex;///< Field to specify the polygonal faces by indexing into the <TextureCoordinate>.
+       std::vector<int32_t> VertexCount;///< Field describes how many vertices are to be used in each polyline(polygon) from the <Coordinate> field.
 
        /***********************************************/
        /****************** Functions ******************/
index 39a4930..1d4632f 100644 (file)
@@ -180,16 +180,16 @@ void X3DImporter::ParseNode_Rendering_Coordinate()
 void X3DImporter::ParseNode_Rendering_IndexedLineSet()
 {
     std::string use, def;
-    std::list<int32_t> colorIndex;
+    std::vector<int32_t> colorIndex;
     bool colorPerVertex = true;
-    std::list<int32_t> coordIndex;
+    std::vector<int32_t> coordIndex;
     CX3DImporter_NodeElement* ne( nullptr );
 
        MACRO_ATTRREAD_LOOPBEG;
                MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
-               MACRO_ATTRREAD_CHECK_REF("colorIndex", colorIndex, XML_ReadNode_GetAttrVal_AsListI32);
+               MACRO_ATTRREAD_CHECK_REF("colorIndex", colorIndex, XML_ReadNode_GetAttrVal_AsArrI32);
                MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool);
-               MACRO_ATTRREAD_CHECK_REF("coordIndex", coordIndex, XML_ReadNode_GetAttrVal_AsListI32);
+               MACRO_ATTRREAD_CHECK_REF("coordIndex", coordIndex, XML_ReadNode_GetAttrVal_AsArrI32);
        MACRO_ATTRREAD_LOOPEND;
 
        // if "USE" defined then find already defined element.
@@ -256,7 +256,7 @@ void X3DImporter::ParseNode_Rendering_IndexedTriangleFanSet()
     std::string use, def;
     bool ccw = true;
     bool colorPerVertex = true;
-    std::list<int32_t> index;
+    std::vector<int32_t> index;
     bool normalPerVertex = true;
     bool solid = true;
     CX3DImporter_NodeElement* ne( nullptr );
@@ -265,7 +265,7 @@ void X3DImporter::ParseNode_Rendering_IndexedTriangleFanSet()
                MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
                MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool);
                MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool);
-               MACRO_ATTRREAD_CHECK_REF("index", index, XML_ReadNode_GetAttrVal_AsListI32);
+               MACRO_ATTRREAD_CHECK_REF("index", index, XML_ReadNode_GetAttrVal_AsArrI32);
                MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool);
                MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
        MACRO_ATTRREAD_LOOPEND;
@@ -294,7 +294,7 @@ void X3DImporter::ParseNode_Rendering_IndexedTriangleFanSet()
                ne_alias.CoordIndex.clear();
                int counter = 0;
                int32_t idx[3];
-               for(std::list<int32_t>::const_iterator idx_it = index.begin(); idx_it != index.end(); idx_it++)
+               for(std::vector<int32_t>::const_iterator idx_it = index.begin(); idx_it != index.end(); idx_it++)
                {
                        idx[2] = *idx_it;
                        if (idx[2] < 0)
@@ -374,7 +374,7 @@ void X3DImporter::ParseNode_Rendering_IndexedTriangleSet()
     std::string use, def;
     bool ccw = true;
     bool colorPerVertex = true;
-    std::list<int32_t> index;
+    std::vector<int32_t> index;
     bool normalPerVertex = true;
     bool solid = true;
     CX3DImporter_NodeElement* ne( nullptr );
@@ -383,7 +383,7 @@ void X3DImporter::ParseNode_Rendering_IndexedTriangleSet()
                MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
                MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool);
                MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool);
-               MACRO_ATTRREAD_CHECK_REF("index", index, XML_ReadNode_GetAttrVal_AsListI32);
+               MACRO_ATTRREAD_CHECK_REF("index", index, XML_ReadNode_GetAttrVal_AsArrI32);
                MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool);
                MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
        MACRO_ATTRREAD_LOOPEND;
@@ -412,7 +412,7 @@ void X3DImporter::ParseNode_Rendering_IndexedTriangleSet()
                ne_alias.CoordIndex.clear();
                int counter = 0;
                int32_t idx[3];
-               for(std::list<int32_t>::const_iterator idx_it = index.begin(); idx_it != index.end(); idx_it++)
+               for(std::vector<int32_t>::const_iterator idx_it = index.begin(); idx_it != index.end(); idx_it++)
                {
                        idx[counter++] = *idx_it;
                        if (counter > 2)
@@ -480,7 +480,7 @@ void X3DImporter::ParseNode_Rendering_IndexedTriangleStripSet()
     std::string use, def;
     bool ccw = true;
     bool colorPerVertex = true;
-    std::list<int32_t> index;
+    std::vector<int32_t> index;
     bool normalPerVertex = true;
     bool solid = true;
     CX3DImporter_NodeElement* ne( nullptr );
@@ -489,7 +489,7 @@ void X3DImporter::ParseNode_Rendering_IndexedTriangleStripSet()
                MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
                MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool);
                MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool);
-               MACRO_ATTRREAD_CHECK_REF("index", index, XML_ReadNode_GetAttrVal_AsListI32);
+               MACRO_ATTRREAD_CHECK_REF("index", index, XML_ReadNode_GetAttrVal_AsArrI32);
                MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool);
                MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
        MACRO_ATTRREAD_LOOPEND;
@@ -518,7 +518,7 @@ void X3DImporter::ParseNode_Rendering_IndexedTriangleStripSet()
                ne_alias.CoordIndex.clear();
                int counter = 0;
                int32_t idx[3];
-               for(std::list<int32_t>::const_iterator idx_it = index.begin(); idx_it != index.end(); idx_it++)
+               for(std::vector<int32_t>::const_iterator idx_it = index.begin(); idx_it != index.end(); idx_it++)
                {
                        idx[2] = *idx_it;
                        if (idx[2] < 0)
@@ -587,12 +587,12 @@ void X3DImporter::ParseNode_Rendering_IndexedTriangleStripSet()
 void X3DImporter::ParseNode_Rendering_LineSet()
 {
     std::string use, def;
-    std::list<int32_t> vertexCount;
+    std::vector<int32_t> vertexCount;
     CX3DImporter_NodeElement* ne( nullptr );
 
        MACRO_ATTRREAD_LOOPBEG;
                MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
-               MACRO_ATTRREAD_CHECK_REF("vertexCount", vertexCount, XML_ReadNode_GetAttrVal_AsListI32);
+               MACRO_ATTRREAD_CHECK_REF("vertexCount", vertexCount, XML_ReadNode_GetAttrVal_AsArrI32);
        MACRO_ATTRREAD_LOOPEND;
 
        // if "USE" defined then find already defined element.
@@ -616,7 +616,7 @@ void X3DImporter::ParseNode_Rendering_LineSet()
                size_t coord_num = 0;
 
                ne_alias.CoordIndex.clear();
-               for(std::list<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); vc_it++)
+               for(std::vector<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); vc_it++)
                {
                        if(*vc_it < 2) throw DeadlyImportError("LineSet. vertexCount shall be greater than or equal to two.");
 
@@ -722,7 +722,7 @@ void X3DImporter::ParseNode_Rendering_TriangleFanSet()
     std::string use, def;
     bool ccw = true;
     bool colorPerVertex = true;
-    std::list<int32_t> fanCount;
+    std::vector<int32_t> fanCount;
     bool normalPerVertex = true;
     bool solid = true;
     CX3DImporter_NodeElement* ne( nullptr );
@@ -731,7 +731,7 @@ void X3DImporter::ParseNode_Rendering_TriangleFanSet()
                MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
                MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool);
                MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool);
-               MACRO_ATTRREAD_CHECK_REF("fanCount", fanCount, XML_ReadNode_GetAttrVal_AsListI32);
+               MACRO_ATTRREAD_CHECK_REF("fanCount", fanCount, XML_ReadNode_GetAttrVal_AsArrI32);
                MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool);
                MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
        MACRO_ATTRREAD_LOOPEND;
@@ -764,7 +764,7 @@ void X3DImporter::ParseNode_Rendering_TriangleFanSet()
                // assign indices for first triangle
                coord_num_first = 0;
                coord_num_prev = 1;
-               for(std::list<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); vc_it++)
+               for(std::vector<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); vc_it++)
                {
                        if(*vc_it < 3) throw DeadlyImportError("TriangleFanSet. fanCount shall be greater than or equal to three.");
 
@@ -913,7 +913,7 @@ void X3DImporter::ParseNode_Rendering_TriangleStripSet()
     std::string use, def;
     bool ccw = true;
     bool colorPerVertex = true;
-    std::list<int32_t> stripCount;
+    std::vector<int32_t> stripCount;
     bool normalPerVertex = true;
     bool solid = true;
     CX3DImporter_NodeElement* ne( nullptr );
@@ -922,7 +922,7 @@ void X3DImporter::ParseNode_Rendering_TriangleStripSet()
                MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use);
                MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool);
                MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool);
-               MACRO_ATTRREAD_CHECK_REF("stripCount", stripCount, XML_ReadNode_GetAttrVal_AsListI32);
+               MACRO_ATTRREAD_CHECK_REF("stripCount", stripCount, XML_ReadNode_GetAttrVal_AsArrI32);
                MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool);
                MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool);
        MACRO_ATTRREAD_LOOPEND;
@@ -955,7 +955,7 @@ void X3DImporter::ParseNode_Rendering_TriangleStripSet()
 
                ne_alias.CoordIndex.clear();
                coord_num_sb = 0;
-               for(std::list<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); vc_it++)
+               for(std::vector<int32_t>::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); vc_it++)
                {
                        if(*vc_it < 3) throw DeadlyImportError("TriangleStripSet. stripCount shall be greater than or equal to three.");
 
diff --git a/code/X3DVocabulary.cpp b/code/X3DVocabulary.cpp
new file mode 100644 (file)
index 0000000..780c4ff
--- /dev/null
@@ -0,0 +1,1675 @@
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2017, 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   X3DVocabulary.cpp
+/// \brief  Vocabulary for Fast Infoset encoded binary X3D files.
+/// \date   2017
+/// \author Patrick Daehne
+
+#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
+
+#include "FIReader.hpp"
+
+namespace Assimp {
+
+static const char *encodingAlgorithmTable_3_2[] = {
+    "encoder://web3d.org/QuantizedFloatArrayEncoder",
+    "encoder://web3d.org/DeltazlibIntArrayEncoder",
+    "encoder://web3d.org/QuantizedzlibFloatArrayEncoder",
+    "encoder://web3d.org/zlibFloatArrayEncoder",
+    "encoder://web3d.org/QuantizedDoubleArrayEncoder",
+    "encoder://web3d.org/zlibDoubleArrayEncoder",
+    "encoder://web3d.org/QuantizedzlibDoubleArrayEncoder",
+    "encoder://web3d.org/RangeIntArrayEncoder"
+};
+
+static const std::shared_ptr<const FIValue> attributeValueTable_3_2[] = {
+    FIStringValue::create("false"),
+    FIStringValue::create("true")
+};
+
+static const FIQName elementNameTable_3_2[] = {
+    { "Shape", nullptr, nullptr },
+    { "Appearance", nullptr, nullptr },
+    { "Material", nullptr, nullptr },
+    { "IndexedFaceSet", nullptr, nullptr },
+    { "ProtoInstance", nullptr, nullptr },
+    { "Transform", nullptr, nullptr },
+    { "ImageTexture", nullptr, nullptr },
+    { "TextureTransform", nullptr, nullptr },
+    { "Coordinate", nullptr, nullptr },
+    { "Normal", nullptr, nullptr },
+    { "Color", nullptr, nullptr },
+    { "ColorRGBA", nullptr, nullptr },
+    { "TextureCoordinate", nullptr, nullptr },
+    { "ROUTE", nullptr, nullptr },
+    { "fieldValue", nullptr, nullptr },
+    { "Group", nullptr, nullptr },
+    { "LOD", nullptr, nullptr },
+    { "Switch", nullptr, nullptr },
+    { "Script", nullptr, nullptr },
+    { "IndexedTriangleFanSet", nullptr, nullptr },
+    { "IndexedTriangleSet", nullptr, nullptr },
+    { "IndexedTriangleStripSet", nullptr, nullptr },
+    { "MultiTexture", nullptr, nullptr },
+    { "MultiTextureCoordinate", nullptr, nullptr },
+    { "MultiTextureTransform", nullptr, nullptr },
+    { "IndexedLineSet", nullptr, nullptr },
+    { "PointSet", nullptr, nullptr },
+    { "StaticGroup", nullptr, nullptr },
+    { "Sphere", nullptr, nullptr },
+    { "Box", nullptr, nullptr },
+    { "Cone", nullptr, nullptr },
+    { "Anchor", nullptr, nullptr },
+    { "Arc2D", nullptr, nullptr },
+    { "ArcClose2D", nullptr, nullptr },
+    { "AudioClip", nullptr, nullptr },
+    { "Background", nullptr, nullptr },
+    { "Billboard", nullptr, nullptr },
+    { "BooleanFilter", nullptr, nullptr },
+    { "BooleanSequencer", nullptr, nullptr },
+    { "BooleanToggle", nullptr, nullptr },
+    { "BooleanTrigger", nullptr, nullptr },
+    { "Circle2D", nullptr, nullptr },
+    { "Collision", nullptr, nullptr },
+    { "ColorInterpolator", nullptr, nullptr },
+    { "Contour2D", nullptr, nullptr },
+    { "ContourPolyline2D", nullptr, nullptr },
+    { "CoordinateDouble", nullptr, nullptr },
+    { "CoordinateInterpolator", nullptr, nullptr },
+    { "CoordinateInterpolator2D", nullptr, nullptr },
+    { "Cylinder", nullptr, nullptr },
+    { "CylinderSensor", nullptr, nullptr },
+    { "DirectionalLight", nullptr, nullptr },
+    { "Disk2D", nullptr, nullptr },
+    { "EXPORT", nullptr, nullptr },
+    { "ElevationGrid", nullptr, nullptr },
+    { "EspduTransform", nullptr, nullptr },
+    { "ExternProtoDeclare", nullptr, nullptr },
+    { "Extrusion", nullptr, nullptr },
+    { "FillProperties", nullptr, nullptr },
+    { "Fog", nullptr, nullptr },
+    { "FontStyle", nullptr, nullptr },
+    { "GeoCoordinate", nullptr, nullptr },
+    { "GeoElevationGrid", nullptr, nullptr },
+    { "GeoLOD", nullptr, nullptr },
+    { "GeoLocation", nullptr, nullptr },
+    { "GeoMetadata", nullptr, nullptr },
+    { "GeoOrigin", nullptr, nullptr },
+    { "GeoPositionInterpolator", nullptr, nullptr },
+    { "GeoTouchSensor", nullptr, nullptr },
+    { "GeoViewpoint", nullptr, nullptr },
+    { "HAnimDisplacer", nullptr, nullptr },
+    { "HAnimHumanoid", nullptr, nullptr },
+    { "HAnimJoint", nullptr, nullptr },
+    { "HAnimSegment", nullptr, nullptr },
+    { "HAnimSite", nullptr, nullptr },
+    { "IMPORT", nullptr, nullptr },
+    { "IS", nullptr, nullptr },
+    { "Inline", nullptr, nullptr },
+    { "IntegerSequencer", nullptr, nullptr },
+    { "IntegerTrigger", nullptr, nullptr },
+    { "KeySensor", nullptr, nullptr },
+    { "LineProperties", nullptr, nullptr },
+    { "LineSet", nullptr, nullptr },
+    { "LoadSensor", nullptr, nullptr },
+    { "MetadataDouble", nullptr, nullptr },
+    { "MetadataFloat", nullptr, nullptr },
+    { "MetadataInteger", nullptr, nullptr },
+    { "MetadataSet", nullptr, nullptr },
+    { "MetadataString", nullptr, nullptr },
+    { "MovieTexture", nullptr, nullptr },
+    { "NavigationInfo", nullptr, nullptr },
+    { "NormalInterpolator", nullptr, nullptr },
+    { "NurbsCurve", nullptr, nullptr },
+    { "NurbsCurve2D", nullptr, nullptr },
+    { "NurbsOrientationInterpolator", nullptr, nullptr },
+    { "NurbsPatchSurface", nullptr, nullptr },
+    { "NurbsPositionInterpolator", nullptr, nullptr },
+    { "NurbsSet", nullptr, nullptr },
+    { "NurbsSurfaceInterpolator", nullptr, nullptr },
+    { "NurbsSweptSurface", nullptr, nullptr },
+    { "NurbsSwungSurface", nullptr, nullptr },
+    { "NurbsTextureCoordinate", nullptr, nullptr },
+    { "NurbsTrimmedSurface", nullptr, nullptr },
+    { "OrientationInterpolator", nullptr, nullptr },
+    { "PixelTexture", nullptr, nullptr },
+    { "PlaneSensor", nullptr, nullptr },
+    { "PointLight", nullptr, nullptr },
+    { "Polyline2D", nullptr, nullptr },
+    { "Polypoint2D", nullptr, nullptr },
+    { "PositionInterpolator", nullptr, nullptr },
+    { "PositionInterpolator2D", nullptr, nullptr },
+    { "ProtoBody", nullptr, nullptr },
+    { "ProtoDeclare", nullptr, nullptr },
+    { "ProtoInterface", nullptr, nullptr },
+    { "ProximitySensor", nullptr, nullptr },
+    { "ReceiverPdu", nullptr, nullptr },
+    { "Rectangle2D", nullptr, nullptr },
+    { "ScalarInterpolator", nullptr, nullptr },
+    { "Scene", nullptr, nullptr },
+    { "SignalPdu", nullptr, nullptr },
+    { "Sound", nullptr, nullptr },
+    { "SphereSensor", nullptr, nullptr },
+    { "SpotLight", nullptr, nullptr },
+    { "StringSensor", nullptr, nullptr },
+    { "Text", nullptr, nullptr },
+    { "TextureBackground", nullptr, nullptr },
+    { "TextureCoordinateGenerator", nullptr, nullptr },
+    { "TimeSensor", nullptr, nullptr },
+    { "TimeTrigger", nullptr, nullptr },
+    { "TouchSensor", nullptr, nullptr },
+    { "TransmitterPdu", nullptr, nullptr },
+    { "TriangleFanSet", nullptr, nullptr },
+    { "TriangleSet", nullptr, nullptr },
+    { "TriangleSet2D", nullptr, nullptr },
+    { "TriangleStripSet", nullptr, nullptr },
+    { "Viewpoint", nullptr, nullptr },
+    { "VisibilitySensor", nullptr, nullptr },
+    { "WorldInfo", nullptr, nullptr },
+    { "X3D", nullptr, nullptr },
+    { "component", nullptr, nullptr },
+    { "connect", nullptr, nullptr },
+    { "field", nullptr, nullptr },
+    { "head", nullptr, nullptr },
+    { "humanoidBodyType", nullptr, nullptr },
+    { "meta", nullptr, nullptr },
+    { "CADAssembly", nullptr, nullptr },
+    { "CADFace", nullptr, nullptr },
+    { "CADLayer", nullptr, nullptr },
+    { "CADPart", nullptr, nullptr },
+    { "ComposedCubeMapTexture", nullptr, nullptr },
+    { "ComposedShader", nullptr, nullptr },
+    { "ComposedTexture3D", nullptr, nullptr },
+    { "FloatVertexAttribute", nullptr, nullptr },
+    { "FogCoordinate", nullptr, nullptr },
+    { "GeneratedCubeMapTexture", nullptr, nullptr },
+    { "ImageCubeMapTexture", nullptr, nullptr },
+    { "ImageTexture3D", nullptr, nullptr },
+    { "IndexedQuadSet", nullptr, nullptr },
+    { "LocalFog", nullptr, nullptr },
+    { "Matrix3VertexAttribute", nullptr, nullptr },
+    { "Matrix4VertexAttribute", nullptr, nullptr },
+    { "PackagedShader", nullptr, nullptr },
+    { "PixelTexture3D", nullptr, nullptr },
+    { "ProgramShader", nullptr, nullptr },
+    { "QuadSet", nullptr, nullptr },
+    { "ShaderPart", nullptr, nullptr },
+    { "ShaderProgram", nullptr, nullptr },
+    { "TextureCoordinate3D", nullptr, nullptr },
+    { "TextureCoordinate4D", nullptr, nullptr },
+    { "TextureTransform3D", nullptr, nullptr },
+    { "TextureTransformMatrix3D", nullptr, nullptr },
+    { "BallJoint", nullptr, nullptr },
+    { "BoundedPhysicsModel", nullptr, nullptr },
+    { "ClipPlane", nullptr, nullptr },
+    { "CollidableOffset", nullptr, nullptr },
+    { "CollidableShape", nullptr, nullptr },
+    { "CollisionCollection", nullptr, nullptr },
+    { "CollisionSensor", nullptr, nullptr },
+    { "CollisionSpace", nullptr, nullptr },
+    { "ColorDamper", nullptr, nullptr },
+    { "ConeEmitter", nullptr, nullptr },
+    { "Contact", nullptr, nullptr },
+    { "CoordinateDamper", nullptr, nullptr },
+    { "DISEntityManager", nullptr, nullptr },
+    { "DISEntityTypeMapping", nullptr, nullptr },
+    { "DoubleAxisHingeJoint", nullptr, nullptr },
+    { "EaseInEaseOut", nullptr, nullptr },
+    { "ExplosionEmitter", nullptr, nullptr },
+    { "ForcePhysicsModel", nullptr, nullptr },
+    { "GeoProximitySensor", nullptr, nullptr },
+    { "GeoTransform", nullptr, nullptr },
+    { "Layer", nullptr, nullptr },
+    { "LayerSet", nullptr, nullptr },
+    { "Layout", nullptr, nullptr },
+    { "LayoutGroup", nullptr, nullptr },
+    { "LayoutLayer", nullptr, nullptr },
+    { "LinePickSensor", nullptr, nullptr },
+    { "MotorJoint", nullptr, nullptr },
+    { "OrientationChaser", nullptr, nullptr },
+    { "OrientationDamper", nullptr, nullptr },
+    { "OrthoViewpoint", nullptr, nullptr },
+    { "ParticleSystem", nullptr, nullptr },
+    { "PickableGroup", nullptr, nullptr },
+    { "PointEmitter", nullptr, nullptr },
+    { "PointPickSensor", nullptr, nullptr },
+    { "PolylineEmitter", nullptr, nullptr },
+    { "PositionChaser", nullptr, nullptr },
+    { "PositionChaser2D", nullptr, nullptr },
+    { "PositionDamper", nullptr, nullptr },
+    { "PositionDamper2D", nullptr, nullptr },
+    { "PrimitivePickSensor", nullptr, nullptr },
+    { "RigidBody", nullptr, nullptr },
+    { "RigidBodyCollection", nullptr, nullptr },
+    { "ScalarChaser", nullptr, nullptr },
+    { "ScreenFontStyle", nullptr, nullptr },
+    { "ScreenGroup", nullptr, nullptr },
+    { "SingleAxisHingeJoint", nullptr, nullptr },
+    { "SliderJoint", nullptr, nullptr },
+    { "SplinePositionInterpolator", nullptr, nullptr },
+    { "SplinePositionInterpolator2D", nullptr, nullptr },
+    { "SplineScalarInterpolator", nullptr, nullptr },
+    { "SquadOrientationInterpolator", nullptr, nullptr },
+    { "SurfaceEmitter", nullptr, nullptr },
+    { "TexCoordDamper", nullptr, nullptr },
+    { "TextureProperties", nullptr, nullptr },
+    { "TransformSensor", nullptr, nullptr },
+    { "TwoSidedMaterial", nullptr, nullptr },
+    { "UniversalJoint", nullptr, nullptr },
+    { "ViewpointGroup", nullptr, nullptr },
+    { "Viewport", nullptr, nullptr },
+    { "VolumeEmitter", nullptr, nullptr },
+    { "VolumePickSensor", nullptr, nullptr },
+    { "WindPhysicsModel", nullptr, nullptr }
+};
+
+static const FIQName attributeNameTable_3_2[] = {
+    { "DEF", nullptr, nullptr },
+    { "USE", nullptr, nullptr },
+    { "containerField", nullptr, nullptr },
+    { "fromNode", nullptr, nullptr },
+    { "fromField", nullptr, nullptr },
+    { "toNode", nullptr, nullptr },
+    { "toField", nullptr, nullptr },
+    { "name", nullptr, nullptr },
+    { "value", nullptr, nullptr },
+    { "color", nullptr, nullptr },
+    { "colorIndex", nullptr, nullptr },
+    { "coordIndex", nullptr, nullptr },
+    { "texCoordIndex", nullptr, nullptr },
+    { "normalIndex", nullptr, nullptr },
+    { "colorPerVertex", nullptr, nullptr },
+    { "normalPerVertex", nullptr, nullptr },
+    { "rotation", nullptr, nullptr },
+    { "scale", nullptr, nullptr },
+    { "center", nullptr, nullptr },
+    { "scaleOrientation", nullptr, nullptr },
+    { "translation", nullptr, nullptr },
+    { "url", nullptr, nullptr },
+    { "repeatS", nullptr, nullptr },
+    { "repeatT", nullptr, nullptr },
+    { "point", nullptr, nullptr },
+    { "vector", nullptr, nullptr },
+    { "range", nullptr, nullptr },
+    { "ambientIntensity", nullptr, nullptr },
+    { "diffuseColor", nullptr, nullptr },
+    { "emissiveColor", nullptr, nullptr },
+    { "shininess", nullptr, nullptr },
+    { "specularColor", nullptr, nullptr },
+    { "transparency", nullptr, nullptr },
+    { "whichChoice", nullptr, nullptr },
+    { "index", nullptr, nullptr },
+    { "mode", nullptr, nullptr },
+    { "source", nullptr, nullptr },
+    { "function", nullptr, nullptr },
+    { "alpha", nullptr, nullptr },
+    { "vertexCount", nullptr, nullptr },
+    { "radius", nullptr, nullptr },
+    { "size", nullptr, nullptr },
+    { "height", nullptr, nullptr },
+    { "solid", nullptr, nullptr },
+    { "ccw", nullptr, nullptr },
+    { "key", nullptr, nullptr },
+    { "keyValue", nullptr, nullptr },
+    { "enabled", nullptr, nullptr },
+    { "direction", nullptr, nullptr },
+    { "position", nullptr, nullptr },
+    { "orientation", nullptr, nullptr },
+    { "bboxCenter", nullptr, nullptr },
+    { "bboxSize", nullptr, nullptr },
+    { "AS", nullptr, nullptr },
+    { "InlineDEF", nullptr, nullptr },
+    { "accessType", nullptr, nullptr },
+    { "actionKeyPress", nullptr, nullptr },
+    { "actionKeyRelease", nullptr, nullptr },
+    { "address", nullptr, nullptr },
+    { "altKey", nullptr, nullptr },
+    { "antennaLocation", nullptr, nullptr },
+    { "antennaPatternLength", nullptr, nullptr },
+    { "antennaPatternType", nullptr, nullptr },
+    { "applicationID", nullptr, nullptr },
+    { "articulationParameterArray", nullptr, nullptr },
+    { "articulationParameterChangeIndicatorArray", nullptr, nullptr },
+    { "articulationParameterCount", nullptr, nullptr },
+    { "articulationParameterDesignatorArray", nullptr, nullptr },
+    { "articulationParameterIdPartAttachedArray", nullptr, nullptr },
+    { "articulationParameterTypeArray", nullptr, nullptr },
+    { "attenuation", nullptr, nullptr },
+    { "autoOffset", nullptr, nullptr },
+    { "avatarSize", nullptr, nullptr },
+    { "axisOfRotation", nullptr, nullptr },
+    { "backUrl", nullptr, nullptr },
+    { "beamWidth", nullptr, nullptr },
+    { "beginCap", nullptr, nullptr },
+    { "bindTime", nullptr, nullptr },
+    { "bottom", nullptr, nullptr },
+    { "bottomRadius", nullptr, nullptr },
+    { "bottomUrl", nullptr, nullptr },
+    { "centerOfMass", nullptr, nullptr },
+    { "centerOfRotation", nullptr, nullptr },
+    { "child1Url", nullptr, nullptr },
+    { "child2Url", nullptr, nullptr },
+    { "child3Url", nullptr, nullptr },
+    { "child4Url", nullptr, nullptr },
+    { "class", nullptr, nullptr },
+    { "closureType", nullptr, nullptr },
+    { "collideTime", nullptr, nullptr },
+    { "content", nullptr, nullptr },
+    { "controlKey", nullptr, nullptr },
+    { "controlPoint", nullptr, nullptr },
+    { "convex", nullptr, nullptr },
+    { "coordinateSystem", nullptr, nullptr },
+    { "copyright", nullptr, nullptr },
+    { "creaseAngle", nullptr, nullptr },
+    { "crossSection", nullptr, nullptr },
+    { "cryptoKeyID", nullptr, nullptr },
+    { "cryptoSystem", nullptr, nullptr },
+    { "cutOffAngle", nullptr, nullptr },
+    { "cycleInterval", nullptr, nullptr },
+    { "cycleTime", nullptr, nullptr },
+    { "data", nullptr, nullptr },
+    { "dataFormat", nullptr, nullptr },
+    { "dataLength", nullptr, nullptr },
+    { "dataUrl", nullptr, nullptr },
+    { "date", nullptr, nullptr },
+    { "deadReckoning", nullptr, nullptr },
+    { "deletionAllowed", nullptr, nullptr },
+    { "description", nullptr, nullptr },
+    { "detonateTime", nullptr, nullptr },
+    { "dir", nullptr, nullptr },
+    { "directOutput", nullptr, nullptr },
+    { "diskAngle", nullptr, nullptr },
+    { "displacements", nullptr, nullptr },
+    { "documentation", nullptr, nullptr },
+    { "elapsedTime", nullptr, nullptr },
+    { "ellipsoid", nullptr, nullptr },
+    { "encodingScheme", nullptr, nullptr },
+    { "endAngle", nullptr, nullptr },
+    { "endCap", nullptr, nullptr },
+    { "enterTime", nullptr, nullptr },
+    { "enteredText", nullptr, nullptr },
+    { "entityCategory", nullptr, nullptr },
+    { "entityCountry", nullptr, nullptr },
+    { "entityDomain", nullptr, nullptr },
+    { "entityExtra", nullptr, nullptr },
+    { "entityID", nullptr, nullptr },
+    { "entityKind", nullptr, nullptr },
+    { "entitySpecific", nullptr, nullptr },
+    { "entitySubCategory", nullptr, nullptr },
+    { "exitTime", nullptr, nullptr },
+    { "extent", nullptr, nullptr },
+    { "family", nullptr, nullptr },
+    { "fanCount", nullptr, nullptr },
+    { "fieldOfView", nullptr, nullptr },
+    { "filled", nullptr, nullptr },
+    { "finalText", nullptr, nullptr },
+    { "fireMissionIndex", nullptr, nullptr },
+    { "fired1", nullptr, nullptr },
+    { "fired2", nullptr, nullptr },
+    { "firedTime", nullptr, nullptr },
+    { "firingRange", nullptr, nullptr },
+    { "firingRate", nullptr, nullptr },
+    { "fogType", nullptr, nullptr },
+    { "forceID", nullptr, nullptr },
+    { "frequency", nullptr, nullptr },
+    { "frontUrl", nullptr, nullptr },
+    { "fuse", nullptr, nullptr },
+    { "geoCoords", nullptr, nullptr },
+    { "geoGridOrigin", nullptr, nullptr },
+    { "geoSystem", nullptr, nullptr },
+    { "groundAngle", nullptr, nullptr },
+    { "groundColor", nullptr, nullptr },
+    { "hatchColor", nullptr, nullptr },
+    { "hatchStyle", nullptr, nullptr },
+    { "hatched", nullptr, nullptr },
+    { "headlight", nullptr, nullptr },
+    { "horizontal", nullptr, nullptr },
+    { "horizontalDatum", nullptr, nullptr },
+    { "http-equiv", nullptr, nullptr },
+    { "image", nullptr, nullptr },
+    { "importedDEF", nullptr, nullptr },
+    { "info", nullptr, nullptr },
+    { "innerRadius", nullptr, nullptr },
+    { "inputFalse", nullptr, nullptr },
+    { "inputNegate", nullptr, nullptr },
+    { "inputSource", nullptr, nullptr },
+    { "inputTrue", nullptr, nullptr },
+    { "integerKey", nullptr, nullptr },
+    { "intensity", nullptr, nullptr },
+    { "jump", nullptr, nullptr },
+    { "justify", nullptr, nullptr },
+    { "keyPress", nullptr, nullptr },
+    { "keyRelease", nullptr, nullptr },
+    { "knot", nullptr, nullptr },
+    { "lang", nullptr, nullptr },
+    { "language", nullptr, nullptr },
+    { "leftToRight", nullptr, nullptr },
+    { "leftUrl", nullptr, nullptr },
+    { "length", nullptr, nullptr },
+    { "lengthOfModulationParameters", nullptr, nullptr },
+    { "level", nullptr, nullptr },
+    { "limitOrientation", nullptr, nullptr },
+    { "lineSegments", nullptr, nullptr },
+    { "linearAcceleration", nullptr, nullptr },
+    { "linearVelocity", nullptr, nullptr },
+    { "linetype", nullptr, nullptr },
+    { "linewidthScaleFactor", nullptr, nullptr },
+    { "llimit", nullptr, nullptr },
+    { "load", nullptr, nullptr },
+    { "loadTime", nullptr, nullptr },
+    { "localDEF", nullptr, nullptr },
+    { "location", nullptr, nullptr },
+    { "loop", nullptr, nullptr },
+    { "marking", nullptr, nullptr },
+    { "mass", nullptr, nullptr },
+    { "maxAngle", nullptr, nullptr },
+    { "maxBack", nullptr, nullptr },
+    { "maxExtent", nullptr, nullptr },
+    { "maxFront", nullptr, nullptr },
+    { "maxPosition", nullptr, nullptr },
+    { "metadataFormat", nullptr, nullptr },
+    { "minAngle", nullptr, nullptr },
+    { "minBack", nullptr, nullptr },
+    { "minFront", nullptr, nullptr },
+    { "minPosition", nullptr, nullptr },
+    { "modulationTypeDetail", nullptr, nullptr },
+    { "modulationTypeMajor", nullptr, nullptr },
+    { "modulationTypeSpreadSpectrum", nullptr, nullptr },
+    { "modulationTypeSystem", nullptr, nullptr },
+    { "momentsOfInertia", nullptr, nullptr },
+    { "multicastRelayHost", nullptr, nullptr },
+    { "multicastRelayPort", nullptr, nullptr },
+    { "munitionApplicationID", nullptr, nullptr },
+    { "munitionEndPoint", nullptr, nullptr },
+    { "munitionEntityID", nullptr, nullptr },
+    { "munitionQuantity", nullptr, nullptr },
+    { "munitionSiteID", nullptr, nullptr },
+    { "munitionStartPoint", nullptr, nullptr },
+    { "mustEvaluate", nullptr, nullptr },
+    { "navType", nullptr, nullptr },
+    { "networkMode", nullptr, nullptr },
+    { "next", nullptr, nullptr },
+    { "nodeField", nullptr, nullptr },
+    { "offset", nullptr, nullptr },
+    { "on", nullptr, nullptr },
+    { "order", nullptr, nullptr },
+    { "originator", nullptr, nullptr },
+    { "outerRadius", nullptr, nullptr },
+    { "parameter", nullptr, nullptr },
+    { "pauseTime", nullptr, nullptr },
+    { "pitch", nullptr, nullptr },
+    { "points", nullptr, nullptr },
+    { "port", nullptr, nullptr },
+    { "power", nullptr, nullptr },
+    { "previous", nullptr, nullptr },
+    { "priority", nullptr, nullptr },
+    { "profile", nullptr, nullptr },
+    { "progress", nullptr, nullptr },
+    { "protoField", nullptr, nullptr },
+    { "radioEntityTypeCategory", nullptr, nullptr },
+    { "radioEntityTypeCountry", nullptr, nullptr },
+    { "radioEntityTypeDomain", nullptr, nullptr },
+    { "radioEntityTypeKind", nullptr, nullptr },
+    { "radioEntityTypeNomenclature", nullptr, nullptr },
+    { "radioEntityTypeNomenclatureVersion", nullptr, nullptr },
+    { "radioID", nullptr, nullptr },
+    { "readInterval", nullptr, nullptr },
+    { "receivedPower", nullptr, nullptr },
+    { "receiverState", nullptr, nullptr },
+    { "reference", nullptr, nullptr },
+    { "relativeAntennaLocation", nullptr, nullptr },
+    { "resolution", nullptr, nullptr },
+    { "resumeTime", nullptr, nullptr },
+    { "rightUrl", nullptr, nullptr },
+    { "rootUrl", nullptr, nullptr },
+    { "rotateYUp", nullptr, nullptr },
+    { "rtpHeaderExpected", nullptr, nullptr },
+    { "sampleRate", nullptr, nullptr },
+    { "samples", nullptr, nullptr },
+    { "shiftKey", nullptr, nullptr },
+    { "side", nullptr, nullptr },
+    { "siteID", nullptr, nullptr },
+    { "skinCoordIndex", nullptr, nullptr },
+    { "skinCoordWeight", nullptr, nullptr },
+    { "skyAngle", nullptr, nullptr },
+    { "skyColor", nullptr, nullptr },
+    { "spacing", nullptr, nullptr },
+    { "spatialize", nullptr, nullptr },
+    { "speed", nullptr, nullptr },
+    { "speedFactor", nullptr, nullptr },
+    { "spine", nullptr, nullptr },
+    { "startAngle", nullptr, nullptr },
+    { "startTime", nullptr, nullptr },
+    { "stiffness", nullptr, nullptr },
+    { "stopTime", nullptr, nullptr },
+    { "string", nullptr, nullptr },
+    { "stripCount", nullptr, nullptr },
+    { "style", nullptr, nullptr },
+    { "summary", nullptr, nullptr },
+    { "tdlType", nullptr, nullptr },
+    { "tessellation", nullptr, nullptr },
+    { "tessellationScale", nullptr, nullptr },
+    { "time", nullptr, nullptr },
+    { "timeOut", nullptr, nullptr },
+    { "timestamp", nullptr, nullptr },
+    { "title", nullptr, nullptr },
+    { "toggle", nullptr, nullptr },
+    { "top", nullptr, nullptr },
+    { "topToBottom", nullptr, nullptr },
+    { "topUrl", nullptr, nullptr },
+    { "touchTime", nullptr, nullptr },
+    { "transmitFrequencyBandwidth", nullptr, nullptr },
+    { "transmitState", nullptr, nullptr },
+    { "transmitterApplicationID", nullptr, nullptr },
+    { "transmitterEntityID", nullptr, nullptr },
+    { "transmitterRadioID", nullptr, nullptr },
+    { "transmitterSiteID", nullptr, nullptr },
+    { "transparent", nullptr, nullptr },
+    { "triggerTime", nullptr, nullptr },
+    { "triggerTrue", nullptr, nullptr },
+    { "triggerValue", nullptr, nullptr },
+    { "type", nullptr, nullptr },
+    { "uDimension", nullptr, nullptr },
+    { "uKnot", nullptr, nullptr },
+    { "uOrder", nullptr, nullptr },
+    { "uTessellation", nullptr, nullptr },
+    { "ulimit", nullptr, nullptr },
+    { "vDimension", nullptr, nullptr },
+    { "vKnot", nullptr, nullptr },
+    { "vOrder", nullptr, nullptr },
+    { "vTessellation", nullptr, nullptr },
+    { "version", nullptr, nullptr },
+    { "verticalDatum", nullptr, nullptr },
+    { "vertices", nullptr, nullptr },
+    { "visibilityLimit", nullptr, nullptr },
+    { "visibilityRange", nullptr, nullptr },
+    { "warhead", nullptr, nullptr },
+    { "weight", nullptr, nullptr },
+    { "whichGeometry", nullptr, nullptr },
+    { "writeInterval", nullptr, nullptr },
+    { "xDimension", nullptr, nullptr },
+    { "xSpacing", nullptr, nullptr },
+    { "yScale", nullptr, nullptr },
+    { "zDimension", nullptr, nullptr },
+    { "zSpacing", nullptr, nullptr },
+    { "visible", nullptr, nullptr },
+    { "repeatR", nullptr, nullptr },
+    { "texture", nullptr, nullptr },
+    { "back", nullptr, nullptr },
+    { "front", nullptr, nullptr },
+    { "left", nullptr, nullptr },
+    { "right", nullptr, nullptr },
+    { "parts", nullptr, nullptr },
+    { "isSelected", nullptr, nullptr },
+    { "isValid", nullptr, nullptr },
+    { "numComponents", nullptr, nullptr },
+    { "depth", nullptr, nullptr },
+    { "update", nullptr, nullptr },
+    { "fogCoord", nullptr, nullptr },
+    { "texCoord", nullptr, nullptr },
+    { "activate", nullptr, nullptr },
+    { "programs", nullptr, nullptr },
+    { "matrix", nullptr, nullptr },
+    { "anchorPoint", nullptr, nullptr },
+    { "body1", nullptr, nullptr },
+    { "body2", nullptr, nullptr },
+    { "mustOutput", nullptr, nullptr },
+    { "body1AnchorPoint", nullptr, nullptr },
+    { "body2AnchorPoint", nullptr, nullptr },
+    { "plane", nullptr, nullptr },
+    { "appliedParameters", nullptr, nullptr },
+    { "bounce", nullptr, nullptr },
+    { "frictionCoefficients", nullptr, nullptr },
+    { "minBounceSpeed", nullptr, nullptr },
+    { "slipFactors", nullptr, nullptr },
+    { "softnessConstantForceMix", nullptr, nullptr },
+    { "softnessErrorCorrection", nullptr, nullptr },
+    { "surfaceSpeed", nullptr, nullptr },
+    { "isActive", nullptr, nullptr },
+    { "useGeometry", nullptr, nullptr },
+    { "set_destination", nullptr, nullptr },
+    { "set_value", nullptr, nullptr },
+    { "tau", nullptr, nullptr },
+    { "tolerance", nullptr, nullptr },
+    { "value_changed", nullptr, nullptr },
+    { "initialDestination", nullptr, nullptr },
+    { "initialValue", nullptr, nullptr },
+    { "angle", nullptr, nullptr },
+    { "variation", nullptr, nullptr },
+    { "surfaceArea", nullptr, nullptr },
+    { "frictionDirection", nullptr, nullptr },
+    { "slipCoefficients", nullptr, nullptr },
+    { "category", nullptr, nullptr },
+    { "country", nullptr, nullptr },
+    { "domain", nullptr, nullptr },
+    { "extra", nullptr, nullptr },
+    { "kind", nullptr, nullptr },
+    { "specific", nullptr, nullptr },
+    { "subcategory", nullptr, nullptr },
+    { "axis1", nullptr, nullptr },
+    { "axis2", nullptr, nullptr },
+    { "desiredAngularVelocity1", nullptr, nullptr },
+    { "desiredAngularVelocity2", nullptr, nullptr },
+    { "maxAngle1", nullptr, nullptr },
+    { "maxTorque1", nullptr, nullptr },
+    { "maxTorque2", nullptr, nullptr },
+    { "minAngle1", nullptr, nullptr },
+    { "stopBounce1", nullptr, nullptr },
+    { "stopConstantForceMix1", nullptr, nullptr },
+    { "stopErrorCorrection1", nullptr, nullptr },
+    { "suspensionErrorCorrection", nullptr, nullptr },
+    { "suspensionForce", nullptr, nullptr },
+    { "body1Axis", nullptr, nullptr },
+    { "body2Axis", nullptr, nullptr },
+    { "hinge1Angle", nullptr, nullptr },
+    { "hinge1AngleRate", nullptr, nullptr },
+    { "hinge2Angle", nullptr, nullptr },
+    { "hinge2AngleRate", nullptr, nullptr },
+    { "set_fraction", nullptr, nullptr },
+    { "easeInEaseOut", nullptr, nullptr },
+    { "modifiedFraction_changed", nullptr, nullptr },
+    { "force", nullptr, nullptr },
+    { "geoCenter", nullptr, nullptr },
+    { "centerOfRotation_changed", nullptr, nullptr },
+    { "geoCoord_changed", nullptr, nullptr },
+    { "orientation_changed", nullptr, nullptr },
+    { "position_changed", nullptr, nullptr },
+    { "isPickable", nullptr, nullptr },
+    { "viewport", nullptr, nullptr },
+    { "activeLayer", nullptr, nullptr },
+    { "align", nullptr, nullptr },
+    { "offsetUnits", nullptr, nullptr },
+    { "scaleMode", nullptr, nullptr },
+    { "sizeUnits", nullptr, nullptr },
+    { "layout", nullptr, nullptr },
+    { "objectType", nullptr, nullptr },
+    { "pickedNormal", nullptr, nullptr },
+    { "pickedPoint", nullptr, nullptr },
+    { "pickedTextureCoordinate", nullptr, nullptr },
+    { "intersectionType", nullptr, nullptr },
+    { "sortOrder", nullptr, nullptr },
+    { "axis1Angle", nullptr, nullptr },
+    { "axis1Torque", nullptr, nullptr },
+    { "axis2Angle", nullptr, nullptr },
+    { "axis2Torque", nullptr, nullptr },
+    { "axis3Angle", nullptr, nullptr },
+    { "axis3Torque", nullptr, nullptr },
+    { "enabledAxies", nullptr, nullptr },
+    { "motor1Axis", nullptr, nullptr },
+    { "motor2Axis", nullptr, nullptr },
+    { "motor3Axis", nullptr, nullptr },
+    { "stop1Bounce", nullptr, nullptr },
+    { "stop1ErrorCorrection", nullptr, nullptr },
+    { "stop2Bounce", nullptr, nullptr },
+    { "stop2ErrorCorrection", nullptr, nullptr },
+    { "stop3Bounce", nullptr, nullptr },
+    { "stop3ErrorCorrection", nullptr, nullptr },
+    { "motor1Angle", nullptr, nullptr },
+    { "motor1AngleRate", nullptr, nullptr },
+    { "motor2Angle", nullptr, nullptr },
+    { "motor2AngleRate", nullptr, nullptr },
+    { "motor3Angle", nullptr, nullptr },
+    { "motor3AngleRate", nullptr, nullptr },
+    { "autoCalc", nullptr, nullptr },
+    { "duration", nullptr, nullptr },
+    { "retainUserOffsets", nullptr, nullptr },
+    { "isBound", nullptr, nullptr },
+    { "appearance", nullptr, nullptr },
+    { "createParticles", nullptr, nullptr },
+    { "lifetimeVariation", nullptr, nullptr },
+    { "maxParticles", nullptr, nullptr },
+    { "particleLifetime", nullptr, nullptr },
+    { "particleSize", nullptr, nullptr },
+    { "colorKey", nullptr, nullptr },
+    { "geometryType", nullptr, nullptr },
+    { "texCoordKey", nullptr, nullptr },
+    { "pickable", nullptr, nullptr },
+    { "angularDampingFactor", nullptr, nullptr },
+    { "angularVelocity", nullptr, nullptr },
+    { "autoDamp", nullptr, nullptr },
+    { "autoDisable", nullptr, nullptr },
+    { "disableAngularSpeed", nullptr, nullptr },
+    { "disableLinearSpeed", nullptr, nullptr },
+    { "disableTime", nullptr, nullptr },
+    { "finiteRotationAxis", nullptr, nullptr },
+    { "fixed", nullptr, nullptr },
+    { "forces", nullptr, nullptr },
+    { "inertia", nullptr, nullptr },
+    { "linearDampingFactor", nullptr, nullptr },
+    { "torques", nullptr, nullptr },
+    { "useFiniteRotation", nullptr, nullptr },
+    { "useGlobalForce", nullptr, nullptr },
+    { "constantForceMix", nullptr, nullptr },
+    { "constantSurfaceThickness", nullptr, nullptr },
+    { "errorCorrection", nullptr, nullptr },
+    { "iterations", nullptr, nullptr },
+    { "maxCorrectionSpeed", nullptr, nullptr },
+    { "preferAccuracy", nullptr, nullptr },
+    { "pointSize", nullptr, nullptr },
+    { "stopBounce", nullptr, nullptr },
+    { "stopErrorCorrection", nullptr, nullptr },
+    { "angleRate", nullptr, nullptr },
+    { "maxSeparation", nullptr, nullptr },
+    { "minSeparation", nullptr, nullptr },
+    { "separation", nullptr, nullptr },
+    { "separationRate", nullptr, nullptr },
+    { "closed", nullptr, nullptr },
+    { "keyVelocity", nullptr, nullptr },
+    { "normalizeVelocity", nullptr, nullptr },
+    { "surface", nullptr, nullptr },
+    { "anisotropicDegree", nullptr, nullptr },
+    { "borderColor", nullptr, nullptr },
+    { "borderWidth", nullptr, nullptr },
+    { "boundaryModeS", nullptr, nullptr },
+    { "boundaryModeT", nullptr, nullptr },
+    { "boundaryModeR", nullptr, nullptr },
+    { "magnificationFilter", nullptr, nullptr },
+    { "minificationFilter", nullptr, nullptr },
+    { "textureCompression", nullptr, nullptr },
+    { "texturePriority", nullptr, nullptr },
+    { "generateMipMaps", nullptr, nullptr },
+    { "targetObject", nullptr, nullptr },
+    { "backAmbientIntensity", nullptr, nullptr },
+    { "backDiffuseColor", nullptr, nullptr },
+    { "backEmissiveColor", nullptr, nullptr },
+    { "backShininess", nullptr, nullptr },
+    { "backSpecularColor", nullptr, nullptr },
+    { "separateBackColor", nullptr, nullptr },
+    { "displayed", nullptr, nullptr },
+    { "clipBoundary", nullptr, nullptr },
+    { "internal", nullptr, nullptr },
+    { "gustiness", nullptr, nullptr },
+    { "turbulence", nullptr, nullptr }
+};
+
+FIVocabulary X3D_vocabulary_3_2 = {
+    nullptr, 0,
+    encodingAlgorithmTable_3_2, 8,
+    nullptr, 0,
+    nullptr, 0,
+    nullptr, 0,
+    nullptr, 0,
+    nullptr, 0,
+    attributeValueTable_3_2, 2,
+    nullptr, 0,
+    nullptr, 0,
+    elementNameTable_3_2, 233,
+    attributeNameTable_3_2, 516
+};
+
+static const char *encodingAlgorithmTable_3_3[] = {
+    "encoder://web3d.org/QuantizedFloatArrayEncoder",
+    "encoder://web3d.org/DeltazlibIntArrayEncoder",
+    "encoder://web3d.org/QuantizedzlibFloatArrayEncoder",
+    "encoder://web3d.org/zlibFloatArrayEncoder",
+    "encoder://web3d.org/QuantizedDoubleArrayEncoder",
+    "encoder://web3d.org/zlibDoubleArrayEncoder",
+    "encoder://web3d.org/QuantizedzlibDoubleArrayEncoder",
+    "encoder://web3d.org/RangeIntArrayEncoder"
+};
+
+static const std::shared_ptr<const FIValue> attributeValueTable_3_3[] = {
+    FIStringValue::create("false"),
+    FIStringValue::create("true")
+};
+
+static const FIQName elementNameTable_3_3[] = {
+    { "Shape", nullptr, nullptr },
+    { "Appearance", nullptr, nullptr },
+    { "Material", nullptr, nullptr },
+    { "IndexedFaceSet", nullptr, nullptr },
+    { "ProtoInstance", nullptr, nullptr },
+    { "Transform", nullptr, nullptr },
+    { "ImageTexture", nullptr, nullptr },
+    { "TextureTransform", nullptr, nullptr },
+    { "Coordinate", nullptr, nullptr },
+    { "Normal", nullptr, nullptr },
+    { "Color", nullptr, nullptr },
+    { "ColorRGBA", nullptr, nullptr },
+    { "TextureCoordinate", nullptr, nullptr },
+    { "ROUTE", nullptr, nullptr },
+    { "fieldValue", nullptr, nullptr },
+    { "Group", nullptr, nullptr },
+    { "LOD", nullptr, nullptr },
+    { "Switch", nullptr, nullptr },
+    { "Script", nullptr, nullptr },
+    { "IndexedTriangleFanSet", nullptr, nullptr },
+    { "IndexedTriangleSet", nullptr, nullptr },
+    { "IndexedTriangleStripSet", nullptr, nullptr },
+    { "MultiTexture", nullptr, nullptr },
+    { "MultiTextureCoordinate", nullptr, nullptr },
+    { "MultiTextureTransform", nullptr, nullptr },
+    { "IndexedLineSet", nullptr, nullptr },
+    { "PointSet", nullptr, nullptr },
+    { "StaticGroup", nullptr, nullptr },
+    { "Sphere", nullptr, nullptr },
+    { "Box", nullptr, nullptr },
+    { "Cone", nullptr, nullptr },
+    { "Anchor", nullptr, nullptr },
+    { "Arc2D", nullptr, nullptr },
+    { "ArcClose2D", nullptr, nullptr },
+    { "AudioClip", nullptr, nullptr },
+    { "Background", nullptr, nullptr },
+    { "Billboard", nullptr, nullptr },
+    { "BooleanFilter", nullptr, nullptr },
+    { "BooleanSequencer", nullptr, nullptr },
+    { "BooleanToggle", nullptr, nullptr },
+    { "BooleanTrigger", nullptr, nullptr },
+    { "Circle2D", nullptr, nullptr },
+    { "Collision", nullptr, nullptr },
+    { "ColorInterpolator", nullptr, nullptr },
+    { "Contour2D", nullptr, nullptr },
+    { "ContourPolyline2D", nullptr, nullptr },
+    { "CoordinateDouble", nullptr, nullptr },
+    { "CoordinateInterpolator", nullptr, nullptr },
+    { "CoordinateInterpolator2D", nullptr, nullptr },
+    { "Cylinder", nullptr, nullptr },
+    { "CylinderSensor", nullptr, nullptr },
+    { "DirectionalLight", nullptr, nullptr },
+    { "Disk2D", nullptr, nullptr },
+    { "EXPORT", nullptr, nullptr },
+    { "ElevationGrid", nullptr, nullptr },
+    { "EspduTransform", nullptr, nullptr },
+    { "ExternProtoDeclare", nullptr, nullptr },
+    { "Extrusion", nullptr, nullptr },
+    { "FillProperties", nullptr, nullptr },
+    { "Fog", nullptr, nullptr },
+    { "FontStyle", nullptr, nullptr },
+    { "GeoCoordinate", nullptr, nullptr },
+    { "GeoElevationGrid", nullptr, nullptr },
+    { "GeoLOD", nullptr, nullptr },
+    { "GeoLocation", nullptr, nullptr },
+    { "GeoMetadata", nullptr, nullptr },
+    { "GeoOrigin", nullptr, nullptr },
+    { "GeoPositionInterpolator", nullptr, nullptr },
+    { "GeoTouchSensor", nullptr, nullptr },
+    { "GeoViewpoint", nullptr, nullptr },
+    { "HAnimDisplacer", nullptr, nullptr },
+    { "HAnimHumanoid", nullptr, nullptr },
+    { "HAnimJoint", nullptr, nullptr },
+    { "HAnimSegment", nullptr, nullptr },
+    { "HAnimSite", nullptr, nullptr },
+    { "IMPORT", nullptr, nullptr },
+    { "IS", nullptr, nullptr },
+    { "Inline", nullptr, nullptr },
+    { "IntegerSequencer", nullptr, nullptr },
+    { "IntegerTrigger", nullptr, nullptr },
+    { "KeySensor", nullptr, nullptr },
+    { "LineProperties", nullptr, nullptr },
+    { "LineSet", nullptr, nullptr },
+    { "LoadSensor", nullptr, nullptr },
+    { "MetadataDouble", nullptr, nullptr },
+    { "MetadataFloat", nullptr, nullptr },
+    { "MetadataInteger", nullptr, nullptr },
+    { "MetadataSet", nullptr, nullptr },
+    { "MetadataString", nullptr, nullptr },
+    { "MovieTexture", nullptr, nullptr },
+    { "NavigationInfo", nullptr, nullptr },
+    { "NormalInterpolator", nullptr, nullptr },
+    { "NurbsCurve", nullptr, nullptr },
+    { "NurbsCurve2D", nullptr, nullptr },
+    { "NurbsOrientationInterpolator", nullptr, nullptr },
+    { "NurbsPatchSurface", nullptr, nullptr },
+    { "NurbsPositionInterpolator", nullptr, nullptr },
+    { "NurbsSet", nullptr, nullptr },
+    { "NurbsSurfaceInterpolator", nullptr, nullptr },
+    { "NurbsSweptSurface", nullptr, nullptr },
+    { "NurbsSwungSurface", nullptr, nullptr },
+    { "NurbsTextureCoordinate", nullptr, nullptr },
+    { "NurbsTrimmedSurface", nullptr, nullptr },
+    { "OrientationInterpolator", nullptr, nullptr },
+    { "PixelTexture", nullptr, nullptr },
+    { "PlaneSensor", nullptr, nullptr },
+    { "PointLight", nullptr, nullptr },
+    { "Polyline2D", nullptr, nullptr },
+    { "Polypoint2D", nullptr, nullptr },
+    { "PositionInterpolator", nullptr, nullptr },
+    { "PositionInterpolator2D", nullptr, nullptr },
+    { "ProtoBody", nullptr, nullptr },
+    { "ProtoDeclare", nullptr, nullptr },
+    { "ProtoInterface", nullptr, nullptr },
+    { "ProximitySensor", nullptr, nullptr },
+    { "ReceiverPdu", nullptr, nullptr },
+    { "Rectangle2D", nullptr, nullptr },
+    { "ScalarInterpolator", nullptr, nullptr },
+    { "Scene", nullptr, nullptr },
+    { "SignalPdu", nullptr, nullptr },
+    { "Sound", nullptr, nullptr },
+    { "SphereSensor", nullptr, nullptr },
+    { "SpotLight", nullptr, nullptr },
+    { "StringSensor", nullptr, nullptr },
+    { "Text", nullptr, nullptr },
+    { "TextureBackground", nullptr, nullptr },
+    { "TextureCoordinateGenerator", nullptr, nullptr },
+    { "TimeSensor", nullptr, nullptr },
+    { "TimeTrigger", nullptr, nullptr },
+    { "TouchSensor", nullptr, nullptr },
+    { "TransmitterPdu", nullptr, nullptr },
+    { "TriangleFanSet", nullptr, nullptr },
+    { "TriangleSet", nullptr, nullptr },
+    { "TriangleSet2D", nullptr, nullptr },
+    { "TriangleStripSet", nullptr, nullptr },
+    { "Viewpoint", nullptr, nullptr },
+    { "VisibilitySensor", nullptr, nullptr },
+    { "WorldInfo", nullptr, nullptr },
+    { "X3D", nullptr, nullptr },
+    { "component", nullptr, nullptr },
+    { "connect", nullptr, nullptr },
+    { "field", nullptr, nullptr },
+    { "head", nullptr, nullptr },
+    { "humanoidBodyType", nullptr, nullptr },
+    { "meta", nullptr, nullptr },
+    { "CADAssembly", nullptr, nullptr },
+    { "CADFace", nullptr, nullptr },
+    { "CADLayer", nullptr, nullptr },
+    { "CADPart", nullptr, nullptr },
+    { "ComposedCubeMapTexture", nullptr, nullptr },
+    { "ComposedShader", nullptr, nullptr },
+    { "ComposedTexture3D", nullptr, nullptr },
+    { "FloatVertexAttribute", nullptr, nullptr },
+    { "FogCoordinate", nullptr, nullptr },
+    { "GeneratedCubeMapTexture", nullptr, nullptr },
+    { "ImageCubeMapTexture", nullptr, nullptr },
+    { "ImageTexture3D", nullptr, nullptr },
+    { "IndexedQuadSet", nullptr, nullptr },
+    { "LocalFog", nullptr, nullptr },
+    { "Matrix3VertexAttribute", nullptr, nullptr },
+    { "Matrix4VertexAttribute", nullptr, nullptr },
+    { "PackagedShader", nullptr, nullptr },
+    { "PixelTexture3D", nullptr, nullptr },
+    { "ProgramShader", nullptr, nullptr },
+    { "QuadSet", nullptr, nullptr },
+    { "ShaderPart", nullptr, nullptr },
+    { "ShaderProgram", nullptr, nullptr },
+    { "TextureCoordinate3D", nullptr, nullptr },
+    { "TextureCoordinate4D", nullptr, nullptr },
+    { "TextureTransform3D", nullptr, nullptr },
+    { "TextureTransformMatrix3D", nullptr, nullptr },
+    { "BallJoint", nullptr, nullptr },
+    { "BoundedPhysicsModel", nullptr, nullptr },
+    { "ClipPlane", nullptr, nullptr },
+    { "CollidableOffset", nullptr, nullptr },
+    { "CollidableShape", nullptr, nullptr },
+    { "CollisionCollection", nullptr, nullptr },
+    { "CollisionSensor", nullptr, nullptr },
+    { "CollisionSpace", nullptr, nullptr },
+    { "ColorDamper", nullptr, nullptr },
+    { "ConeEmitter", nullptr, nullptr },
+    { "Contact", nullptr, nullptr },
+    { "CoordinateDamper", nullptr, nullptr },
+    { "DISEntityManager", nullptr, nullptr },
+    { "DISEntityTypeMapping", nullptr, nullptr },
+    { "DoubleAxisHingeJoint", nullptr, nullptr },
+    { "EaseInEaseOut", nullptr, nullptr },
+    { "ExplosionEmitter", nullptr, nullptr },
+    { "ForcePhysicsModel", nullptr, nullptr },
+    { "GeoProximitySensor", nullptr, nullptr },
+    { "GeoTransform", nullptr, nullptr },
+    { "Layer", nullptr, nullptr },
+    { "LayerSet", nullptr, nullptr },
+    { "Layout", nullptr, nullptr },
+    { "LayoutGroup", nullptr, nullptr },
+    { "LayoutLayer", nullptr, nullptr },
+    { "LinePickSensor", nullptr, nullptr },
+    { "MotorJoint", nullptr, nullptr },
+    { "OrientationChaser", nullptr, nullptr },
+    { "OrientationDamper", nullptr, nullptr },
+    { "OrthoViewpoint", nullptr, nullptr },
+    { "ParticleSystem", nullptr, nullptr },
+    { "PickableGroup", nullptr, nullptr },
+    { "PointEmitter", nullptr, nullptr },
+    { "PointPickSensor", nullptr, nullptr },
+    { "PolylineEmitter", nullptr, nullptr },
+    { "PositionChaser", nullptr, nullptr },
+    { "PositionChaser2D", nullptr, nullptr },
+    { "PositionDamper", nullptr, nullptr },
+    { "PositionDamper2D", nullptr, nullptr },
+    { "PrimitivePickSensor", nullptr, nullptr },
+    { "RigidBody", nullptr, nullptr },
+    { "RigidBodyCollection", nullptr, nullptr },
+    { "ScalarChaser", nullptr, nullptr },
+    { "ScreenFontStyle", nullptr, nullptr },
+    { "ScreenGroup", nullptr, nullptr },
+    { "SingleAxisHingeJoint", nullptr, nullptr },
+    { "SliderJoint", nullptr, nullptr },
+    { "SplinePositionInterpolator", nullptr, nullptr },
+    { "SplinePositionInterpolator2D", nullptr, nullptr },
+    { "SplineScalarInterpolator", nullptr, nullptr },
+    { "SquadOrientationInterpolator", nullptr, nullptr },
+    { "SurfaceEmitter", nullptr, nullptr },
+    { "TexCoordDamper2D", nullptr, nullptr },
+    { "TextureProperties", nullptr, nullptr },
+    { "TransformSensor", nullptr, nullptr },
+    { "TwoSidedMaterial", nullptr, nullptr },
+    { "UniversalJoint", nullptr, nullptr },
+    { "ViewpointGroup", nullptr, nullptr },
+    { "Viewport", nullptr, nullptr },
+    { "VolumeEmitter", nullptr, nullptr },
+    { "VolumePickSensor", nullptr, nullptr },
+    { "WindPhysicsModel", nullptr, nullptr },
+    { "BlendedVolumeStyle", nullptr, nullptr },
+    { "BoundaryEnhancementVolumeStyle", nullptr, nullptr },
+    { "CartoonVolumeStyle", nullptr, nullptr },
+    { "ComposedVolumeStyle", nullptr, nullptr },
+    { "EdgeEnhancementVolumeStyle", nullptr, nullptr },
+    { "IsoSurfaceVolumeData", nullptr, nullptr },
+    { "MetadataBoolean", nullptr, nullptr },
+    { "OpacityMapVolumeStyle", nullptr, nullptr },
+    { "ProjectionVolumeStyle", nullptr, nullptr },
+    { "SegmentedVolumeData", nullptr, nullptr },
+    { "ShadedVolumeStyle", nullptr, nullptr },
+    { "SilhouetteEnhancementVolumeStyle", nullptr, nullptr },
+    { "ToneMappedVolumeStyle", nullptr, nullptr },
+    { "VolumeData", nullptr, nullptr },
+    { "ColorChaser", nullptr, nullptr },
+    { "CoordinateChaser", nullptr, nullptr },
+    { "ScalarDamper", nullptr, nullptr },
+    { "TexCoordChaser2D", nullptr, nullptr },
+    { "unit", nullptr, nullptr }
+};
+
+static const FIQName attributeNameTable_3_3[] = {
+    { "DEF", nullptr, nullptr },
+    { "USE", nullptr, nullptr },
+    { "containerField", nullptr, nullptr },
+    { "fromNode", nullptr, nullptr },
+    { "fromField", nullptr, nullptr },
+    { "toNode", nullptr, nullptr },
+    { "toField", nullptr, nullptr },
+    { "name", nullptr, nullptr },
+    { "value", nullptr, nullptr },
+    { "color", nullptr, nullptr },
+    { "colorIndex", nullptr, nullptr },
+    { "coordIndex", nullptr, nullptr },
+    { "texCoordIndex", nullptr, nullptr },
+    { "normalIndex", nullptr, nullptr },
+    { "colorPerVertex", nullptr, nullptr },
+    { "normalPerVertex", nullptr, nullptr },
+    { "rotation", nullptr, nullptr },
+    { "scale", nullptr, nullptr },
+    { "center", nullptr, nullptr },
+    { "scaleOrientation", nullptr, nullptr },
+    { "translation", nullptr, nullptr },
+    { "url", nullptr, nullptr },
+    { "repeatS", nullptr, nullptr },
+    { "repeatT", nullptr, nullptr },
+    { "point", nullptr, nullptr },
+    { "vector", nullptr, nullptr },
+    { "range", nullptr, nullptr },
+    { "ambientIntensity", nullptr, nullptr },
+    { "diffuseColor", nullptr, nullptr },
+    { "emissiveColor", nullptr, nullptr },
+    { "shininess", nullptr, nullptr },
+    { "specularColor", nullptr, nullptr },
+    { "transparency", nullptr, nullptr },
+    { "whichChoice", nullptr, nullptr },
+    { "index", nullptr, nullptr },
+    { "mode", nullptr, nullptr },
+    { "source", nullptr, nullptr },
+    { "function", nullptr, nullptr },
+    { "alpha", nullptr, nullptr },
+    { "vertexCount", nullptr, nullptr },
+    { "radius", nullptr, nullptr },
+    { "size", nullptr, nullptr },
+    { "height", nullptr, nullptr },
+    { "solid", nullptr, nullptr },
+    { "ccw", nullptr, nullptr },
+    { "key", nullptr, nullptr },
+    { "keyValue", nullptr, nullptr },
+    { "enabled", nullptr, nullptr },
+    { "direction", nullptr, nullptr },
+    { "position", nullptr, nullptr },
+    { "orientation", nullptr, nullptr },
+    { "bboxCenter", nullptr, nullptr },
+    { "bboxSize", nullptr, nullptr },
+    { "AS", nullptr, nullptr },
+    { "InlineDEF", nullptr, nullptr },
+    { "accessType", nullptr, nullptr },
+    { "actionKeyPress", nullptr, nullptr },
+    { "actionKeyRelease", nullptr, nullptr },
+    { "address", nullptr, nullptr },
+    { "altKey", nullptr, nullptr },
+    { "antennaLocation", nullptr, nullptr },
+    { "antennaPatternLength", nullptr, nullptr },
+    { "antennaPatternType", nullptr, nullptr },
+    { "applicationID", nullptr, nullptr },
+    { "articulationParameterArray", nullptr, nullptr },
+    { "articulationParameterChangeIndicatorArray", nullptr, nullptr },
+    { "articulationParameterCount", nullptr, nullptr },
+    { "articulationParameterDesignatorArray", nullptr, nullptr },
+    { "articulationParameterIdPartAttachedArray", nullptr, nullptr },
+    { "articulationParameterTypeArray", nullptr, nullptr },
+    { "attenuation", nullptr, nullptr },
+    { "autoOffset", nullptr, nullptr },
+    { "avatarSize", nullptr, nullptr },
+    { "axisOfRotation", nullptr, nullptr },
+    { "backUrl", nullptr, nullptr },
+    { "beamWidth", nullptr, nullptr },
+    { "beginCap", nullptr, nullptr },
+    { "bindTime", nullptr, nullptr },
+    { "bottom", nullptr, nullptr },
+    { "bottomRadius", nullptr, nullptr },
+    { "bottomUrl", nullptr, nullptr },
+    { "centerOfMass", nullptr, nullptr },
+    { "centerOfRotation", nullptr, nullptr },
+    { "child1Url", nullptr, nullptr },
+    { "child2Url", nullptr, nullptr },
+    { "child3Url", nullptr, nullptr },
+    { "child4Url", nullptr, nullptr },
+    { "class", nullptr, nullptr },
+    { "closureType", nullptr, nullptr },
+    { "collideTime", nullptr, nullptr },
+    { "content", nullptr, nullptr },
+    { "controlKey", nullptr, nullptr },
+    { "controlPoint", nullptr, nullptr },
+    { "convex", nullptr, nullptr },
+    { "coordinateSystem", nullptr, nullptr },
+    { "copyright", nullptr, nullptr },
+    { "creaseAngle", nullptr, nullptr },
+    { "crossSection", nullptr, nullptr },
+    { "cryptoKeyID", nullptr, nullptr },
+    { "cryptoSystem", nullptr, nullptr },
+    { "cutOffAngle", nullptr, nullptr },
+    { "cycleInterval", nullptr, nullptr },
+    { "cycleTime", nullptr, nullptr },
+    { "data", nullptr, nullptr },
+    { "dataFormat", nullptr, nullptr },
+    { "dataLength", nullptr, nullptr },
+    { "dataUrl", nullptr, nullptr },
+    { "date", nullptr, nullptr },
+    { "deadReckoning", nullptr, nullptr },
+    { "deletionAllowed", nullptr, nullptr },
+    { "description", nullptr, nullptr },
+    { "detonateTime", nullptr, nullptr },
+    { "dir", nullptr, nullptr },
+    { "directOutput", nullptr, nullptr },
+    { "diskAngle", nullptr, nullptr },
+    { "displacements", nullptr, nullptr },
+    { "documentation", nullptr, nullptr },
+    { "elapsedTime", nullptr, nullptr },
+    { "ellipsoid", nullptr, nullptr },
+    { "encodingScheme", nullptr, nullptr },
+    { "endAngle", nullptr, nullptr },
+    { "endCap", nullptr, nullptr },
+    { "enterTime", nullptr, nullptr },
+    { "enteredText", nullptr, nullptr },
+    { "entityCategory", nullptr, nullptr },
+    { "entityCountry", nullptr, nullptr },
+    { "entityDomain", nullptr, nullptr },
+    { "entityExtra", nullptr, nullptr },
+    { "entityID", nullptr, nullptr },
+    { "entityKind", nullptr, nullptr },
+    { "entitySpecific", nullptr, nullptr },
+    { "entitySubCategory", nullptr, nullptr },
+    { "exitTime", nullptr, nullptr },
+    { "extent", nullptr, nullptr },
+    { "family", nullptr, nullptr },
+    { "fanCount", nullptr, nullptr },
+    { "fieldOfView", nullptr, nullptr },
+    { "filled", nullptr, nullptr },
+    { "finalText", nullptr, nullptr },
+    { "fireMissionIndex", nullptr, nullptr },
+    { "fired1", nullptr, nullptr },
+    { "fired2", nullptr, nullptr },
+    { "firedTime", nullptr, nullptr },
+    { "firingRange", nullptr, nullptr },
+    { "firingRate", nullptr, nullptr },
+    { "fogType", nullptr, nullptr },
+    { "forceID", nullptr, nullptr },
+    { "frequency", nullptr, nullptr },
+    { "frontUrl", nullptr, nullptr },
+    { "fuse", nullptr, nullptr },
+    { "geoCoords", nullptr, nullptr },
+    { "geoGridOrigin", nullptr, nullptr },
+    { "geoSystem", nullptr, nullptr },
+    { "groundAngle", nullptr, nullptr },
+    { "groundColor", nullptr, nullptr },
+    { "hatchColor", nullptr, nullptr },
+    { "hatchStyle", nullptr, nullptr },
+    { "hatched", nullptr, nullptr },
+    { "headlight", nullptr, nullptr },
+    { "horizontal", nullptr, nullptr },
+    { "horizontalDatum", nullptr, nullptr },
+    { "http-equiv", nullptr, nullptr },
+    { "image", nullptr, nullptr },
+    { "importedDEF", nullptr, nullptr },
+    { "info", nullptr, nullptr },
+    { "innerRadius", nullptr, nullptr },
+    { "inputFalse", nullptr, nullptr },
+    { "inputNegate", nullptr, nullptr },
+    { "inputSource", nullptr, nullptr },
+    { "inputTrue", nullptr, nullptr },
+    { "integerKey", nullptr, nullptr },
+    { "intensity", nullptr, nullptr },
+    { "jump", nullptr, nullptr },
+    { "justify", nullptr, nullptr },
+    { "keyPress", nullptr, nullptr },
+    { "keyRelease", nullptr, nullptr },
+    { "knot", nullptr, nullptr },
+    { "lang", nullptr, nullptr },
+    { "language", nullptr, nullptr },
+    { "leftToRight", nullptr, nullptr },
+    { "leftUrl", nullptr, nullptr },
+    { "length", nullptr, nullptr },
+    { "lengthOfModulationParameters", nullptr, nullptr },
+    { "level", nullptr, nullptr },
+    { "limitOrientation", nullptr, nullptr },
+    { "lineSegments", nullptr, nullptr },
+    { "linearAcceleration", nullptr, nullptr },
+    { "linearVelocity", nullptr, nullptr },
+    { "linetype", nullptr, nullptr },
+    { "linewidthScaleFactor", nullptr, nullptr },
+    { "llimit", nullptr, nullptr },
+    { "load", nullptr, nullptr },
+    { "loadTime", nullptr, nullptr },
+    { "localDEF", nullptr, nullptr },
+    { "location", nullptr, nullptr },
+    { "loop", nullptr, nullptr },
+    { "marking", nullptr, nullptr },
+    { "mass", nullptr, nullptr },
+    { "maxAngle", nullptr, nullptr },
+    { "maxBack", nullptr, nullptr },
+    { "maxExtent", nullptr, nullptr },
+    { "maxFront", nullptr, nullptr },
+    { "maxPosition", nullptr, nullptr },
+    { "metadataFormat", nullptr, nullptr },
+    { "minAngle", nullptr, nullptr },
+    { "minBack", nullptr, nullptr },
+    { "minFront", nullptr, nullptr },
+    { "minPosition", nullptr, nullptr },
+    { "modulationTypeDetail", nullptr, nullptr },
+    { "modulationTypeMajor", nullptr, nullptr },
+    { "modulationTypeSpreadSpectrum", nullptr, nullptr },
+    { "modulationTypeSystem", nullptr, nullptr },
+    { "momentsOfInertia", nullptr, nullptr },
+    { "multicastRelayHost", nullptr, nullptr },
+    { "multicastRelayPort", nullptr, nullptr },
+    { "munitionApplicationID", nullptr, nullptr },
+    { "munitionEndPoint", nullptr, nullptr },
+    { "munitionEntityID", nullptr, nullptr },
+    { "munitionQuantity", nullptr, nullptr },
+    { "munitionSiteID", nullptr, nullptr },
+    { "munitionStartPoint", nullptr, nullptr },
+    { "mustEvaluate", nullptr, nullptr },
+    { "navType", nullptr, nullptr },
+    { "networkMode", nullptr, nullptr },
+    { "next", nullptr, nullptr },
+    { "nodeField", nullptr, nullptr },
+    { "offset", nullptr, nullptr },
+    { "on", nullptr, nullptr },
+    { "order", nullptr, nullptr },
+    { "originator", nullptr, nullptr },
+    { "outerRadius", nullptr, nullptr },
+    { "parameter", nullptr, nullptr },
+    { "pauseTime", nullptr, nullptr },
+    { "pitch", nullptr, nullptr },
+    { "points", nullptr, nullptr },
+    { "port", nullptr, nullptr },
+    { "power", nullptr, nullptr },
+    { "previous", nullptr, nullptr },
+    { "priority", nullptr, nullptr },
+    { "profile", nullptr, nullptr },
+    { "progress", nullptr, nullptr },
+    { "protoField", nullptr, nullptr },
+    { "radioEntityTypeCategory", nullptr, nullptr },
+    { "radioEntityTypeCountry", nullptr, nullptr },
+    { "radioEntityTypeDomain", nullptr, nullptr },
+    { "radioEntityTypeKind", nullptr, nullptr },
+    { "radioEntityTypeNomenclature", nullptr, nullptr },
+    { "radioEntityTypeNomenclatureVersion", nullptr, nullptr },
+    { "radioID", nullptr, nullptr },
+    { "readInterval", nullptr, nullptr },
+    { "receivedPower", nullptr, nullptr },
+    { "receiverState", nullptr, nullptr },
+    { "reference", nullptr, nullptr },
+    { "relativeAntennaLocation", nullptr, nullptr },
+    { "resolution", nullptr, nullptr },
+    { "resumeTime", nullptr, nullptr },
+    { "rightUrl", nullptr, nullptr },
+    { "rootUrl", nullptr, nullptr },
+    { "rotateYUp", nullptr, nullptr },
+    { "rtpHeaderExpected", nullptr, nullptr },
+    { "sampleRate", nullptr, nullptr },
+    { "samples", nullptr, nullptr },
+    { "shiftKey", nullptr, nullptr },
+    { "side", nullptr, nullptr },
+    { "siteID", nullptr, nullptr },
+    { "skinCoordIndex", nullptr, nullptr },
+    { "skinCoordWeight", nullptr, nullptr },
+    { "skyAngle", nullptr, nullptr },
+    { "skyColor", nullptr, nullptr },
+    { "spacing", nullptr, nullptr },
+    { "spatialize", nullptr, nullptr },
+    { "speed", nullptr, nullptr },
+    { "speedFactor", nullptr, nullptr },
+    { "spine", nullptr, nullptr },
+    { "startAngle", nullptr, nullptr },
+    { "startTime", nullptr, nullptr },
+    { "stiffness", nullptr, nullptr },
+    { "stopTime", nullptr, nullptr },
+    { "string", nullptr, nullptr },
+    { "stripCount", nullptr, nullptr },
+    { "style", nullptr, nullptr },
+    { "summary", nullptr, nullptr },
+    { "tdlType", nullptr, nullptr },
+    { "tessellation", nullptr, nullptr },
+    { "tessellationScale", nullptr, nullptr },
+    { "time", nullptr, nullptr },
+    { "timeOut", nullptr, nullptr },
+    { "timestamp", nullptr, nullptr },
+    { "title", nullptr, nullptr },
+    { "toggle", nullptr, nullptr },
+    { "top", nullptr, nullptr },
+    { "topToBottom", nullptr, nullptr },
+    { "topUrl", nullptr, nullptr },
+    { "touchTime", nullptr, nullptr },
+    { "transmitFrequencyBandwidth", nullptr, nullptr },
+    { "transmitState", nullptr, nullptr },
+    { "transmitterApplicationID", nullptr, nullptr },
+    { "transmitterEntityID", nullptr, nullptr },
+    { "transmitterRadioID", nullptr, nullptr },
+    { "transmitterSiteID", nullptr, nullptr },
+    { "transparent", nullptr, nullptr },
+    { "triggerTime", nullptr, nullptr },
+    { "triggerTrue", nullptr, nullptr },
+    { "triggerValue", nullptr, nullptr },
+    { "type", nullptr, nullptr },
+    { "uDimension", nullptr, nullptr },
+    { "uKnot", nullptr, nullptr },
+    { "uOrder", nullptr, nullptr },
+    { "uTessellation", nullptr, nullptr },
+    { "ulimit", nullptr, nullptr },
+    { "vDimension", nullptr, nullptr },
+    { "vKnot", nullptr, nullptr },
+    { "vOrder", nullptr, nullptr },
+    { "vTessellation", nullptr, nullptr },
+    { "version", nullptr, nullptr },
+    { "verticalDatum", nullptr, nullptr },
+    { "vertices", nullptr, nullptr },
+    { "visibilityLimit", nullptr, nullptr },
+    { "visibilityRange", nullptr, nullptr },
+    { "warhead", nullptr, nullptr },
+    { "weight", nullptr, nullptr },
+    { "whichGeometry", nullptr, nullptr },
+    { "writeInterval", nullptr, nullptr },
+    { "xDimension", nullptr, nullptr },
+    { "xSpacing", nullptr, nullptr },
+    { "yScale", nullptr, nullptr },
+    { "zDimension", nullptr, nullptr },
+    { "zSpacing", nullptr, nullptr },
+    { "visible", nullptr, nullptr },
+    { "repeatR", nullptr, nullptr },
+    { "texture", nullptr, nullptr },
+    { "back", nullptr, nullptr },
+    { "front", nullptr, nullptr },
+    { "left", nullptr, nullptr },
+    { "right", nullptr, nullptr },
+    { "parts", nullptr, nullptr },
+    { "isSelected", nullptr, nullptr },
+    { "isValid", nullptr, nullptr },
+    { "numComponents", nullptr, nullptr },
+    { "depth", nullptr, nullptr },
+    { "update", nullptr, nullptr },
+    { "fogCoord", nullptr, nullptr },
+    { "texCoord", nullptr, nullptr },
+    { "activate", nullptr, nullptr },
+    { "programs", nullptr, nullptr },
+    { "matrix", nullptr, nullptr },
+    { "anchorPoint", nullptr, nullptr },
+    { "body1", nullptr, nullptr },
+    { "body2", nullptr, nullptr },
+    { "forceOutput", nullptr, nullptr },
+    { "body1AnchorPoint", nullptr, nullptr },
+    { "body2AnchorPoint", nullptr, nullptr },
+    { "plane", nullptr, nullptr },
+    { "appliedParameters", nullptr, nullptr },
+    { "bounce", nullptr, nullptr },
+    { "frictionCoefficients", nullptr, nullptr },
+    { "minBounceSpeed", nullptr, nullptr },
+    { "slipFactors", nullptr, nullptr },
+    { "softnessConstantForceMix", nullptr, nullptr },
+    { "softnessErrorCorrection", nullptr, nullptr },
+    { "surfaceSpeed", nullptr, nullptr },
+    { "isActive", nullptr, nullptr },
+    { "useGeometry", nullptr, nullptr },
+    { "set_destination", nullptr, nullptr },
+    { "set_value", nullptr, nullptr },
+    { "tau", nullptr, nullptr },
+    { "tolerance", nullptr, nullptr },
+    { "value_changed", nullptr, nullptr },
+    { "initialDestination", nullptr, nullptr },
+    { "initialValue", nullptr, nullptr },
+    { "angle", nullptr, nullptr },
+    { "variation", nullptr, nullptr },
+    { "surfaceArea", nullptr, nullptr },
+    { "frictionDirection", nullptr, nullptr },
+    { "slipCoefficients", nullptr, nullptr },
+    { "category", nullptr, nullptr },
+    { "country", nullptr, nullptr },
+    { "domain", nullptr, nullptr },
+    { "extra", nullptr, nullptr },
+    { "kind", nullptr, nullptr },
+    { "specific", nullptr, nullptr },
+    { "subcategory", nullptr, nullptr },
+    { "axis1", nullptr, nullptr },
+    { "axis2", nullptr, nullptr },
+    { "desiredAngularVelocity1", nullptr, nullptr },
+    { "desiredAngularVelocity2", nullptr, nullptr },
+    { "maxAngle1", nullptr, nullptr },
+    { "maxTorque1", nullptr, nullptr },
+    { "maxTorque2", nullptr, nullptr },
+    { "minAngle1", nullptr, nullptr },
+    { "stopBounce1", nullptr, nullptr },
+    { "stopConstantForceMix1", nullptr, nullptr },
+    { "stopErrorCorrection1", nullptr, nullptr },
+    { "suspensionErrorCorrection", nullptr, nullptr },
+    { "suspensionForce", nullptr, nullptr },
+    { "body1Axis", nullptr, nullptr },
+    { "body2Axis", nullptr, nullptr },
+    { "hinge1Angle", nullptr, nullptr },
+    { "hinge1AngleRate", nullptr, nullptr },
+    { "hinge2Angle", nullptr, nullptr },
+    { "hinge2AngleRate", nullptr, nullptr },
+    { "set_fraction", nullptr, nullptr },
+    { "easeInEaseOut", nullptr, nullptr },
+    { "modifiedFraction_changed", nullptr, nullptr },
+    { "force", nullptr, nullptr },
+    { "geoCenter", nullptr, nullptr },
+    { "centerOfRotation_changed", nullptr, nullptr },
+    { "geoCoord_changed", nullptr, nullptr },
+    { "orientation_changed", nullptr, nullptr },
+    { "position_changed", nullptr, nullptr },
+    { "isPickable", nullptr, nullptr },
+    { "viewport", nullptr, nullptr },
+    { "activeLayer", nullptr, nullptr },
+    { "align", nullptr, nullptr },
+    { "offsetUnits", nullptr, nullptr },
+    { "scaleMode", nullptr, nullptr },
+    { "sizeUnits", nullptr, nullptr },
+    { "layout", nullptr, nullptr },
+    { "objectType", nullptr, nullptr },
+    { "pickedNormal", nullptr, nullptr },
+    { "pickedPoint", nullptr, nullptr },
+    { "pickedTextureCoordinate", nullptr, nullptr },
+    { "intersectionType", nullptr, nullptr },
+    { "sortOrder", nullptr, nullptr },
+    { "axis1Angle", nullptr, nullptr },
+    { "axis1Torque", nullptr, nullptr },
+    { "axis2Angle", nullptr, nullptr },
+    { "axis2Torque", nullptr, nullptr },
+    { "axis3Angle", nullptr, nullptr },
+    { "axis3Torque", nullptr, nullptr },
+    { "enabledAxies", nullptr, nullptr },
+    { "motor1Axis", nullptr, nullptr },
+    { "motor2Axis", nullptr, nullptr },
+    { "motor3Axis", nullptr, nullptr },
+    { "stop1Bounce", nullptr, nullptr },
+    { "stop1ErrorCorrection", nullptr, nullptr },
+    { "stop2Bounce", nullptr, nullptr },
+    { "stop2ErrorCorrection", nullptr, nullptr },
+    { "stop3Bounce", nullptr, nullptr },
+    { "stop3ErrorCorrection", nullptr, nullptr },
+    { "motor1Angle", nullptr, nullptr },
+    { "motor1AngleRate", nullptr, nullptr },
+    { "motor2Angle", nullptr, nullptr },
+    { "motor2AngleRate", nullptr, nullptr },
+    { "motor3Angle", nullptr, nullptr },
+    { "motor3AngleRate", nullptr, nullptr },
+    { "autoCalc", nullptr, nullptr },
+    { "duration", nullptr, nullptr },
+    { "retainUserOffsets", nullptr, nullptr },
+    { "isBound", nullptr, nullptr },
+    { "appearance", nullptr, nullptr },
+    { "createParticles", nullptr, nullptr },
+    { "lifetimeVariation", nullptr, nullptr },
+    { "maxParticles", nullptr, nullptr },
+    { "particleLifetime", nullptr, nullptr },
+    { "particleSize", nullptr, nullptr },
+    { "colorKey", nullptr, nullptr },
+    { "geometryType", nullptr, nullptr },
+    { "texCoordKey", nullptr, nullptr },
+    { "pickable", nullptr, nullptr },
+    { "angularDampingFactor", nullptr, nullptr },
+    { "angularVelocity", nullptr, nullptr },
+    { "autoDamp", nullptr, nullptr },
+    { "autoDisable", nullptr, nullptr },
+    { "disableAngularSpeed", nullptr, nullptr },
+    { "disableLinearSpeed", nullptr, nullptr },
+    { "disableTime", nullptr, nullptr },
+    { "finiteRotationAxis", nullptr, nullptr },
+    { "fixed", nullptr, nullptr },
+    { "forces", nullptr, nullptr },
+    { "inertia", nullptr, nullptr },
+    { "linearDampingFactor", nullptr, nullptr },
+    { "torques", nullptr, nullptr },
+    { "useFiniteRotation", nullptr, nullptr },
+    { "useGlobalForce", nullptr, nullptr },
+    { "constantForceMix", nullptr, nullptr },
+    { "constantSurfaceThickness", nullptr, nullptr },
+    { "errorCorrection", nullptr, nullptr },
+    { "iterations", nullptr, nullptr },
+    { "maxCorrectionSpeed", nullptr, nullptr },
+    { "preferAccuracy", nullptr, nullptr },
+    { "pointSize", nullptr, nullptr },
+    { "stopBounce", nullptr, nullptr },
+    { "stopErrorCorrection", nullptr, nullptr },
+    { "angleRate", nullptr, nullptr },
+    { "maxSeparation", nullptr, nullptr },
+    { "minSeparation", nullptr, nullptr },
+    { "separation", nullptr, nullptr },
+    { "separationRate", nullptr, nullptr },
+    { "closed", nullptr, nullptr },
+    { "keyVelocity", nullptr, nullptr },
+    { "normalizeVelocity", nullptr, nullptr },
+    { "surface", nullptr, nullptr },
+    { "anisotropicDegree", nullptr, nullptr },
+    { "borderColor", nullptr, nullptr },
+    { "borderWidth", nullptr, nullptr },
+    { "boundaryModeS", nullptr, nullptr },
+    { "boundaryModeT", nullptr, nullptr },
+    { "boundaryModeR", nullptr, nullptr },
+    { "magnificationFilter", nullptr, nullptr },
+    { "minificationFilter", nullptr, nullptr },
+    { "textureCompression", nullptr, nullptr },
+    { "texturePriority", nullptr, nullptr },
+    { "generateMipMaps", nullptr, nullptr },
+    { "targetObject", nullptr, nullptr },
+    { "backAmbientIntensity", nullptr, nullptr },
+    { "backDiffuseColor", nullptr, nullptr },
+    { "backEmissiveColor", nullptr, nullptr },
+    { "backShininess", nullptr, nullptr },
+    { "backSpecularColor", nullptr, nullptr },
+    { "separateBackColor", nullptr, nullptr },
+    { "displayed", nullptr, nullptr },
+    { "clipBoundary", nullptr, nullptr },
+    { "internal", nullptr, nullptr },
+    { "gustiness", nullptr, nullptr },
+    { "turbulence", nullptr, nullptr },
+    { "unitCategory", nullptr, nullptr },
+    { "unitName", nullptr, nullptr },
+    { "unitConversionFactor", nullptr, nullptr },
+    { "weightConstant1", nullptr, nullptr },
+    { "weightConstant2", nullptr, nullptr },
+    { "weightFunction1", nullptr, nullptr },
+    { "weightFunction2", nullptr, nullptr },
+    { "boundaryOpacity", nullptr, nullptr },
+    { "opacityFactor", nullptr, nullptr },
+    { "retainedOpacity", nullptr, nullptr },
+    { "colorSteps", nullptr, nullptr },
+    { "orthogonalColor", nullptr, nullptr },
+    { "parallelColor", nullptr, nullptr },
+    { "ordered", nullptr, nullptr },
+    { "edgeColor", nullptr, nullptr },
+    { "gradientThreshold", nullptr, nullptr },
+    { "contourStepSize", nullptr, nullptr },
+    { "dimensions", nullptr, nullptr },
+    { "surfaceTolerance", nullptr, nullptr },
+    { "surfaceValues", nullptr, nullptr },
+    { "intensityThreshold", nullptr, nullptr },
+    { "segmentEnabled", nullptr, nullptr },
+    { "lighting", nullptr, nullptr },
+    { "shadows", nullptr, nullptr },
+    { "phaseFunction", nullptr, nullptr },
+    { "silhouetteBoundaryOpacity", nullptr, nullptr },
+    { "silhouetteRetainedOpacity", nullptr, nullptr },
+    { "silhouetteSharpness", nullptr, nullptr },
+    { "coolColor", nullptr, nullptr },
+    { "warmColor", nullptr, nullptr }
+};
+
+FIVocabulary X3D_vocabulary_3_3 = {
+    nullptr, 0,
+    encodingAlgorithmTable_3_3, 8,
+    nullptr, 0,
+    nullptr, 0,
+    nullptr, 0,
+    nullptr, 0,
+    nullptr, 0,
+    attributeValueTable_3_3, 2,
+    nullptr, 0,
+    nullptr, 0,
+    elementNameTable_3_3, 252,
+    attributeNameTable_3_3, 546
+};
+
+}// namespace Assimp
+
+#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER