Coverage Report

Created: 2026-04-01 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/assimp/code/Common/BaseImporter.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  BaseImporter.cpp
43
 *  @brief Implementation of BaseImporter
44
 */
45
46
#include "FileSystemFilter.h"
47
#include "Importer.h"
48
#include <assimp/BaseImporter.h>
49
#include <assimp/ByteSwapper.h>
50
#include <assimp/ParsingUtils.h>
51
#include <assimp/importerdesc.h>
52
#include <assimp/postprocess.h>
53
#include <assimp/scene.h>
54
#include <assimp/Importer.hpp>
55
56
#include <cctype>
57
#include <ios>
58
#include <list>
59
#include <memory>
60
#include <sstream>
61
62
namespace {
63
// Checks whether the passed string is a gcs version.
64
106
bool IsGcsVersion(const std::string &s) {
65
106
    if (s.empty()) return false;
66
106
    return std::all_of(s.cbegin(), s.cend(), [](const char c) {
67
        // gcs only permits numeric characters.
68
106
        return std::isdigit(static_cast<int>(c));
69
106
    });
70
106
}
71
72
// Removes a possible version hash from a filename, as found for example in
73
// gcs uris (e.g. `gs://bucket/model.glb#1234`), see also
74
// https://github.com/GoogleCloudPlatform/gsutil/blob/c80f329bc3c4011236c78ce8910988773b2606cb/gslib/storage_url.py#L39.
75
32.5k
std::string StripVersionHash(const std::string &filename) {
76
32.5k
    const std::string::size_type pos = filename.find_last_of('#');
77
    // Only strip if the hash is behind a possible file extension and the part
78
    // behind the hash is a version string.
79
32.5k
    if (pos != std::string::npos && pos > filename.find_last_of('.') &&
80
106
        IsGcsVersion(filename.substr(pos + 1))) {
81
0
        return filename.substr(0, pos);
82
0
    }
83
32.5k
    return filename;
84
32.5k
}
85
}  // namespace
86
87
using namespace Assimp;
88
89
// ------------------------------------------------------------------------------------------------
90
// Constructor to be privately used by Importer
91
BaseImporter::BaseImporter() AI_NO_EXCEPT
92
29.9k
        : m_progress() {
93
    // empty
94
29.9k
}
95
96
290
void BaseImporter::UpdateImporterScale(Importer *pImp) {
97
290
    ai_assert(pImp != nullptr);
98
290
    ai_assert(importerScale != 0.0);
99
290
    ai_assert(fileScale != 0.0);
100
101
290
    double activeScale = importerScale * fileScale;
102
103
    // Set active scaling
104
290
    pImp->SetPropertyFloat(AI_CONFIG_APP_SCALE_KEY, static_cast<float>(activeScale));
105
106
290
    ASSIMP_LOG_DEBUG("UpdateImporterScale scale set: ", activeScale);
107
290
}
108
109
// ------------------------------------------------------------------------------------------------
110
// Imports the given file and returns the imported data.
111
582
aiScene *BaseImporter::ReadFile(Importer *pImp, const std::string &pFile, IOSystem *pIOHandler) {
112
113
582
    m_progress = pImp->GetProgressHandler();
114
582
    if (nullptr == m_progress) {
115
0
        return nullptr;
116
0
    }
117
118
582
    ai_assert(m_progress);
119
120
    // Gather configuration properties for this run
121
582
    SetupProperties(pImp);
122
123
    // Construct a file system filter to improve our success ratio at reading external files
124
582
    FileSystemFilter filter(pFile, pIOHandler);
125
126
    // create a scene object to hold the data
127
582
    std::unique_ptr<aiScene> sc(new aiScene());
128
129
    // dispatch importing
130
582
    try {
131
582
        InternReadFile(pFile, sc.get(), &filter);
132
133
        // Calculate import scale hook - required because pImp not available anywhere else
134
        // passes scale into ScaleProcess
135
582
        UpdateImporterScale(pImp);
136
137
582
    } catch( const std::exception &err ) {
138
        // extract error description
139
292
        m_ErrorText = err.what();
140
292
        ASSIMP_LOG_ERROR(err.what());
141
292
        m_Exception = std::current_exception();
142
292
        return nullptr;
143
292
    }
144
145
    // return what we gathered from the import.
146
290
    return sc.release();
147
582
}
148
149
// ------------------------------------------------------------------------------------------------
150
202
void BaseImporter::SetupProperties(const Importer *) {
151
    // the default implementation does nothing
152
202
}
153
154
// ------------------------------------------------------------------------------------------------
155
30.4k
void BaseImporter::GetExtensionList(std::set<std::string> &extensions) {
156
30.4k
    const aiImporterDesc *desc = GetInfo();
157
30.4k
    ai_assert(desc != nullptr);
158
159
30.4k
    const char *ext = desc->mFileExtensions;
160
30.4k
    ai_assert(ext != nullptr);
161
162
30.4k
    const char *last = ext;
163
212k
    do {
164
212k
        if (!*ext || *ext == ' ') {
165
47.5k
            extensions.insert(std::string(last, ext - last));
166
47.5k
            ai_assert(ext - last > 0);
167
47.5k
            last = ext;
168
64.6k
            while (*last == ' ') {
169
17.1k
                ++last;
170
17.1k
            }
171
47.5k
        }
172
212k
    } while (*ext++);
173
30.4k
}
174
175
// ------------------------------------------------------------------------------------------------
176
/*static*/ bool BaseImporter::SearchFileHeaderForToken(IOSystem *pIOHandler,
177
        const std::string &pFile,
178
        const char **tokens,
179
        std::size_t numTokens,
180
        unsigned int searchBytes /* = 200 */,
181
        bool tokensSol /* false */,
182
6.37k
        bool noGraphBeforeTokens /* false */) {
183
6.37k
    ai_assert(nullptr != tokens);
184
6.37k
    ai_assert(0 != numTokens);
185
6.37k
    ai_assert(0 != searchBytes);
186
187
6.37k
    if (nullptr == pIOHandler) {
188
0
        return false;
189
0
    }
190
191
6.37k
    std::unique_ptr<IOStream> pStream(pIOHandler->Open(pFile));
192
6.37k
    if (pStream) {
193
        // read 200 characters from the file
194
6.37k
        std::unique_ptr<char[]> _buffer(new char[searchBytes + 1 /* for the '\0' */]);
195
6.37k
        char *buffer(_buffer.get());
196
6.37k
        const size_t read(pStream->Read(buffer, 1, searchBytes));
197
6.37k
        if (0 == read) {
198
0
            return false;
199
0
        }
200
201
1.13M
        for (size_t i = 0; i < read; ++i) {
202
1.13M
            buffer[i] = static_cast<char>(::tolower((unsigned char)buffer[i]));
203
1.13M
        }
204
205
        // It is not a proper handling of unicode files here ...
206
        // ehm ... but it works in most cases.
207
6.37k
        char *cur = buffer, *cur2 = buffer, *end = &buffer[read];
208
1.13M
        while (cur != end) {
209
1.13M
            if (*cur) {
210
968k
                *cur2++ = *cur;
211
968k
            }
212
1.13M
            ++cur;
213
1.13M
        }
214
6.37k
        *cur2 = '\0';
215
216
6.37k
        std::string token;
217
18.7k
        for (unsigned int i = 0; i < numTokens; ++i) {
218
12.6k
            ai_assert(nullptr != tokens[i]);
219
12.6k
            const size_t len(strlen(tokens[i]));
220
12.6k
            token.clear();
221
12.6k
            const char *ptr(tokens[i]);
222
88.3k
            for (size_t tokIdx = 0; tokIdx < len; ++tokIdx) {
223
75.7k
                token.push_back(static_cast<char>(tolower(static_cast<unsigned char>(*ptr))));
224
75.7k
                ++ptr;
225
75.7k
            }
226
12.6k
            const char *r = strstr(buffer, token.c_str());
227
12.6k
            if (!r) {
228
12.2k
                continue;
229
12.2k
            }
230
            // We need to make sure that we didn't accidentally identify the end of another token as our token,
231
            // e.g. in a previous version the "gltf " present in some gltf files was detected as "f ", or a
232
            // Blender-exported glb file containing "Khronos glTF Blender I/O " was detected as "o "
233
358
            if (noGraphBeforeTokens && (r != buffer && isgraph(static_cast<unsigned char>(r[-1])))) {
234
89
                continue;
235
89
            }
236
            // We got a match, either we don't care where it is, or it happens to
237
            // be in the beginning of the file / line
238
269
            if (!tokensSol || r == buffer || r[-1] == '\r' || r[-1] == '\n') {
239
269
                ASSIMP_LOG_DEBUG("Found positive match for header keyword: ", tokens[i]);
240
269
                return true;
241
269
            }
242
269
        }
243
6.37k
    }
244
245
6.10k
    return false;
246
6.37k
}
247
248
// ------------------------------------------------------------------------------------------------
249
// Simple check for file extension
250
/*static*/ bool BaseImporter::SimpleExtensionCheck(const std::string &pFile,
251
        const char *ext0,
252
        const char *ext1,
253
        const char *ext2,
254
1.75k
        const char *ext3) {
255
1.75k
    std::set<std::string> extensions;
256
7.02k
    for (const char* ext : {ext0, ext1, ext2, ext3}) {
257
7.02k
        if (ext == nullptr) continue;
258
2.80k
        extensions.emplace(ext);
259
2.80k
    }
260
1.75k
    return HasExtension(pFile, extensions);
261
1.75k
}
262
263
// ------------------------------------------------------------------------------------------------
264
// Check for file extension
265
32.1k
/*static*/ bool BaseImporter::HasExtension(const std::string &pFile, const std::set<std::string> &extensions) {
266
32.1k
    const std::string file = StripVersionHash(pFile);
267
    // CAUTION: Do not just search for the extension!
268
    // GetExtension() returns the part after the *last* dot, but some extensions
269
    // have dots inside them, e.g. ogre.mesh.xml. Compare the entire end of the
270
    // string.
271
50.3k
    for (const std::string& ext : extensions) {
272
        // Yay for C++<20 not having std::string::ends_with()
273
50.3k
        const std::string dotExt = "." + ext;
274
50.3k
        if (dotExt.length() > file.length()) continue;
275
        // Possible optimization: Fetch the lowercase filename!
276
50.3k
        if (0 == ASSIMP_stricmp(file.c_str() + file.length() - dotExt.length(), dotExt.c_str())) {
277
62
            return true;
278
62
        }
279
50.3k
    }
280
32.1k
    return false;
281
32.1k
}
282
283
// ------------------------------------------------------------------------------------------------
284
// Get file extension from path
285
398
std::string BaseImporter::GetExtension(const std::string &pFile) {
286
398
    const std::string file = StripVersionHash(pFile);
287
398
    std::string::size_type pos = file.find_last_of('.');
288
289
    // no file extension at all
290
398
    if (pos == std::string::npos) {
291
8
        return std::string();
292
8
    }
293
294
    // thanks to Andy Maloney for the hint
295
390
    std::string ret = file.substr(pos + 1);
296
390
    ret = ai_tolower(ret);
297
298
390
    return ret;
299
398
}
300
301
302
// ------------------------------------------------------------------------------------------------
303
// Check for magic bytes at the beginning of the file.
304
/* static */ bool BaseImporter::CheckMagicToken(IOSystem *pIOHandler, const std::string &pFile,
305
4.41k
        const void *_magic, std::size_t num, unsigned int offset, unsigned int size) {
306
4.41k
    ai_assert(size <= 16);
307
4.41k
    ai_assert(_magic);
308
309
4.41k
    if (!pIOHandler) {
310
0
        return false;
311
0
    }
312
4.41k
    const char *magic = reinterpret_cast<const char *>(_magic);
313
4.41k
    std::unique_ptr<IOStream> pStream(pIOHandler->Open(pFile));
314
4.41k
    if (pStream) {
315
316
        // skip to offset
317
4.41k
        pStream->Seek(offset, aiOrigin_SET);
318
319
        // read 'size' characters from the file
320
4.41k
        union {
321
4.41k
            char data[16];
322
4.41k
            uint16_t data_u16[8];
323
4.41k
            uint32_t data_u32[4];
324
4.41k
        };
325
4.41k
        if (size != pStream->Read(data, 1, size)) {
326
0
            return false;
327
0
        }
328
329
14.1k
        for (unsigned int i = 0; i < num; ++i) {
330
            // also check against big endian versions of tokens with size 2,4
331
            // that's just for convenience, the chance that we cause conflicts
332
            // is quite low and it can save some lines and prevent nasty bugs
333
9.96k
            if (2 == size) {
334
957
                uint16_t magic_u16;
335
957
                memcpy(&magic_u16, magic, 2);
336
957
                if (data_u16[0] == magic_u16 || data_u16[0] == ByteSwap::Swapped(magic_u16)) {
337
9
                    return true;
338
9
                }
339
9.01k
            } else if (4 == size) {
340
9.01k
                uint32_t magic_u32;
341
9.01k
                memcpy(&magic_u32, magic, 4);
342
9.01k
                if (data_u32[0] == magic_u32 || data_u32[0] == ByteSwap::Swapped(magic_u32)) {
343
202
                    return true;
344
202
                }
345
9.01k
            } else {
346
                // any length ... just compare
347
0
                if (!memcmp(magic, data, size)) {
348
0
                    return true;
349
0
                }
350
0
            }
351
9.75k
            magic += size;
352
9.75k
        }
353
4.41k
    }
354
4.20k
    return false;
355
4.41k
}
356
357
#include "utf8.h"
358
359
// ------------------------------------------------------------------------------------------------
360
// Convert to UTF8 data
361
8.00k
void BaseImporter::ConvertToUTF8(std::vector<char> &data) {
362
    //ConversionResult result;
363
8.00k
    if (data.size() < 8) {
364
0
        throw DeadlyImportError("File is too small");
365
0
    }
366
367
    // UTF 8 with BOM
368
8.00k
    if ((uint8_t)data[0] == 0xEF && (uint8_t)data[1] == 0xBB && (uint8_t)data[2] == 0xBF) {
369
0
        ASSIMP_LOG_DEBUG("Found UTF-8 BOM ...");
370
371
0
        std::copy(data.begin() + 3, data.end(), data.begin());
372
0
        data.resize(data.size() - 3);
373
0
        return;
374
0
    }
375
376
    // UTF 32 BE with BOM
377
8.00k
    if (*((uint32_t *)&data.front()) == 0xFFFE0000) {
378
0
        if (data.size() % sizeof(uint32_t) != 0) {
379
0
            throw DeadlyImportError("Not valid UTF-32 BE");
380
0
        }
381
382
        // swap the endianness ..
383
0
        for (uint32_t *p = (uint32_t *)&data.front(), *end = (uint32_t *)&data.back(); p <= end; ++p) {
384
0
            AI_SWAP4P(p);
385
0
        }
386
0
    }
387
388
    // UTF 32 LE with BOM
389
8.00k
    if (*((uint32_t *)&data.front()) == 0x0000FFFE) {
390
0
        if (data.size() % sizeof(uint32_t) != 0) {
391
0
            throw DeadlyImportError("Not valid UTF-32 LE");
392
0
        }
393
0
        ASSIMP_LOG_DEBUG("Found UTF-32 BOM ...");
394
395
0
        std::vector<char> output;
396
0
        auto *ptr = (uint32_t *)&data[0];
397
0
        uint32_t *end = ptr + (data.size() / sizeof(uint32_t)) + 1;
398
0
        utf8::utf32to8(ptr, end, back_inserter(output));
399
0
        return;
400
0
    }
401
402
    // UTF 16 BE with BOM
403
8.00k
    if (*((uint16_t *)&data.front()) == 0xFFFE) {
404
        // Check to ensure no overflow can happen
405
1
        if (data.size() % sizeof(uint16_t) != 0) {
406
0
            throw DeadlyImportError("Not valid UTF-16 BE");
407
0
        }
408
        // swap the endianness ..
409
8.92k
        for (uint16_t *p = (uint16_t *)&data.front(), *end = (uint16_t *)&data.back(); p <= end; ++p) {
410
8.92k
            ByteSwap::Swap2(p);
411
8.92k
        }
412
1
    }
413
414
    // UTF 16 LE with BOM
415
8.00k
    if (*((uint16_t *)&data.front()) == 0xFEFF) {
416
1
        if (data.size() % sizeof(uint16_t) != 0) {
417
0
            throw DeadlyImportError("Not valid UTF-16 LE");
418
0
        }
419
1
        ASSIMP_LOG_DEBUG("Found UTF-16 BOM ...");
420
421
1
        std::vector<unsigned char> output;
422
1
        utf8::utf16to8(data.begin(), data.end(), back_inserter(output));
423
1
        return;
424
1
    }
425
8.00k
}
426
427
// ------------------------------------------------------------------------------------------------
428
// Convert to UTF8 data to ISO-8859-1
429
0
void BaseImporter::ConvertUTF8toISO8859_1(std::string &data) {
430
0
    size_t size = data.size();
431
0
    size_t i = 0, j = 0;
432
433
0
    while (i < size) {
434
0
        if ((unsigned char)data[i] < (size_t)0x80) {
435
0
            data[j] = data[i];
436
0
        } else if (i < size - 1) {
437
0
            if ((unsigned char)data[i] == 0xC2) {
438
0
                data[j] = data[++i];
439
0
            } else if ((unsigned char)data[i] == 0xC3) {
440
0
                data[j] = ((unsigned char)data[++i] + 0x40);
441
0
            } else {
442
0
                std::stringstream stream;
443
0
                stream << "UTF8 code " << std::hex << data[i] << data[i + 1] << " can not be converted into ISA-8859-1.";
444
0
                ASSIMP_LOG_ERROR(stream.str());
445
446
0
                data[j++] = data[i++];
447
0
                data[j] = data[i];
448
0
            }
449
0
        } else {
450
0
            ASSIMP_LOG_ERROR("UTF8 code but only one character remaining");
451
452
0
            data[j] = data[i];
453
0
        }
454
455
0
        i++;
456
0
        j++;
457
0
    }
458
459
0
    data.resize(j);
460
0
}
461
462
// ------------------------------------------------------------------------------------------------
463
void BaseImporter::TextFileToBuffer(IOStream *stream,
464
        std::vector<char> &data,
465
7.98k
        TextFileMode mode) {
466
7.98k
    ai_assert(nullptr != stream);
467
468
7.98k
    const size_t fileSize = stream->FileSize();
469
7.98k
    if (mode == FORBID_EMPTY) {
470
5.37k
        if (!fileSize) {
471
0
            throw DeadlyImportError("File is empty");
472
0
        }
473
5.37k
    }
474
475
7.98k
    data.reserve(fileSize + 1);
476
7.98k
    data.resize(fileSize);
477
7.98k
    if (fileSize > 0) {
478
7.98k
        if (fileSize != stream->Read(&data[0], 1, fileSize)) {
479
0
            throw DeadlyImportError("File read error");
480
0
        }
481
482
7.98k
        ConvertToUTF8(data);
483
7.98k
    }
484
485
    // append a binary zero to simplify string parsing
486
7.98k
    data.push_back(0);
487
7.98k
}
488
489
// ------------------------------------------------------------------------------------------------
490
namespace Assimp {
491
// Represents an import request
492
struct LoadRequest {
493
    LoadRequest(const std::string &_file, unsigned int _flags, const BatchLoader::PropertyMap *_map, unsigned int _id) :
494
1.51k
            file(_file),
495
1.51k
            flags(_flags),
496
1.51k
            refCnt(1),
497
1.51k
            scene(nullptr),
498
1.51k
            loaded(false),
499
1.51k
            id(_id) {
500
1.51k
        if (_map) {
501
704
            map = *_map;
502
704
        }
503
1.51k
    }
504
505
0
    bool operator==(const std::string &f) const {
506
0
        return file == f;
507
0
    }
508
509
    const std::string file;
510
    unsigned int flags;
511
    unsigned int refCnt;
512
    aiScene *scene;
513
    bool loaded;
514
    BatchLoader::PropertyMap map;
515
    unsigned int id;
516
};
517
} // namespace Assimp
518
519
// ------------------------------------------------------------------------------------------------
520
// BatchLoader::pimpl data structure
521
struct Assimp::BatchData {
522
    BatchData(IOSystem *pIO, bool validate) :
523
155
            pIOSystem(pIO), pImporter(nullptr), next_id(0xffff), validate(validate) {
524
155
        ai_assert(nullptr != pIO);
525
526
155
        pImporter = new Importer();
527
155
        pImporter->SetIOHandler(pIO);
528
155
    }
529
530
155
    ~BatchData() {
531
155
        pImporter->SetIOHandler(nullptr); /* get pointer back into our possession */
532
155
        delete pImporter;
533
155
    }
534
535
    // IO system to be used for all imports
536
    IOSystem *pIOSystem;
537
538
    // Importer used to load all meshes
539
    Importer *pImporter;
540
541
    // List of all imports
542
    std::list<LoadRequest> requests;
543
544
    // Base path
545
    std::string pathBase;
546
547
    // Id for next item
548
    unsigned int next_id;
549
550
    // Validation enabled state
551
    bool validate;
552
};
553
554
typedef std::list<LoadRequest>::iterator LoadReqIt;
555
556
// ------------------------------------------------------------------------------------------------
557
155
BatchLoader::BatchLoader(IOSystem *pIO, bool validate) {
558
155
    ai_assert(nullptr != pIO);
559
560
155
    m_data = new BatchData(pIO, validate);
561
155
}
562
563
// ------------------------------------------------------------------------------------------------
564
155
BatchLoader::~BatchLoader() {
565
    // delete all scenes what have not been polled by the user
566
1.23k
    for (LoadReqIt it = m_data->requests.begin(); it != m_data->requests.end(); ++it) {
567
1.08k
        delete (*it).scene;
568
1.08k
    }
569
155
    delete m_data;
570
155
}
571
572
// ------------------------------------------------------------------------------------------------
573
0
void BatchLoader::setValidation(bool enabled) {
574
0
    m_data->validate = enabled;
575
0
}
576
577
// ------------------------------------------------------------------------------------------------
578
0
bool BatchLoader::getValidation() const {
579
0
    return m_data->validate;
580
0
}
581
582
// ------------------------------------------------------------------------------------------------
583
unsigned int BatchLoader::AddLoadRequest(const std::string &file,
584
17.6k
        unsigned int steps /*= 0*/, const PropertyMap *map /*= nullptr*/) {
585
17.6k
    ai_assert(!file.empty());
586
587
    // check whether we have this loading request already
588
113k
    for (LoadReqIt it = m_data->requests.begin(); it != m_data->requests.end(); ++it) {
589
        // Call IOSystem's path comparison function here
590
111k
        if (m_data->pIOSystem->ComparePaths((*it).file, file)) {
591
16.1k
            if (map) {
592
4.35k
                if (!((*it).map == *map)) {
593
5
                    continue;
594
5
                }
595
11.8k
            } else if (!(*it).map.empty()) {
596
0
                continue;
597
0
            }
598
599
16.1k
            (*it).refCnt++;
600
16.1k
            return (*it).id;
601
16.1k
        }
602
111k
    }
603
604
    // no, we don't have it. So add it to the queue ...
605
1.51k
    m_data->requests.emplace_back(file, steps, map, m_data->next_id);
606
1.51k
    return m_data->next_id++;
607
17.6k
}
608
609
// ------------------------------------------------------------------------------------------------
610
11.0k
aiScene *BatchLoader::GetImport(unsigned int which) {
611
26.4k
    for (LoadReqIt it = m_data->requests.begin(); it != m_data->requests.end(); ++it) {
612
26.4k
        if ((*it).id == which && (*it).loaded) {
613
11.0k
            aiScene *sc = (*it).scene;
614
11.0k
            if (!(--(*it).refCnt)) {
615
435
                m_data->requests.erase(it);
616
435
            }
617
11.0k
            return sc;
618
11.0k
        }
619
26.4k
    }
620
0
    return nullptr;
621
11.0k
}
622
623
// ------------------------------------------------------------------------------------------------
624
50
void BatchLoader::LoadAll() {
625
    // no threaded implementation for the moment
626
490
    for (LoadReqIt it = m_data->requests.begin(); it != m_data->requests.end(); ++it) {
627
        // force validation in debug builds
628
440
        unsigned int pp = (*it).flags;
629
440
        if (m_data->validate) {
630
0
            pp |= aiProcess_ValidateDataStructure;
631
0
        }
632
633
        // setup config properties if necessary
634
440
        ImporterPimpl *pimpl = m_data->pImporter->Pimpl();
635
440
        pimpl->mFloatProperties = (*it).map.floats;
636
440
        pimpl->mIntProperties = (*it).map.ints;
637
440
        pimpl->mStringProperties = (*it).map.strings;
638
440
        pimpl->mMatrixProperties = (*it).map.matrices;
639
640
440
        if (!DefaultLogger::isNullLogger()) {
641
0
            ASSIMP_LOG_INFO("%%% BEGIN EXTERNAL FILE %%%");
642
0
            ASSIMP_LOG_INFO("File: ", (*it).file);
643
0
        }
644
440
        m_data->pImporter->ReadFile((*it).file, pp);
645
440
        (*it).scene = m_data->pImporter->GetOrphanedScene();
646
440
        (*it).loaded = true;
647
648
        ASSIMP_LOG_INFO("%%% END EXTERNAL FILE %%%");
649
440
    }
650
50
}