Coverage Report

Created: 2026-01-25 07:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/assimp/code/AssetLib/X/XFileParser.cpp
Line
Count
Source
1
/*
2
---------------------------------------------------------------------------
3
Open Asset Import Library (assimp)
4
---------------------------------------------------------------------------
5
6
Copyright (c) 2006-2026, assimp team
7
8
All rights reserved.
9
10
Redistribution and use of this software in source and binary forms,
11
with or without modification, are permitted provided that the following
12
conditions are met:
13
14
* Redistributions of source code must retain the above
15
  copyright notice, this list of conditions and the
16
  following disclaimer.
17
18
* Redistributions in binary form must reproduce the above
19
  copyright notice, this list of conditions and the
20
  following disclaimer in the documentation and/or other
21
  materials provided with the distribution.
22
23
* Neither the name of the assimp team, nor the names of its
24
  contributors may be used to endorse or promote products
25
  derived from this software without specific prior
26
  written permission of the assimp team.
27
28
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39
---------------------------------------------------------------------------
40
*/
41
42
/** @file Implementation of the XFile parser helper class */
43
44
#ifndef ASSIMP_BUILD_NO_X_IMPORTER
45
46
#include "XFileParser.h"
47
#include "XFileHelper.h"
48
#include <assimp/ByteSwapper.h>
49
#include <assimp/Exceptional.h>
50
#include <assimp/StringUtils.h>
51
#include <assimp/TinyFormatter.h>
52
#include <assimp/fast_atof.h>
53
#include <assimp/DefaultLogger.hpp>
54
55
using namespace Assimp;
56
using namespace Assimp::XFile;
57
using namespace Assimp::Formatter;
58
59
#ifndef ASSIMP_BUILD_NO_COMPRESSED_X
60
61
#include "Common/Compression.h"
62
63
// Magic identifier for MSZIP compressed data
64
constexpr unsigned int MSZIP_MAGIC = 0x4B43;
65
constexpr size_t MSZIP_BLOCK = 32786l;
66
67
#endif // !! ASSIMP_BUILD_NO_COMPRESSED_X
68
69
// ------------------------------------------------------------------------------------------------
70
// Throws an exception with a line number and the given text.
71
template<typename... T>
72
0
AI_WONT_RETURN void XFileParser::ThrowException(T&&... args) {
73
0
    if (mIsBinaryFormat) {
74
0
        throw DeadlyImportError(args...);
75
0
    } else {
76
0
        throw DeadlyImportError("Line ", mLineNumber, ": ", args...);
77
0
    }
78
0
}
Unexecuted instantiation: void Assimp::XFileParser::ThrowException<char const (&) [28], char const&, char const&, char const&, char const&, char const (&) [2]>(char const (&) [28], char const&, char const&, char const&, char const&, char const (&) [2])
Unexecuted instantiation: void Assimp::XFileParser::ThrowException<char const (&) [20], unsigned int&, char const (&) [29]>(char const (&) [20], unsigned int&, char const (&) [29])
Unexecuted instantiation: void Assimp::XFileParser::ThrowException<char const (&) [65]>(char const (&) [65])
Unexecuted instantiation: void Assimp::XFileParser::ThrowException<char const (&) [51]>(char const (&) [51])
Unexecuted instantiation: void Assimp::XFileParser::ThrowException<char const (&) [52]>(char const (&) [52])
Unexecuted instantiation: void Assimp::XFileParser::ThrowException<char const (&) [37]>(char const (&) [37])
Unexecuted instantiation: void Assimp::XFileParser::ThrowException<char const (&) [48]>(char const (&) [48])
Unexecuted instantiation: void Assimp::XFileParser::ThrowException<char const (&) [19]>(char const (&) [19])
Unexecuted instantiation: void Assimp::XFileParser::ThrowException<char const (&) [47]>(char const (&) [47])
Unexecuted instantiation: void Assimp::XFileParser::ThrowException<char const (&) [33]>(char const (&) [33])
Unexecuted instantiation: void Assimp::XFileParser::ThrowException<char const (&) [57]>(char const (&) [57])
Unexecuted instantiation: void Assimp::XFileParser::ThrowException<char const (&) [60]>(char const (&) [60])
Unexecuted instantiation: void Assimp::XFileParser::ThrowException<char const (&) [56]>(char const (&) [56])
Unexecuted instantiation: void Assimp::XFileParser::ThrowException<char const (&) [18], unsigned int&, char const (&) [15]>(char const (&) [18], unsigned int&, char const (&) [15])
Unexecuted instantiation: void Assimp::XFileParser::ThrowException<char const (&) [54]>(char const (&) [54])
Unexecuted instantiation: void Assimp::XFileParser::ThrowException<char const (&) [24]>(char const (&) [24])
Unexecuted instantiation: void Assimp::XFileParser::ThrowException<char const (&) [20]>(char const (&) [20])
Unexecuted instantiation: void Assimp::XFileParser::ThrowException<char const (&) [43]>(char const (&) [43])
Unexecuted instantiation: void Assimp::XFileParser::ThrowException<char const (&) [44]>(char const (&) [44])
Unexecuted instantiation: void Assimp::XFileParser::ThrowException<char const (&) [25]>(char const (&) [25])
Unexecuted instantiation: void Assimp::XFileParser::ThrowException<char const (&) [62]>(char const (&) [62])
Unexecuted instantiation: void Assimp::XFileParser::ThrowException<char const (&) [17]>(char const (&) [17])
79
80
// ------------------------------------------------------------------------------------------------
81
// Constructor. Creates a data structure out of the XFile given in the memory block.
82
XFileParser::XFileParser(const std::vector<char> &pBuffer) :
83
4
        mMajorVersion(0), mMinorVersion(0), mIsBinaryFormat(false), mBinaryNumCount(0), mP(nullptr), mEnd(nullptr), mLineNumber(0), mScene(nullptr) {
84
    // vector to store uncompressed file for INFLATE'd X files
85
4
    std::vector<char> uncompressed;
86
87
    // set up memory pointers
88
4
    mP = &pBuffer.front();
89
4
    mEnd = mP + pBuffer.size() - 1;
90
91
    // check header
92
4
    if (0 != strncmp(mP, "xof ", 4)) {
93
0
        throw DeadlyImportError("Header mismatch, file is not an XFile.");
94
0
    }
95
96
    // read version. It comes in a four byte format such as "0302"
97
4
    mMajorVersion = (unsigned int)(mP[4] - 48) * 10 + (unsigned int)(mP[5] - 48);
98
4
    mMinorVersion = (unsigned int)(mP[6] - 48) * 10 + (unsigned int)(mP[7] - 48);
99
100
4
    bool compressed = false;
101
102
    // txt - pure ASCII text format
103
4
    if (strncmp(mP + 8, "txt ", 4) == 0)
104
3
        mIsBinaryFormat = false;
105
106
    // bin - Binary format
107
1
    else if (strncmp(mP + 8, "bin ", 4) == 0)
108
1
        mIsBinaryFormat = true;
109
110
    // tzip - Inflate compressed text format
111
0
    else if (strncmp(mP + 8, "tzip", 4) == 0) {
112
0
        mIsBinaryFormat = false;
113
0
        compressed = true;
114
0
    }
115
    // bzip - Inflate compressed binary format
116
0
    else if (strncmp(mP + 8, "bzip", 4) == 0) {
117
0
        mIsBinaryFormat = true;
118
0
        compressed = true;
119
0
    } else
120
0
        ThrowException("Unsupported x-file format '", mP[8], mP[9], mP[10], mP[11], "'");
121
122
    // float size
123
4
    mBinaryFloatSize = (unsigned int)(mP[12] - 48) * 1000 + (unsigned int)(mP[13] - 48) * 100 + (unsigned int)(mP[14] - 48) * 10 + (unsigned int)(mP[15] - 48);
124
125
4
    if (mBinaryFloatSize != 32 && mBinaryFloatSize != 64)
126
0
        ThrowException("Unknown float size ", mBinaryFloatSize, " specified in x-file header.");
127
128
    // The x format specifies size in bits, but we work in bytes
129
4
    mBinaryFloatSize /= 8;
130
131
4
    mP += 16;
132
133
    // If this is a compressed X file, apply the inflate algorithm to it
134
4
    if (compressed) {
135
#ifdef ASSIMP_BUILD_NO_COMPRESSED_X
136
        throw DeadlyImportError("Assimp was built without compressed X support");
137
#else
138
        /* ///////////////////////////////////////////////////////////////////////
139
         * COMPRESSED X FILE FORMAT
140
         * ///////////////////////////////////////////////////////////////////////
141
         *    [xhead]
142
         *    2 major
143
         *    2 minor
144
         *    4 type    // bzip,tzip
145
         *    [mszip_master_head]
146
         *    4 unkn    // checksum?
147
         *    2 unkn    // flags? (seems to be constant)
148
         *    [mszip_head]
149
         *    2 ofs     // offset to next section
150
         *    2 magic   // 'CK'
151
         *    ... ofs bytes of data
152
         *    ... next mszip_head
153
         *
154
         *  http://www.kdedevelopers.org/node/3181 has been very helpful.
155
         * ///////////////////////////////////////////////////////////////////////
156
         */
157
158
        // skip unknown data (checksum, flags?)
159
0
        mP += 6;
160
161
        // First find out how much storage we'll need. Count sections.
162
0
        const char *P1 = mP;
163
0
        unsigned int est_out = 0;
164
165
0
        while (P1 + 3 < mEnd) {
166
            // read next offset
167
0
            uint16_t ofs = *((uint16_t *)P1);
168
0
            AI_SWAP2(ofs);
169
0
            P1 += 2;
170
171
0
            if (ofs >= MSZIP_BLOCK)
172
0
                throw DeadlyImportError("X: Invalid offset to next MSZIP compressed block");
173
174
            // check magic word
175
0
            uint16_t magic = *((uint16_t *)P1);
176
0
            AI_SWAP2(magic);
177
0
            P1 += 2;
178
179
0
            if (magic != MSZIP_MAGIC)
180
0
                throw DeadlyImportError("X: Unsupported compressed format, expected MSZIP header");
181
182
            // and advance to the next offset
183
0
            P1 += ofs;
184
0
            est_out += MSZIP_BLOCK; // one decompressed block is 327861 in size
185
0
        }
186
187
        // Allocate storage and terminating zero and do the actual uncompressing
188
0
        Compression compression;
189
0
        uncompressed.resize(est_out + 1);
190
0
        char *out = &uncompressed.front();
191
0
        if (compression.open(mIsBinaryFormat ? Compression::Format::Binary : Compression::Format::ASCII,
192
0
                Compression::FlushMode::SyncFlush, -Compression::MaxWBits)) {
193
0
            while (mP + 3 < mEnd) {
194
0
                uint16_t ofs = *((uint16_t *)mP);
195
0
                AI_SWAP2(ofs);
196
0
                mP += 4;
197
198
0
                if (mP + ofs > mEnd + 2) {
199
0
                    throw DeadlyImportError("X: Unexpected EOF in compressed chunk");
200
0
                }
201
0
                out += compression.decompressBlock(mP, ofs, out, MSZIP_BLOCK);
202
0
                mP += ofs;
203
0
            }
204
0
            compression.close();
205
0
        }
206
207
        // ok, update pointers to point to the uncompressed file data
208
0
        mP = &uncompressed[0];
209
0
        mEnd = out;
210
211
        // FIXME: we don't need the compressed data anymore, could release
212
        // it already for better memory usage. Consider breaking const-co.
213
0
        ASSIMP_LOG_INFO("Successfully decompressed MSZIP-compressed file");
214
0
#endif // !! ASSIMP_BUILD_NO_COMPRESSED_X
215
4
    } else {
216
        // start reading here
217
4
        ReadUntilEndOfLine();
218
4
    }
219
220
4
    mScene = new Scene;
221
4
    ParseFile();
222
223
    // filter the imported hierarchy for some degenerated cases
224
4
    if (mScene->mRootNode) {
225
4
        FilterHierarchy(mScene->mRootNode);
226
4
    }
227
4
}
228
229
// ------------------------------------------------------------------------------------------------
230
// Destructor. Destroys all imported data along with it
231
4
XFileParser::~XFileParser() {
232
    // kill everything we created
233
4
    delete mScene;
234
4
}
235
236
// ------------------------------------------------------------------------------------------------
237
4
void XFileParser::ParseFile() {
238
4
    bool running = true;
239
73
    while (running) {
240
        // read name of next object
241
73
        std::string objectName = GetNextToken();
242
73
        if (objectName.length() == 0) {
243
4
            break;
244
4
        }
245
246
        // parse specific object
247
69
        if (objectName == "template") {
248
40
            ParseDataObjectTemplate();
249
40
        } else if (objectName == "Frame") {
250
10
            ParseDataObjectFrame(nullptr);
251
19
        } else if (objectName == "Mesh") {
252
            // some meshes have no frames at all
253
0
            Mesh *mesh = new Mesh;
254
0
            ParseDataObjectMesh(mesh);
255
0
            mScene->mGlobalMeshes.push_back(mesh);
256
19
        } else if (objectName == "AnimTicksPerSecond")
257
5
            ParseDataObjectAnimTicksPerSecond();
258
14
        else if (objectName == "AnimationSet")
259
5
            ParseDataObjectAnimationSet();
260
9
        else if (objectName == "Material") {
261
            // Material outside of a mesh or node
262
0
            Material material;
263
0
            ParseDataObjectMaterial(&material);
264
0
            mScene->mGlobalMaterials.push_back(material);
265
9
        } else if (objectName == "}") {
266
            // whatever?
267
0
            ASSIMP_LOG_WARN("} found in dataObject");
268
9
        } else {
269
            // unknown format
270
9
            ASSIMP_LOG_WARN("Unknown data object in animation of .x file");
271
9
            ParseUnknownDataObject();
272
9
        }
273
69
    }
274
4
}
275
276
// ------------------------------------------------------------------------------------------------
277
40
void XFileParser::ParseDataObjectTemplate() {
278
    // parse a template data object. Currently not stored.
279
40
    std::string name;
280
40
    readHeadOfDataObject(&name);
281
282
    // read GUID
283
40
    std::string guid = GetNextToken();
284
285
    // read and ignore data members
286
40
    bool running = true;
287
312
    while (running) {
288
312
        std::string s = GetNextToken();
289
290
312
        if (s == "}") {
291
40
            break;
292
40
        }
293
294
272
        if (s.length() == 0) {
295
0
            ThrowException("Unexpected end of file reached while parsing template definition");
296
0
        }
297
272
    }
298
40
}
299
300
// ------------------------------------------------------------------------------------------------
301
101
void XFileParser::ParseDataObjectFrame(Node *pParent) {
302
    // A coordinate frame, or "frame of reference." The Frame template
303
    // is open and can contain any object. The Direct3D extensions (D3DX)
304
    // mesh-loading functions recognize Mesh, FrameTransformMatrix, and
305
    // Frame template instances as child objects when loading a Frame
306
    // instance.
307
101
    std::string name;
308
101
    readHeadOfDataObject(&name);
309
310
    // create a named node and place it at its parent, if given
311
101
    Node *node = new Node(pParent);
312
101
    node->mName = name;
313
101
    if (pParent) {
314
91
        pParent->mChildren.push_back(node);
315
91
    } else {
316
        // there might be multiple root nodes
317
10
        if (mScene->mRootNode != nullptr) {
318
            // place a dummy root if not there
319
6
            if (mScene->mRootNode->mName != "$dummy_root") {
320
3
                Node *exroot = mScene->mRootNode;
321
3
                mScene->mRootNode = new Node(nullptr);
322
3
                mScene->mRootNode->mName = "$dummy_root";
323
3
                mScene->mRootNode->mChildren.push_back(exroot);
324
3
                exroot->mParent = mScene->mRootNode;
325
3
            }
326
            // put the new node as its child instead
327
6
            mScene->mRootNode->mChildren.push_back(node);
328
6
            node->mParent = mScene->mRootNode;
329
6
        } else {
330
            // it's the first node imported. place it as root
331
4
            mScene->mRootNode = node;
332
4
        }
333
10
    }
334
335
    // Now inside a frame.
336
    // read tokens until closing brace is reached.
337
101
    bool running = true;
338
395
    while (running) {
339
395
        std::string objectName = GetNextToken();
340
395
        if (objectName.size() == 0)
341
0
            ThrowException("Unexpected end of file reached while parsing frame");
342
343
395
        if (objectName == "}")
344
101
            break; // frame finished
345
294
        else if (objectName == "Frame")
346
91
            ParseDataObjectFrame(node); // child frame
347
203
        else if (objectName == "FrameTransformMatrix")
348
101
            ParseDataObjectTransformationMatrix(node->mTrafoMatrix);
349
102
        else if (objectName == "Mesh") {
350
6
            Mesh *mesh = new Mesh(name);
351
6
            node->mMeshes.push_back(mesh);
352
6
            ParseDataObjectMesh(mesh);
353
96
        } else {
354
96
            ASSIMP_LOG_WARN("Unknown data object in frame in x file");
355
96
            ParseUnknownDataObject();
356
96
        }
357
395
    }
358
101
}
359
360
// ------------------------------------------------------------------------------------------------
361
101
void XFileParser::ParseDataObjectTransformationMatrix(aiMatrix4x4 &pMatrix) {
362
    // read header, we're not interested if it has a name
363
101
    readHeadOfDataObject();
364
365
    // read its components
366
101
    pMatrix.a1 = ReadFloat();
367
101
    pMatrix.b1 = ReadFloat();
368
101
    pMatrix.c1 = ReadFloat();
369
101
    pMatrix.d1 = ReadFloat();
370
101
    pMatrix.a2 = ReadFloat();
371
101
    pMatrix.b2 = ReadFloat();
372
101
    pMatrix.c2 = ReadFloat();
373
101
    pMatrix.d2 = ReadFloat();
374
101
    pMatrix.a3 = ReadFloat();
375
101
    pMatrix.b3 = ReadFloat();
376
101
    pMatrix.c3 = ReadFloat();
377
101
    pMatrix.d3 = ReadFloat();
378
101
    pMatrix.a4 = ReadFloat();
379
101
    pMatrix.b4 = ReadFloat();
380
101
    pMatrix.c4 = ReadFloat();
381
101
    pMatrix.d4 = ReadFloat();
382
383
    // trailing symbols
384
101
    CheckForSemicolon();
385
101
    CheckForClosingBrace();
386
101
}
387
388
// ------------------------------------------------------------------------------------------------
389
6
void XFileParser::ParseDataObjectMesh(Mesh *pMesh) {
390
6
    std::string name;
391
6
    readHeadOfDataObject(&name);
392
393
    // read vertex count
394
6
    unsigned int numVertices = ReadInt();
395
6
    pMesh->mPositions.resize(numVertices);
396
397
    // read vertices
398
12.0k
    for (unsigned int a = 0; a < numVertices; a++)
399
12.0k
        pMesh->mPositions[a] = ReadVector3();
400
401
    // read position faces
402
6
    unsigned int numPosFaces = ReadInt();
403
6
    pMesh->mPosFaces.resize(numPosFaces);
404
16.3k
    for (unsigned int a = 0; a < numPosFaces; ++a) {
405
        // read indices
406
16.3k
        unsigned int numIndices = ReadInt();
407
16.3k
        Face &face = pMesh->mPosFaces[a];
408
65.4k
        for (unsigned int b = 0; b < numIndices; ++b) {
409
49.0k
            const int idx(ReadInt());
410
49.0k
            if (static_cast<unsigned int>(idx) <= numVertices) {
411
49.0k
                face.mIndices.push_back(idx);
412
49.0k
            }
413
49.0k
        }
414
16.3k
        TestForSeparator();
415
16.3k
    }
416
417
    // here, other data objects may follow
418
6
    bool running = true;
419
122
    while (running) {
420
122
        std::string objectName = GetNextToken();
421
422
122
        if (objectName.empty())
423
0
            ThrowException("Unexpected end of file while parsing mesh structure");
424
122
        else if (objectName == "}")
425
6
            break; // mesh finished
426
116
        else if (objectName == "MeshNormals")
427
6
            ParseDataObjectMeshNormals(pMesh);
428
110
        else if (objectName == "MeshTextureCoords")
429
6
            ParseDataObjectMeshTextureCoords(pMesh);
430
104
        else if (objectName == "MeshVertexColors")
431
0
            ParseDataObjectMeshVertexColors(pMesh);
432
104
        else if (objectName == "MeshMaterialList")
433
2
            ParseDataObjectMeshMaterialList(pMesh);
434
102
        else if (objectName == "VertexDuplicationIndices")
435
1
            ParseUnknownDataObject(); // we'll ignore vertex duplication indices
436
101
        else if (objectName == "XSkinMeshHeader")
437
5
            ParseDataObjectSkinMeshHeader(pMesh);
438
96
        else if (objectName == "SkinWeights")
439
95
            ParseDataObjectSkinWeights(pMesh);
440
1
        else {
441
1
            ASSIMP_LOG_WARN("Unknown data object in mesh in x file");
442
1
            ParseUnknownDataObject();
443
1
        }
444
122
    }
445
6
}
446
447
// ------------------------------------------------------------------------------------------------
448
95
void XFileParser::ParseDataObjectSkinWeights(Mesh *pMesh) {
449
95
    if (nullptr == pMesh) {
450
0
        return;
451
0
    }
452
95
    readHeadOfDataObject();
453
454
95
    std::string transformNodeName;
455
95
    GetNextTokenAsString(transformNodeName);
456
457
95
    pMesh->mBones.emplace_back();
458
95
    Bone &bone = pMesh->mBones.back();
459
95
    bone.mName = transformNodeName;
460
461
    // read vertex weights
462
95
    unsigned int numWeights = ReadInt();
463
95
    bone.mWeights.reserve(numWeights);
464
465
17.0k
    for (unsigned int a = 0; a < numWeights; a++) {
466
16.9k
        BoneWeight weight = {};
467
16.9k
        weight.mVertex = ReadInt();
468
16.9k
        bone.mWeights.push_back(weight);
469
16.9k
    }
470
471
    // read vertex weights
472
17.0k
    for (unsigned int a = 0; a < numWeights; a++)
473
16.9k
        bone.mWeights[a].mWeight = ReadFloat();
474
475
    // read matrix offset
476
95
    bone.mOffsetMatrix.a1 = ReadFloat();
477
95
    bone.mOffsetMatrix.b1 = ReadFloat();
478
95
    bone.mOffsetMatrix.c1 = ReadFloat();
479
95
    bone.mOffsetMatrix.d1 = ReadFloat();
480
95
    bone.mOffsetMatrix.a2 = ReadFloat();
481
95
    bone.mOffsetMatrix.b2 = ReadFloat();
482
95
    bone.mOffsetMatrix.c2 = ReadFloat();
483
95
    bone.mOffsetMatrix.d2 = ReadFloat();
484
95
    bone.mOffsetMatrix.a3 = ReadFloat();
485
95
    bone.mOffsetMatrix.b3 = ReadFloat();
486
95
    bone.mOffsetMatrix.c3 = ReadFloat();
487
95
    bone.mOffsetMatrix.d3 = ReadFloat();
488
95
    bone.mOffsetMatrix.a4 = ReadFloat();
489
95
    bone.mOffsetMatrix.b4 = ReadFloat();
490
95
    bone.mOffsetMatrix.c4 = ReadFloat();
491
95
    bone.mOffsetMatrix.d4 = ReadFloat();
492
493
95
    CheckForSemicolon();
494
95
    CheckForClosingBrace();
495
95
}
496
497
// ------------------------------------------------------------------------------------------------
498
5
void XFileParser::ParseDataObjectSkinMeshHeader(Mesh * /*pMesh*/) {
499
5
    readHeadOfDataObject();
500
501
    /*unsigned int maxSkinWeightsPerVertex =*/ReadInt();
502
5
    /*unsigned int maxSkinWeightsPerFace =*/ReadInt();
503
5
    /*unsigned int numBonesInMesh = */ ReadInt();
504
505
5
    CheckForClosingBrace();
506
5
}
507
508
// ------------------------------------------------------------------------------------------------
509
6
void XFileParser::ParseDataObjectMeshNormals(Mesh *pMesh) {
510
6
    readHeadOfDataObject();
511
512
    // read count
513
6
    unsigned int numNormals = ReadInt();
514
6
    pMesh->mNormals.resize(numNormals);
515
516
    // read normal vectors
517
12.0k
    for (unsigned int a = 0; a < numNormals; ++a) {
518
12.0k
        pMesh->mNormals[a] = ReadVector3();
519
12.0k
    }
520
521
    // read normal indices
522
6
    unsigned int numFaces = ReadInt();
523
6
    if (numFaces != pMesh->mPosFaces.size()) {
524
0
        ThrowException("Normal face count does not match vertex face count.");
525
0
    }
526
527
    // do not crah when no face definitions are there
528
6
    if (numFaces > 0) {
529
        // normal face creation
530
6
        pMesh->mNormFaces.resize(numFaces);
531
16.3k
        for (unsigned int a = 0; a < numFaces; ++a) {
532
16.3k
            unsigned int numIndices = ReadInt();
533
16.3k
            pMesh->mNormFaces[a] = Face();
534
16.3k
            Face &face = pMesh->mNormFaces[a];
535
65.4k
            for (unsigned int b = 0; b < numIndices; ++b) {
536
49.0k
                face.mIndices.push_back(ReadInt());
537
49.0k
            }
538
539
16.3k
            TestForSeparator();
540
16.3k
        }
541
6
    }
542
543
6
    CheckForClosingBrace();
544
6
}
545
546
// ------------------------------------------------------------------------------------------------
547
6
void XFileParser::ParseDataObjectMeshTextureCoords(Mesh *pMesh) {
548
6
    readHeadOfDataObject();
549
6
    if (pMesh->mNumTextures + 1 > AI_MAX_NUMBER_OF_TEXTURECOORDS)
550
0
        ThrowException("Too many sets of texture coordinates");
551
552
6
    std::vector<aiVector2D> &coords = pMesh->mTexCoords[pMesh->mNumTextures++];
553
554
6
    unsigned int numCoords = ReadInt();
555
6
    if (numCoords != pMesh->mPositions.size())
556
0
        ThrowException("Texture coord count does not match vertex count");
557
558
6
    coords.resize(numCoords);
559
12.0k
    for (unsigned int a = 0; a < numCoords; a++)
560
12.0k
        coords[a] = ReadVector2();
561
562
6
    CheckForClosingBrace();
563
6
}
564
565
// ------------------------------------------------------------------------------------------------
566
0
void XFileParser::ParseDataObjectMeshVertexColors(Mesh *pMesh) {
567
0
    readHeadOfDataObject();
568
0
    if (pMesh->mNumColorSets + 1 > AI_MAX_NUMBER_OF_COLOR_SETS)
569
0
        ThrowException("Too many colorsets");
570
0
    std::vector<aiColor4D> &colors = pMesh->mColors[pMesh->mNumColorSets++];
571
572
0
    unsigned int numColors = ReadInt();
573
0
    if (numColors != pMesh->mPositions.size())
574
0
        ThrowException("Vertex color count does not match vertex count");
575
576
0
    colors.resize(numColors, aiColor4D(0, 0, 0, 1));
577
0
    for (unsigned int a = 0; a < numColors; a++) {
578
0
        unsigned int index = ReadInt();
579
0
        if (index >= pMesh->mPositions.size())
580
0
            ThrowException("Vertex color index out of bounds");
581
582
0
        colors[index] = ReadRGBA();
583
        // HACK: (thom) Maxon Cinema XPort plugin puts a third separator here, kwxPort puts a comma.
584
        // Ignore gracefully.
585
0
        if (!mIsBinaryFormat) {
586
0
            FindNextNoneWhiteSpace();
587
0
            if (*mP == ';' || *mP == ',')
588
0
                mP++;
589
0
        }
590
0
    }
591
592
0
    CheckForClosingBrace();
593
0
}
594
595
// ------------------------------------------------------------------------------------------------
596
2
void XFileParser::ParseDataObjectMeshMaterialList(Mesh *pMesh) {
597
2
    readHeadOfDataObject();
598
599
    // read material count
600
    /*unsigned int numMaterials =*/ReadInt();
601
    // read non triangulated face material index count
602
2
    unsigned int numMatIndices = ReadInt();
603
604
    // some models have a material index count of 1... to be able to read them we
605
    // replicate this single material index on every face
606
2
    if (numMatIndices != pMesh->mPosFaces.size() && numMatIndices != 1)
607
0
        ThrowException("Per-Face material index count does not match face count.");
608
609
    // read per-face material indices
610
7.49k
    for (unsigned int a = 0; a < numMatIndices; a++)
611
7.49k
        pMesh->mFaceMaterials.push_back(ReadInt());
612
613
    // in version 03.02, the face indices end with two semicolons.
614
    // commented out version check, as version 03.03 exported from blender also has 2 semicolons
615
2
    if (!mIsBinaryFormat) // && MajorVersion == 3 && MinorVersion <= 2)
616
1
    {
617
1
        if (mP < mEnd && *mP == ';')
618
0
            ++mP;
619
1
    }
620
621
    // if there was only a single material index, replicate it on all faces
622
2
    while (pMesh->mFaceMaterials.size() < pMesh->mPosFaces.size())
623
0
        pMesh->mFaceMaterials.push_back(pMesh->mFaceMaterials.front());
624
625
    // read following data objects
626
2
    bool running = true;
627
4
    while (running) {
628
4
        std::string objectName = GetNextToken();
629
4
        if (objectName.size() == 0)
630
0
            ThrowException("Unexpected end of file while parsing mesh material list.");
631
4
        else if (objectName == "}")
632
2
            break; // material list finished
633
2
        else if (objectName == "{") {
634
            // template materials
635
0
            std::string matName = GetNextToken();
636
0
            Material material;
637
0
            material.mIsReference = true;
638
0
            material.mName = matName;
639
0
            pMesh->mMaterials.push_back(material);
640
641
0
            CheckForClosingBrace(); // skip }
642
2
        } else if (objectName == "Material") {
643
2
            pMesh->mMaterials.emplace_back();
644
2
            ParseDataObjectMaterial(&pMesh->mMaterials.back());
645
2
        } else if (objectName == ";") {
646
            // ignore
647
0
        } else {
648
0
            ASSIMP_LOG_WARN("Unknown data object in material list in x file");
649
0
            ParseUnknownDataObject();
650
0
        }
651
4
    }
652
2
}
653
654
// ------------------------------------------------------------------------------------------------
655
2
void XFileParser::ParseDataObjectMaterial(Material *pMaterial) {
656
2
    std::string matName;
657
2
    readHeadOfDataObject(&matName);
658
2
    if (matName.empty())
659
1
        matName = std::string("material") + ai_to_string(mLineNumber);
660
2
    pMaterial->mName = matName;
661
2
    pMaterial->mIsReference = false;
662
663
    // read material values
664
2
    pMaterial->mDiffuse = ReadRGBA();
665
2
    pMaterial->mSpecularExponent = ReadFloat();
666
2
    pMaterial->mSpecular = ReadRGB();
667
2
    pMaterial->mEmissive = ReadRGB();
668
669
    // read other data objects
670
2
    bool running = true;
671
3
    while (running) {
672
3
        std::string objectName = GetNextToken();
673
3
        if (objectName.size() == 0)
674
0
            ThrowException("Unexpected end of file while parsing mesh material");
675
3
        else if (objectName == "}")
676
2
            break; // material finished
677
1
        else if (objectName == "TextureFilename" || objectName == "TextureFileName") {
678
            // some exporters write "TextureFileName" instead.
679
1
            std::string texname;
680
1
            ParseDataObjectTextureFilename(texname);
681
1
            pMaterial->mTextures.emplace_back(texname);
682
1
        } else if (objectName == "NormalmapFilename" || objectName == "NormalmapFileName") {
683
            // one exporter writes out the normal map in a separate filename tag
684
0
            std::string texname;
685
0
            ParseDataObjectTextureFilename(texname);
686
0
            pMaterial->mTextures.emplace_back(texname, true);
687
0
        } else {
688
0
            ASSIMP_LOG_WARN("Unknown data object in material in x file");
689
0
            ParseUnknownDataObject();
690
0
        }
691
3
    }
692
2
}
693
694
// ------------------------------------------------------------------------------------------------
695
5
void XFileParser::ParseDataObjectAnimTicksPerSecond() {
696
5
    readHeadOfDataObject();
697
5
    mScene->mAnimTicksPerSecond = ReadInt();
698
5
    CheckForClosingBrace();
699
5
}
700
701
// ------------------------------------------------------------------------------------------------
702
5
void XFileParser::ParseDataObjectAnimationSet() {
703
5
    std::string animName;
704
5
    readHeadOfDataObject(&animName);
705
706
5
    Animation *anim = new Animation;
707
5
    mScene->mAnims.push_back(anim);
708
5
    anim->mName = animName;
709
710
5
    bool running = true;
711
183
    while (running) {
712
183
        std::string objectName = GetNextToken();
713
183
        if (objectName.length() == 0)
714
0
            ThrowException("Unexpected end of file while parsing animation set.");
715
183
        else if (objectName == "}")
716
5
            break; // animation set finished
717
178
        else if (objectName == "Animation")
718
178
            ParseDataObjectAnimation(anim);
719
0
        else {
720
0
            ASSIMP_LOG_WARN("Unknown data object in animation set in x file");
721
0
            ParseUnknownDataObject();
722
0
        }
723
183
    }
724
5
}
725
726
// ------------------------------------------------------------------------------------------------
727
178
void XFileParser::ParseDataObjectAnimation(Animation *pAnim) {
728
178
    readHeadOfDataObject();
729
178
    AnimBone *banim = new AnimBone;
730
178
    pAnim->mAnims.push_back(banim);
731
732
178
    bool running = true;
733
1.06k
    while (running) {
734
1.06k
        std::string objectName = GetNextToken();
735
736
1.06k
        if (objectName.length() == 0)
737
0
            ThrowException("Unexpected end of file while parsing animation.");
738
1.06k
        else if (objectName == "}")
739
178
            break; // animation finished
740
886
        else if (objectName == "AnimationKey")
741
534
            ParseDataObjectAnimationKey(banim);
742
352
        else if (objectName == "AnimationOptions")
743
174
            ParseUnknownDataObject(); // not interested
744
178
        else if (objectName == "{") {
745
            // read frame name
746
178
            banim->mBoneName = GetNextToken();
747
178
            CheckForClosingBrace();
748
178
        } else {
749
0
            ASSIMP_LOG_WARN("Unknown data object in animation in x file");
750
0
            ParseUnknownDataObject();
751
0
        }
752
1.06k
    }
753
178
}
754
755
// ------------------------------------------------------------------------------------------------
756
534
void XFileParser::ParseDataObjectAnimationKey(AnimBone *pAnimBone) {
757
534
    readHeadOfDataObject();
758
759
    // read key type
760
534
    unsigned int keyType = ReadInt();
761
762
    // read number of keys
763
534
    unsigned int numKeys = ReadInt();
764
765
7.22k
    for (unsigned int a = 0; a < numKeys; a++) {
766
        // read time
767
6.68k
        unsigned int time = ReadInt();
768
769
        // read keys
770
6.68k
        switch (keyType) {
771
6.01k
        case 0: // rotation quaternion
772
6.01k
        {
773
            // read count
774
6.01k
            if (ReadInt() != 4)
775
0
                ThrowException("Invalid number of arguments for quaternion key in animation");
776
777
6.01k
            aiQuatKey key;
778
6.01k
            key.mTime = double(time);
779
6.01k
            key.mValue.w = ReadFloat();
780
6.01k
            key.mValue.x = ReadFloat();
781
6.01k
            key.mValue.y = ReadFloat();
782
6.01k
            key.mValue.z = ReadFloat();
783
6.01k
            pAnimBone->mRotKeys.push_back(key);
784
785
6.01k
            CheckForSemicolon();
786
6.01k
            break;
787
0
        }
788
789
270
        case 1: // scale vector
790
668
        case 2: // position vector
791
668
        {
792
            // read count
793
668
            if (ReadInt() != 3)
794
0
                ThrowException("Invalid number of arguments for vector key in animation");
795
796
668
            aiVectorKey key;
797
668
            key.mTime = double(time);
798
668
            key.mValue = ReadVector3();
799
800
668
            if (keyType == 2)
801
398
                pAnimBone->mPosKeys.push_back(key);
802
270
            else
803
270
                pAnimBone->mScaleKeys.push_back(key);
804
805
668
            break;
806
270
        }
807
808
0
        case 3: // combined transformation matrix
809
0
        case 4: // denoted both as 3 or as 4
810
0
        {
811
            // read count
812
0
            if (ReadInt() != 16)
813
0
                ThrowException("Invalid number of arguments for matrix key in animation");
814
815
            // read matrix
816
0
            MatrixKey key;
817
0
            key.mTime = double(time);
818
0
            key.mMatrix.a1 = ReadFloat();
819
0
            key.mMatrix.b1 = ReadFloat();
820
0
            key.mMatrix.c1 = ReadFloat();
821
0
            key.mMatrix.d1 = ReadFloat();
822
0
            key.mMatrix.a2 = ReadFloat();
823
0
            key.mMatrix.b2 = ReadFloat();
824
0
            key.mMatrix.c2 = ReadFloat();
825
0
            key.mMatrix.d2 = ReadFloat();
826
0
            key.mMatrix.a3 = ReadFloat();
827
0
            key.mMatrix.b3 = ReadFloat();
828
0
            key.mMatrix.c3 = ReadFloat();
829
0
            key.mMatrix.d3 = ReadFloat();
830
0
            key.mMatrix.a4 = ReadFloat();
831
0
            key.mMatrix.b4 = ReadFloat();
832
0
            key.mMatrix.c4 = ReadFloat();
833
0
            key.mMatrix.d4 = ReadFloat();
834
0
            pAnimBone->mTrafoKeys.push_back(key);
835
836
0
            CheckForSemicolon();
837
0
            break;
838
0
        }
839
840
0
        default:
841
0
            ThrowException("Unknown key type ", keyType, " in animation.");
842
6.68k
        } // end switch
843
844
        // key separator
845
6.68k
        CheckForSeparator();
846
6.68k
    }
847
848
534
    CheckForClosingBrace();
849
534
}
850
851
// ------------------------------------------------------------------------------------------------
852
1
void XFileParser::ParseDataObjectTextureFilename(std::string &pName) {
853
1
    readHeadOfDataObject();
854
1
    GetNextTokenAsString(pName);
855
1
    CheckForClosingBrace();
856
857
    // FIX: some files (e.g. AnimationTest.x) have "" as texture file name
858
1
    if (!pName.length()) {
859
1
        ASSIMP_LOG_WARN("Length of texture file name is zero. Skipping this texture.");
860
1
    }
861
862
    // some exporters write double backslash paths out. We simply replace them if we find them
863
1
    while (pName.find("\\\\") != std::string::npos)
864
0
        pName.replace(pName.find("\\\\"), 2, "\\");
865
1
}
866
867
// ------------------------------------------------------------------------------------------------
868
281
void XFileParser::ParseUnknownDataObject() {
869
    // find opening delimiter
870
281
    bool running = true;
871
377
    while (running) {
872
377
        std::string t = GetNextToken();
873
377
        if (t.length() == 0)
874
0
            ThrowException("Unexpected end of file while parsing unknown segment.");
875
876
377
        if (t == "{")
877
281
            break;
878
377
    }
879
880
281
    unsigned int counter = 1;
881
882
    // parse until closing delimiter
883
28.5k
    while (counter > 0) {
884
28.2k
        std::string t = GetNextToken();
885
886
28.2k
        if (t.length() == 0)
887
0
            ThrowException("Unexpected end of file while parsing unknown segment.");
888
889
28.2k
        if (t == "{")
890
0
            ++counter;
891
28.2k
        else if (t == "}")
892
281
            --counter;
893
28.2k
    }
894
281
}
895
896
// ------------------------------------------------------------------------------------------------
897
//! checks for closing curly brace
898
931
void XFileParser::CheckForClosingBrace() {
899
931
    if (GetNextToken() != "}")
900
0
        ThrowException("Closing brace expected.");
901
931
}
902
903
// ------------------------------------------------------------------------------------------------
904
//! checks for one following semicolon
905
6.21k
void XFileParser::CheckForSemicolon() {
906
6.21k
    if (mIsBinaryFormat)
907
1
        return;
908
909
6.21k
    if (GetNextToken() != ";")
910
0
        ThrowException("Semicolon expected.");
911
6.21k
}
912
913
// ------------------------------------------------------------------------------------------------
914
//! checks for a separator char, either a ',' or a ';'
915
226k
void XFileParser::CheckForSeparator() {
916
226k
    if (mIsBinaryFormat)
917
0
        return;
918
919
226k
    std::string token = GetNextToken();
920
226k
    if (token != "," && token != ";")
921
0
        ThrowException("Separator character (';' or ',') expected.");
922
226k
}
923
924
// ------------------------------------------------------------------------------------------------
925
// tests and possibly consumes a separator char, but does nothing if there was no separator
926
69.5k
void XFileParser::TestForSeparator() {
927
69.5k
    if (mIsBinaryFormat)
928
25.7k
        return;
929
930
43.8k
    FindNextNoneWhiteSpace();
931
43.8k
    if (mP >= mEnd)
932
0
        return;
933
934
    // test and skip
935
43.8k
    if (*mP == ';' || *mP == ',')
936
43.8k
        mP++;
937
43.8k
}
938
939
// ------------------------------------------------------------------------------------------------
940
1.08k
void XFileParser::readHeadOfDataObject(std::string *poName) {
941
1.08k
    std::string nameOrBrace = GetNextToken();
942
1.08k
    if (nameOrBrace != "{") {
943
1.04k
        if (poName)
944
153
            *poName = nameOrBrace;
945
946
1.04k
        if (GetNextToken() != "{") {
947
0
            delete mScene;
948
0
            ThrowException("Opening brace expected.");
949
0
        }
950
1.04k
    }
951
1.08k
}
952
953
// ------------------------------------------------------------------------------------------------
954
266k
std::string XFileParser::GetNextToken() {
955
266k
    std::string s;
956
957
    // process binary-formatted file
958
266k
    if (mIsBinaryFormat) {
959
        // in binary mode it will only return NAME and STRING token
960
        // and (correctly) skip over other tokens.
961
29
        if (mEnd - mP < 2) {
962
1
            return s;
963
1
        }
964
28
        unsigned int tok = ReadBinWord();
965
28
        unsigned int len;
966
967
        // standalone tokens
968
28
        switch (tok) {
969
11
        case 1: {
970
            // name token
971
11
            if (mEnd - mP < 4) {
972
0
                return s;
973
0
            }
974
11
            len = ReadBinDWord();
975
11
            const int bounds = int(mEnd - mP);
976
11
            const int iLen = int(len);
977
11
            if (iLen < 0) {
978
0
                return s;
979
0
            }
980
11
            if (bounds < iLen) {
981
0
                return s;
982
0
            }
983
11
            s = std::string(mP, len);
984
11
            mP += len;
985
11
        }
986
0
            return s;
987
988
0
        case 2:
989
            // string token
990
0
            if (mEnd - mP < 4) return s;
991
0
            len = ReadBinDWord();
992
0
            if (mEnd - mP < int(len)) return s;
993
0
            s = std::string(mP, len);
994
0
            mP += (len + 2);
995
0
            return s;
996
0
        case 3:
997
            // integer token
998
0
            mP += 4;
999
0
            return "<integer>";
1000
0
        case 5:
1001
            // GUID token
1002
0
            mP += 16;
1003
0
            return "<guid>";
1004
1
        case 6:
1005
1
            if (mEnd - mP < 4) return s;
1006
1
            len = ReadBinDWord();
1007
1
            mP += (len * 4);
1008
1
            return "<int_list>";
1009
0
        case 7:
1010
0
            if (mEnd - mP < 4) return s;
1011
0
            len = ReadBinDWord();
1012
0
            mP += (len * mBinaryFloatSize);
1013
0
            return "<flt_list>";
1014
8
        case 0x0a:
1015
8
            return "{";
1016
8
        case 0x0b:
1017
8
            return "}";
1018
0
        case 0x0c:
1019
0
            return "(";
1020
0
        case 0x0d:
1021
0
            return ")";
1022
0
        case 0x0e:
1023
0
            return "[";
1024
0
        case 0x0f:
1025
0
            return "]";
1026
0
        case 0x10:
1027
0
            return "<";
1028
0
        case 0x11:
1029
0
            return ">";
1030
0
        case 0x12:
1031
0
            return ".";
1032
0
        case 0x13:
1033
0
            return ",";
1034
0
        case 0x14:
1035
0
            return ";";
1036
0
        case 0x1f:
1037
0
            return "template";
1038
0
        case 0x28:
1039
0
            return "WORD";
1040
0
        case 0x29:
1041
0
            return "DWORD";
1042
0
        case 0x2a:
1043
0
            return "FLOAT";
1044
0
        case 0x2b:
1045
0
            return "DOUBLE";
1046
0
        case 0x2c:
1047
0
            return "CHAR";
1048
0
        case 0x2d:
1049
0
            return "UCHAR";
1050
0
        case 0x2e:
1051
0
            return "SWORD";
1052
0
        case 0x2f:
1053
0
            return "SDWORD";
1054
0
        case 0x30:
1055
0
            return "void";
1056
0
        case 0x31:
1057
0
            return "string";
1058
0
        case 0x32:
1059
0
            return "unicode";
1060
0
        case 0x33:
1061
0
            return "cstring";
1062
0
        case 0x34:
1063
0
            return "array";
1064
28
        }
1065
28
    }
1066
    // process text-formatted file
1067
266k
    else {
1068
266k
        FindNextNoneWhiteSpace();
1069
266k
        if (mP >= mEnd)
1070
3
            return s;
1071
1072
418k
        while ((mP < mEnd) && !isspace((unsigned char)*mP)) {
1073
            // either keep token delimiters when already holding a token, or return if first valid char
1074
415k
            if (*mP == ';' || *mP == '}' || *mP == '{' || *mP == ',') {
1075
263k
                if (!s.size())
1076
249k
                    s.append(mP++, 1);
1077
263k
                break; // stop for delimiter
1078
263k
            }
1079
152k
            s.append(mP++, 1);
1080
152k
        }
1081
266k
    }
1082
266k
    return s;
1083
266k
}
1084
1085
// ------------------------------------------------------------------------------------------------
1086
530k
void XFileParser::FindNextNoneWhiteSpace() {
1087
530k
    if (mIsBinaryFormat)
1088
0
        return;
1089
1090
530k
    bool running = true;
1091
530k
    while (running) {
1092
1.22M
        while (mP < mEnd && isspace((unsigned char)*mP)) {
1093
693k
            if (*mP == '\n')
1094
103k
                mLineNumber++;
1095
693k
            ++mP;
1096
693k
        }
1097
1098
530k
        if (mP >= mEnd)
1099
3
            return;
1100
1101
        // check if this is a comment
1102
530k
        if ((mP[0] == '/' && mP[1] == '/') || mP[0] == '#')
1103
0
            ReadUntilEndOfLine();
1104
530k
        else
1105
530k
            break;
1106
530k
    }
1107
530k
}
1108
1109
// ------------------------------------------------------------------------------------------------
1110
96
void XFileParser::GetNextTokenAsString(std::string &poString) {
1111
96
    if (mIsBinaryFormat) {
1112
0
        poString = GetNextToken();
1113
0
        return;
1114
0
    }
1115
1116
96
    FindNextNoneWhiteSpace();
1117
96
    if (mP >= mEnd) {
1118
0
        delete mScene;
1119
0
        ThrowException("Unexpected end of file while parsing string");
1120
0
    }
1121
1122
96
    if (*mP != '"') {
1123
0
        delete mScene;
1124
0
        ThrowException("Expected quotation mark.");
1125
0
    }
1126
96
    ++mP;
1127
1128
1.18k
    while (mP < mEnd && *mP != '"')
1129
1.08k
        poString.append(mP++, 1);
1130
1131
96
    if (mP >= mEnd - 1) {
1132
0
        delete mScene;
1133
0
        ThrowException("Unexpected end of file while parsing string");
1134
0
    }
1135
1136
96
    if (mP[1] != ';' || mP[0] != '"') {
1137
0
        delete mScene;
1138
0
        ThrowException("Expected quotation mark and semicolon at the end of a string.");
1139
0
    }
1140
96
    mP += 2;
1141
96
}
1142
1143
// ------------------------------------------------------------------------------------------------
1144
4
void XFileParser::ReadUntilEndOfLine() {
1145
4
    if (mIsBinaryFormat)
1146
1
        return;
1147
1148
3
    while (mP < mEnd) {
1149
3
        if (*mP == '\n' || *mP == '\r') {
1150
3
            ++mP;
1151
3
            mLineNumber++;
1152
3
            return;
1153
3
        }
1154
1155
0
        ++mP;
1156
0
    }
1157
3
}
1158
1159
// ------------------------------------------------------------------------------------------------
1160
39
unsigned short XFileParser::ReadBinWord() {
1161
39
    ai_assert(mEnd - mP >= 2);
1162
39
    const unsigned char *q = (const unsigned char *)mP;
1163
39
    unsigned short tmp = q[0] | (q[1] << 8);
1164
39
    mP += 2;
1165
39
    return tmp;
1166
39
}
1167
1168
// ------------------------------------------------------------------------------------------------
1169
59.9k
unsigned int XFileParser::ReadBinDWord() {
1170
59.9k
    ai_assert(mEnd - mP >= 4);
1171
1172
59.9k
    const unsigned char *q = (const unsigned char *)mP;
1173
59.9k
    unsigned int tmp = q[0] | (q[1] << 8) | (q[2] << 16) | (q[3] << 24);
1174
59.9k
    mP += 4;
1175
59.9k
    return tmp;
1176
59.9k
}
1177
1178
// ------------------------------------------------------------------------------------------------
1179
169k
unsigned int XFileParser::ReadInt() {
1180
169k
    if (mIsBinaryFormat) {
1181
59.9k
        if (mBinaryNumCount == 0 && mEnd - mP >= 2) {
1182
6
            unsigned short tmp = ReadBinWord(); // 0x06 or 0x03
1183
6
            if (tmp == 0x06 && mEnd - mP >= 4) // array of ints follows
1184
6
                mBinaryNumCount = ReadBinDWord();
1185
0
            else // single int follows
1186
0
                mBinaryNumCount = 1;
1187
6
        }
1188
1189
59.9k
        --mBinaryNumCount;
1190
59.9k
        const size_t len(mEnd - mP);
1191
59.9k
        if (len >= 4) {
1192
59.9k
            return ReadBinDWord();
1193
59.9k
        } else {
1194
0
            mP = mEnd;
1195
0
            return 0;
1196
0
        }
1197
109k
    } else {
1198
109k
        FindNextNoneWhiteSpace();
1199
1200
        // TODO: consider using strtol10 instead???
1201
1202
        // check preceding minus sign
1203
109k
        bool isNegative = false;
1204
109k
        if (*mP == '-') {
1205
0
            isNegative = true;
1206
0
            mP++;
1207
0
        }
1208
1209
        // at least one digit expected
1210
109k
        if (!isdigit((unsigned char)*mP))
1211
0
            ThrowException("Number expected.");
1212
1213
        // read digits
1214
109k
        unsigned int number = 0;
1215
412k
        while (mP < mEnd) {
1216
412k
            if (!isdigit((unsigned char)*mP))
1217
109k
                break;
1218
302k
            number = number * 10 + (*mP - 48);
1219
302k
            mP++;
1220
302k
        }
1221
1222
109k
        CheckForSeparator();
1223
1224
109k
        return isNegative ? ((unsigned int)-int(number)) : number;
1225
109k
    }
1226
169k
}
1227
1228
// ------------------------------------------------------------------------------------------------
1229
142k
ai_real XFileParser::ReadFloat() {
1230
142k
    if (mIsBinaryFormat) {
1231
33.0k
        if (mBinaryNumCount == 0 && mEnd - mP >= 2) {
1232
5
            unsigned short tmp = ReadBinWord(); // 0x07 or 0x42
1233
5
            if (tmp == 0x07 && mEnd - mP >= 4) // array of floats following
1234
5
                mBinaryNumCount = ReadBinDWord();
1235
0
            else // single float following
1236
0
                mBinaryNumCount = 1;
1237
5
        }
1238
1239
33.0k
        --mBinaryNumCount;
1240
33.0k
        if (mBinaryFloatSize == 8) {
1241
0
            if (mEnd - mP >= 8) {
1242
0
                double res;
1243
0
                ::memcpy(&res, mP, 8);
1244
0
                mP += 8;
1245
0
                const ai_real result(static_cast<ai_real>(res));
1246
0
                return result;
1247
0
            } else {
1248
0
                mP = mEnd;
1249
0
                return 0;
1250
0
            }
1251
33.0k
        } else {
1252
33.0k
            if (mEnd - mP >= 4) {
1253
33.0k
                ai_real result;
1254
33.0k
                ::memcpy(&result, mP, 4);
1255
33.0k
                mP += 4;
1256
33.0k
                return result;
1257
33.0k
            } else {
1258
0
                mP = mEnd;
1259
0
                return 0;
1260
0
            }
1261
33.0k
        }
1262
33.0k
    }
1263
1264
    // text version
1265
109k
    FindNextNoneWhiteSpace();
1266
    // check for various special strings to allow reading files from faulty exporters
1267
    // I mean you, Blender!
1268
    // Reading is safe because of the terminating zero
1269
109k
    if (strncmp(mP, "-1.#IND00", 9) == 0 || strncmp(mP, "1.#IND00", 8) == 0) {
1270
0
        mP += 9;
1271
0
        CheckForSeparator();
1272
0
        return 0.0;
1273
109k
    } else if (strncmp(mP, "1.#QNAN0", 8) == 0) {
1274
0
        mP += 8;
1275
0
        CheckForSeparator();
1276
0
        return 0.0;
1277
0
    }
1278
1279
109k
    ai_real result = 0.0;
1280
109k
    mP = fast_atoreal_move(mP, result);
1281
1282
109k
    CheckForSeparator();
1283
1284
109k
    return result;
1285
109k
}
1286
1287
// ------------------------------------------------------------------------------------------------
1288
12.0k
aiVector2D XFileParser::ReadVector2() {
1289
12.0k
    aiVector2D vector;
1290
12.0k
    vector.x = ReadFloat();
1291
12.0k
    vector.y = ReadFloat();
1292
12.0k
    TestForSeparator();
1293
1294
12.0k
    return vector;
1295
12.0k
}
1296
1297
// ------------------------------------------------------------------------------------------------
1298
24.8k
aiVector3D XFileParser::ReadVector3() {
1299
24.8k
    aiVector3D vector;
1300
24.8k
    vector.x = ReadFloat();
1301
24.8k
    vector.y = ReadFloat();
1302
24.8k
    vector.z = ReadFloat();
1303
24.8k
    TestForSeparator();
1304
1305
24.8k
    return vector;
1306
24.8k
}
1307
1308
// ------------------------------------------------------------------------------------------------
1309
2
aiColor4D XFileParser::ReadRGBA() {
1310
2
    aiColor4D color;
1311
2
    color.r = ReadFloat();
1312
2
    color.g = ReadFloat();
1313
2
    color.b = ReadFloat();
1314
2
    color.a = ReadFloat();
1315
2
    TestForSeparator();
1316
1317
2
    return color;
1318
2
}
1319
1320
// ------------------------------------------------------------------------------------------------
1321
4
aiColor3D XFileParser::ReadRGB() {
1322
4
    aiColor3D color;
1323
4
    color.r = ReadFloat();
1324
4
    color.g = ReadFloat();
1325
4
    color.b = ReadFloat();
1326
4
    TestForSeparator();
1327
1328
4
    return color;
1329
4
}
1330
1331
// ------------------------------------------------------------------------------------------------
1332
// Filters the imported hierarchy for some degenerated cases that some exporters produce.
1333
104
void XFileParser::FilterHierarchy(XFile::Node *pNode) {
1334
    // if the node has just a single unnamed child containing a mesh, remove
1335
    // the anonymous node between. The 3DSMax kwXport plugin seems to produce this
1336
    // mess in some cases
1337
104
    if (pNode->mChildren.size() == 1 && pNode->mMeshes.empty()) {
1338
54
        XFile::Node *child = pNode->mChildren.front();
1339
54
        if (child->mName.length() == 0 && child->mMeshes.size() > 0) {
1340
            // transfer its meshes to us
1341
0
            for (unsigned int a = 0; a < child->mMeshes.size(); a++)
1342
0
                pNode->mMeshes.push_back(child->mMeshes[a]);
1343
0
            child->mMeshes.clear();
1344
1345
            // transfer the transform as well
1346
0
            pNode->mTrafoMatrix = pNode->mTrafoMatrix * child->mTrafoMatrix;
1347
1348
            // then kill it
1349
0
            delete child;
1350
0
            pNode->mChildren.clear();
1351
0
        }
1352
54
    }
1353
1354
    // recurse
1355
204
    for (unsigned int a = 0; a < pNode->mChildren.size(); a++)
1356
100
        FilterHierarchy(pNode->mChildren[a]);
1357
104
}
1358
1359
#endif // !! ASSIMP_BUILD_NO_X_IMPORTER