Coverage Report

Created: 2025-11-11 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/assimp/code/AssetLib/FBX/FBXParser.cpp
Line
Count
Source
1
/*
2
Open Asset Import Library (assimp)
3
----------------------------------------------------------------------
4
5
Copyright (c) 2006-2025, assimp team
6
7
All rights reserved.
8
9
Redistribution and use of this software in source and binary forms,
10
with or without modification, are permitted provided that the
11
following conditions are met:
12
13
* Redistributions of source code must retain the above
14
  copyright notice, this list of conditions and the
15
  following disclaimer.
16
17
* Redistributions in binary form must reproduce the above
18
  copyright notice, this list of conditions and the
19
  following disclaimer in the documentation and/or other
20
  materials provided with the distribution.
21
22
* Neither the name of the assimp team, nor the names of its
23
  contributors may be used to endorse or promote products
24
  derived from this software without specific prior
25
  written permission of the assimp team.
26
27
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
39
----------------------------------------------------------------------
40
*/
41
42
/** @file  FBXParser.cpp
43
 *  @brief Implementation of the FBX parser and the rudimentary DOM that we use
44
 */
45
46
#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
47
48
#include "Common/Compression.h"
49
50
#include "FBXTokenizer.h"
51
#include "FBXParser.h"
52
#include "FBXUtil.h"
53
54
#include <assimp/ParsingUtils.h>
55
#include <assimp/fast_atof.h>
56
#include <assimp/ByteSwapper.h>
57
#include <assimp/DefaultLogger.hpp>
58
59
#include <iostream>
60
61
using namespace Assimp;
62
using namespace Assimp::FBX;
63
64
namespace {
65
66
    // ------------------------------------------------------------------------------------------------
67
    // signal parse error, this is always unrecoverable. Throws DeadlyImportError.
68
    AI_WONT_RETURN void ParseError(const std::string& message, const Token& token) AI_WONT_RETURN_SUFFIX;
69
    AI_WONT_RETURN void ParseError(const std::string& message, const Token& token)
70
0
    {
71
0
        throw DeadlyImportError("FBX-Parser", Util::GetTokenText(&token), message);
72
0
    }
73
74
    // ------------------------------------------------------------------------------------------------
75
    AI_WONT_RETURN void ParseError(const std::string &message, const Element *element = nullptr) AI_WONT_RETURN_SUFFIX;
76
    AI_WONT_RETURN void ParseError(const std::string& message, const Element* element)
77
0
    {
78
0
        if(element) {
79
0
            ParseError(message,element->KeyToken());
80
0
        }
81
0
        throw DeadlyImportError("FBX-Parser ", message);
82
0
    }
83
84
85
    // ------------------------------------------------------------------------------------------------
86
    AI_WONT_RETURN void ParseError(const std::string& message, TokenPtr token) AI_WONT_RETURN_SUFFIX;
87
    void ParseError(const std::string& message, TokenPtr token)
88
0
    {
89
0
        if(token) {
90
0
            ParseError(message, *token);
91
0
        }
92
0
        ParseError(message);
93
0
    }
94
95
    // Initially, we did reinterpret_cast, breaking strict aliasing rules.
96
    // This actually caused trouble on Android, so let's be safe this time.
97
    // https://github.com/assimp/assimp/issues/24
98
    template <typename T>
99
0
    T SafeParse(const char* data, const char* end) {
100
        // Actual size validation happens during Tokenization so
101
        // this is valid as an assertion.
102
0
        (void)(end);
103
0
        ai_assert(static_cast<size_t>(end - data) >= sizeof(T));
104
0
        T result = static_cast<T>(0);
105
0
        ::memcpy(&result, data, sizeof(T));
106
0
        return result;
107
0
    }
Unexecuted instantiation: FBXParser.cpp:unsigned int (anonymous namespace)::SafeParse<unsigned int>(char const*, char const*)
Unexecuted instantiation: FBXParser.cpp:unsigned long (anonymous namespace)::SafeParse<unsigned long>(char const*, char const*)
Unexecuted instantiation: FBXParser.cpp:float (anonymous namespace)::SafeParse<float>(char const*, char const*)
Unexecuted instantiation: FBXParser.cpp:double (anonymous namespace)::SafeParse<double>(char const*, char const*)
Unexecuted instantiation: FBXParser.cpp:int (anonymous namespace)::SafeParse<int>(char const*, char const*)
Unexecuted instantiation: FBXParser.cpp:long (anonymous namespace)::SafeParse<long>(char const*, char const*)
108
}
109
110
namespace Assimp {
111
namespace FBX {
112
113
// ------------------------------------------------------------------------------------------------
114
Element::Element(const Token& key_token, Parser& parser) :
115
0
    key_token(key_token), compound(nullptr)
116
0
{
117
0
    TokenPtr n = nullptr;
118
0
    StackAllocator &allocator = parser.GetAllocator();
119
0
    do {
120
0
        n = parser.AdvanceToNextToken();
121
0
        if(!n) {
122
0
            ParseError("unexpected end of file, expected closing bracket",parser.LastToken());
123
0
        }
124
125
0
        if (n->Type() == TokenType_DATA) {
126
0
            tokens.push_back(n);
127
0
      TokenPtr prev = n;
128
0
            n = parser.AdvanceToNextToken();
129
0
            if(!n) {
130
0
                ParseError("unexpected end of file, expected bracket, comma or key",parser.LastToken());
131
0
            }
132
133
0
      const TokenType ty = n->Type();
134
135
      // some exporters are missing a comma on the next line
136
0
      if (ty == TokenType_DATA && prev->Type() == TokenType_DATA && (n->Line() == prev->Line() + 1)) {
137
0
        tokens.push_back(n);
138
0
        continue;
139
0
      }
140
141
0
            if (ty != TokenType_OPEN_BRACKET && ty != TokenType_CLOSE_BRACKET && ty != TokenType_COMMA && ty != TokenType_KEY) {
142
0
                ParseError("unexpected token; expected bracket, comma or key",n);
143
0
            }
144
0
        }
145
146
0
        if (n->Type() == TokenType_OPEN_BRACKET) {
147
0
            compound = new_Scope(parser);
148
149
            // current token should be a TOK_CLOSE_BRACKET
150
0
            n = parser.CurrentToken();
151
0
            ai_assert(n);
152
153
0
            if (n->Type() != TokenType_CLOSE_BRACKET) {
154
0
                ParseError("expected closing bracket",n);
155
0
            }
156
157
0
            parser.AdvanceToNextToken();
158
0
            return;
159
0
        }
160
0
    }
161
0
    while(n->Type() != TokenType_KEY && n->Type() != TokenType_CLOSE_BRACKET);
162
0
}
163
164
// ------------------------------------------------------------------------------------------------
165
Element::~Element()
166
0
{
167
0
    if (compound) {
168
0
        delete_Scope(compound);
169
0
    }
170
171
     // no need to delete tokens, they are owned by the parser
172
0
}
173
174
Scope::Scope(Parser& parser,bool topLevel)
175
0
{
176
0
    if(!topLevel) {
177
0
        TokenPtr t = parser.CurrentToken();
178
0
        if (t->Type() != TokenType_OPEN_BRACKET) {
179
0
            ParseError("expected open bracket",t);
180
0
        }
181
0
    }
182
183
0
    StackAllocator &allocator = parser.GetAllocator();
184
0
    TokenPtr n = parser.AdvanceToNextToken();
185
0
    if (n == nullptr) {
186
0
        ParseError("unexpected end of file");
187
0
    }
188
189
    // note: empty scopes are allowed
190
0
    while(n->Type() != TokenType_CLOSE_BRACKET) {
191
0
        if (n->Type() != TokenType_KEY) {
192
0
            ParseError("unexpected token, expected TOK_KEY",n);
193
0
        }
194
195
0
        const std::string& str = n->StringContents();
196
0
        if (str.empty()) {
197
0
            ParseError("unexpected content: empty string.");
198
0
        }
199
200
0
        auto *element = new_Element(*n, parser);
201
202
        // Element() should stop at the next Key token (or right after a Close token)
203
0
        n = parser.CurrentToken();
204
0
        if (n == nullptr) {
205
0
            if (topLevel) {
206
0
                elements.insert(ElementMap::value_type(str, element));
207
0
                return;
208
0
            }
209
0
            delete_Element(element);
210
0
            ParseError("unexpected end of file",parser.LastToken());
211
0
        } else {
212
0
            elements.insert(ElementMap::value_type(str, element));
213
0
        }
214
0
    }
215
0
}
216
217
// ------------------------------------------------------------------------------------------------
218
Scope::~Scope()
219
0
{
220
  // This collection does not own the memory for the elements, but we need to call their d'tor:
221
222
0
    for (ElementMap::value_type &v : elements) {
223
0
        delete_Element(v.second);
224
0
    }
225
0
}
226
227
// ------------------------------------------------------------------------------------------------
228
Parser::Parser(const TokenList &tokens, StackAllocator &allocator, bool is_binary) :
229
0
        tokens(tokens), allocator(allocator), last(), current(), cursor(tokens.begin()), is_binary(is_binary)
230
0
{
231
0
    ASSIMP_LOG_DEBUG("Parsing FBX tokens");
232
0
    root = new_Scope(*this, true);
233
0
}
234
235
// ------------------------------------------------------------------------------------------------
236
Parser::~Parser()
237
0
{
238
0
    delete_Scope(root);
239
0
}
240
241
// ------------------------------------------------------------------------------------------------
242
TokenPtr Parser::AdvanceToNextToken()
243
0
{
244
0
    last = current;
245
0
    if (cursor == tokens.end()) {
246
0
        current = nullptr;
247
0
    } else {
248
0
        current = *cursor++;
249
0
    }
250
0
    return current;
251
0
}
252
253
// ------------------------------------------------------------------------------------------------
254
TokenPtr Parser::CurrentToken() const
255
0
{
256
0
    return current;
257
0
}
258
259
// ------------------------------------------------------------------------------------------------
260
TokenPtr Parser::LastToken() const
261
0
{
262
0
    return last;
263
0
}
264
265
// ------------------------------------------------------------------------------------------------
266
uint64_t ParseTokenAsID(const Token& t, const char*& err_out)
267
0
{
268
0
    err_out = nullptr;
269
270
0
    if (t.Type() != TokenType_DATA) {
271
0
        err_out = "expected TOK_DATA token";
272
0
        return 0L;
273
0
    }
274
275
0
    if(t.IsBinary())
276
0
    {
277
0
        const char* data = t.begin();
278
0
        if (data[0] != 'L') {
279
0
            err_out = "failed to parse ID, unexpected data type, expected L(ong) (binary)";
280
0
            return 0L;
281
0
        }
282
283
0
        BE_NCONST uint64_t id = SafeParse<uint64_t>(data+1, t.end());
284
0
        AI_SWAP8(id);
285
0
        return id;
286
0
    }
287
288
    // XXX: should use size_t here
289
0
    unsigned int length = static_cast<unsigned int>(t.end() - t.begin());
290
0
    ai_assert(length > 0);
291
292
0
    const char* out = nullptr;
293
0
    const uint64_t id = strtoul10_64(t.begin(),&out,&length);
294
0
    if (out > t.end()) {
295
0
        err_out = "failed to parse ID (text)";
296
0
        return 0L;
297
0
    }
298
299
0
    return id;
300
0
}
301
302
// ------------------------------------------------------------------------------------------------
303
size_t ParseTokenAsDim(const Token& t, const char*& err_out)
304
0
{
305
    // same as ID parsing, except there is a trailing asterisk
306
0
    err_out = nullptr;
307
308
0
    if (t.Type() != TokenType_DATA) {
309
0
        err_out = "expected TOK_DATA token";
310
0
        return 0;
311
0
    }
312
313
0
    if(t.IsBinary())
314
0
    {
315
0
        const char* data = t.begin();
316
0
        if (data[0] != 'L') {
317
0
            err_out = "failed to parse ID, unexpected data type, expected L(ong) (binary)";
318
0
            return 0;
319
0
        }
320
321
0
        BE_NCONST uint64_t id = SafeParse<uint64_t>(data+1, t.end());
322
0
        AI_SWAP8(id);
323
0
        return static_cast<size_t>(id);
324
0
    }
325
326
0
    if(*t.begin() != '*') {
327
0
        err_out = "expected asterisk before array dimension";
328
0
        return 0;
329
0
    }
330
331
    // XXX: should use size_t here
332
0
    unsigned int length = static_cast<unsigned int>(t.end() - t.begin());
333
0
    if(length == 0) {
334
0
        err_out = "expected valid integer number after asterisk";
335
0
        return 0;
336
0
    }
337
338
0
    const char* out = nullptr;
339
0
    const size_t id = static_cast<size_t>(strtoul10_64(t.begin() + 1,&out,&length));
340
0
    if (out > t.end()) {
341
0
        err_out = "failed to parse ID";
342
0
        return 0;
343
0
    }
344
345
0
    return id;
346
0
}
347
348
349
// ------------------------------------------------------------------------------------------------
350
float ParseTokenAsFloat(const Token& t, const char*& err_out)
351
0
{
352
0
    err_out = nullptr;
353
354
0
    if (t.Type() != TokenType_DATA) {
355
0
        err_out = "expected TOK_DATA token";
356
0
        return 0.0f;
357
0
    }
358
359
0
    if(t.IsBinary())
360
0
    {
361
0
        const char* data = t.begin();
362
0
        if (data[0] != 'F' && data[0] != 'D') {
363
0
            err_out = "failed to parse F(loat) or D(ouble), unexpected data type (binary)";
364
0
            return 0.0f;
365
0
        }
366
367
0
        if (data[0] == 'F') {
368
0
            BE_NCONST float id = SafeParse<float>(data+1, t.end());
369
0
            AI_SWAP4(id);
370
0
            return id;
371
0
        }
372
0
        else {
373
0
            BE_NCONST double id = SafeParse<double>(data+1, t.end());
374
0
            AI_SWAP8(id);
375
0
            return static_cast<float>(id);
376
0
        }
377
0
    }
378
379
    // need to copy the input string to a temporary buffer
380
    // first - next in the fbx token stream comes ',',
381
    // which fast_atof could interpret as decimal point.
382
0
#define MAX_FLOAT_LENGTH 31
383
0
    const size_t length = static_cast<size_t>(t.end()-t.begin());
384
0
    if (length > MAX_FLOAT_LENGTH) {
385
0
        return 0.f;
386
0
    }
387
388
0
    char temp[MAX_FLOAT_LENGTH + 1];
389
0
    std::copy(t.begin(), t.end(), temp);
390
0
    temp[std::min(static_cast<size_t>(MAX_FLOAT_LENGTH),length)] = '\0';
391
392
0
    return fast_atof(temp);
393
0
}
394
395
396
// ------------------------------------------------------------------------------------------------
397
int ParseTokenAsInt(const Token& t, const char*& err_out)
398
0
{
399
0
    err_out = nullptr;
400
401
0
    if (t.Type() != TokenType_DATA) {
402
0
        err_out = "expected TOK_DATA token";
403
0
        return 0;
404
0
    }
405
406
0
    if(t.IsBinary())
407
0
    {
408
0
        const char* data = t.begin();
409
0
        if (data[0] != 'I') {
410
0
            err_out = "failed to parse I(nt), unexpected data type (binary)";
411
0
            return 0;
412
0
        }
413
414
0
        BE_NCONST int32_t ival = SafeParse<int32_t>(data+1, t.end());
415
0
        AI_SWAP4(ival);
416
0
        return static_cast<int>(ival);
417
0
    }
418
419
0
    ai_assert(static_cast<size_t>(t.end() - t.begin()) > 0);
420
421
0
    const char* out;
422
0
    const int intval = strtol10(t.begin(),&out);
423
0
    if (out != t.end()) {
424
0
        err_out = "failed to parse ID";
425
0
        return 0;
426
0
    }
427
428
0
    return intval;
429
0
}
430
431
432
// ------------------------------------------------------------------------------------------------
433
int64_t ParseTokenAsInt64(const Token& t, const char*& err_out)
434
0
{
435
0
    err_out = nullptr;
436
437
0
    if (t.Type() != TokenType_DATA) {
438
0
        err_out = "expected TOK_DATA token";
439
0
        return 0L;
440
0
    }
441
442
0
    if (t.IsBinary())
443
0
    {
444
0
        const char* data = t.begin();
445
0
        if (data[0] != 'L') {
446
0
            err_out = "failed to parse Int64, unexpected data type";
447
0
            return 0L;
448
0
        }
449
450
0
        BE_NCONST int64_t id = SafeParse<int64_t>(data + 1, t.end());
451
0
        AI_SWAP8(id);
452
0
        return id;
453
0
    }
454
455
    // XXX: should use size_t here
456
0
    unsigned int length = static_cast<unsigned int>(t.end() - t.begin());
457
0
    ai_assert(length > 0);
458
459
0
    const char* out = nullptr;
460
0
    const int64_t id = strtol10_64(t.begin(), &out, &length);
461
0
    if (out > t.end()) {
462
0
        err_out = "failed to parse Int64 (text)";
463
0
        return 0L;
464
0
    }
465
466
0
    return id;
467
0
}
468
469
// ------------------------------------------------------------------------------------------------
470
std::string ParseTokenAsString(const Token& t, const char*& err_out)
471
0
{
472
0
    err_out = nullptr;
473
474
0
    if (t.Type() != TokenType_DATA) {
475
0
        err_out = "expected TOK_DATA token";
476
0
        return std::string();
477
0
    }
478
479
0
    if(t.IsBinary())
480
0
    {
481
0
        const char* data = t.begin();
482
0
        if (data[0] != 'S') {
483
0
            err_out = "failed to parse S(tring), unexpected data type (binary)";
484
0
            return std::string();
485
0
        }
486
487
        // read string length
488
0
        BE_NCONST int32_t len = SafeParse<int32_t>(data+1, t.end());
489
0
        AI_SWAP4(len);
490
491
0
        ai_assert(t.end() - data == 5 + len);
492
0
        return std::string(data + 5, len);
493
0
    }
494
495
0
    const size_t length = static_cast<size_t>(t.end() - t.begin());
496
0
    if(length < 2) {
497
0
        err_out = "token is too short to hold a string";
498
0
        return std::string();
499
0
    }
500
501
0
    const char* s = t.begin(), *e = t.end() - 1;
502
0
    if (*s != '\"' || *e != '\"') {
503
0
        err_out = "expected double quoted string";
504
0
        return std::string();
505
0
    }
506
507
0
    return std::string(s+1,length-2);
508
0
}
509
510
511
namespace {
512
513
// ------------------------------------------------------------------------------------------------
514
// read the type code and element count of a binary data array and stop there
515
void ReadBinaryDataArrayHead(const char*& data, const char* end, char& type, uint32_t& count,
516
    const Element& el)
517
0
{
518
0
    if (static_cast<size_t>(end-data) < 5) {
519
0
        ParseError("binary data array is too short, need five (5) bytes for type signature and element count",&el);
520
0
    }
521
522
    // data type
523
0
    type = *data;
524
525
    // read number of elements
526
0
    BE_NCONST uint32_t len = SafeParse<uint32_t>(data+1, end);
527
0
    AI_SWAP4(len);
528
529
0
    count = len;
530
0
    data += 5;
531
0
}
532
533
534
// ------------------------------------------------------------------------------------------------
535
// read binary data array, assume cursor points to the 'compression mode' field (i.e. behind the header)
536
void ReadBinaryDataArray(char type, uint32_t count, const char*& data, const char* end,
537
0
        std::vector<char>& buff, const Element& /*el*/) {
538
0
    BE_NCONST uint32_t encmode = SafeParse<uint32_t>(data, end);
539
0
    AI_SWAP4(encmode);
540
0
    data += 4;
541
542
    // next comes the compressed length
543
0
    BE_NCONST uint32_t comp_len = SafeParse<uint32_t>(data, end);
544
0
    AI_SWAP4(comp_len);
545
0
    data += 4;
546
547
0
    ai_assert(data + comp_len == end);
548
549
    // determine the length of the uncompressed data by looking at the type signature
550
0
    uint32_t stride = 0;
551
0
    switch(type)
552
0
    {
553
0
        case 'f':
554
0
        case 'i':
555
0
            stride = 4;
556
0
            break;
557
558
0
        case 'd':
559
0
        case 'l':
560
0
            stride = 8;
561
0
            break;
562
563
0
        default:
564
0
            ai_assert(false);
565
0
    };
566
567
0
    const uint32_t full_length = stride * count;
568
0
    buff.resize(full_length);
569
570
0
    if(encmode == 0) {
571
0
        ai_assert(full_length == comp_len);
572
573
        // plain data, no compression
574
0
        std::copy(data, end, buff.begin());
575
0
    }
576
0
    else if(encmode == 1) {
577
        // zlib/deflate, next comes ZIP head (0x78 0x01)
578
        // see http://www.ietf.org/rfc/rfc1950.txt
579
0
         Compression compress;
580
0
        if (compress.open(Compression::Format::Binary, Compression::FlushMode::Finish, 0)) {
581
0
            compress.decompress(data, comp_len, buff);
582
0
            compress.close();
583
0
        }
584
0
    }
585
0
#ifdef ASSIMP_BUILD_DEBUG
586
0
    else {
587
        // runtime check for this happens at tokenization stage
588
0
        ai_assert(false);
589
0
    }
590
0
#endif
591
592
0
    data += comp_len;
593
0
    ai_assert(data == end);
594
0
}
595
596
} // !anon
597
598
599
// ------------------------------------------------------------------------------------------------
600
// read an array of float3 tuples
601
void ParseVectorDataArray(std::vector<aiVector3D>& out, const Element& el)
602
0
{
603
0
    out.resize( 0 );
604
605
0
    const TokenList& tok = el.Tokens();
606
0
    if(tok.empty()) {
607
0
        ParseError("unexpected empty element",&el);
608
0
    }
609
610
0
    if(tok[0]->IsBinary()) {
611
0
        const char* data = tok[0]->begin(), *end = tok[0]->end();
612
613
0
        char type;
614
0
        uint32_t count;
615
0
        ReadBinaryDataArrayHead(data, end, type, count, el);
616
617
0
        if(count % 3 != 0) {
618
0
            ParseError("number of floats is not a multiple of three (3) (binary)",&el);
619
0
        }
620
621
0
        if(!count) {
622
0
            return;
623
0
        }
624
625
0
        if (type != 'd' && type != 'f') {
626
0
            ParseError("expected float or double array (binary)",&el);
627
0
        }
628
629
0
        std::vector<char> buff;
630
0
        ReadBinaryDataArray(type, count, data, end, buff, el);
631
632
0
        ai_assert(data == end);
633
0
        uint64_t dataToRead = static_cast<uint64_t>(count) * (type == 'd' ? 8 : 4);
634
0
        if (dataToRead != buff.size()) {
635
0
            ParseError("Invalid read size (binary)",&el);
636
0
        }
637
638
0
        const uint32_t count3 = count / 3;
639
0
        out.reserve(count3);
640
641
0
        if (type == 'd') {
642
0
            const double* d = reinterpret_cast<const double*>(&buff[0]);
643
0
            for (unsigned int i = 0; i < count3; ++i, d += 3) {
644
0
                BE_NCONST double val1 = d[0];
645
0
                BE_NCONST double val2 = d[1];
646
0
                BE_NCONST double val3 = d[2];
647
0
                AI_SWAP8(val1);
648
0
                AI_SWAP8(val2);
649
0
                AI_SWAP8(val3);
650
0
                out.emplace_back(static_cast<ai_real>(val1),
651
0
                    static_cast<ai_real>(val2),
652
0
                    static_cast<ai_real>(val3));
653
0
            }
654
            // for debugging
655
            /*for ( size_t i = 0; i < out.size(); i++ ) {
656
                aiVector3D vec3( out[ i ] );
657
                std::stringstream stream;
658
                stream << " vec3.x = " << vec3.x << " vec3.y = " << vec3.y << " vec3.z = " << vec3.z << std::endl;
659
                DefaultLogger::get()->info( stream.str() );
660
            }*/
661
0
        }
662
0
        else if (type == 'f') {
663
0
            const float* f = reinterpret_cast<const float*>(&buff[0]);
664
0
            for (unsigned int i = 0; i < count3; ++i, f += 3) {
665
0
                BE_NCONST float val1 = f[0];
666
0
                BE_NCONST float val2 = f[1];
667
0
                BE_NCONST float val3 = f[2];
668
0
                AI_SWAP4(val1);
669
0
                AI_SWAP4(val2);
670
0
                AI_SWAP4(val3);
671
0
                out.emplace_back(val1,val2,val3);
672
0
            }
673
0
        }
674
675
0
        return;
676
0
    }
677
678
0
    const size_t dim = ParseTokenAsDim(*tok[0]);
679
680
    // may throw bad_alloc if the input is rubbish, but this need
681
    // not to be prevented - importing would fail but we wouldn't
682
    // crash since assimp handles this case properly.
683
0
    out.reserve(dim);
684
685
0
    const Scope& scope = GetRequiredScope(el);
686
0
    const Element& a = GetRequiredElement(scope,"a",&el);
687
688
0
    if (a.Tokens().size() % 3 != 0) {
689
0
        ParseError("number of floats is not a multiple of three (3)",&el);
690
0
    }
691
0
    for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) {
692
0
        aiVector3D v;
693
0
        v.x = ParseTokenAsFloat(**it++);
694
0
        v.y = ParseTokenAsFloat(**it++);
695
0
        v.z = ParseTokenAsFloat(**it++);
696
697
0
        out.push_back(v);
698
0
    }
699
0
}
700
701
// ------------------------------------------------------------------------------------------------
702
// read an array of color4 tuples
703
void ParseVectorDataArray(std::vector<aiColor4D>& out, const Element& el)
704
0
{
705
0
    out.resize( 0 );
706
0
    const TokenList& tok = el.Tokens();
707
0
    if(tok.empty()) {
708
0
        ParseError("unexpected empty element",&el);
709
0
    }
710
711
0
    if(tok[0]->IsBinary()) {
712
0
        const char* data = tok[0]->begin(), *end = tok[0]->end();
713
714
0
        char type;
715
0
        uint32_t count;
716
0
        ReadBinaryDataArrayHead(data, end, type, count, el);
717
718
0
        if(count % 4 != 0) {
719
0
            ParseError("number of floats is not a multiple of four (4) (binary)",&el);
720
0
        }
721
722
0
        if(!count) {
723
0
            return;
724
0
        }
725
726
0
        if (type != 'd' && type != 'f') {
727
0
            ParseError("expected float or double array (binary)",&el);
728
0
        }
729
730
0
        std::vector<char> buff;
731
0
        ReadBinaryDataArray(type, count, data, end, buff, el);
732
733
0
        ai_assert(data == end);
734
0
        uint64_t dataToRead = static_cast<uint64_t>(count) * (type == 'd' ? 8 : 4);
735
0
        if (dataToRead != buff.size()) {
736
0
            ParseError("Invalid read size (binary)",&el);
737
0
        }
738
739
0
        const uint32_t count4 = count / 4;
740
0
        out.reserve(count4);
741
742
0
        if (type == 'd') {
743
0
            const double* d = reinterpret_cast<const double*>(&buff[0]);
744
0
            for (unsigned int i = 0; i < count4; ++i, d += 4) {
745
0
                BE_NCONST double val1 = d[0];
746
0
                BE_NCONST double val2 = d[1];
747
0
                BE_NCONST double val3 = d[2];
748
0
                BE_NCONST double val4 = d[3];
749
0
                AI_SWAP8(val1);
750
0
                AI_SWAP8(val2);
751
0
                AI_SWAP8(val3);
752
0
                AI_SWAP8(val4);
753
0
                out.emplace_back(static_cast<float>(val1),
754
0
                    static_cast<float>(val2),
755
0
                    static_cast<float>(val3),
756
0
                    static_cast<float>(val4));
757
0
            }
758
0
        }
759
0
        else if (type == 'f') {
760
0
            const float* f = reinterpret_cast<const float*>(&buff[0]);
761
0
            for (unsigned int i = 0; i < count4; ++i, f += 4) {
762
0
                BE_NCONST float val1 = f[0];
763
0
                BE_NCONST float val2 = f[1];
764
0
                BE_NCONST float val3 = f[2];
765
0
                BE_NCONST float val4 = f[3];
766
0
                AI_SWAP4(val1);
767
0
                AI_SWAP4(val2);
768
0
                AI_SWAP4(val3);
769
0
                AI_SWAP4(val4);
770
0
                out.emplace_back(val1,val2,val3,val4);
771
0
            }
772
0
        }
773
0
        return;
774
0
    }
775
776
0
    const size_t dim = ParseTokenAsDim(*tok[0]);
777
778
    //  see notes in ParseVectorDataArray() above
779
0
    out.reserve(dim);
780
781
0
    const Scope& scope = GetRequiredScope(el);
782
0
    const Element& a = GetRequiredElement(scope,"a",&el);
783
784
0
    if (a.Tokens().size() % 4 != 0) {
785
0
        ParseError("number of floats is not a multiple of four (4)",&el);
786
0
    }
787
0
    for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) {
788
0
        aiColor4D v;
789
0
        v.r = ParseTokenAsFloat(**it++);
790
0
        v.g = ParseTokenAsFloat(**it++);
791
0
        v.b = ParseTokenAsFloat(**it++);
792
0
        v.a = ParseTokenAsFloat(**it++);
793
794
0
        out.push_back(v);
795
0
    }
796
0
}
797
798
799
// ------------------------------------------------------------------------------------------------
800
// read an array of float2 tuples
801
0
void ParseVectorDataArray(std::vector<aiVector2D>& out, const Element& el) {
802
0
    out.resize( 0 );
803
0
    const TokenList& tok = el.Tokens();
804
0
    if(tok.empty()) {
805
0
        ParseError("unexpected empty element",&el);
806
0
    }
807
808
0
    if(tok[0]->IsBinary()) {
809
0
        const char* data = tok[0]->begin(), *end = tok[0]->end();
810
811
0
        char type;
812
0
        uint32_t count;
813
0
        ReadBinaryDataArrayHead(data, end, type, count, el);
814
815
0
        if(count % 2 != 0) {
816
0
            ParseError("number of floats is not a multiple of two (2) (binary)",&el);
817
0
        }
818
819
0
        if(!count) {
820
0
            return;
821
0
        }
822
823
0
        if (type != 'd' && type != 'f') {
824
0
            ParseError("expected float or double array (binary)",&el);
825
0
        }
826
827
0
        std::vector<char> buff;
828
0
        ReadBinaryDataArray(type, count, data, end, buff, el);
829
830
0
        ai_assert(data == end);
831
0
        uint64_t dataToRead = static_cast<uint64_t>(count) * (type == 'd' ? 8 : 4);
832
0
        if (dataToRead != buff.size()) {
833
0
            ParseError("Invalid read size (binary)",&el);
834
0
        }
835
836
0
        const uint32_t count2 = count / 2;
837
0
        out.reserve(count2);
838
839
0
        if (type == 'd') {
840
0
            const double* d = reinterpret_cast<const double*>(&buff[0]);
841
0
            for (unsigned int i = 0; i < count2; ++i, d += 2) {
842
0
                BE_NCONST double val1 = d[0];
843
0
                BE_NCONST double val2 = d[1];
844
0
                AI_SWAP8(val1);
845
0
                AI_SWAP8(val2);
846
0
                out.emplace_back(static_cast<float>(val1),
847
0
                    static_cast<float>(val2));
848
0
            }
849
0
        } else if (type == 'f') {
850
0
            const float* f = reinterpret_cast<const float*>(&buff[0]);
851
0
            for (unsigned int i = 0; i < count2; ++i, f += 2) {
852
0
                BE_NCONST float val1 = f[0];
853
0
                BE_NCONST float val2 = f[1];
854
0
                AI_SWAP4(val1);
855
0
                AI_SWAP4(val2);
856
0
                out.emplace_back(val1,val2);
857
0
            }
858
0
        }
859
860
0
        return;
861
0
    }
862
863
0
    const size_t dim = ParseTokenAsDim(*tok[0]);
864
865
    // see notes in ParseVectorDataArray() above
866
0
    out.reserve(dim);
867
868
0
    const Scope& scope = GetRequiredScope(el);
869
0
    const Element& a = GetRequiredElement(scope,"a",&el);
870
871
0
    if (a.Tokens().size() % 2 != 0) {
872
0
        ParseError("number of floats is not a multiple of two (2)",&el);
873
0
    }
874
0
    for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) {
875
0
        aiVector2D v;
876
0
        v.x = ParseTokenAsFloat(**it++);
877
0
        v.y = ParseTokenAsFloat(**it++);
878
879
0
        out.push_back(v);
880
0
    }
881
0
}
882
883
884
// ------------------------------------------------------------------------------------------------
885
// read an array of ints
886
0
void ParseVectorDataArray(std::vector<int>& out, const Element& el) {
887
0
    out.resize( 0 );
888
0
    const TokenList& tok = el.Tokens();
889
0
    if(tok.empty()) {
890
0
        ParseError("unexpected empty element",&el);
891
0
    }
892
893
0
    if(tok[0]->IsBinary()) {
894
0
        const char* data = tok[0]->begin(), *end = tok[0]->end();
895
896
0
        char type;
897
0
        uint32_t count;
898
0
        ReadBinaryDataArrayHead(data, end, type, count, el);
899
900
0
        if(!count) {
901
0
            return;
902
0
        }
903
904
0
        if (type != 'i') {
905
0
            ParseError("expected int array (binary)",&el);
906
0
        }
907
908
0
        std::vector<char> buff;
909
0
        ReadBinaryDataArray(type, count, data, end, buff, el);
910
911
0
        ai_assert(data == end);
912
0
        uint64_t dataToRead = static_cast<uint64_t>(count) * 4;
913
0
        if (dataToRead != buff.size()) {
914
0
            ParseError("Invalid read size (binary)",&el);
915
0
        }
916
917
0
        out.reserve(count);
918
919
0
        const int32_t* ip = reinterpret_cast<const int32_t*>(&buff[0]);
920
0
        for (unsigned int i = 0; i < count; ++i, ++ip) {
921
0
            BE_NCONST int32_t val = *ip;
922
0
            AI_SWAP4(val);
923
0
            out.push_back(val);
924
0
        }
925
926
0
        return;
927
0
    }
928
929
0
    const size_t dim = ParseTokenAsDim(*tok[0]);
930
931
    // see notes in ParseVectorDataArray()
932
0
    out.reserve(dim);
933
934
0
    const Scope& scope = GetRequiredScope(el);
935
0
    const Element& a = GetRequiredElement(scope,"a",&el);
936
937
0
    for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) {
938
0
        const int ival = ParseTokenAsInt(**it++);
939
0
        out.push_back(ival);
940
0
    }
941
0
}
942
943
944
// ------------------------------------------------------------------------------------------------
945
// read an array of floats
946
void ParseVectorDataArray(std::vector<float>& out, const Element& el)
947
0
{
948
0
    out.resize( 0 );
949
0
    const TokenList& tok = el.Tokens();
950
0
    if(tok.empty()) {
951
0
        ParseError("unexpected empty element",&el);
952
0
    }
953
954
0
    if(tok[0]->IsBinary()) {
955
0
        const char* data = tok[0]->begin(), *end = tok[0]->end();
956
957
0
        char type;
958
0
        uint32_t count;
959
0
        ReadBinaryDataArrayHead(data, end, type, count, el);
960
961
0
        if(!count) {
962
0
            return;
963
0
        }
964
965
0
        if (type != 'd' && type != 'f') {
966
0
            ParseError("expected float or double array (binary)",&el);
967
0
        }
968
969
0
        std::vector<char> buff;
970
0
        ReadBinaryDataArray(type, count, data, end, buff, el);
971
972
0
        ai_assert(data == end);
973
0
        uint64_t dataToRead = static_cast<uint64_t>(count) * (type == 'd' ? 8 : 4);
974
0
        if (dataToRead != buff.size()) {
975
0
            ParseError("Invalid read size (binary)",&el);
976
0
        }
977
978
0
        if (type == 'd') {
979
0
            const double* d = reinterpret_cast<const double*>(&buff[0]);
980
0
            for (unsigned int i = 0; i < count; ++i, ++d) {
981
0
                BE_NCONST double val = *d;
982
0
                AI_SWAP8(val);
983
0
                out.push_back(static_cast<float>(val));
984
0
            }
985
0
        }
986
0
        else if (type == 'f') {
987
0
            const float* f = reinterpret_cast<const float*>(&buff[0]);
988
0
            for (unsigned int i = 0; i < count; ++i, ++f) {
989
0
                BE_NCONST float val = *f;
990
0
                AI_SWAP4(val);
991
0
                out.push_back(val);
992
0
            }
993
0
        }
994
995
0
        return;
996
0
    }
997
998
0
    const size_t dim = ParseTokenAsDim(*tok[0]);
999
1000
    // see notes in ParseVectorDataArray()
1001
0
    out.reserve(dim);
1002
1003
0
    const Scope& scope = GetRequiredScope(el);
1004
0
    const Element& a = GetRequiredElement(scope,"a",&el);
1005
1006
0
    for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) {
1007
0
        const float ival = ParseTokenAsFloat(**it++);
1008
0
        out.push_back(ival);
1009
0
    }
1010
0
}
1011
1012
// ------------------------------------------------------------------------------------------------
1013
// read an array of uints
1014
0
void ParseVectorDataArray(std::vector<unsigned int>& out, const Element& el) {
1015
0
    out.resize( 0 );
1016
0
    const TokenList& tok = el.Tokens();
1017
0
    if(tok.empty()) {
1018
0
        ParseError("unexpected empty element",&el);
1019
0
    }
1020
1021
0
    if(tok[0]->IsBinary()) {
1022
0
        const char* data = tok[0]->begin(), *end = tok[0]->end();
1023
1024
0
        char type;
1025
0
        uint32_t count;
1026
0
        ReadBinaryDataArrayHead(data, end, type, count, el);
1027
1028
0
        if(!count) {
1029
0
            return;
1030
0
        }
1031
1032
0
        if (type != 'i') {
1033
0
            ParseError("expected (u)int array (binary)",&el);
1034
0
        }
1035
1036
0
        std::vector<char> buff;
1037
0
        ReadBinaryDataArray(type, count, data, end, buff, el);
1038
1039
0
        ai_assert(data == end);
1040
0
        uint64_t dataToRead = static_cast<uint64_t>(count) * 4;
1041
0
        if (dataToRead != buff.size()) {
1042
0
            ParseError("Invalid read size (binary)",&el);
1043
0
        }
1044
1045
0
        out.reserve(count);
1046
1047
0
        const int32_t* ip = reinterpret_cast<const int32_t*>(&buff[0]);
1048
0
        for (unsigned int i = 0; i < count; ++i, ++ip) {
1049
0
            BE_NCONST int32_t val = *ip;
1050
0
            if(val < 0) {
1051
0
                ParseError("encountered negative integer index (binary)");
1052
0
            }
1053
1054
0
            AI_SWAP4(val);
1055
0
            out.push_back(val);
1056
0
        }
1057
1058
0
        return;
1059
0
    }
1060
1061
0
    const size_t dim = ParseTokenAsDim(*tok[0]);
1062
1063
    // see notes in ParseVectorDataArray()
1064
0
    out.reserve(dim);
1065
1066
0
    const Scope& scope = GetRequiredScope(el);
1067
0
    const Element& a = GetRequiredElement(scope,"a",&el);
1068
1069
0
    for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) {
1070
0
        const int ival = ParseTokenAsInt(**it++);
1071
0
        if(ival < 0) {
1072
0
            ParseError("encountered negative integer index");
1073
0
        }
1074
0
        out.push_back(static_cast<unsigned int>(ival));
1075
0
    }
1076
0
}
1077
1078
1079
// ------------------------------------------------------------------------------------------------
1080
// read an array of uint64_ts
1081
void ParseVectorDataArray(std::vector<uint64_t>& out, const Element& el)
1082
0
{
1083
0
    out.resize( 0 );
1084
0
    const TokenList& tok = el.Tokens();
1085
0
    if(tok.empty()) {
1086
0
        ParseError("unexpected empty element",&el);
1087
0
    }
1088
1089
0
    if(tok[0]->IsBinary()) {
1090
0
        const char* data = tok[0]->begin(), *end = tok[0]->end();
1091
1092
0
        char type;
1093
0
        uint32_t count;
1094
0
        ReadBinaryDataArrayHead(data, end, type, count, el);
1095
1096
0
        if(!count) {
1097
0
            return;
1098
0
        }
1099
1100
0
        if (type != 'l') {
1101
0
            ParseError("expected long array (binary)",&el);
1102
0
        }
1103
1104
0
        std::vector<char> buff;
1105
0
        ReadBinaryDataArray(type, count, data, end, buff, el);
1106
1107
0
        ai_assert(data == end);
1108
0
        uint64_t dataToRead = static_cast<uint64_t>(count) * 8;
1109
0
        if (dataToRead != buff.size()) {
1110
0
            ParseError("Invalid read size (binary)",&el);
1111
0
        }
1112
1113
0
        out.reserve(count);
1114
1115
0
        const uint64_t* ip = reinterpret_cast<const uint64_t*>(&buff[0]);
1116
0
        for (unsigned int i = 0; i < count; ++i, ++ip) {
1117
0
            BE_NCONST uint64_t val = *ip;
1118
0
            AI_SWAP8(val);
1119
0
            out.push_back(val);
1120
0
        }
1121
1122
0
        return;
1123
0
    }
1124
1125
0
    const size_t dim = ParseTokenAsDim(*tok[0]);
1126
1127
    // see notes in ParseVectorDataArray()
1128
0
    out.reserve(dim);
1129
1130
0
    const Scope& scope = GetRequiredScope(el);
1131
0
    const Element& a = GetRequiredElement(scope,"a",&el);
1132
1133
0
    for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) {
1134
0
        const uint64_t ival = ParseTokenAsID(**it++);
1135
1136
0
        out.push_back(ival);
1137
0
    }
1138
0
}
1139
1140
// ------------------------------------------------------------------------------------------------
1141
// read an array of int64_ts
1142
void ParseVectorDataArray(std::vector<int64_t>& out, const Element& el)
1143
0
{
1144
0
    out.resize( 0 );
1145
0
    const TokenList& tok = el.Tokens();
1146
0
    if (tok.empty()) {
1147
0
        ParseError("unexpected empty element", &el);
1148
0
    }
1149
1150
0
    if (tok[0]->IsBinary()) {
1151
0
        const char* data = tok[0]->begin(), *end = tok[0]->end();
1152
1153
0
        char type;
1154
0
        uint32_t count;
1155
0
        ReadBinaryDataArrayHead(data, end, type, count, el);
1156
1157
0
        if (!count) {
1158
0
            return;
1159
0
        }
1160
1161
0
        if (type != 'l') {
1162
0
            ParseError("expected long array (binary)", &el);
1163
0
        }
1164
1165
0
        std::vector<char> buff;
1166
0
        ReadBinaryDataArray(type, count, data, end, buff, el);
1167
1168
0
        ai_assert(data == end);
1169
0
        uint64_t dataToRead = static_cast<uint64_t>(count) * 8;
1170
0
        if (dataToRead != buff.size()) {
1171
0
            ParseError("Invalid read size (binary)",&el);
1172
0
        }
1173
1174
0
        out.reserve(count);
1175
1176
0
        const int64_t* ip = reinterpret_cast<const int64_t*>(&buff[0]);
1177
0
        for (unsigned int i = 0; i < count; ++i, ++ip) {
1178
0
            BE_NCONST int64_t val = *ip;
1179
0
            AI_SWAP8(val);
1180
0
            out.push_back(val);
1181
0
        }
1182
1183
0
        return;
1184
0
    }
1185
1186
0
    const size_t dim = ParseTokenAsDim(*tok[0]);
1187
1188
    // see notes in ParseVectorDataArray()
1189
0
    out.reserve(dim);
1190
1191
0
    const Scope& scope = GetRequiredScope(el);
1192
0
    const Element& a = GetRequiredElement(scope, "a", &el);
1193
1194
0
    for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end;) {
1195
0
        const int64_t ival = ParseTokenAsInt64(**it++);
1196
1197
0
        out.push_back(ival);
1198
0
    }
1199
0
}
1200
1201
// ------------------------------------------------------------------------------------------------
1202
aiMatrix4x4 ReadMatrix(const Element& element)
1203
0
{
1204
0
    std::vector<float> values;
1205
0
    ParseVectorDataArray(values,element);
1206
1207
0
    if(values.size() != 16) {
1208
0
        ParseError("expected 16 matrix elements");
1209
0
    }
1210
1211
0
    aiMatrix4x4 result;
1212
1213
1214
0
    result.a1 = values[0];
1215
0
    result.a2 = values[1];
1216
0
    result.a3 = values[2];
1217
0
    result.a4 = values[3];
1218
1219
0
    result.b1 = values[4];
1220
0
    result.b2 = values[5];
1221
0
    result.b3 = values[6];
1222
0
    result.b4 = values[7];
1223
1224
0
    result.c1 = values[8];
1225
0
    result.c2 = values[9];
1226
0
    result.c3 = values[10];
1227
0
    result.c4 = values[11];
1228
1229
0
    result.d1 = values[12];
1230
0
    result.d2 = values[13];
1231
0
    result.d3 = values[14];
1232
0
    result.d4 = values[15];
1233
1234
0
    result.Transpose();
1235
0
    return result;
1236
0
}
1237
1238
// ------------------------------------------------------------------------------------------------
1239
// wrapper around ParseTokenAsString() with ParseError handling
1240
std::string ParseTokenAsString(const Token& t)
1241
0
{
1242
0
    const char* err;
1243
0
    const std::string& i = ParseTokenAsString(t,err);
1244
0
    if(err) {
1245
0
        ParseError(err,t);
1246
0
    }
1247
0
    return i;
1248
0
}
1249
1250
0
bool HasElement( const Scope& sc, const std::string& index ) {
1251
0
    const Element* el = sc[ index ];
1252
0
    if ( nullptr == el ) {
1253
0
        return false;
1254
0
    }
1255
1256
0
    return true;
1257
0
}
1258
1259
// ------------------------------------------------------------------------------------------------
1260
// extract a required element from a scope, abort if the element cannot be found
1261
const Element& GetRequiredElement(const Scope& sc, const std::string& index, const Element* element /*= nullptr*/)
1262
0
{
1263
0
    const Element* el = sc[index];
1264
0
    if(!el) {
1265
0
        ParseError("did not find required element \"" + index + "\"",element);
1266
0
    }
1267
0
    return *el;
1268
0
}
1269
1270
1271
// ------------------------------------------------------------------------------------------------
1272
// extract required compound scope
1273
const Scope& GetRequiredScope(const Element& el)
1274
0
{
1275
0
    const Scope* const s = el.Compound();
1276
0
    if(!s) {
1277
0
        ParseError("expected compound scope",&el);
1278
0
    }
1279
1280
0
    return *s;
1281
0
}
1282
1283
1284
// ------------------------------------------------------------------------------------------------
1285
// get token at a particular index
1286
const Token& GetRequiredToken(const Element& el, unsigned int index)
1287
0
{
1288
0
    const TokenList& t = el.Tokens();
1289
0
    if(index >= t.size()) {
1290
0
        ParseError(Formatter::format( "missing token at index " ) << index,&el);
1291
0
    }
1292
1293
0
    return *t[index];
1294
0
}
1295
1296
1297
// ------------------------------------------------------------------------------------------------
1298
// wrapper around ParseTokenAsID() with ParseError handling
1299
uint64_t ParseTokenAsID(const Token& t)
1300
0
{
1301
0
    const char* err;
1302
0
    const uint64_t i = ParseTokenAsID(t,err);
1303
0
    if(err) {
1304
0
        ParseError(err,t);
1305
0
    }
1306
0
    return i;
1307
0
}
1308
1309
1310
// ------------------------------------------------------------------------------------------------
1311
// wrapper around ParseTokenAsDim() with ParseError handling
1312
size_t ParseTokenAsDim(const Token& t)
1313
0
{
1314
0
    const char* err;
1315
0
    const size_t i = ParseTokenAsDim(t,err);
1316
0
    if(err) {
1317
0
        ParseError(err,t);
1318
0
    }
1319
0
    return i;
1320
0
}
1321
1322
1323
// ------------------------------------------------------------------------------------------------
1324
// wrapper around ParseTokenAsFloat() with ParseError handling
1325
float ParseTokenAsFloat(const Token& t)
1326
0
{
1327
0
    const char* err;
1328
0
    const float i = ParseTokenAsFloat(t,err);
1329
0
    if(err) {
1330
0
        ParseError(err,t);
1331
0
    }
1332
0
    return i;
1333
0
}
1334
1335
// ------------------------------------------------------------------------------------------------
1336
// wrapper around ParseTokenAsInt() with ParseError handling
1337
int ParseTokenAsInt(const Token& t)
1338
0
{
1339
0
    const char* err;
1340
0
    const int i = ParseTokenAsInt(t,err);
1341
0
    if(err) {
1342
0
        ParseError(err,t);
1343
0
    }
1344
0
    return i;
1345
0
}
1346
1347
// ------------------------------------------------------------------------------------------------
1348
// wrapper around ParseTokenAsInt64() with ParseError handling
1349
int64_t ParseTokenAsInt64(const Token& t)
1350
0
{
1351
0
    const char* err;
1352
0
    const int64_t i = ParseTokenAsInt64(t, err);
1353
0
    if (err) {
1354
0
        ParseError(err, t);
1355
0
    }
1356
0
    return i;
1357
0
}
1358
1359
} // !FBX
1360
} // !Assimp
1361
1362
#endif