Coverage Report

Created: 2026-10-28 07:46

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