1/*
2Open Asset Import Library (assimp)
3----------------------------------------------------------------------
4
5Copyright (c) 2006-2017, assimp team
6
7All rights reserved.
8
9Redistribution and use of this software in source and binary forms,
10with or without modification, are permitted provided that the
11following conditions are met:
12
13* Redistributions of source code must retain the above
14copyright notice, this list of conditions and the
15following disclaimer.
16
17* Redistributions in binary form must reproduce the above
18copyright notice, this list of conditions and the
19following disclaimer in the documentation and/or other
20materials provided with the distribution.
21
22* Neither the name of the assimp team, nor the names of its
23contributors may be used to endorse or promote products
24derived from this software without specific prior
25written permission of the assimp team.
26
27THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
39----------------------------------------------------------------------
40*/
41/// \file FIReader.cpp
42/// \brief Reader for Fast Infoset encoded binary XML files.
43/// \date 2017
44/// \author Patrick Daehne
45
46#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER
47
48#include "FIReader.hpp"
49#include "StringUtils.h"
50
51// Workaround for issue #1361
52// https://github.com/assimp/assimp/issues/1361
53#ifdef __ANDROID__
54# define _GLIBCXX_USE_C99 1
55#endif
56
57#include "Exceptional.h"
58#include <assimp/IOStream.hpp>
59#include <assimp/types.h>
60#include "MemoryIOWrapper.h"
61#include "irrXMLWrapper.h"
62#include "../contrib/utf8cpp/source/utf8.h"
63#include "fast_atof.h"
64#include <stack>
65#include <map>
66#include <iostream>
67#include <sstream>
68#include <iomanip>
69
70namespace Assimp {
71
72static const std::string parseErrorMessage = "Fast Infoset parse error";
73
74static const char *xmlDeclarations[] = {
75 "<?xml encoding='finf'?>",
76 "<?xml encoding='finf' standalone='yes'?>",
77 "<?xml encoding='finf' standalone='no'?>",
78 "<?xml version='1.0' encoding='finf'?>",
79 "<?xml version='1.0' encoding='finf' standalone='yes'?>",
80 "<?xml version='1.0' encoding='finf' standalone='no'?>",
81 "<?xml version='1.1' encoding='finf'?>",
82 "<?xml version='1.1' encoding='finf' standalone='yes'?>",
83 "<?xml version='1.1' encoding='finf' standalone='no'?>"
84};
85
86static size_t parseMagic(const uint8_t *data, const uint8_t *dataEnd) {
87 if (dataEnd - data < 4) {
88 return 0;
89 }
90 uint32_t magic = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
91 switch (magic) {
92 case 0xe0000001:
93 return 4;
94 case 0x3c3f786d: // "<?xm"
95 {
96 size_t xmlDeclarationsLength = sizeof(xmlDeclarations) / sizeof(xmlDeclarations[0]);
97 for (size_t i = 0; i < xmlDeclarationsLength; ++i) {
98 auto xmlDeclaration = xmlDeclarations[i];
99 ptrdiff_t xmlDeclarationLength = strlen(xmlDeclaration);
100 if ((dataEnd - data >= xmlDeclarationLength) && (memcmp(xmlDeclaration, data, xmlDeclarationLength) == 0)) {
101 data += xmlDeclarationLength;
102 if (dataEnd - data < 4) {
103 return 0;
104 }
105 magic = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
106 return magic == 0xe0000001 ? xmlDeclarationLength + 4 : 0;
107 }
108 }
109 return 0;
110 }
111 default:
112 return 0;
113 }
114}
115
116static std::string parseUTF8String(const uint8_t *data, size_t len) {
117 return std::string((char*)data, len);
118}
119
120static std::string parseUTF16String(const uint8_t *data, size_t len) {
121 if (len & 1) {
122 throw DeadlyImportError(parseErrorMessage);
123 }
124 size_t numShorts = len / 2;
125 std::vector<short> utf16;
126 utf16.reserve(numShorts);
127 for (size_t i = 0; i < numShorts; ++i) {
128 short v = (data[0] << 8) | data[1];
129 utf16.push_back(v);
130 data += 2;
131 }
132 std::string result;
133 utf8::utf16to8(utf16.begin(), utf16.end(), back_inserter(result));
134 return result;
135}
136
137struct FIStringValueImpl: public FIStringValue {
138 inline FIStringValueImpl(std::string &&value_) { value = std::move(value_); }
139 virtual const std::string &toString() const /*override*/ { return value; }
140};
141
142std::shared_ptr<FIStringValue> FIStringValue::create(std::string &&value) {
143 return std::make_shared<FIStringValueImpl>(std::move(value));
144}
145
146struct FIHexValueImpl: public FIHexValue {
147 mutable std::string strValue;
148 mutable bool strValueValid;
149 inline FIHexValueImpl(std::vector<uint8_t> &&value_): strValueValid(false) { value = std::move(value_); }
150 virtual const std::string &toString() const /*override*/ {
151 if (!strValueValid) {
152 strValueValid = true;
153 std::ostringstream os;
154 os << std::hex << std::uppercase << std::setfill('0');
155 std::for_each(value.begin(), value.end(), [&](uint8_t c) { os << std::setw(2) << static_cast<int>(c); });
156 strValue = os.str();
157 }
158 return strValue;
159 };
160};
161
162std::shared_ptr<FIHexValue> FIHexValue::create(std::vector<uint8_t> &&value) {
163 return std::make_shared<FIHexValueImpl>(std::move(value));
164}
165
166struct FIBase64ValueImpl: public FIBase64Value {
167 mutable std::string strValue;
168 mutable bool strValueValid;
169 inline FIBase64ValueImpl(std::vector<uint8_t> &&value_): strValueValid(false) { value = std::move(value_); }
170 virtual const std::string &toString() const /*override*/ {
171 if (!strValueValid) {
172 strValueValid = true;
173 std::ostringstream os;
174 uint8_t c1 = 0, c2;
175 int imod3 = 0;
176 std::vector<uint8_t>::size_type valueSize = value.size();
177 for (std::vector<uint8_t>::size_type i = 0; i < valueSize; ++i) {
178 c2 = value[i];
179 switch (imod3) {
180 case 0:
181 os << basis_64[c2 >> 2];
182 imod3 = 1;
183 break;
184 case 1:
185 os << basis_64[((c1 & 0x03) << 4) | ((c2 & 0xf0) >> 4)];
186 imod3 = 2;
187 break;
188 case 2:
189 os << basis_64[((c1 & 0x0f) << 2) | ((c2 & 0xc0) >> 6)] << basis_64[c2 & 0x3f];
190 imod3 = 0;
191 break;
192 }
193 c1 = c2;
194 }
195 switch (imod3) {
196 case 1:
197 os << basis_64[(c1 & 0x03) << 4] << "==";
198 break;
199 case 2:
200 os << basis_64[(c1 & 0x0f) << 2] << '=';
201 break;
202 }
203 strValue = os.str();
204 }
205 return strValue;
206 };
207 static const char basis_64[];
208};
209
210const char FIBase64ValueImpl::basis_64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
211
212std::shared_ptr<FIBase64Value> FIBase64Value::create(std::vector<uint8_t> &&value) {
213 return std::make_shared<FIBase64ValueImpl>(std::move(value));
214}
215
216struct FIShortValueImpl: public FIShortValue {
217 mutable std::string strValue;
218 mutable bool strValueValid;
219 inline FIShortValueImpl(std::vector<int16_t> &&value_): strValueValid(false) { value = std::move(value_); }
220 virtual const std::string &toString() const /*override*/ {
221 if (!strValueValid) {
222 strValueValid = true;
223 std::ostringstream os;
224 int n = 0;
225 std::for_each(value.begin(), value.end(), [&](int16_t s) { if (++n > 1) os << ' '; os << s; });
226 strValue = os.str();
227 }
228 return strValue;
229 }
230};
231
232std::shared_ptr<FIShortValue> FIShortValue::create(std::vector<int16_t> &&value) {
233 return std::make_shared<FIShortValueImpl>(std::move(value));
234}
235
236struct FIIntValueImpl: public FIIntValue {
237 mutable std::string strValue;
238 mutable bool strValueValid;
239 inline FIIntValueImpl(std::vector<int32_t> &&value_): strValueValid(false) { value = std::move(value_); }
240 virtual const std::string &toString() const /*override*/ {
241 if (!strValueValid) {
242 strValueValid = true;
243 std::ostringstream os;
244 int n = 0;
245 std::for_each(value.begin(), value.end(), [&](int32_t i) { if (++n > 1) os << ' '; os << i; });
246 strValue = os.str();
247 }
248 return strValue;
249 };
250};
251
252std::shared_ptr<FIIntValue> FIIntValue::create(std::vector<int32_t> &&value) {
253 return std::make_shared<FIIntValueImpl>(std::move(value));
254}
255
256struct FILongValueImpl: public FILongValue {
257 mutable std::string strValue;
258 mutable bool strValueValid;
259 inline FILongValueImpl(std::vector<int64_t> &&value_): strValueValid(false) { value = std::move(value_); }
260 virtual const std::string &toString() const /*override*/ {
261 if (!strValueValid) {
262 strValueValid = true;
263 std::ostringstream os;
264 int n = 0;
265 std::for_each(value.begin(), value.end(), [&](int64_t l) { if (++n > 1) os << ' '; os << l; });
266 strValue = os.str();
267 }
268 return strValue;
269 };
270};
271
272std::shared_ptr<FILongValue> FILongValue::create(std::vector<int64_t> &&value) {
273 return std::make_shared<FILongValueImpl>(std::move(value));
274}
275
276struct FIBoolValueImpl: public FIBoolValue {
277 mutable std::string strValue;
278 mutable bool strValueValid;
279 inline FIBoolValueImpl(std::vector<bool> &&value_): strValueValid(false) { value = std::move(value_); }
280 virtual const std::string &toString() const /*override*/ {
281 if (!strValueValid) {
282 strValueValid = true;
283 std::ostringstream os;
284 os << std::boolalpha;
285 int n = 0;
286 std::for_each(value.begin(), value.end(), [&](bool b) { if (++n > 1) os << ' '; os << b; });
287 strValue = os.str();
288 }
289 return strValue;
290 };
291};
292
293std::shared_ptr<FIBoolValue> FIBoolValue::create(std::vector<bool> &&value) {
294 return std::make_shared<FIBoolValueImpl>(std::move(value));
295}
296
297struct FIFloatValueImpl: public FIFloatValue {
298 mutable std::string strValue;
299 mutable bool strValueValid;
300 inline FIFloatValueImpl(std::vector<float> &&value_): strValueValid(false) { value = std::move(value_); }
301 virtual const std::string &toString() const /*override*/ {
302 if (!strValueValid) {
303 strValueValid = true;
304 std::ostringstream os;
305 int n = 0;
306 std::for_each(value.begin(), value.end(), [&](float f) { if (++n > 1) os << ' '; os << f; });
307 strValue = os.str();
308 }
309 return strValue;
310 }
311};
312
313std::shared_ptr<FIFloatValue> FIFloatValue::create(std::vector<float> &&value) {
314 return std::make_shared<FIFloatValueImpl>(std::move(value));
315}
316
317struct FIDoubleValueImpl: public FIDoubleValue {
318 mutable std::string strValue;
319 mutable bool strValueValid;
320 inline FIDoubleValueImpl(std::vector<double> &&value_): strValueValid(false) { value = std::move(value_); }
321 virtual const std::string &toString() const /*override*/ {
322 if (!strValueValid) {
323 strValueValid = true;
324 std::ostringstream os;
325 int n = 0;
326 std::for_each(value.begin(), value.end(), [&](double d) { if (++n > 1) os << ' '; os << d; });
327 strValue = os.str();
328 }
329 return strValue;
330 }
331};
332
333std::shared_ptr<FIDoubleValue> FIDoubleValue::create(std::vector<double> &&value) {
334 return std::make_shared<FIDoubleValueImpl>(std::move(value));
335}
336
337struct FIUUIDValueImpl: public FIUUIDValue {
338 mutable std::string strValue;
339 mutable bool strValueValid;
340 inline FIUUIDValueImpl(std::vector<uint8_t> &&value_): strValueValid(false) { value = std::move(value_); }
341 virtual const std::string &toString() const /*override*/ {
342 if (!strValueValid) {
343 strValueValid = true;
344 std::ostringstream os;
345 os << std::hex << std::uppercase << std::setfill('0');
346 std::vector<uint8_t>::size_type valueSize = value.size();
347 for (std::vector<uint8_t>::size_type i = 0; i < valueSize; ++i) {
348 switch (i & 15) {
349 case 0:
350 if (i > 0) {
351 os << ' ';
352 }
353 os << std::setw(2) << static_cast<int>(value[i]);
354 break;
355 case 4:
356 case 6:
357 case 8:
358 case 10:
359 os << '-';
360 // intentionally fall through!
361 case 1:
362 case 2:
363 case 3:
364 case 5:
365 case 7:
366 case 9:
367 case 11:
368 case 12:
369 case 13:
370 case 14:
371 case 15:
372 os << std::setw(2) << static_cast<int>(value[i]);
373 break;
374 }
375 }
376 strValue = os.str();
377 }
378 return strValue;
379 };
380};
381
382std::shared_ptr<FIUUIDValue> FIUUIDValue::create(std::vector<uint8_t> &&value) {
383 return std::make_shared<FIUUIDValueImpl>(std::move(value));
384}
385
386struct FICDATAValueImpl: public FICDATAValue {
387 inline FICDATAValueImpl(std::string &&value_) { value = std::move(value_); }
388 virtual const std::string &toString() const /*override*/ { return value; }
389};
390
391std::shared_ptr<FICDATAValue> FICDATAValue::create(std::string &&value) {
392 return std::make_shared<FICDATAValueImpl>(std::move(value));
393}
394
395struct FIHexDecoder: public FIDecoder {
396 virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
397 return FIHexValue::create(std::vector<uint8_t>(data, data + len));
398 }
399};
400
401struct FIBase64Decoder: public FIDecoder {
402 virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
403 return FIBase64Value::create(std::vector<uint8_t>(data, data + len));
404 }
405};
406
407struct FIShortDecoder: public FIDecoder {
408 virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
409 if (len & 1) {
410 throw DeadlyImportError(parseErrorMessage);
411 }
412 std::vector<int16_t> value;
413 size_t numShorts = len / 2;
414 value.reserve(numShorts);
415 for (size_t i = 0; i < numShorts; ++i) {
416 int16_t v = (data[0] << 8) | data[1];
417 value.push_back(v);
418 data += 2;
419 }
420 return FIShortValue::create(std::move(value));
421 }
422};
423
424struct FIIntDecoder: public FIDecoder {
425 virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
426 if (len & 3) {
427 throw DeadlyImportError(parseErrorMessage);
428 }
429 std::vector<int32_t> value;
430 size_t numInts = len / 4;
431 value.reserve(numInts);
432 for (size_t i = 0; i < numInts; ++i) {
433 int32_t v = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
434 value.push_back(v);
435 data += 4;
436 }
437 return FIIntValue::create(std::move(value));
438 }
439};
440
441struct FILongDecoder: public FIDecoder {
442 virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
443 if (len & 7) {
444 throw DeadlyImportError(parseErrorMessage);
445 }
446 std::vector<int64_t> value;
447 size_t numLongs = len / 8;
448 value.reserve(numLongs);
449 for (size_t i = 0; i < numLongs; ++i) {
450 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];
451 int64_t v = (b0 << 56) | (b1 << 48) | (b2 << 40) | (b3 << 32) | (b4 << 24) | (b5 << 16) | (b6 << 8) | b7;
452 value.push_back(v);
453 data += 8;
454 }
455 return FILongValue::create(std::move(value));
456 }
457};
458
459struct FIBoolDecoder: public FIDecoder {
460 virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
461 if (len < 1) {
462 throw DeadlyImportError(parseErrorMessage);
463 }
464 std::vector<bool> value;
465 uint8_t b = *data++;
466 size_t unusedBits = b >> 4;
467 size_t numBools = (len * 8) - 4 - unusedBits;
468 value.reserve(numBools);
469 uint8_t mask = 1 << 3;
470 for (size_t i = 0; i < numBools; ++i) {
471 if (!mask) {
472 mask = 1 << 7;
473 b = *data++;
474 }
475 value.push_back((b & mask) != 0);
476 }
477 return FIBoolValue::create(std::move(value));
478 }
479};
480
481struct FIFloatDecoder: public FIDecoder {
482 virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
483 if (len & 3) {
484 throw DeadlyImportError(parseErrorMessage);
485 }
486 std::vector<float> value;
487 size_t numFloats = len / 4;
488 value.reserve(numFloats);
489 for (size_t i = 0; i < numFloats; ++i) {
490 int v = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
491 float f;
492 memcpy(&f, &v, 4);
493 value.push_back(f);
494 data += 4;
495 }
496 return FIFloatValue::create(std::move(value));
497 }
498};
499
500struct FIDoubleDecoder: public FIDecoder {
501 virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
502 if (len & 7) {
503 throw DeadlyImportError(parseErrorMessage);
504 }
505 std::vector<double> value;
506 size_t numDoubles = len / 8;
507 value.reserve(numDoubles);
508 for (size_t i = 0; i < numDoubles; ++i) {
509 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];
510 long long v = (b0 << 56) | (b1 << 48) | (b2 << 40) | (b3 << 32) | (b4 << 24) | (b5 << 16) | (b6 << 8) | b7;
511 double f;
512 memcpy(&f, &v, 8);
513 value.push_back(f);
514 data += 8;
515 }
516 return FIDoubleValue::create(std::move(value));
517 }
518};
519
520struct FIUUIDDecoder: public FIDecoder {
521 virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
522 if (len & 15) {
523 throw DeadlyImportError(parseErrorMessage);
524 }
525 return FIUUIDValue::create(std::vector<uint8_t>(data, data + len));
526 }
527};
528
529struct FICDATADecoder: public FIDecoder {
530 virtual std::shared_ptr<const FIValue> decode(const uint8_t *data, size_t len) /*override*/ {
531 return FICDATAValue::create(parseUTF8String(data, len));
532 }
533};
534
535class CFIReaderImpl: public FIReader {
536public:
537
538 CFIReaderImpl(std::unique_ptr<uint8_t[]> data_, size_t size):
539 data(std::move(data_)), dataP(data.get()), dataEnd(data.get() + size), currentNodeType(irr::io::EXN_NONE),
540 emptyElement(false), headerPending(true), terminatorPending(false)
541 {}
542
543 virtual ~CFIReaderImpl() {}
544
545 virtual bool read() /*override*/ {
546 if (headerPending) {
547 headerPending = false;
548 parseHeader();
549 }
550 if (terminatorPending) {
551 terminatorPending = false;
552 if (elementStack.empty()) {
553 return false;
554 }
555 else {
556 nodeName = elementStack.top();
557 elementStack.pop();
558 currentNodeType = nodeName.empty() ? irr::io::EXN_UNKNOWN : irr::io::EXN_ELEMENT_END;
559 return true;
560 }
561 }
562 if (dataP >= dataEnd) {
563 return false;
564 }
565 uint8_t b = *dataP;
566 if (b < 0x80) { // Element (C.2.11.2, C.3.7.2)
567 // C.3
568 parseElement();
569 return true;
570 }
571 else if (b < 0xc0) { // Characters (C.3.7.5)
572 // C.7
573 auto chars = parseNonIdentifyingStringOrIndex3(vocabulary.charactersTable);
574 nodeName = chars->toString();
575 currentNodeType = irr::io::EXN_TEXT;
576 return true;
577 }
578 else if (b < 0xe0) {
579 if ((b & 0xfc) == 0xc4) { // DTD (C.2.11.5)
580 // C.9
581 ++dataP;
582 if (b & 0x02) {
583 /*const std::string &systemID =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
584 }
585 if (b & 0x01) {
586 /*const std::string &publicID =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
587 }
588 elementStack.push(EmptyString);
589 currentNodeType = irr::io::EXN_UNKNOWN;
590 return true;
591 }
592 else if ((b & 0xfc) == 0xc8) { // Unexpanded entity reference (C.3.7.4)
593 // C.6
594 ++dataP;
595 /*const std::string &name =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable);
596 if (b & 0x02) {
597 /*const std::string &systemID =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
598 }
599 if (b & 0x01) {
600 /*const std::string &publicID =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
601 }
602 currentNodeType = irr::io::EXN_UNKNOWN;
603 return true;
604 }
605 }
606 else if (b < 0xf0) {
607 if (b == 0xe1) { // Processing instruction (C.2.11.3, C.3.7.3)
608 // C.5
609 ++dataP;
610 /*const std::string &target =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable);
611 if (dataEnd - dataP < 1) {
612 throw DeadlyImportError(parseErrorMessage);
613 }
614 /*std::shared_ptr<const FIValue> data =*/ parseNonIdentifyingStringOrIndex1(vocabulary.otherStringTable);
615 currentNodeType = irr::io::EXN_UNKNOWN;
616 return true;
617 }
618 else if (b == 0xe2) { // Comment (C.2.11.4, C.3.7.6)
619 // C.8
620 ++dataP;
621 if (dataEnd - dataP < 1) {
622 throw DeadlyImportError(parseErrorMessage);
623 }
624 std::shared_ptr<const FIValue> comment = parseNonIdentifyingStringOrIndex1(vocabulary.otherStringTable);
625 nodeName = comment->toString();
626 currentNodeType = irr::io::EXN_COMMENT;
627 return true;
628 }
629 }
630 else { // Terminator (C.2.12, C.3.8)
631 ++dataP;
632 if (b == 0xff) {
633 terminatorPending = true;
634 }
635 if (elementStack.empty()) {
636 return false;
637 }
638 else {
639 nodeName = elementStack.top();
640 elementStack.pop();
641 currentNodeType = nodeName.empty() ? irr::io::EXN_UNKNOWN : irr::io::EXN_ELEMENT_END;
642 return true;
643 }
644 }
645 throw DeadlyImportError(parseErrorMessage);
646 }
647
648 virtual irr::io::EXML_NODE getNodeType() const /*override*/ {
649 return currentNodeType;
650 }
651
652 virtual int getAttributeCount() const /*override*/ {
653 return static_cast<int>(attributes.size());
654 }
655
656 virtual const char* getAttributeName(int idx) const /*override*/ {
657 if (idx < 0 || idx >= (int)attributes.size()) {
658 return nullptr;
659 }
660 return attributes[idx].name.c_str();
661 }
662
663 virtual const char* getAttributeValue(int idx) const /*override*/ {
664 if (idx < 0 || idx >= (int)attributes.size()) {
665 return nullptr;
666 }
667 return attributes[idx].value->toString().c_str();
668 }
669
670 virtual const char* getAttributeValue(const char* name) const /*override*/ {
671 const Attribute* attr = getAttributeByName(name);
672 if (!attr) {
673 return nullptr;
674 }
675 return attr->value->toString().c_str();
676 }
677
678 virtual const char* getAttributeValueSafe(const char* name) const /*override*/ {
679 const Attribute* attr = getAttributeByName(name);
680 if (!attr) {
681 return EmptyString.c_str();
682 }
683 return attr->value->toString().c_str();
684 }
685
686 virtual int getAttributeValueAsInt(const char* name) const /*override*/ {
687 const Attribute* attr = getAttributeByName(name);
688 if (!attr) {
689 return 0;
690 }
691 std::shared_ptr<const FIIntValue> intValue = std::dynamic_pointer_cast<const FIIntValue>(attr->value);
692 if (intValue) {
693 return intValue->value.size() == 1 ? intValue->value.front() : 0;
694 }
695 return atoi(attr->value->toString().c_str());
696 }
697
698 virtual int getAttributeValueAsInt(int idx) const /*override*/ {
699 if (idx < 0 || idx >= (int)attributes.size()) {
700 return 0;
701 }
702 std::shared_ptr<const FIIntValue> intValue = std::dynamic_pointer_cast<const FIIntValue>(attributes[idx].value);
703 if (intValue) {
704 return intValue->value.size() == 1 ? intValue->value.front() : 0;
705 }
706 return atoi(attributes[idx].value->toString().c_str());
707 }
708
709 virtual float getAttributeValueAsFloat(const char* name) const /*override*/ {
710 const Attribute* attr = getAttributeByName(name);
711 if (!attr) {
712 return 0;
713 }
714 std::shared_ptr<const FIFloatValue> floatValue = std::dynamic_pointer_cast<const FIFloatValue>(attr->value);
715 if (floatValue) {
716 return floatValue->value.size() == 1 ? floatValue->value.front() : 0;
717 }
718
719 return fast_atof(attr->value->toString().c_str());
720 }
721
722 virtual float getAttributeValueAsFloat(int idx) const /*override*/ {
723 if (idx < 0 || idx >= (int)attributes.size()) {
724 return 0;
725 }
726 std::shared_ptr<const FIFloatValue> floatValue = std::dynamic_pointer_cast<const FIFloatValue>(attributes[idx].value);
727 if (floatValue) {
728 return floatValue->value.size() == 1 ? floatValue->value.front() : 0;
729 }
730 return fast_atof(attributes[idx].value->toString().c_str());
731 }
732
733 virtual const char* getNodeName() const /*override*/ {
734 return nodeName.c_str();
735 }
736
737 virtual const char* getNodeData() const /*override*/ {
738 return nodeName.c_str();
739 }
740
741 virtual bool isEmptyElement() const /*override*/ {
742 return emptyElement;
743 }
744
745 virtual irr::io::ETEXT_FORMAT getSourceFormat() const /*override*/ {
746 return irr::io::ETF_UTF8;
747 }
748
749 virtual irr::io::ETEXT_FORMAT getParserFormat() const /*override*/ {
750 return irr::io::ETF_UTF8;
751 }
752
753 virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(int idx) const /*override*/ {
754 if (idx < 0 || idx >= (int)attributes.size()) {
755 return nullptr;
756 }
757 return attributes[idx].value;
758 }
759
760 virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(const char* name) const /*override*/ {
761 const Attribute* attr = getAttributeByName(name);
762 if (!attr) {
763 return nullptr;
764 }
765 return attr->value;
766 }
767
768 virtual void registerDecoder(const std::string &algorithmUri, std::unique_ptr<FIDecoder> decoder) /*override*/ {
769 decoderMap[algorithmUri] = std::move(decoder);
770 }
771
772 virtual void registerVocabulary(const std::string &vocabularyUri, const FIVocabulary *vocabulary) /*override*/ {
773 vocabularyMap[vocabularyUri] = vocabulary;
774 }
775
776private:
777
778 struct QName {
779 std::string prefix;
780 std::string uri;
781 std::string name;
782 inline QName() {}
783 inline QName(const FIQName &qname): prefix(qname.prefix ? qname.prefix : ""), uri(qname.uri ? qname.uri : ""), name(qname.name) {}
784 };
785
786 struct Attribute {
787 QName qname;
788 std::string name;
789 std::shared_ptr<const FIValue> value;
790 };
791
792 struct Vocabulary {
793 std::vector<std::string> restrictedAlphabetTable;
794 std::vector<std::string> encodingAlgorithmTable;
795 std::vector<std::string> prefixTable;
796 std::vector<std::string> namespaceNameTable;
797 std::vector<std::string> localNameTable;
798 std::vector<std::string> otherNCNameTable;
799 std::vector<std::string> otherURITable;
800 std::vector<std::shared_ptr<const FIValue>> attributeValueTable;
801 std::vector<std::shared_ptr<const FIValue>> charactersTable;
802 std::vector<std::shared_ptr<const FIValue>> otherStringTable;
803 std::vector<QName> elementNameTable;
804 std::vector<QName> attributeNameTable;
805 Vocabulary() {
806 prefixTable.push_back("xml");
807 namespaceNameTable.push_back("http://www.w3.org/XML/1998/namespace");
808 }
809 };
810
811 const Attribute* getAttributeByName(const char* name) const {
812 if (!name) {
813 return 0;
814 }
815 std::string n = name;
816 for (int i=0; i<(int)attributes.size(); ++i) {
817 if (attributes[i].name == n) {
818 return &attributes[i];
819 }
820 }
821 return 0;
822 }
823
824 size_t parseInt2() { // C.25
825 uint8_t b = *dataP++;
826 if (!(b & 0x40)) { // x0...... (C.25.2)
827 return b & 0x3f;
828 }
829 else if ((b & 0x60) == 0x40) { // x10..... ........ (C.25.3)
830 if (dataEnd - dataP > 0) {
831 return (((b & 0x1f) << 8) | *dataP++) + 0x40;
832 }
833 }
834 else if ((b & 0x70) == 0x60) { // x110.... ........ ........ (C.25.4)
835 if (dataEnd - dataP > 1) {
836 size_t result = (((b & 0x0f) << 16) | (dataP[0] << 8) | dataP[1]) + 0x2040;
837 dataP += 2;
838 return result;
839 }
840 }
841 throw DeadlyImportError(parseErrorMessage);
842 }
843
844 size_t parseInt3() { // C.27
845 uint8_t b = *dataP++;
846 if (!(b & 0x20)) { // xx0..... (C.27.2)
847 return b & 0x1f;
848 }
849 else if ((b & 0x38) == 0x20) { // xx100... ........ (C.27.3)
850 if (dataEnd - dataP > 0) {
851 return (((b & 0x07) << 8) | *dataP++) + 0x20;
852 }
853 }
854 else if ((b & 0x38) == 0x28) { // xx101... ........ ........ (C.27.4)
855 if (dataEnd - dataP > 1) {
856 size_t result = (((b & 0x07) << 16) | (dataP[0] << 8) | dataP[1]) + 0x820;
857 dataP += 2;
858 return result;
859 }
860 }
861 else if ((b & 0x3f) == 0x30) { // xx110000 0000.... ........ ........ (C.27.5)
862 if ((dataEnd - dataP > 2) && !(dataP[0] & 0xf0)) {
863 size_t result = (((dataP[0] & 0x0f) << 16) | (dataP[1] << 8) | dataP[2]) + 0x80820;
864 dataP += 3;
865 return result;
866 }
867 }
868 throw DeadlyImportError(parseErrorMessage);
869 }
870
871 size_t parseInt4() { // C.28
872 uint8_t b = *dataP++;
873 if (!(b & 0x10)) { // xxx0.... (C.28.2)
874 return b & 0x0f;
875 }
876 else if ((b & 0x1c) == 0x10) { // xxx100.. ........ (C.28.3)
877 if (dataEnd - dataP > 0) {
878 return (((b & 0x03) << 8) | *dataP++) + 0x10;
879 }
880 }
881 else if ((b & 0x1c) == 0x14) { // xxx101.. ........ ........ (C.28.4)
882 if (dataEnd - dataP > 1) {
883 size_t result = (((b & 0x03) << 16) | (dataP[0] << 8) | dataP[1]) + 0x410;
884 dataP += 2;
885 return result;
886 }
887 }
888 else if ((b & 0x1f) == 0x18) { // xxx11000 0000.... ........ ........ (C.28.5)
889 if ((dataEnd - dataP > 2) && !(dataP[0] & 0xf0)) {
890 size_t result = (((dataP[0] & 0x0f) << 16) | (dataP[1] << 8) | dataP[2]) + 0x40410;
891 dataP += 3;
892 return result;
893 }
894 }
895 throw DeadlyImportError(parseErrorMessage);
896 }
897
898 size_t parseSequenceLen() { // C.21
899 if (dataEnd - dataP > 0) {
900 uint8_t b = *dataP++;
901 if (b < 0x80) { // 0....... (C.21.2)
902 return b;
903 }
904 else if ((b & 0xf0) == 0x80) { // 1000.... ........ ........ (C.21.3)
905 if (dataEnd - dataP > 1) {
906 size_t result = (((b & 0x0f) << 16) | (dataP[0] << 8) | dataP[1]) + 0x80;
907 dataP += 2;
908 return result;
909 }
910 }
911 }
912 throw DeadlyImportError(parseErrorMessage);
913 }
914
915 std::string parseNonEmptyOctetString2() { // C.22
916 // Parse the length of the string
917 uint8_t b = *dataP++ & 0x7f;
918 size_t len;
919 if (!(b & 0x40)) { // x0...... (C.22.3.1)
920 len = b + 1;
921 }
922 else if (b == 0x40) { // x1000000 ........ (C.22.3.2)
923 if (dataEnd - dataP < 1) {
924 throw DeadlyImportError(parseErrorMessage);
925 }
926 len = *dataP++ + 0x41;
927 }
928 else if (b == 0x60) { // x1100000 ........ ........ ........ ........ (C.22.3.3)
929 if (dataEnd - dataP < 4) {
930 throw DeadlyImportError(parseErrorMessage);
931 }
932 len = ((dataP[0] << 24) | (dataP[1] << 16) | (dataP[2] << 8) | dataP[3]) + 0x141;
933 dataP += 4;
934 }
935 else {
936 throw DeadlyImportError(parseErrorMessage);
937 }
938
939 // Parse the string (C.22.4)
940 if (dataEnd - dataP < static_cast<ptrdiff_t>(len)) {
941 throw DeadlyImportError(parseErrorMessage);
942 }
943 std::string s = parseUTF8String(dataP, len);
944 dataP += len;
945
946 return s;
947 }
948
949 size_t parseNonEmptyOctetString5Length() { // C.23
950 // Parse the length of the string
951 size_t b = *dataP++ & 0x0f;
952 if (!(b & 0x08)) { // xxxx0... (C.23.3.1)
953 return b + 1;
954 }
955 else if (b == 0x08) { // xxxx1000 ........ (C.23.3.2)
956 if (dataEnd - dataP > 0) {
957 return *dataP++ + 0x09;
958 }
959 }
960 else if (b == 0x0c) { // xxxx1100 ........ ........ ........ ........ (C.23.3.3)
961 if (dataEnd - dataP > 3) {
962 size_t result = ((dataP[0] << 24) | (dataP[1] << 16) | (dataP[2] << 8) | dataP[3]) + 0x109;
963 dataP += 4;
964 return result;
965 }
966 }
967 throw DeadlyImportError(parseErrorMessage);
968 }
969
970 size_t parseNonEmptyOctetString7Length() { // C.24
971 // Parse the length of the string
972 size_t b = *dataP++ & 0x03;
973 if (!(b & 0x02)) { // xxxxxx0. (C.24.3.1)
974 return b + 1;
975 }
976 else if (b == 0x02) { // xxxxxx10 ........ (C.24.3.2)
977 if (dataEnd - dataP > 0) {
978 return *dataP++ + 0x3;
979 }
980 }
981 else if (b == 0x03) { // xxxxxx11 ........ ........ ........ ........ (C.24.3.3)
982 if (dataEnd - dataP > 3) {
983 size_t result = ((dataP[0] << 24) | (dataP[1] << 16) | (dataP[2] << 8) | dataP[3]) + 0x103;
984 dataP += 4;
985 return result;
986 }
987 }
988 throw DeadlyImportError(parseErrorMessage);
989 }
990
991 std::shared_ptr<const FIValue> parseEncodedData(size_t index, size_t len) {
992 if (index < 32) {
993 FIDecoder *decoder = defaultDecoder[index];
994 if (!decoder) {
995 throw DeadlyImportError("Invalid encoding algorithm index " + to_string(index));
996 }
997 return decoder->decode(dataP, len);
998 }
999 else {
1000 if (index - 32 >= vocabulary.encodingAlgorithmTable.size()) {
1001 throw DeadlyImportError("Invalid encoding algorithm index " + to_string(index));
1002 }
1003 std::string uri = vocabulary.encodingAlgorithmTable[index - 32];
1004 auto it = decoderMap.find(uri);
1005 if (it == decoderMap.end()) {
1006 throw DeadlyImportError("Unsupported encoding algorithm " + uri);
1007 }
1008 else {
1009 return it->second->decode(dataP, len);
1010 }
1011 }
1012 }
1013
1014 std::shared_ptr<const FIValue> parseRestrictedAlphabet(size_t index, size_t len) {
1015 std::string alphabet;
1016 if (index < 16) {
1017 switch (index) {
1018 case 0: // numeric
1019 alphabet = "0123456789-+.e ";
1020 break;
1021 case 1: // date and time
1022 alphabet = "0123456789-:TZ ";
1023 break;
1024 default:
1025 throw DeadlyImportError("Invalid restricted alphabet index " + to_string(index));
1026 }
1027 }
1028 else {
1029 if (index - 16 >= vocabulary.restrictedAlphabetTable.size()) {
1030 throw DeadlyImportError("Invalid restricted alphabet index " + to_string(index));
1031 }
1032 alphabet = vocabulary.restrictedAlphabetTable[index - 16];
1033 }
1034 std::vector<uint32_t> alphabetUTF32;
1035 utf8::utf8to32(alphabet.begin(), alphabet.end(), back_inserter(alphabetUTF32));
1036 std::string::size_type alphabetLength = alphabetUTF32.size();
1037 if (alphabetLength < 2) {
1038 throw DeadlyImportError("Invalid restricted alphabet length " + to_string(alphabetLength));
1039 }
1040 std::string::size_type bitsPerCharacter = 1;
1041 while ((1ull << bitsPerCharacter) <= alphabetLength) {
1042 ++bitsPerCharacter;
1043 }
1044 size_t bitsAvail = 0;
1045 uint8_t mask = (1 << bitsPerCharacter) - 1;
1046 uint32_t bits = 0;
1047 std::string s;
1048 for (size_t i = 0; i < len; ++i) {
1049 bits = (bits << 8) | dataP[i];
1050 bitsAvail += 8;
1051 while (bitsAvail >= bitsPerCharacter) {
1052 bitsAvail -= bitsPerCharacter;
1053 size_t charIndex = (bits >> bitsAvail) & mask;
1054 if (charIndex < alphabetLength) {
1055 s.push_back(alphabetUTF32[charIndex]);
1056 }
1057 else if (charIndex != mask) {
1058 throw DeadlyImportError(parseErrorMessage);
1059 }
1060 }
1061 }
1062 return FIStringValue::create(std::move(s));
1063 }
1064
1065 std::shared_ptr<const FIValue> parseEncodedCharacterString3() { // C.19
1066 std::shared_ptr<const FIValue> result;
1067 size_t len;
1068 uint8_t b = *dataP;
1069 if (b & 0x20) {
1070 ++dataP;
1071 if (dataEnd - dataP < 1) {
1072 throw DeadlyImportError(parseErrorMessage);
1073 }
1074 size_t index = ((b & 0x0f) << 4) | ((*dataP & 0xf0) >> 4); // C.29
1075 len = parseNonEmptyOctetString5Length();
1076 if (dataEnd - dataP < static_cast<ptrdiff_t>(len)) {
1077 throw DeadlyImportError(parseErrorMessage);
1078 }
1079 if (b & 0x10) {
1080 // encoding algorithm (C.19.3.4)
1081 result = parseEncodedData(index, len);
1082 }
1083 else {
1084 // Restricted alphabet (C.19.3.3)
1085 result = parseRestrictedAlphabet(index, len);
1086 }
1087 }
1088 else {
1089 len = parseNonEmptyOctetString5Length();
1090 if (dataEnd - dataP < static_cast<ptrdiff_t>(len)) {
1091 throw DeadlyImportError(parseErrorMessage);
1092 }
1093 if (b & 0x10) {
1094 // UTF-16 (C.19.3.2)
1095 if (len & 1) {
1096 throw DeadlyImportError(parseErrorMessage);
1097 }
1098 result = FIStringValue::create(parseUTF16String(dataP, len));
1099 }
1100 else {
1101 // UTF-8 (C.19.3.1)
1102 result = FIStringValue::create(parseUTF8String(dataP, len));
1103 }
1104 }
1105 dataP += len;
1106 return result;
1107 }
1108
1109 std::shared_ptr<const FIValue> parseEncodedCharacterString5() { // C.20
1110 std::shared_ptr<const FIValue> result;
1111 size_t len;
1112 uint8_t b = *dataP;
1113 if (b & 0x08) {
1114 ++dataP;
1115 if (dataEnd - dataP < 1) {
1116 throw DeadlyImportError(parseErrorMessage);
1117 }
1118 size_t index = ((b & 0x03) << 6) | ((*dataP & 0xfc) >> 2); /* C.29 */
1119 len = parseNonEmptyOctetString7Length();
1120 if (dataEnd - dataP < static_cast<ptrdiff_t>(len)) {
1121 throw DeadlyImportError(parseErrorMessage);
1122 }
1123 if (b & 0x04) {
1124 // encoding algorithm (C.20.3.4)
1125 result = parseEncodedData(index, len);
1126 }
1127 else {
1128 // Restricted alphabet (C.20.3.3)
1129 result = parseRestrictedAlphabet(index, len);
1130 }
1131 }
1132 else {
1133 len = parseNonEmptyOctetString7Length();
1134 if (dataEnd - dataP < static_cast<ptrdiff_t>(len)) {
1135 throw DeadlyImportError(parseErrorMessage);
1136 }
1137 if (b & 0x04) {
1138 // UTF-16 (C.20.3.2)
1139 if (len & 1) {
1140 throw DeadlyImportError(parseErrorMessage);
1141 }
1142 result = FIStringValue::create(parseUTF16String(dataP, len));
1143 }
1144 else {
1145 // UTF-8 (C.20.3.1)
1146 result = FIStringValue::create(parseUTF8String(dataP, len));
1147 }
1148 }
1149 dataP += len;
1150 return result;
1151 }
1152
1153 const std::string &parseIdentifyingStringOrIndex(std::vector<std::string> &stringTable) { // C.13
1154 if (dataEnd - dataP < 1) {
1155 throw DeadlyImportError(parseErrorMessage);
1156 }
1157 uint8_t b = *dataP;
1158 if (b & 0x80) {
1159 // We have an index (C.13.4)
1160 size_t index = parseInt2();
1161 if (index >= stringTable.size()) {
1162 throw DeadlyImportError(parseErrorMessage);
1163 }
1164 return stringTable[index];
1165 }
1166 else {
1167 // We have a string (C.13.3)
1168 stringTable.push_back(parseNonEmptyOctetString2());
1169 return stringTable.back();
1170 }
1171 }
1172
1173 QName parseNameSurrogate() { // C.16
1174 if (dataEnd - dataP < 1) {
1175 throw DeadlyImportError(parseErrorMessage);
1176 }
1177 uint8_t b = *dataP++;
1178 if (b & 0xfc) { // Padding '000000' C.2.5.5
1179 throw DeadlyImportError(parseErrorMessage);
1180 }
1181 QName result;
1182 size_t index;
1183 if (b & 0x02) { // prefix (C.16.3)
1184 if ((dataEnd - dataP < 1) || (*dataP & 0x80)) {
1185 throw DeadlyImportError(parseErrorMessage);
1186 }
1187 index = parseInt2();
1188 if (index >= vocabulary.prefixTable.size()) {
1189 throw DeadlyImportError(parseErrorMessage);
1190 }
1191 result.prefix = vocabulary.prefixTable[index];
1192 }
1193 if (b & 0x01) { // namespace-name (C.16.4)
1194 if ((dataEnd - dataP < 1) || (*dataP & 0x80)) {
1195 throw DeadlyImportError(parseErrorMessage);
1196 }
1197 index = parseInt2();
1198 if (index >= vocabulary.namespaceNameTable.size()) {
1199 throw DeadlyImportError(parseErrorMessage);
1200 }
1201 result.uri = vocabulary.namespaceNameTable[index];
1202 }
1203 // local-name
1204 if ((dataEnd - dataP < 1) || (*dataP & 0x80)) {
1205 throw DeadlyImportError(parseErrorMessage);
1206 }
1207 index = parseInt2();
1208 if (index >= vocabulary.localNameTable.size()) {
1209 throw DeadlyImportError(parseErrorMessage);
1210 }
1211 result.name = vocabulary.localNameTable[index];
1212 return result;
1213 }
1214
1215 const QName &parseQualifiedNameOrIndex2(std::vector<QName> &qNameTable) { // C.17
1216 uint8_t b = *dataP;
1217 if ((b & 0x7c) == 0x78) { // x11110..
1218 // We have a literal (C.17.3)
1219 ++dataP;
1220 QName result;
1221 // prefix (C.17.3.1)
1222 result.prefix = b & 0x02 ? parseIdentifyingStringOrIndex(vocabulary.prefixTable) : std::string();
1223 // namespace-name (C.17.3.1)
1224 result.uri = b & 0x01 ? parseIdentifyingStringOrIndex(vocabulary.namespaceNameTable) : std::string();
1225 // local-name
1226 result.name = parseIdentifyingStringOrIndex(vocabulary.localNameTable);
1227 qNameTable.push_back(result);
1228 return qNameTable.back();
1229 }
1230 else {
1231 // We have an index (C.17.4)
1232 size_t index = parseInt2();
1233 if (index >= qNameTable.size()) {
1234 throw DeadlyImportError(parseErrorMessage);
1235 }
1236 return qNameTable[index];
1237 }
1238 }
1239
1240 const QName &parseQualifiedNameOrIndex3(std::vector<QName> &qNameTable) { // C.18
1241 uint8_t b = *dataP;
1242 if ((b & 0x3c) == 0x3c) { // xx1111..
1243 // We have a literal (C.18.3)
1244 ++dataP;
1245 QName result;
1246 // prefix (C.18.3.1)
1247 result.prefix = b & 0x02 ? parseIdentifyingStringOrIndex(vocabulary.prefixTable) : std::string();
1248 // namespace-name (C.18.3.1)
1249 result.uri = b & 0x01 ? parseIdentifyingStringOrIndex(vocabulary.namespaceNameTable) : std::string();
1250 // local-name
1251 result.name = parseIdentifyingStringOrIndex(vocabulary.localNameTable);
1252 qNameTable.push_back(result);
1253 return qNameTable.back();
1254 }
1255 else {
1256 // We have an index (C.18.4)
1257 size_t index = parseInt3();
1258 if (index >= qNameTable.size()) {
1259 throw DeadlyImportError(parseErrorMessage);
1260 }
1261 return qNameTable[index];
1262 }
1263 }
1264
1265 std::shared_ptr<const FIValue> parseNonIdentifyingStringOrIndex1(std::vector<std::shared_ptr<const FIValue>> &valueTable) { // C.14
1266 uint8_t b = *dataP;
1267 if (b == 0xff) { // C.26.2
1268 // empty string
1269 ++dataP;
1270 return EmptyFIString;
1271 }
1272 else if (b & 0x80) { // C.14.4
1273 // We have an index
1274 size_t index = parseInt2();
1275 if (index >= valueTable.size()) {
1276 throw DeadlyImportError(parseErrorMessage);
1277 }
1278 return valueTable[index];
1279 }
1280 else { // C.14.3
1281 // We have a literal
1282 std::shared_ptr<const FIValue> result = parseEncodedCharacterString3();
1283 if (b & 0x40) { // C.14.3.1
1284 valueTable.push_back(result);
1285 }
1286 return result;
1287 }
1288 }
1289
1290 std::shared_ptr<const FIValue> parseNonIdentifyingStringOrIndex3(std::vector<std::shared_ptr<const FIValue>> &valueTable) { // C.15
1291 uint8_t b = *dataP;
1292 if (b & 0x20) { // C.15.4
1293 // We have an index
1294 size_t index = parseInt4();
1295 if (index >= valueTable.size()) {
1296 throw DeadlyImportError(parseErrorMessage);
1297 }
1298 return valueTable[index];
1299 }
1300 else { // C.15.3
1301 // We have a literal
1302 std::shared_ptr<const FIValue> result = parseEncodedCharacterString5();
1303 if (b & 0x10) { // C.15.3.1
1304 valueTable.push_back(result);
1305 }
1306 return result;
1307 }
1308 }
1309
1310 void parseElement() {
1311 // C.3
1312
1313 attributes.clear();
1314
1315 uint8_t b = *dataP;
1316 bool hasAttributes = (b & 0x40) != 0; // C.3.3
1317 if ((b & 0x3f) == 0x38) { // C.3.4.1
1318 // Parse namespaces
1319 ++dataP;
1320 for (;;) {
1321 if (dataEnd - dataP < 1) {
1322 throw DeadlyImportError(parseErrorMessage);
1323 }
1324 b = *dataP++;
1325 if (b == 0xf0) { // C.3.4.3
1326 break;
1327 }
1328 if ((b & 0xfc) != 0xcc) { // C.3.4.2
1329 throw DeadlyImportError(parseErrorMessage);
1330 }
1331 // C.12
1332 Attribute attr;
1333 attr.qname.prefix = "xmlns";
1334 attr.qname.name = b & 0x02 ? parseIdentifyingStringOrIndex(vocabulary.prefixTable) : std::string();
1335 attr.qname.uri = b & 0x01 ? parseIdentifyingStringOrIndex(vocabulary.namespaceNameTable) : std::string();
1336 attr.name = attr.qname.name.empty() ? "xmlns" : "xmlns:" + attr.qname.name;
1337 attr.value = FIStringValue::create(std::string(attr.qname.uri));
1338 attributes.push_back(attr);
1339 }
1340 if ((dataEnd - dataP < 1) || (*dataP & 0xc0)) {
1341 throw DeadlyImportError(parseErrorMessage);
1342 }
1343 }
1344
1345 // Parse Element name (C.3.5)
1346 const QName &elemName = parseQualifiedNameOrIndex3(vocabulary.elementNameTable);
1347 nodeName = elemName.prefix.empty() ? elemName.name : elemName.prefix + ':' + elemName.name;
1348
1349 if (hasAttributes) {
1350 for (;;) {
1351 if (dataEnd - dataP < 1) {
1352 throw DeadlyImportError(parseErrorMessage);
1353 }
1354 b = *dataP;
1355 if (b < 0x80) { // C.3.6.1
1356 // C.4
1357 Attribute attr;
1358 attr.qname = parseQualifiedNameOrIndex2(vocabulary.attributeNameTable);
1359 attr.name = attr.qname.prefix.empty() ? attr.qname.name : attr.qname.prefix + ':' + attr.qname.name;
1360 if (dataEnd - dataP < 1) {
1361 throw DeadlyImportError(parseErrorMessage);
1362 }
1363 attr.value = parseNonIdentifyingStringOrIndex1(vocabulary.attributeValueTable);
1364 attributes.push_back(attr);
1365 }
1366 else {
1367 if ((b & 0xf0) != 0xf0) { // C.3.6.2
1368 throw DeadlyImportError(parseErrorMessage);
1369 }
1370 emptyElement = b == 0xff; // C.3.6.2, C.3.8
1371 ++dataP;
1372 break;
1373 }
1374 }
1375 }
1376 else {
1377 if (dataEnd - dataP < 1) {
1378 throw DeadlyImportError(parseErrorMessage);
1379 }
1380 b = *dataP;
1381 switch (b) {
1382 case 0xff:
1383 terminatorPending = true;
1384 // Intentionally fall through
1385 case 0xf0:
1386 emptyElement = true;
1387 ++dataP;
1388 break;
1389 default:
1390 emptyElement = false;
1391 }
1392 }
1393 if (!emptyElement) {
1394 elementStack.push(nodeName);
1395 }
1396
1397 currentNodeType = irr::io::EXN_ELEMENT;
1398 }
1399
1400 void parseHeader() {
1401 // Parse header (C.1.3)
1402 size_t magicSize = parseMagic(dataP, dataEnd);
1403 if (!magicSize) {
1404 throw DeadlyImportError(parseErrorMessage);
1405 }
1406 dataP += magicSize;
1407 // C.2.3
1408 if (dataEnd - dataP < 1) {
1409 throw DeadlyImportError(parseErrorMessage);
1410 }
1411 uint8_t b = *dataP++;
1412 if (b & 0x40) {
1413 // Parse additional data (C.2.4)
1414 size_t len = parseSequenceLen();
1415 for (size_t i = 0; i < len; ++i) {
1416 if (dataEnd - dataP < 1) {
1417 throw DeadlyImportError(parseErrorMessage);
1418 }
1419 /*std::string id =*/ parseNonEmptyOctetString2();
1420 if (dataEnd - dataP < 1) {
1421 throw DeadlyImportError(parseErrorMessage);
1422 }
1423 /*std::string data =*/ parseNonEmptyOctetString2();
1424 }
1425 }
1426 if (b & 0x20) {
1427 // Parse initial vocabulary (C.2.5)
1428 if (dataEnd - dataP < 2) {
1429 throw DeadlyImportError(parseErrorMessage);
1430 }
1431 uint16_t b1 = (dataP[0] << 8) | dataP[1];
1432 dataP += 2;
1433 if (b1 & 0x1000) {
1434 // External vocabulary (C.2.5.2)
1435 if (dataEnd - dataP < 1) {
1436 throw DeadlyImportError(parseErrorMessage);
1437 }
1438 std::string uri = parseNonEmptyOctetString2();
1439 auto it = vocabularyMap.find(uri);
1440 if (it == vocabularyMap.end()) {
1441 throw DeadlyImportError("Unknown vocabulary " + uri);
1442 }
1443 const FIVocabulary *externalVocabulary = it->second;
1444 if (externalVocabulary->restrictedAlphabetTable) {
1445 std::copy(externalVocabulary->restrictedAlphabetTable, externalVocabulary->restrictedAlphabetTable + externalVocabulary->restrictedAlphabetTableSize, std::back_inserter(vocabulary.restrictedAlphabetTable));
1446 }
1447 if (externalVocabulary->encodingAlgorithmTable) {
1448 std::copy(externalVocabulary->encodingAlgorithmTable, externalVocabulary->encodingAlgorithmTable + externalVocabulary->encodingAlgorithmTableSize, std::back_inserter(vocabulary.encodingAlgorithmTable));
1449 }
1450 if (externalVocabulary->prefixTable) {
1451 std::copy(externalVocabulary->prefixTable, externalVocabulary->prefixTable + externalVocabulary->prefixTableSize, std::back_inserter(vocabulary.prefixTable));
1452 }
1453 if (externalVocabulary->namespaceNameTable) {
1454 std::copy(externalVocabulary->namespaceNameTable, externalVocabulary->namespaceNameTable + externalVocabulary->namespaceNameTableSize, std::back_inserter(vocabulary.namespaceNameTable));
1455 }
1456 if (externalVocabulary->localNameTable) {
1457 std::copy(externalVocabulary->localNameTable, externalVocabulary->localNameTable + externalVocabulary->localNameTableSize, std::back_inserter(vocabulary.localNameTable));
1458 }
1459 if (externalVocabulary->otherNCNameTable) {
1460 std::copy(externalVocabulary->otherNCNameTable, externalVocabulary->otherNCNameTable + externalVocabulary->otherNCNameTableSize, std::back_inserter(vocabulary.otherNCNameTable));
1461 }
1462 if (externalVocabulary->otherURITable) {
1463 std::copy(externalVocabulary->otherURITable, externalVocabulary->otherURITable + externalVocabulary->otherURITableSize, std::back_inserter(vocabulary.otherURITable));
1464 }
1465 if (externalVocabulary->attributeValueTable) {
1466 std::copy(externalVocabulary->attributeValueTable, externalVocabulary->attributeValueTable + externalVocabulary->attributeValueTableSize, std::back_inserter(vocabulary.attributeValueTable));
1467 }
1468 if (externalVocabulary->charactersTable) {
1469 std::copy(externalVocabulary->charactersTable, externalVocabulary->charactersTable + externalVocabulary->charactersTableSize, std::back_inserter(vocabulary.charactersTable));
1470 }
1471 if (externalVocabulary->otherStringTable) {
1472 std::copy(externalVocabulary->otherStringTable, externalVocabulary->otherStringTable + externalVocabulary->otherStringTableSize, std::back_inserter(vocabulary.otherStringTable));
1473 }
1474 if (externalVocabulary->elementNameTable) {
1475 std::copy(externalVocabulary->elementNameTable, externalVocabulary->elementNameTable + externalVocabulary->elementNameTableSize, std::back_inserter(vocabulary.elementNameTable));
1476 }
1477 if (externalVocabulary->attributeNameTable) {
1478 std::copy(externalVocabulary->attributeNameTable, externalVocabulary->attributeNameTable + externalVocabulary->attributeNameTableSize, std::back_inserter(vocabulary.attributeNameTable));
1479 }
1480 }
1481 if (b1 & 0x0800) {
1482 // Parse restricted alphabets (C.2.5.3)
1483 for (size_t len = parseSequenceLen(); len > 0; --len) {
1484 if (dataEnd - dataP < 1) {
1485 throw DeadlyImportError(parseErrorMessage);
1486 }
1487 vocabulary.restrictedAlphabetTable.push_back(parseNonEmptyOctetString2());
1488 }
1489 }
1490 if (b1 & 0x0400) {
1491 // Parse encoding algorithms (C.2.5.3)
1492 for (size_t len = parseSequenceLen(); len > 0; --len) {
1493 if (dataEnd - dataP < 1) {
1494 throw DeadlyImportError(parseErrorMessage);
1495 }
1496 vocabulary.encodingAlgorithmTable.push_back(parseNonEmptyOctetString2());
1497 }
1498 }
1499 if (b1 & 0x0200) {
1500 // Parse prefixes (C.2.5.3)
1501 for (size_t len = parseSequenceLen(); len > 0; --len) {
1502 if (dataEnd - dataP < 1) {
1503 throw DeadlyImportError(parseErrorMessage);
1504 }
1505 vocabulary.prefixTable.push_back(parseNonEmptyOctetString2());
1506 }
1507 }
1508 if (b1 & 0x0100) {
1509 // Parse namespace names (C.2.5.3)
1510 for (size_t len = parseSequenceLen(); len > 0; --len) {
1511 if (dataEnd - dataP < 1) {
1512 throw DeadlyImportError(parseErrorMessage);
1513 }
1514 vocabulary.namespaceNameTable.push_back(parseNonEmptyOctetString2());
1515 }
1516 }
1517 if (b1 & 0x0080) {
1518 // Parse local names (C.2.5.3)
1519 for (size_t len = parseSequenceLen(); len > 0; --len) {
1520 if (dataEnd - dataP < 1) {
1521 throw DeadlyImportError(parseErrorMessage);
1522 }
1523 vocabulary.localNameTable.push_back(parseNonEmptyOctetString2());
1524 }
1525 }
1526 if (b1 & 0x0040) {
1527 // Parse other ncnames (C.2.5.3)
1528 for (size_t len = parseSequenceLen(); len > 0; --len) {
1529 if (dataEnd - dataP < 1) {
1530 throw DeadlyImportError(parseErrorMessage);
1531 }
1532 vocabulary.otherNCNameTable.push_back(parseNonEmptyOctetString2());
1533 }
1534 }
1535 if (b1 & 0x0020) {
1536 // Parse other uris (C.2.5.3)
1537 for (size_t len = parseSequenceLen(); len > 0; --len) {
1538 if (dataEnd - dataP < 1) {
1539 throw DeadlyImportError(parseErrorMessage);
1540 }
1541 vocabulary.otherURITable.push_back(parseNonEmptyOctetString2());
1542 }
1543 }
1544 if (b1 & 0x0010) {
1545 // Parse attribute values (C.2.5.4)
1546 for (size_t len = parseSequenceLen(); len > 0; --len) {
1547 if (dataEnd - dataP < 1) {
1548 throw DeadlyImportError(parseErrorMessage);
1549 }
1550 vocabulary.attributeValueTable.push_back(parseEncodedCharacterString3());
1551 }
1552 }
1553 if (b1 & 0x0008) {
1554 // Parse content character chunks (C.2.5.4)
1555 for (size_t len = parseSequenceLen(); len > 0; --len) {
1556 if (dataEnd - dataP < 1) {
1557 throw DeadlyImportError(parseErrorMessage);
1558 }
1559 vocabulary.charactersTable.push_back(parseEncodedCharacterString3());
1560 }
1561 }
1562 if (b1 & 0x0004) {
1563 // Parse other strings (C.2.5.4)
1564 for (size_t len = parseSequenceLen(); len > 0; --len) {
1565 if (dataEnd - dataP < 1) {
1566 throw DeadlyImportError(parseErrorMessage);
1567 }
1568 vocabulary.otherStringTable.push_back(parseEncodedCharacterString3());
1569 }
1570 }
1571 if (b1 & 0x0002) {
1572 // Parse element name surrogates (C.2.5.5)
1573 for (size_t len = parseSequenceLen(); len > 0; --len) {
1574 vocabulary.elementNameTable.push_back(parseNameSurrogate());
1575 }
1576 }
1577 if (b1 & 0x0001) {
1578 // Parse attribute name surrogates (C.2.5.5)
1579 for (size_t len = parseSequenceLen(); len > 0; --len) {
1580 vocabulary.attributeNameTable.push_back(parseNameSurrogate());
1581 }
1582 }
1583 }
1584 if (b & 0x10) {
1585 // Parse notations (C.2.6)
1586 for (;;) {
1587 if (dataEnd - dataP < 1) {
1588 throw DeadlyImportError(parseErrorMessage);
1589 }
1590 uint8_t b1 = *dataP++;
1591 if (b1 == 0xf0) {
1592 break;
1593 }
1594 if ((b1 & 0xfc) != 0xc0) {
1595 throw DeadlyImportError(parseErrorMessage);
1596 }
1597 /* C.11 */
1598 /*const std::string &name =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable);
1599 if (b1 & 0x02) {
1600 /*const std::string &systemId =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
1601 }
1602 if (b1 & 0x01) {
1603 /*const std::string &publicId =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
1604 }
1605 }
1606 }
1607 if (b & 0x08) {
1608 // Parse unparsed entities (C.2.7)
1609 for (;;) {
1610 if (dataEnd - dataP < 1) {
1611 throw DeadlyImportError(parseErrorMessage);
1612 }
1613 uint8_t b1 = *dataP++;
1614 if (b1 == 0xf0) {
1615 break;
1616 }
1617 if ((b1 & 0xfe) != 0xd0) {
1618 throw DeadlyImportError(parseErrorMessage);
1619 }
1620 /* C.10 */
1621 /*const std::string &name =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable);
1622 /*const std::string &systemId =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
1623 if (b1 & 0x01) {
1624 /*const std::string &publicId =*/ parseIdentifyingStringOrIndex(vocabulary.otherURITable);
1625 }
1626 /*const std::string &notationName =*/ parseIdentifyingStringOrIndex(vocabulary.otherNCNameTable);
1627 }
1628 }
1629 if (b & 0x04) {
1630 // Parse character encoding scheme (C.2.8)
1631 if (dataEnd - dataP < 1) {
1632 throw DeadlyImportError(parseErrorMessage);
1633 }
1634 /*std::string characterEncodingScheme =*/ parseNonEmptyOctetString2();
1635 }
1636 if (b & 0x02) {
1637 // Parse standalone flag (C.2.9)
1638 if (dataEnd - dataP < 1) {
1639 throw DeadlyImportError(parseErrorMessage);
1640 }
1641 uint8_t b1 = *dataP++;
1642 if (b1 & 0xfe) {
1643 throw DeadlyImportError(parseErrorMessage);
1644 }
1645 //bool standalone = b1 & 0x01;
1646 }
1647 if (b & 0x01) {
1648 // Parse version (C.2.10)
1649 if (dataEnd - dataP < 1) {
1650 throw DeadlyImportError(parseErrorMessage);
1651 }
1652 /*std::shared_ptr<const FIValue> version =*/ parseNonIdentifyingStringOrIndex1(vocabulary.otherStringTable);
1653 }
1654 }
1655
1656 std::unique_ptr<uint8_t[]> data;
1657 uint8_t *dataP, *dataEnd;
1658 irr::io::EXML_NODE currentNodeType;
1659 bool emptyElement;
1660 bool headerPending;
1661 bool terminatorPending;
1662 Vocabulary vocabulary;
1663 std::vector<Attribute> attributes;
1664 std::stack<std::string> elementStack;
1665 std::string nodeName;
1666 std::map<std::string, std::unique_ptr<FIDecoder>> decoderMap;
1667 std::map<std::string, const FIVocabulary*> vocabularyMap;
1668
1669 static const std::string EmptyString;
1670 static std::shared_ptr<const FIValue> EmptyFIString;
1671
1672 static FIHexDecoder hexDecoder;
1673 static FIBase64Decoder base64Decoder;
1674 static FIShortDecoder shortDecoder;
1675 static FIIntDecoder intDecoder;
1676 static FILongDecoder longDecoder;
1677 static FIBoolDecoder boolDecoder;
1678 static FIFloatDecoder floatDecoder;
1679 static FIDoubleDecoder doubleDecoder;
1680 static FIUUIDDecoder uuidDecoder;
1681 static FICDATADecoder cdataDecoder;
1682 static FIDecoder *defaultDecoder[32];
1683};
1684
1685const std::string CFIReaderImpl::EmptyString;
1686std::shared_ptr<const FIValue> CFIReaderImpl::EmptyFIString = FIStringValue::create(std::string());
1687
1688FIHexDecoder CFIReaderImpl::hexDecoder;
1689FIBase64Decoder CFIReaderImpl::base64Decoder;
1690FIShortDecoder CFIReaderImpl::shortDecoder;
1691FIIntDecoder CFIReaderImpl::intDecoder;
1692FILongDecoder CFIReaderImpl::longDecoder;
1693FIBoolDecoder CFIReaderImpl::boolDecoder;
1694FIFloatDecoder CFIReaderImpl::floatDecoder;
1695FIDoubleDecoder CFIReaderImpl::doubleDecoder;
1696FIUUIDDecoder CFIReaderImpl::uuidDecoder;
1697FICDATADecoder CFIReaderImpl::cdataDecoder;
1698
1699FIDecoder *CFIReaderImpl::defaultDecoder[32] = {
1700 &hexDecoder,
1701 &base64Decoder,
1702 &shortDecoder,
1703 &intDecoder,
1704 &longDecoder,
1705 &boolDecoder,
1706 &floatDecoder,
1707 &doubleDecoder,
1708 &uuidDecoder,
1709 &cdataDecoder
1710};
1711
1712class CXMLReaderImpl : public FIReader
1713{
1714public:
1715
1716 //! Constructor
1717 CXMLReaderImpl(std::unique_ptr<irr::io::IIrrXMLReader<char, irr::io::IXMLBase>> reader_)
1718 : reader(std::move(reader_))
1719 {}
1720
1721 virtual ~CXMLReaderImpl() {}
1722
1723 virtual bool read() /*override*/ {
1724 return reader->read();
1725 }
1726
1727 virtual irr::io::EXML_NODE getNodeType() const /*override*/ {
1728 return reader->getNodeType();
1729 }
1730
1731 virtual int getAttributeCount() const /*override*/ {
1732 return reader->getAttributeCount();
1733 }
1734
1735 virtual const char* getAttributeName(int idx) const /*override*/ {
1736 return reader->getAttributeName(idx);
1737 }
1738
1739 virtual const char* getAttributeValue(int idx) const /*override*/ {
1740 return reader->getAttributeValue(idx);
1741 }
1742
1743 virtual const char* getAttributeValue(const char* name) const /*override*/ {
1744 return reader->getAttributeValue(name);
1745 }
1746
1747 virtual const char* getAttributeValueSafe(const char* name) const /*override*/ {
1748 return reader->getAttributeValueSafe(name);
1749 }
1750
1751 virtual int getAttributeValueAsInt(const char* name) const /*override*/ {
1752 return reader->getAttributeValueAsInt(name);
1753 }
1754
1755 virtual int getAttributeValueAsInt(int idx) const /*override*/ {
1756 return reader->getAttributeValueAsInt(idx);
1757 }
1758
1759 virtual float getAttributeValueAsFloat(const char* name) const /*override*/ {
1760 return reader->getAttributeValueAsFloat(name);
1761 }
1762
1763 virtual float getAttributeValueAsFloat(int idx) const /*override*/ {
1764 return reader->getAttributeValueAsFloat(idx);
1765 }
1766
1767 virtual const char* getNodeName() const /*override*/ {
1768 return reader->getNodeName();
1769 }
1770
1771 virtual const char* getNodeData() const /*override*/ {
1772 return reader->getNodeData();
1773 }
1774
1775 virtual bool isEmptyElement() const /*override*/ {
1776 return reader->isEmptyElement();
1777 }
1778
1779 virtual irr::io::ETEXT_FORMAT getSourceFormat() const /*override*/ {
1780 return reader->getSourceFormat();
1781 }
1782
1783 virtual irr::io::ETEXT_FORMAT getParserFormat() const /*override*/ {
1784 return reader->getParserFormat();
1785 }
1786
1787 virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(int /*idx*/) const /*override*/ {
1788 return nullptr;
1789 }
1790
1791 virtual std::shared_ptr<const FIValue> getAttributeEncodedValue(const char* /*name*/) const /*override*/ {
1792 return nullptr;
1793 }
1794
1795 virtual void registerDecoder(const std::string & /*algorithmUri*/, std::unique_ptr<FIDecoder> /*decoder*/) /*override*/ {}
1796
1797
1798 virtual void registerVocabulary(const std::string &/*vocabularyUri*/, const FIVocabulary * /*vocabulary*/) /*override*/ {}
1799
1800private:
1801
1802 std::unique_ptr<irr::io::IIrrXMLReader<char, irr::io::IXMLBase>> reader;
1803};
1804
1805static std::unique_ptr<uint8_t[]> readFile(IOStream *stream, size_t &size, bool &isFI) {
1806 size = stream->FileSize();
1807 std::unique_ptr<uint8_t[]> data = std::unique_ptr<uint8_t[]>(new uint8_t[size]);
1808 if (stream->Read(data.get(), size, 1) != 1) {
1809 size = 0;
1810 data.reset();
1811 }
1812 isFI = parseMagic(data.get(), data.get() + size) > 0;
1813 return data;
1814}
1815
1816std::unique_ptr<FIReader> FIReader::create(IOStream *stream)
1817{
1818 size_t size;
1819 bool isFI;
1820 auto data = readFile(stream, size, isFI);
1821 if (isFI) {
1822 return std::unique_ptr<FIReader>(new CFIReaderImpl(std::move(data), size));
1823 }
1824 else {
1825 auto memios = std::unique_ptr<MemoryIOStream>(new MemoryIOStream(data.release(), size, true));
1826 auto callback = std::unique_ptr<CIrrXML_IOStreamReader>(new CIrrXML_IOStreamReader(memios.get()));
1827 return std::unique_ptr<FIReader>(new CXMLReaderImpl(std::unique_ptr<irr::io::IIrrXMLReader<char, irr::io::IXMLBase>>(createIrrXMLReader(callback.get()))));
1828 }
1829}
1830
1831}// namespace Assimp
1832
1833#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER
1834