Coverage Report

Created: 2026-03-12 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/assimp/code/AssetLib/MD5/MD5Parser.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  MD5Parser.cpp
43
 *  @brief Implementation of the MD5 parser class
44
 */
45
46
// internal headers
47
#include "MD5Loader.h"
48
#include "Material/MaterialSystem.h"
49
50
#include <assimp/ParsingUtils.h>
51
#include <assimp/StringComparison.h>
52
#include <assimp/fast_atof.h>
53
#include <assimp/mesh.h>
54
#include <assimp/DefaultLogger.hpp>
55
56
using namespace Assimp;
57
using namespace Assimp::MD5;
58
59
// ------------------------------------------------------------------------------------------------
60
// Parse the segment structure for an MD5 file
61
22
MD5Parser::MD5Parser(char *_buffer, unsigned int _fileSize) : buffer(_buffer), bufferEnd(nullptr), fileSize(_fileSize), lineNumber(0) {
62
22
    ai_assert(nullptr != _buffer);
63
22
    ai_assert(0 != _fileSize);
64
65
22
    bufferEnd = buffer + fileSize;
66
22
    ASSIMP_LOG_DEBUG("MD5Parser begin");
67
68
    // parse the file header
69
22
    ParseHeader();
70
71
    // and read all sections until we're finished
72
22
    bool running = true;
73
15.3k
    while (running) {
74
15.3k
        mSections.emplace_back();
75
15.3k
        Section &sec = mSections.back();
76
15.3k
        if (!ParseSection(sec)) {
77
22
            break;
78
22
        }
79
15.3k
    }
80
81
22
    if (!DefaultLogger::isNullLogger()) {
82
0
        char szBuffer[128]; // should be sufficiently large
83
0
        ::ai_snprintf(szBuffer, 128, "MD5Parser end. Parsed %i sections", (int)mSections.size());
84
0
        ASSIMP_LOG_DEBUG(szBuffer);
85
0
    }
86
22
}
87
88
// ------------------------------------------------------------------------------------------------
89
// Report error to the log stream
90
0
AI_WONT_RETURN void MD5Parser::ReportError(const char *error, unsigned int line) {
91
0
    char szBuffer[1024];
92
0
    ::ai_snprintf(szBuffer, 1024, "[MD5] Line %u: %s", line, error);
93
0
    throw DeadlyImportError(szBuffer);
94
0
}
95
96
// ------------------------------------------------------------------------------------------------
97
// Report warning to the log stream
98
15.7k
void MD5Parser::ReportWarning(const char *warn, unsigned int line) {
99
15.7k
    char szBuffer[1024];
100
15.7k
    ::snprintf(szBuffer, sizeof(szBuffer), "[MD5] Line %u: %s", line, warn);
101
15.7k
    ASSIMP_LOG_WARN(szBuffer);
102
15.7k
}
103
104
// ------------------------------------------------------------------------------------------------
105
// Parse and validate the MD5 header
106
22
void MD5Parser::ParseHeader() {
107
    // parse and validate the file version
108
22
    SkipSpaces();
109
22
    if (!TokenMatch(buffer, "MD5Version", 10)) {
110
0
        ReportError("Invalid MD5 file: MD5Version tag has not been found");
111
0
    }
112
22
    SkipSpaces();
113
22
    unsigned int iVer = ::strtoul10(buffer, (const char **)&buffer);
114
22
    if (10 != iVer) {
115
0
        ReportError("MD5 version tag is unknown (10 is expected)");
116
0
    }
117
22
    SkipLine();
118
119
    // print the command line options to the console
120
22
    char *sz = buffer;
121
404
    while (buffer < bufferEnd) {
122
404
        if (IsLineEnd(*buffer++)) {
123
22
            break;
124
22
        }
125
404
    }
126
127
22
    if (buffer == bufferEnd) {
128
0
        return;
129
0
    }
130
131
22
    ASSIMP_LOG_INFO(std::string(sz, std::min((uintptr_t)MAX_LOG_MESSAGE_LENGTH, (uintptr_t)(buffer - sz))));
132
22
    SkipSpacesAndLineEnd();
133
22
}
134
135
// ------------------------------------------------------------------------------------------------
136
// Recursive MD5 parsing function
137
15.3k
bool MD5Parser::ParseSection(Section &out) {
138
    // store the current line number for use in error messages
139
15.3k
    out.iLineNumber = lineNumber;
140
141
    // first parse the name of the section
142
15.3k
    char *sz = buffer;
143
131k
    while (!IsSpaceOrNewLine(*buffer)) {
144
115k
        ++buffer;
145
115k
        if (buffer == bufferEnd) {
146
4
            return false;
147
4
      }
148
115k
    }
149
15.3k
    out.mName = std::string(sz, (uintptr_t)(buffer - sz));
150
24.5k
    while (IsSpace(*buffer)) {
151
9.13k
        ++buffer;
152
9.13k
        if (buffer == bufferEnd) {
153
0
            return false;
154
0
      }
155
9.13k
    }
156
157
15.3k
    bool running = true;
158
28.0k
    while (running) {
159
28.0k
        if ('{' == *buffer) {
160
            // it is a normal section so read all lines
161
148
            ++buffer;
162
148
            if (buffer == bufferEnd) {
163
0
                return false;
164
0
          }
165
148
            bool run = true;
166
29.8k
            while (run) {
167
63.3k
                while (IsSpaceOrNewLine(*buffer)) {
168
33.5k
                    ++buffer;
169
33.5k
                    if (buffer == bufferEnd) {
170
0
                        return false;
171
0
                }
172
33.5k
                }
173
29.8k
                if ('\0' == *buffer) {
174
0
                    return false; // seems this was the last section
175
0
                }
176
29.8k
                if ('}' == *buffer) {
177
138
                    ++buffer;
178
138
                    break;
179
138
                }
180
181
29.6k
                out.mElements.emplace_back();
182
29.6k
                Element &elem = out.mElements.back();
183
184
29.6k
                elem.iLineNumber = lineNumber;
185
29.6k
                elem.szStart = buffer;
186
29.6k
                elem.end = bufferEnd;
187
188
                // terminate the line with zero
189
340k
                while (!IsLineEnd(*buffer)) {
190
310k
                    ++buffer;
191
310k
                    if (buffer == bufferEnd) {
192
10
                        return false;
193
10
                }
194
310k
                }
195
29.6k
                if (*buffer) {
196
21.1k
                    ++lineNumber;
197
21.1k
                    *buffer++ = '\0';
198
21.1k
                    if (buffer == bufferEnd) {
199
0
                        return false;
200
0
                }
201
21.1k
                }
202
29.6k
            }
203
138
            break;
204
27.9k
        } else if (!IsSpaceOrNewLine(*buffer)) {
205
            // it is an element at global scope. Parse its value and go on
206
12.7k
            sz = buffer;
207
160k
            while (!IsSpaceOrNewLine(*buffer++)) {
208
147k
                if (buffer == bufferEnd) {
209
8
                    return false;
210
8
            }
211
147k
            }
212
12.7k
            out.mGlobalValue = std::string(sz, (uintptr_t)(buffer - sz));
213
12.7k
            continue;
214
12.7k
        }
215
15.2k
        break;
216
28.0k
    }
217
15.3k
    if (buffer == bufferEnd) {
218
0
        return false;
219
0
    }
220
41.3k
    while (IsSpaceOrNewLine(*buffer)) {
221
25.9k
        if (buffer == bufferEnd) {
222
0
            break;
223
0
      }
224
25.9k
        ++buffer;
225
25.9k
    }
226
15.3k
    return '\0' != *buffer;
227
15.3k
}
228
229
// skip all spaces ... handle EOL correctly
230
16.8k
inline void AI_MD5_SKIP_SPACES(const char **sz, const char *bufferEnd, int linenumber) {
231
16.8k
    if (!SkipSpaces(sz, bufferEnd)) {
232
15.6k
        MD5Parser::ReportWarning("Unexpected end of line", linenumber);
233
15.6k
    }
234
16.8k
}
235
236
// read a triple float in brackets: (1.0 1.0 1.0)
237
2
inline void AI_MD5_READ_TRIPLE(aiVector3D &vec, const char **sz, const char *bufferEnd, int linenumber) {
238
2
    AI_MD5_SKIP_SPACES(sz, bufferEnd, linenumber);
239
2
    if ('(' != **sz) {
240
2
        MD5Parser::ReportWarning("Unexpected token: ( was expected", linenumber);
241
2
        if (*sz == bufferEnd)
242
0
            return;
243
2
        ++*sz;
244
2
    }
245
2
    if (*sz == bufferEnd)
246
0
        return;
247
2
    ++*sz;
248
2
    AI_MD5_SKIP_SPACES(sz, bufferEnd, linenumber);
249
2
    *sz = fast_atoreal_move(*sz, vec.x);
250
2
    AI_MD5_SKIP_SPACES(sz, bufferEnd, linenumber);
251
2
    *sz = fast_atoreal_move(*sz, vec.y);
252
2
    AI_MD5_SKIP_SPACES(sz, bufferEnd, linenumber);
253
2
    *sz = fast_atoreal_move(*sz, vec.z);
254
2
    AI_MD5_SKIP_SPACES(sz, bufferEnd, linenumber);
255
2
    if (')' != **sz) {
256
0
        MD5Parser::ReportWarning("Unexpected token: ) was expected", linenumber);
257
0
    }
258
2
    if (*sz == bufferEnd)
259
0
        return;
260
2
    ++*sz;
261
2
}
262
263
// parse a string, enclosed in quotation marks or not
264
0
inline bool AI_MD5_PARSE_STRING(const char **sz, const char *bufferEnd, aiString &out, int linenumber) {
265
0
    bool bQuota = (**sz == '\"');
266
0
    const char *szStart = *sz;
267
0
    while (!IsSpaceOrNewLine(**sz)) {
268
0
        ++*sz;
269
0
        if (*sz == bufferEnd) break;
270
0
    }
271
0
    const char *szEnd = *sz;
272
0
    if (bQuota) {
273
0
        szStart++;
274
0
        if ('\"' != *(szEnd -= 1)) {
275
0
            MD5Parser::ReportWarning("Expected closing quotation marks in string", linenumber);
276
0
            ++*sz;
277
0
        }
278
0
    }
279
0
    out.length = (ai_uint32)(szEnd - szStart);
280
0
    if (out.length >= AI_MAXLEN)
281
0
        out.length = AI_MAXLEN - 1;
282
0
    ::memcpy(out.data, szStart, out.length);
283
0
    out.data[out.length] = '\0';
284
0
285
0
    return true;
286
0
}
287
288
// parse a string, enclosed in quotation marks
289
5.27k
inline void AI_MD5_PARSE_STRING_IN_QUOTATION(const char **sz, const char *bufferEnd, aiString &out) {
290
5.27k
    out.length = 0u;
291
40.7k
    while (('\"' != **sz && '\0' != **sz) && *sz != bufferEnd) {
292
35.5k
        ++*sz;
293
35.5k
    }
294
5.27k
    if ('\0' != **sz) {
295
121
        const char *szStart = ++(*sz);
296
297
1.13k
        while (('\"' != **sz && '\0' != **sz) && *sz != bufferEnd) {
298
1.01k
            ++*sz;
299
1.01k
        }
300
121
        if ('\0' != **sz) {
301
92
            const char *szEnd = *sz;
302
92
            ++*sz;
303
92
            out.length = (ai_uint32)(szEnd - szStart);
304
92
            if (out.length >= AI_MAXLEN)
305
0
                out.length = AI_MAXLEN - 1;
306
92
            ::memcpy(out.data, szStart, out.length);
307
92
        }
308
121
    }
309
5.27k
    out.data[out.length] = '\0';
310
5.27k
}
311
312
// ------------------------------------------------------------------------------------------------
313
// .MD5MESH parsing function
314
12
MD5MeshParser::MD5MeshParser(SectionArray &mSections) {
315
12
    ASSIMP_LOG_DEBUG("MD5MeshParser begin");
316
317
    // now parse all sections
318
8.00k
    for (SectionArray::const_iterator iter = mSections.begin(), iterEnd = mSections.end(); iter != iterEnd; ++iter) {
319
7.98k
        if ((*iter).mName == "numMeshes") {
320
0
            mMeshes.reserve(::strtoul10((*iter).mGlobalValue.c_str()));
321
7.98k
        } else if ((*iter).mName == "numJoints") {
322
132
            mJoints.reserve(::strtoul10((*iter).mGlobalValue.c_str()));
323
7.85k
        } else if ((*iter).mName == "joints") {
324
            // "origin" -1 ( -0.000000 0.016430 -0.006044 ) ( 0.707107 0.000000 0.707107 )
325
30
            for (const auto &elem : (*iter).mElements) {
326
2
                mJoints.emplace_back();
327
2
                BoneDesc &desc = mJoints.back();
328
329
2
                const char *sz = elem.szStart;
330
2
                AI_MD5_PARSE_STRING_IN_QUOTATION(&sz, elem.end, desc.mName);
331
332
2
                AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
333
334
                // negative values, at least -1, is allowed here
335
2
                desc.mParentIndex = (int)strtol10(sz, &sz);
336
337
2
                AI_MD5_READ_TRIPLE(desc.mPositionXYZ, &sz, elem.end, elem.iLineNumber);
338
2
                AI_MD5_READ_TRIPLE(desc.mRotationQuat, &sz, elem.end, elem.iLineNumber); // normalized quaternion, so w is not there
339
2
            }
340
7.82k
        } else if ((*iter).mName == "mesh") {
341
91
            mMeshes.emplace_back();
342
91
            MeshDesc &desc = mMeshes.back();
343
344
9.23k
            for (const auto &elem : (*iter).mElements) {
345
9.23k
                const char *sz = elem.szStart;
346
347
                // shader attribute
348
9.23k
                if (TokenMatch(sz, "shader", 6)) {
349
74
                    AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
350
74
                    AI_MD5_PARSE_STRING_IN_QUOTATION(&sz, elem.end, desc.mShader);
351
74
                }
352
                // numverts attribute
353
9.15k
                else if (TokenMatch(sz, "numverts", 8)) {
354
0
                    AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
355
0
                    desc.mVertices.resize(strtoul10(sz));
356
0
                }
357
                // numtris attribute
358
9.15k
                else if (TokenMatch(sz, "numtris", 7)) {
359
33
                    AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
360
33
                    desc.mFaces.resize(strtoul10(sz));
361
33
                }
362
                // numweights attribute
363
9.12k
                else if (TokenMatch(sz, "numweights", 10)) {
364
3
                    AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
365
3
                    desc.mWeights.resize(strtoul10(sz));
366
3
                }
367
                // vert attribute
368
                // "vert 0 ( 0.394531 0.513672 ) 0 1"
369
9.12k
                else if (TokenMatch(sz, "vert", 4)) {
370
66
                    AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
371
66
                    const unsigned int idx = ::strtoul10(sz, &sz);
372
66
                    AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
373
66
                    if (idx >= desc.mVertices.size())
374
48
                        desc.mVertices.resize(idx + 1);
375
376
66
                    VertexDesc &vert = desc.mVertices[idx];
377
66
                    if ('(' != *sz++)
378
66
                        MD5Parser::ReportWarning("Unexpected token: ( was expected", elem.iLineNumber);
379
66
                    AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
380
66
                    sz = fast_atoreal_move(sz, vert.mUV.x);
381
66
                    AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
382
66
                    sz = fast_atoreal_move(sz, vert.mUV.y);
383
66
                    AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
384
66
                    if (')' != *sz++)
385
66
                        MD5Parser::ReportWarning("Unexpected token: ) was expected", elem.iLineNumber);
386
66
                    AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
387
66
                    vert.mFirstWeight = ::strtoul10(sz, &sz);
388
66
                    AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
389
66
                    vert.mNumWeights = ::strtoul10(sz, &sz);
390
66
                }
391
                // tri attribute
392
                // "tri 0 15 13 12"
393
9.05k
                else if (TokenMatch(sz, "tri", 3)) {
394
171
                    AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
395
171
                    const unsigned int idx = strtoul10(sz, &sz);
396
171
                    if (idx >= desc.mFaces.size()) {
397
50
                        desc.mFaces.resize(idx + 1);
398
50
          }
399
400
171
                    aiFace &face = desc.mFaces[idx];
401
171
                    if (face.mNumIndices != 3) {
402
65
            delete [] face.mIndices;
403
65
            face.mIndices = new unsigned int[face.mNumIndices = 3];
404
65
          }
405
684
                    for (unsigned int i = 0; i < 3; ++i) {
406
513
                        AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
407
513
                        face.mIndices[i] = strtoul10(sz, &sz);
408
513
                    }
409
171
                }
410
                // weight attribute
411
                // "weight 362 5 0.500000 ( -3.553583 11.893474 9.719339 )"
412
8.88k
                else if (TokenMatch(sz, "weight", 6)) {
413
0
                    AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
414
0
                    const unsigned int idx = strtoul10(sz, &sz);
415
0
                    AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
416
0
                    if (idx >= desc.mWeights.size())
417
0
                        desc.mWeights.resize(idx + 1);
418
419
0
                    WeightDesc &weight = desc.mWeights[idx];
420
0
                    weight.mBone = strtoul10(sz, &sz);
421
0
                    AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
422
0
                    sz = fast_atoreal_move(sz, weight.mWeight);
423
0
                    AI_MD5_READ_TRIPLE(weight.vOffsetPosition, &sz, elem.end, elem.iLineNumber);
424
0
                }
425
9.23k
            }
426
91
        }
427
7.98k
    }
428
12
    ASSIMP_LOG_DEBUG("MD5MeshParser end");
429
12
}
430
431
// ------------------------------------------------------------------------------------------------
432
// .MD5ANIM parsing function
433
10
MD5AnimParser::MD5AnimParser(SectionArray &mSections) {
434
10
    ASSIMP_LOG_DEBUG("MD5AnimParser begin");
435
436
10
    fFrameRate = 24.0f;
437
10
    mNumAnimatedComponents = UINT_MAX;
438
7.39k
    for (SectionArray::const_iterator iter = mSections.begin(), iterEnd = mSections.end(); iter != iterEnd; ++iter) {
439
7.38k
        if ((*iter).mName == "hierarchy") {
440
            // "sheath" 0 63 6
441
5.20k
            for (const auto &elem : (*iter).mElements) {
442
5.20k
                mAnimatedBones.emplace_back();
443
5.20k
                AnimBoneDesc &desc = mAnimatedBones.back();
444
445
5.20k
                const char *sz = elem.szStart;
446
5.20k
                AI_MD5_PARSE_STRING_IN_QUOTATION(&sz, elem.end, desc.mName);
447
5.20k
                AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
448
449
                // parent index - negative values are allowed (at least -1)
450
5.20k
                desc.mParentIndex = ::strtol10(sz, &sz);
451
452
                // flags (highest is 2^6-1)
453
5.20k
                AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
454
5.20k
                if (63 < (desc.iFlags = ::strtoul10(sz, &sz))) {
455
13
                    MD5Parser::ReportWarning("Invalid flag combination in hierarchy section", elem.iLineNumber);
456
13
                }
457
5.20k
                AI_MD5_SKIP_SPACES(&  sz, elem.end, elem.iLineNumber);
458
459
                // index of the first animation keyframe component for this joint
460
5.20k
                desc.iFirstKeyIndex = ::strtoul10(sz, &sz);
461
5.20k
            }
462
7.38k
        } else if ((*iter).mName == "baseframe") {
463
            // ( -0.000000 0.016430 -0.006044 ) ( 0.707107 0.000242 0.707107 )
464
0
            for (const auto &elem : (*iter).mElements) {
465
0
                const char *sz = elem.szStart;
466
467
0
                mBaseFrames.emplace_back();
468
0
                BaseFrameDesc &desc = mBaseFrames.back();
469
470
0
                AI_MD5_READ_TRIPLE(desc.vPositionXYZ, &sz, elem.end, elem.iLineNumber);
471
0
                AI_MD5_READ_TRIPLE(desc.vRotationQuat, &sz, elem.end, elem.iLineNumber);
472
0
            }
473
7.38k
        } else if ((*iter).mName == "frame") {
474
48
            if (!(*iter).mGlobalValue.length()) {
475
0
                MD5Parser::ReportWarning("A frame section must have a frame index", (*iter).iLineNumber);
476
0
                continue;
477
0
            }
478
479
48
            mFrames.emplace_back();
480
48
            FrameDesc &desc = mFrames.back();
481
48
            desc.iIndex = strtoul10((*iter).mGlobalValue.c_str());
482
483
            // we do already know how much storage we will presumably need
484
48
            if (UINT_MAX != mNumAnimatedComponents) {
485
46
                desc.mValues.reserve(mNumAnimatedComponents);
486
46
            }
487
488
            // now read all elements (continuous list of floats)
489
48
            for (const auto &elem : (*iter).mElements) {
490
0
                const char *sz = elem.szStart;
491
0
                while (SkipSpacesAndLineEnd(&sz, elem.end)) {
492
0
                    float f;
493
0
                    sz = fast_atoreal_move(sz, f);
494
0
                    desc.mValues.push_back(f);
495
0
                }
496
0
            }
497
7.33k
        } else if ((*iter).mName == "numFrames") {
498
0
            mFrames.reserve(strtoul10((*iter).mGlobalValue.c_str()));
499
7.33k
        } else if ((*iter).mName == "numJoints") {
500
80
            const unsigned int num = strtoul10((*iter).mGlobalValue.c_str());
501
80
            mAnimatedBones.reserve(num);
502
503
            // try to guess the number of animated components if that element is not given
504
80
            if (UINT_MAX == mNumAnimatedComponents) {
505
1
                mNumAnimatedComponents = num * 6;
506
1
            }
507
7.25k
        } else if ((*iter).mName == "numAnimatedComponents") {
508
6
            mAnimatedBones.reserve(strtoul10((*iter).mGlobalValue.c_str()));
509
7.25k
        } else if ((*iter).mName == "frameRate") {
510
0
            fast_atoreal_move((*iter).mGlobalValue.c_str(), fFrameRate);
511
0
        }
512
7.38k
    }
513
10
    ASSIMP_LOG_DEBUG("MD5AnimParser end");
514
10
}
515
516
// ------------------------------------------------------------------------------------------------
517
// .MD5CAMERA parsing function
518
0
MD5CameraParser::MD5CameraParser(SectionArray &mSections) {
519
0
    ASSIMP_LOG_DEBUG("MD5CameraParser begin");
520
0
    fFrameRate = 24.0f;
521
522
0
    for (SectionArray::const_iterator iter = mSections.begin(), iterEnd = mSections.end(); iter != iterEnd; ++iter) {
523
0
        if ((*iter).mName == "numFrames") {
524
0
            frames.reserve(strtoul10((*iter).mGlobalValue.c_str()));
525
0
        } else if ((*iter).mName == "frameRate") {
526
0
            fFrameRate = fast_atof((*iter).mGlobalValue.c_str());
527
0
        } else if ((*iter).mName == "numCuts") {
528
0
            cuts.reserve(strtoul10((*iter).mGlobalValue.c_str()));
529
0
        } else if ((*iter).mName == "cuts") {
530
0
            for (const auto &elem : (*iter).mElements) {
531
0
                cuts.push_back(strtoul10(elem.szStart) + 1);
532
0
            }
533
0
        } else if ((*iter).mName == "camera") {
534
0
            for (const auto &elem : (*iter).mElements) {
535
0
                const char *sz = elem.szStart;
536
537
0
                frames.emplace_back();
538
0
                CameraAnimFrameDesc &cur = frames.back();
539
0
                AI_MD5_READ_TRIPLE(cur.vPositionXYZ, &sz, elem.end, elem.iLineNumber);
540
0
                AI_MD5_READ_TRIPLE(cur.vRotationQuat, &sz, elem.end, elem.iLineNumber);
541
0
                AI_MD5_SKIP_SPACES(&sz, elem.end, elem.iLineNumber);
542
0
                cur.fFOV = fast_atof(sz);
543
0
            }
544
0
        }
545
0
    }
546
    ASSIMP_LOG_DEBUG("MD5CameraParser end");
547
0
}