Coverage Report

Created: 2026-04-29 07:04

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
0
        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
0
    std::vector<char> uncompressed;
86
87
    // set up memory pointers
88
0
    mP = &pBuffer.front();
89
0
    mEnd = mP + pBuffer.size() - 1;
90
91
    // check header
92
0
    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
0
    mMajorVersion = (unsigned int)(mP[4] - 48) * 10 + (unsigned int)(mP[5] - 48);
98
0
    mMinorVersion = (unsigned int)(mP[6] - 48) * 10 + (unsigned int)(mP[7] - 48);
99
100
0
    bool compressed = false;
101
102
    // txt - pure ASCII text format
103
0
    if (strncmp(mP + 8, "txt ", 4) == 0)
104
0
        mIsBinaryFormat = false;
105
106
    // bin - Binary format
107
0
    else if (strncmp(mP + 8, "bin ", 4) == 0)
108
0
        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
0
    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
0
    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
0
    mBinaryFloatSize /= 8;
130
131
0
    mP += 16;
132
133
    // If this is a compressed X file, apply the inflate algorithm to it
134
0
    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
0
    } else {
216
        // start reading here
217
0
        ReadUntilEndOfLine();
218
0
    }
219
220
0
    mScene = new Scene;
221
0
    ParseFile();
222
223
    // filter the imported hierarchy for some degenerated cases
224
0
    if (mScene->mRootNode) {
225
0
        FilterHierarchy(mScene->mRootNode);
226
0
    }
227
0
}
228
229
// ------------------------------------------------------------------------------------------------
230
// Destructor. Destroys all imported data along with it
231
0
XFileParser::~XFileParser() {
232
    // kill everything we created
233
0
    delete mScene;
234
0
}
235
236
// ------------------------------------------------------------------------------------------------
237
0
void XFileParser::ParseFile() {
238
0
    bool running = true;
239
0
    while (running) {
240
        // read name of next object
241
0
        std::string objectName = GetNextToken();
242
0
        if (objectName.length() == 0) {
243
0
            break;
244
0
        }
245
246
        // parse specific object
247
0
        if (objectName == "template") {
248
0
            ParseDataObjectTemplate();
249
0
        } else if (objectName == "Frame") {
250
0
            ParseDataObjectFrame(nullptr);
251
0
        } 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
0
        } else if (objectName == "AnimTicksPerSecond")
257
0
            ParseDataObjectAnimTicksPerSecond();
258
0
        else if (objectName == "AnimationSet")
259
0
            ParseDataObjectAnimationSet();
260
0
        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
0
        } else if (objectName == "}") {
266
            // whatever?
267
0
            ASSIMP_LOG_WARN("} found in dataObject");
268
0
        } else {
269
            // unknown format
270
0
            ASSIMP_LOG_WARN("Unknown data object in animation of .x file");
271
0
            ParseUnknownDataObject();
272
0
        }
273
0
    }
274
0
}
275
276
// ------------------------------------------------------------------------------------------------
277
0
void XFileParser::ParseDataObjectTemplate() {
278
    // parse a template data object. Currently not stored.
279
0
    std::string name;
280
0
    readHeadOfDataObject(&name);
281
282
    // read GUID
283
0
    std::string guid = GetNextToken();
284
285
    // read and ignore data members
286
0
    bool running = true;
287
0
    while (running) {
288
0
        std::string s = GetNextToken();
289
290
0
        if (s == "}") {
291
0
            break;
292
0
        }
293
294
0
        if (s.length() == 0) {
295
0
            ThrowException("Unexpected end of file reached while parsing template definition");
296
0
        }
297
0
    }
298
0
}
299
300
// ------------------------------------------------------------------------------------------------
301
0
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
0
    std::string name;
308
0
    readHeadOfDataObject(&name);
309
310
    // create a named node and place it at its parent, if given
311
0
    Node *node = new Node(pParent);
312
0
    node->mName = name;
313
0
    if (pParent) {
314
0
        pParent->mChildren.push_back(node);
315
0
    } else {
316
        // there might be multiple root nodes
317
0
        if (mScene->mRootNode != nullptr) {
318
            // place a dummy root if not there
319
0
            if (mScene->mRootNode->mName != "$dummy_root") {
320
0
                Node *exroot = mScene->mRootNode;
321
0
                mScene->mRootNode = new Node(nullptr);
322
0
                mScene->mRootNode->mName = "$dummy_root";
323
0
                mScene->mRootNode->mChildren.push_back(exroot);
324
0
                exroot->mParent = mScene->mRootNode;
325
0
            }
326
            // put the new node as its child instead
327
0
            mScene->mRootNode->mChildren.push_back(node);
328
0
            node->mParent = mScene->mRootNode;
329
0
        } else {
330
            // it's the first node imported. place it as root
331
0
            mScene->mRootNode = node;
332
0
        }
333
0
    }
334
335
    // Now inside a frame.
336
    // read tokens until closing brace is reached.
337
0
    bool running = true;
338
0
    while (running) {
339
0
        std::string objectName = GetNextToken();
340
0
        if (objectName.size() == 0)
341
0
            ThrowException("Unexpected end of file reached while parsing frame");
342
343
0
        if (objectName == "}")
344
0
            break; // frame finished
345
0
        else if (objectName == "Frame")
346
0
            ParseDataObjectFrame(node); // child frame
347
0
        else if (objectName == "FrameTransformMatrix")
348
0
            ParseDataObjectTransformationMatrix(node->mTrafoMatrix);
349
0
        else if (objectName == "Mesh") {
350
0
            Mesh *mesh = new Mesh(name);
351
0
            node->mMeshes.push_back(mesh);
352
0
            ParseDataObjectMesh(mesh);
353
0
        } else {
354
0
            ASSIMP_LOG_WARN("Unknown data object in frame in x file");
355
0
            ParseUnknownDataObject();
356
0
        }
357
0
    }
358
0
}
359
360
// ------------------------------------------------------------------------------------------------
361
0
void XFileParser::ParseDataObjectTransformationMatrix(aiMatrix4x4 &pMatrix) {
362
    // read header, we're not interested if it has a name
363
0
    readHeadOfDataObject();
364
365
    // read its components
366
0
    pMatrix.a1 = ReadFloat();
367
0
    pMatrix.b1 = ReadFloat();
368
0
    pMatrix.c1 = ReadFloat();
369
0
    pMatrix.d1 = ReadFloat();
370
0
    pMatrix.a2 = ReadFloat();
371
0
    pMatrix.b2 = ReadFloat();
372
0
    pMatrix.c2 = ReadFloat();
373
0
    pMatrix.d2 = ReadFloat();
374
0
    pMatrix.a3 = ReadFloat();
375
0
    pMatrix.b3 = ReadFloat();
376
0
    pMatrix.c3 = ReadFloat();
377
0
    pMatrix.d3 = ReadFloat();
378
0
    pMatrix.a4 = ReadFloat();
379
0
    pMatrix.b4 = ReadFloat();
380
0
    pMatrix.c4 = ReadFloat();
381
0
    pMatrix.d4 = ReadFloat();
382
383
    // trailing symbols
384
0
    CheckForSemicolon();
385
0
    CheckForClosingBrace();
386
0
}
387
388
// ------------------------------------------------------------------------------------------------
389
0
void XFileParser::ParseDataObjectMesh(Mesh *pMesh) {
390
0
    std::string name;
391
0
    readHeadOfDataObject(&name);
392
393
    // read vertex count
394
0
    unsigned int numVertices = ReadInt();
395
0
    pMesh->mPositions.resize(numVertices);
396
397
    // read vertices
398
0
    for (unsigned int a = 0; a < numVertices; a++)
399
0
        pMesh->mPositions[a] = ReadVector3();
400
401
    // read position faces
402
0
    unsigned int numPosFaces = ReadInt();
403
0
    pMesh->mPosFaces.resize(numPosFaces);
404
0
    for (unsigned int a = 0; a < numPosFaces; ++a) {
405
        // read indices
406
0
        unsigned int numIndices = ReadInt();
407
0
        Face &face = pMesh->mPosFaces[a];
408
0
        for (unsigned int b = 0; b < numIndices; ++b) {
409
0
            const int idx(ReadInt());
410
0
            if (static_cast<unsigned int>(idx) <= numVertices) {
411
0
                face.mIndices.push_back(idx);
412
0
            }
413
0
        }
414
0
        TestForSeparator();
415
0
    }
416
417
    // here, other data objects may follow
418
0
    bool running = true;
419
0
    while (running) {
420
0
        std::string objectName = GetNextToken();
421
422
0
        if (objectName.empty())
423
0
            ThrowException("Unexpected end of file while parsing mesh structure");
424
0
        else if (objectName == "}")
425
0
            break; // mesh finished
426
0
        else if (objectName == "MeshNormals")
427
0
            ParseDataObjectMeshNormals(pMesh);
428
0
        else if (objectName == "MeshTextureCoords")
429
0
            ParseDataObjectMeshTextureCoords(pMesh);
430
0
        else if (objectName == "MeshVertexColors")
431
0
            ParseDataObjectMeshVertexColors(pMesh);
432
0
        else if (objectName == "MeshMaterialList")
433
0
            ParseDataObjectMeshMaterialList(pMesh);
434
0
        else if (objectName == "VertexDuplicationIndices")
435
0
            ParseUnknownDataObject(); // we'll ignore vertex duplication indices
436
0
        else if (objectName == "XSkinMeshHeader")
437
0
            ParseDataObjectSkinMeshHeader(pMesh);
438
0
        else if (objectName == "SkinWeights")
439
0
            ParseDataObjectSkinWeights(pMesh);
440
0
        else {
441
0
            ASSIMP_LOG_WARN("Unknown data object in mesh in x file");
442
0
            ParseUnknownDataObject();
443
0
        }
444
0
    }
445
0
}
446
447
// ------------------------------------------------------------------------------------------------
448
0
void XFileParser::ParseDataObjectSkinWeights(Mesh *pMesh) {
449
0
    if (nullptr == pMesh) {
450
0
        return;
451
0
    }
452
0
    readHeadOfDataObject();
453
454
0
    std::string transformNodeName;
455
0
    GetNextTokenAsString(transformNodeName);
456
457
0
    pMesh->mBones.emplace_back();
458
0
    Bone &bone = pMesh->mBones.back();
459
0
    bone.mName = transformNodeName;
460
461
    // read vertex weights
462
0
    unsigned int numWeights = ReadInt();
463
0
    bone.mWeights.reserve(numWeights);
464
465
0
    for (unsigned int a = 0; a < numWeights; a++) {
466
0
        BoneWeight weight = {};
467
0
        weight.mVertex = ReadInt();
468
0
        bone.mWeights.push_back(weight);
469
0
    }
470
471
    // read vertex weights
472
0
    for (unsigned int a = 0; a < numWeights; a++)
473
0
        bone.mWeights[a].mWeight = ReadFloat();
474
475
    // read matrix offset
476
0
    bone.mOffsetMatrix.a1 = ReadFloat();
477
0
    bone.mOffsetMatrix.b1 = ReadFloat();
478
0
    bone.mOffsetMatrix.c1 = ReadFloat();
479
0
    bone.mOffsetMatrix.d1 = ReadFloat();
480
0
    bone.mOffsetMatrix.a2 = ReadFloat();
481
0
    bone.mOffsetMatrix.b2 = ReadFloat();
482
0
    bone.mOffsetMatrix.c2 = ReadFloat();
483
0
    bone.mOffsetMatrix.d2 = ReadFloat();
484
0
    bone.mOffsetMatrix.a3 = ReadFloat();
485
0
    bone.mOffsetMatrix.b3 = ReadFloat();
486
0
    bone.mOffsetMatrix.c3 = ReadFloat();
487
0
    bone.mOffsetMatrix.d3 = ReadFloat();
488
0
    bone.mOffsetMatrix.a4 = ReadFloat();
489
0
    bone.mOffsetMatrix.b4 = ReadFloat();
490
0
    bone.mOffsetMatrix.c4 = ReadFloat();
491
0
    bone.mOffsetMatrix.d4 = ReadFloat();
492
493
0
    CheckForSemicolon();
494
0
    CheckForClosingBrace();
495
0
}
496
497
// ------------------------------------------------------------------------------------------------
498
0
void XFileParser::ParseDataObjectSkinMeshHeader(Mesh * /*pMesh*/) {
499
0
    readHeadOfDataObject();
500
501
    /*unsigned int maxSkinWeightsPerVertex =*/ReadInt();
502
0
    /*unsigned int maxSkinWeightsPerFace =*/ReadInt();
503
0
    /*unsigned int numBonesInMesh = */ ReadInt();
504
505
0
    CheckForClosingBrace();
506
0
}
507
508
// ------------------------------------------------------------------------------------------------
509
0
void XFileParser::ParseDataObjectMeshNormals(Mesh *pMesh) {
510
0
    readHeadOfDataObject();
511
512
    // read count
513
0
    unsigned int numNormals = ReadInt();
514
0
    pMesh->mNormals.resize(numNormals);
515
516
    // read normal vectors
517
0
    for (unsigned int a = 0; a < numNormals; ++a) {
518
0
        pMesh->mNormals[a] = ReadVector3();
519
0
    }
520
521
    // read normal indices
522
0
    unsigned int numFaces = ReadInt();
523
0
    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
0
    if (numFaces > 0) {
529
        // normal face creation
530
0
        pMesh->mNormFaces.resize(numFaces);
531
0
        for (unsigned int a = 0; a < numFaces; ++a) {
532
0
            unsigned int numIndices = ReadInt();
533
0
            pMesh->mNormFaces[a] = Face();
534
0
            Face &face = pMesh->mNormFaces[a];
535
0
            for (unsigned int b = 0; b < numIndices; ++b) {
536
0
                face.mIndices.push_back(ReadInt());
537
0
            }
538
539
0
            TestForSeparator();
540
0
        }
541
0
    }
542
543
0
    CheckForClosingBrace();
544
0
}
545
546
// ------------------------------------------------------------------------------------------------
547
0
void XFileParser::ParseDataObjectMeshTextureCoords(Mesh *pMesh) {
548
0
    readHeadOfDataObject();
549
0
    if (pMesh->mNumTextures + 1 > AI_MAX_NUMBER_OF_TEXTURECOORDS)
550
0
        ThrowException("Too many sets of texture coordinates");
551
552
0
    std::vector<aiVector2D> &coords = pMesh->mTexCoords[pMesh->mNumTextures++];
553
554
0
    unsigned int numCoords = ReadInt();
555
0
    if (numCoords != pMesh->mPositions.size())
556
0
        ThrowException("Texture coord count does not match vertex count");
557
558
0
    coords.resize(numCoords);
559
0
    for (unsigned int a = 0; a < numCoords; a++)
560
0
        coords[a] = ReadVector2();
561
562
0
    CheckForClosingBrace();
563
0
}
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
0
void XFileParser::ParseDataObjectMeshMaterialList(Mesh *pMesh) {
597
0
    readHeadOfDataObject();
598
599
    // read material count
600
    /*unsigned int numMaterials =*/ReadInt();
601
    // read non triangulated face material index count
602
0
    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
0
    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
0
    for (unsigned int a = 0; a < numMatIndices; a++)
611
0
        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
0
    if (!mIsBinaryFormat) // && MajorVersion == 3 && MinorVersion <= 2)
616
0
    {
617
0
        if (mP < mEnd && *mP == ';')
618
0
            ++mP;
619
0
    }
620
621
    // if there was only a single material index, replicate it on all faces
622
0
    while (pMesh->mFaceMaterials.size() < pMesh->mPosFaces.size())
623
0
        pMesh->mFaceMaterials.push_back(pMesh->mFaceMaterials.front());
624
625
    // read following data objects
626
0
    bool running = true;
627
0
    while (running) {
628
0
        std::string objectName = GetNextToken();
629
0
        if (objectName.size() == 0)
630
0
            ThrowException("Unexpected end of file while parsing mesh material list.");
631
0
        else if (objectName == "}")
632
0
            break; // material list finished
633
0
        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
0
        } else if (objectName == "Material") {
643
0
            pMesh->mMaterials.emplace_back();
644
0
            ParseDataObjectMaterial(&pMesh->mMaterials.back());
645
0
        } 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
0
    }
652
0
}
653
654
// ------------------------------------------------------------------------------------------------
655
0
void XFileParser::ParseDataObjectMaterial(Material *pMaterial) {
656
0
    std::string matName;
657
0
    readHeadOfDataObject(&matName);
658
0
    if (matName.empty())
659
0
        matName = std::string("material") + ai_to_string(mLineNumber);
660
0
    pMaterial->mName = matName;
661
0
    pMaterial->mIsReference = false;
662
663
    // read material values
664
0
    pMaterial->mDiffuse = ReadRGBA();
665
0
    pMaterial->mSpecularExponent = ReadFloat();
666
0
    pMaterial->mSpecular = ReadRGB();
667
0
    pMaterial->mEmissive = ReadRGB();
668
669
    // read other data objects
670
0
    bool running = true;
671
0
    while (running) {
672
0
        std::string objectName = GetNextToken();
673
0
        if (objectName.size() == 0)
674
0
            ThrowException("Unexpected end of file while parsing mesh material");
675
0
        else if (objectName == "}")
676
0
            break; // material finished
677
0
        else if (objectName == "TextureFilename" || objectName == "TextureFileName") {
678
            // some exporters write "TextureFileName" instead.
679
0
            std::string texname;
680
0
            ParseDataObjectTextureFilename(texname);
681
0
            pMaterial->mTextures.emplace_back(texname);
682
0
        } 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
0
    }
692
0
}
693
694
// ------------------------------------------------------------------------------------------------
695
0
void XFileParser::ParseDataObjectAnimTicksPerSecond() {
696
0
    readHeadOfDataObject();
697
0
    mScene->mAnimTicksPerSecond = ReadInt();
698
0
    CheckForClosingBrace();
699
0
}
700
701
// ------------------------------------------------------------------------------------------------
702
0
void XFileParser::ParseDataObjectAnimationSet() {
703
0
    std::string animName;
704
0
    readHeadOfDataObject(&animName);
705
706
0
    Animation *anim = new Animation;
707
0
    mScene->mAnims.push_back(anim);
708
0
    anim->mName = animName;
709
710
0
    bool running = true;
711
0
    while (running) {
712
0
        std::string objectName = GetNextToken();
713
0
        if (objectName.length() == 0)
714
0
            ThrowException("Unexpected end of file while parsing animation set.");
715
0
        else if (objectName == "}")
716
0
            break; // animation set finished
717
0
        else if (objectName == "Animation")
718
0
            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
0
    }
724
0
}
725
726
// ------------------------------------------------------------------------------------------------
727
0
void XFileParser::ParseDataObjectAnimation(Animation *pAnim) {
728
0
    readHeadOfDataObject();
729
0
    AnimBone *banim = new AnimBone;
730
0
    pAnim->mAnims.push_back(banim);
731
732
0
    bool running = true;
733
0
    while (running) {
734
0
        std::string objectName = GetNextToken();
735
736
0
        if (objectName.length() == 0)
737
0
            ThrowException("Unexpected end of file while parsing animation.");
738
0
        else if (objectName == "}")
739
0
            break; // animation finished
740
0
        else if (objectName == "AnimationKey")
741
0
            ParseDataObjectAnimationKey(banim);
742
0
        else if (objectName == "AnimationOptions")
743
0
            ParseUnknownDataObject(); // not interested
744
0
        else if (objectName == "{") {
745
            // read frame name
746
0
            banim->mBoneName = GetNextToken();
747
0
            CheckForClosingBrace();
748
0
        } else {
749
0
            ASSIMP_LOG_WARN("Unknown data object in animation in x file");
750
0
            ParseUnknownDataObject();
751
0
        }
752
0
    }
753
0
}
754
755
// ------------------------------------------------------------------------------------------------
756
0
void XFileParser::ParseDataObjectAnimationKey(AnimBone *pAnimBone) {
757
0
    readHeadOfDataObject();
758
759
    // read key type
760
0
    unsigned int keyType = ReadInt();
761
762
    // read number of keys
763
0
    unsigned int numKeys = ReadInt();
764
765
0
    for (unsigned int a = 0; a < numKeys; a++) {
766
        // read time
767
0
        unsigned int time = ReadInt();
768
769
        // read keys
770
0
        switch (keyType) {
771
0
        case 0: // rotation quaternion
772
0
        {
773
            // read count
774
0
            if (ReadInt() != 4)
775
0
                ThrowException("Invalid number of arguments for quaternion key in animation");
776
777
0
            aiQuatKey key;
778
0
            key.mTime = double(time);
779
0
            key.mValue.w = ReadFloat();
780
0
            key.mValue.x = ReadFloat();
781
0
            key.mValue.y = ReadFloat();
782
0
            key.mValue.z = ReadFloat();
783
0
            pAnimBone->mRotKeys.push_back(key);
784
785
0
            CheckForSemicolon();
786
0
            break;
787
0
        }
788
789
0
        case 1: // scale vector
790
0
        case 2: // position vector
791
0
        {
792
            // read count
793
0
            if (ReadInt() != 3)
794
0
                ThrowException("Invalid number of arguments for vector key in animation");
795
796
0
            aiVectorKey key;
797
0
            key.mTime = double(time);
798
0
            key.mValue = ReadVector3();
799
800
0
            if (keyType == 2)
801
0
                pAnimBone->mPosKeys.push_back(key);
802
0
            else
803
0
                pAnimBone->mScaleKeys.push_back(key);
804
805
0
            break;
806
0
        }
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
0
        } // end switch
843
844
        // key separator
845
0
        CheckForSeparator();
846
0
    }
847
848
0
    CheckForClosingBrace();
849
0
}
850
851
// ------------------------------------------------------------------------------------------------
852
0
void XFileParser::ParseDataObjectTextureFilename(std::string &pName) {
853
0
    readHeadOfDataObject();
854
0
    GetNextTokenAsString(pName);
855
0
    CheckForClosingBrace();
856
857
    // FIX: some files (e.g. AnimationTest.x) have "" as texture file name
858
0
    if (!pName.length()) {
859
0
        ASSIMP_LOG_WARN("Length of texture file name is zero. Skipping this texture.");
860
0
    }
861
862
    // some exporters write double backslash paths out. We simply replace them if we find them
863
0
    while (pName.find("\\\\") != std::string::npos)
864
0
        pName.replace(pName.find("\\\\"), 2, "\\");
865
0
}
866
867
// ------------------------------------------------------------------------------------------------
868
0
void XFileParser::ParseUnknownDataObject() {
869
    // find opening delimiter
870
0
    bool running = true;
871
0
    while (running) {
872
0
        std::string t = GetNextToken();
873
0
        if (t.length() == 0)
874
0
            ThrowException("Unexpected end of file while parsing unknown segment.");
875
876
0
        if (t == "{")
877
0
            break;
878
0
    }
879
880
0
    unsigned int counter = 1;
881
882
    // parse until closing delimiter
883
0
    while (counter > 0) {
884
0
        std::string t = GetNextToken();
885
886
0
        if (t.length() == 0)
887
0
            ThrowException("Unexpected end of file while parsing unknown segment.");
888
889
0
        if (t == "{")
890
0
            ++counter;
891
0
        else if (t == "}")
892
0
            --counter;
893
0
    }
894
0
}
895
896
// ------------------------------------------------------------------------------------------------
897
//! checks for closing curly brace
898
0
void XFileParser::CheckForClosingBrace() {
899
0
    if (GetNextToken() != "}")
900
0
        ThrowException("Closing brace expected.");
901
0
}
902
903
// ------------------------------------------------------------------------------------------------
904
//! checks for one following semicolon
905
0
void XFileParser::CheckForSemicolon() {
906
0
    if (mIsBinaryFormat)
907
0
        return;
908
909
0
    if (GetNextToken() != ";")
910
0
        ThrowException("Semicolon expected.");
911
0
}
912
913
// ------------------------------------------------------------------------------------------------
914
//! checks for a separator char, either a ',' or a ';'
915
0
void XFileParser::CheckForSeparator() {
916
0
    if (mIsBinaryFormat)
917
0
        return;
918
919
0
    std::string token = GetNextToken();
920
0
    if (token != "," && token != ";")
921
0
        ThrowException("Separator character (';' or ',') expected.");
922
0
}
923
924
// ------------------------------------------------------------------------------------------------
925
// tests and possibly consumes a separator char, but does nothing if there was no separator
926
0
void XFileParser::TestForSeparator() {
927
0
    if (mIsBinaryFormat)
928
0
        return;
929
930
0
    FindNextNoneWhiteSpace();
931
0
    if (mP >= mEnd)
932
0
        return;
933
934
    // test and skip
935
0
    if (*mP == ';' || *mP == ',')
936
0
        mP++;
937
0
}
938
939
// ------------------------------------------------------------------------------------------------
940
0
void XFileParser::readHeadOfDataObject(std::string *poName) {
941
0
    std::string nameOrBrace = GetNextToken();
942
0
    if (nameOrBrace != "{") {
943
0
        if (poName)
944
0
            *poName = nameOrBrace;
945
946
0
        if (GetNextToken() != "{") {
947
0
            delete mScene;
948
0
            ThrowException("Opening brace expected.");
949
0
        }
950
0
    }
951
0
}
952
953
// ------------------------------------------------------------------------------------------------
954
0
std::string XFileParser::GetNextToken() {
955
0
    std::string s;
956
957
    // process binary-formatted file
958
0
    if (mIsBinaryFormat) {
959
        // in binary mode it will only return NAME and STRING token
960
        // and (correctly) skip over other tokens.
961
0
        if (mEnd - mP < 2) {
962
0
            return s;
963
0
        }
964
0
        unsigned int tok = ReadBinWord();
965
0
        unsigned int len;
966
967
        // standalone tokens
968
0
        switch (tok) {
969
0
        case 1: {
970
            // name token
971
0
            if (mEnd - mP < 4) {
972
0
                return s;
973
0
            }
974
0
            len = ReadBinDWord();
975
0
            const int bounds = int(mEnd - mP);
976
0
            const int iLen = int(len);
977
0
            if (iLen < 0) {
978
0
                return s;
979
0
            }
980
0
            if (bounds < iLen) {
981
0
                return s;
982
0
            }
983
0
            s = std::string(mP, len);
984
0
            mP += len;
985
0
        }
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
0
        case 6:
1005
0
            if (mEnd - mP < 4) return s;
1006
0
            len = ReadBinDWord();
1007
0
            mP += (len * 4);
1008
0
            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
0
        case 0x0a:
1015
0
            return "{";
1016
0
        case 0x0b:
1017
0
            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
0
        }
1065
0
    }
1066
    // process text-formatted file
1067
0
    else {
1068
0
        FindNextNoneWhiteSpace();
1069
0
        if (mP >= mEnd)
1070
0
            return s;
1071
1072
0
        while ((mP < mEnd) && !isspace((unsigned char)*mP)) {
1073
            // either keep token delimiters when already holding a token, or return if first valid char
1074
0
            if (*mP == ';' || *mP == '}' || *mP == '{' || *mP == ',') {
1075
0
                if (!s.size())
1076
0
                    s.append(mP++, 1);
1077
0
                break; // stop for delimiter
1078
0
            }
1079
0
            s.append(mP++, 1);
1080
0
        }
1081
0
    }
1082
0
    return s;
1083
0
}
1084
1085
// ------------------------------------------------------------------------------------------------
1086
0
void XFileParser::FindNextNoneWhiteSpace() {
1087
0
    if (mIsBinaryFormat)
1088
0
        return;
1089
1090
0
    bool running = true;
1091
0
    while (running) {
1092
0
        while (mP < mEnd && isspace((unsigned char)*mP)) {
1093
0
            if (*mP == '\n')
1094
0
                mLineNumber++;
1095
0
            ++mP;
1096
0
        }
1097
1098
0
        if (mP >= mEnd)
1099
0
            return;
1100
1101
        // check if this is a comment
1102
0
        if ((mP[0] == '/' && mP[1] == '/') || mP[0] == '#')
1103
0
            ReadUntilEndOfLine();
1104
0
        else
1105
0
            break;
1106
0
    }
1107
0
}
1108
1109
// ------------------------------------------------------------------------------------------------
1110
0
void XFileParser::GetNextTokenAsString(std::string &poString) {
1111
0
    if (mIsBinaryFormat) {
1112
0
        poString = GetNextToken();
1113
0
        return;
1114
0
    }
1115
1116
0
    FindNextNoneWhiteSpace();
1117
0
    if (mP >= mEnd) {
1118
0
        delete mScene;
1119
0
        ThrowException("Unexpected end of file while parsing string");
1120
0
    }
1121
1122
0
    if (*mP != '"') {
1123
0
        delete mScene;
1124
0
        ThrowException("Expected quotation mark.");
1125
0
    }
1126
0
    ++mP;
1127
1128
0
    while (mP < mEnd && *mP != '"')
1129
0
        poString.append(mP++, 1);
1130
1131
0
    if (mP >= mEnd - 1) {
1132
0
        delete mScene;
1133
0
        ThrowException("Unexpected end of file while parsing string");
1134
0
    }
1135
1136
0
    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
0
    mP += 2;
1141
0
}
1142
1143
// ------------------------------------------------------------------------------------------------
1144
0
void XFileParser::ReadUntilEndOfLine() {
1145
0
    if (mIsBinaryFormat)
1146
0
        return;
1147
1148
0
    while (mP < mEnd) {
1149
0
        if (*mP == '\n' || *mP == '\r') {
1150
0
            ++mP;
1151
0
            mLineNumber++;
1152
0
            return;
1153
0
        }
1154
1155
0
        ++mP;
1156
0
    }
1157
0
}
1158
1159
// ------------------------------------------------------------------------------------------------
1160
0
unsigned short XFileParser::ReadBinWord() {
1161
0
    ai_assert(mEnd - mP >= 2);
1162
0
    const unsigned char *q = (const unsigned char *)mP;
1163
0
    unsigned short tmp = q[0] | (q[1] << 8);
1164
0
    mP += 2;
1165
0
    return tmp;
1166
0
}
1167
1168
// ------------------------------------------------------------------------------------------------
1169
0
unsigned int XFileParser::ReadBinDWord() {
1170
0
    ai_assert(mEnd - mP >= 4);
1171
1172
0
    const unsigned char *q = (const unsigned char *)mP;
1173
0
    unsigned int tmp = q[0] | (q[1] << 8) | (q[2] << 16) | (q[3] << 24);
1174
0
    mP += 4;
1175
0
    return tmp;
1176
0
}
1177
1178
// ------------------------------------------------------------------------------------------------
1179
0
unsigned int XFileParser::ReadInt() {
1180
0
    if (mIsBinaryFormat) {
1181
0
        if (mBinaryNumCount == 0 && mEnd - mP >= 2) {
1182
0
            unsigned short tmp = ReadBinWord(); // 0x06 or 0x03
1183
0
            if (tmp == 0x06 && mEnd - mP >= 4) // array of ints follows
1184
0
                mBinaryNumCount = ReadBinDWord();
1185
0
            else // single int follows
1186
0
                mBinaryNumCount = 1;
1187
0
        }
1188
1189
0
        --mBinaryNumCount;
1190
0
        const size_t len(mEnd - mP);
1191
0
        if (len >= 4) {
1192
0
            return ReadBinDWord();
1193
0
        } else {
1194
0
            mP = mEnd;
1195
0
            return 0;
1196
0
        }
1197
0
    } else {
1198
0
        FindNextNoneWhiteSpace();
1199
1200
        // TODO: consider using strtol10 instead???
1201
1202
        // check preceding minus sign
1203
0
        bool isNegative = false;
1204
0
        if (*mP == '-') {
1205
0
            isNegative = true;
1206
0
            mP++;
1207
0
        }
1208
1209
        // at least one digit expected
1210
0
        if (!isdigit((unsigned char)*mP))
1211
0
            ThrowException("Number expected.");
1212
1213
        // read digits
1214
0
        unsigned int number = 0;
1215
0
        while (mP < mEnd) {
1216
0
            if (!isdigit((unsigned char)*mP))
1217
0
                break;
1218
0
            number = number * 10 + (*mP - 48);
1219
0
            mP++;
1220
0
        }
1221
1222
0
        CheckForSeparator();
1223
1224
0
        return isNegative ? ((unsigned int)-int(number)) : number;
1225
0
    }
1226
0
}
1227
1228
// ------------------------------------------------------------------------------------------------
1229
0
ai_real XFileParser::ReadFloat() {
1230
0
    if (mIsBinaryFormat) {
1231
0
        if (mBinaryNumCount == 0 && mEnd - mP >= 2) {
1232
0
            unsigned short tmp = ReadBinWord(); // 0x07 or 0x42
1233
0
            if (tmp == 0x07 && mEnd - mP >= 4) // array of floats following
1234
0
                mBinaryNumCount = ReadBinDWord();
1235
0
            else // single float following
1236
0
                mBinaryNumCount = 1;
1237
0
        }
1238
1239
0
        --mBinaryNumCount;
1240
0
        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
0
        } else {
1252
0
            if (mEnd - mP >= 4) {
1253
0
                ai_real result;
1254
0
                ::memcpy(&result, mP, 4);
1255
0
                mP += 4;
1256
0
                return result;
1257
0
            } else {
1258
0
                mP = mEnd;
1259
0
                return 0;
1260
0
            }
1261
0
        }
1262
0
    }
1263
1264
    // text version
1265
0
    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
0
    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
0
    } else if (strncmp(mP, "1.#QNAN0", 8) == 0) {
1274
0
        mP += 8;
1275
0
        CheckForSeparator();
1276
0
        return 0.0;
1277
0
    }
1278
1279
0
    ai_real result = 0.0;
1280
0
    mP = fast_atoreal_move(mP, result);
1281
1282
0
    CheckForSeparator();
1283
1284
0
    return result;
1285
0
}
1286
1287
// ------------------------------------------------------------------------------------------------
1288
0
aiVector2D XFileParser::ReadVector2() {
1289
0
    aiVector2D vector;
1290
0
    vector.x = ReadFloat();
1291
0
    vector.y = ReadFloat();
1292
0
    TestForSeparator();
1293
1294
0
    return vector;
1295
0
}
1296
1297
// ------------------------------------------------------------------------------------------------
1298
0
aiVector3D XFileParser::ReadVector3() {
1299
0
    aiVector3D vector;
1300
0
    vector.x = ReadFloat();
1301
0
    vector.y = ReadFloat();
1302
0
    vector.z = ReadFloat();
1303
0
    TestForSeparator();
1304
1305
0
    return vector;
1306
0
}
1307
1308
// ------------------------------------------------------------------------------------------------
1309
0
aiColor4D XFileParser::ReadRGBA() {
1310
0
    aiColor4D color;
1311
0
    color.r = ReadFloat();
1312
0
    color.g = ReadFloat();
1313
0
    color.b = ReadFloat();
1314
0
    color.a = ReadFloat();
1315
0
    TestForSeparator();
1316
1317
0
    return color;
1318
0
}
1319
1320
// ------------------------------------------------------------------------------------------------
1321
0
aiColor3D XFileParser::ReadRGB() {
1322
0
    aiColor3D color;
1323
0
    color.r = ReadFloat();
1324
0
    color.g = ReadFloat();
1325
0
    color.b = ReadFloat();
1326
0
    TestForSeparator();
1327
1328
0
    return color;
1329
0
}
1330
1331
// ------------------------------------------------------------------------------------------------
1332
// Filters the imported hierarchy for some degenerated cases that some exporters produce.
1333
0
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
0
    if (pNode->mChildren.size() == 1 && pNode->mMeshes.empty()) {
1338
0
        XFile::Node *child = pNode->mChildren.front();
1339
0
        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
0
    }
1353
1354
    // recurse
1355
0
    for (unsigned int a = 0; a < pNode->mChildren.size(); a++)
1356
0
        FilterHierarchy(pNode->mChildren[a]);
1357
0
}
1358
1359
#endif // !! ASSIMP_BUILD_NO_X_IMPORTER