Coverage Report

Created: 2025-08-28 06:38

/src/assimp/code/AssetLib/Ply/PlyParser.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
---------------------------------------------------------------------------
3
Open Asset Import Library (assimp)
4
---------------------------------------------------------------------------
5
6
Copyright (c) 2006-2025, 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 PLY parser class */
43
44
#ifndef ASSIMP_BUILD_NO_PLY_IMPORTER
45
46
#include "PlyLoader.h"
47
#include <assimp/ByteSwapper.h>
48
#include <assimp/fast_atof.h>
49
#include <assimp/DefaultLogger.hpp>
50
#include <unordered_set>
51
#include <utility>
52
53
namespace Assimp {
54
55
0
std::string to_string(EElementSemantic e) {
56
57
0
    switch (e) {
58
0
    case EEST_Vertex:
59
0
        return std::string{ "vertex" };
60
0
    case EEST_TriStrip:
61
0
        return std::string{ "tristrips" };
62
0
    case EEST_Edge:
63
0
        return std::string{ "edge" };
64
0
    case EEST_Material:
65
0
        return std::string{ "material" };
66
0
    case EEST_TextureFile:
67
0
        return std::string{ "TextureFile" };
68
0
    default:
69
0
        return std::string{ "invalid" };
70
0
    }
71
0
}
72
73
// ------------------------------------------------------------------------------------------------
74
0
PLY::EDataType PLY::Property::ParseDataType(std::vector<char> &buffer) {
75
0
    ai_assert(!buffer.empty());
76
77
0
    PLY::EDataType eOut = PLY::EDT_INVALID;
78
79
0
    if (PLY::DOM::TokenMatch(buffer, "char", 4) ||
80
0
            PLY::DOM::TokenMatch(buffer, "int8", 4)) {
81
0
        eOut = PLY::EDT_Char;
82
0
    } else if (PLY::DOM::TokenMatch(buffer, "uchar", 5) ||
83
0
               PLY::DOM::TokenMatch(buffer, "uint8", 5)) {
84
0
        eOut = PLY::EDT_UChar;
85
0
    } else if (PLY::DOM::TokenMatch(buffer, "short", 5) ||
86
0
               PLY::DOM::TokenMatch(buffer, "int16", 5)) {
87
0
        eOut = PLY::EDT_Short;
88
0
    } else if (PLY::DOM::TokenMatch(buffer, "ushort", 6) ||
89
0
               PLY::DOM::TokenMatch(buffer, "uint16", 6)) {
90
0
        eOut = PLY::EDT_UShort;
91
0
    } else if (PLY::DOM::TokenMatch(buffer, "int32", 5) || PLY::DOM::TokenMatch(buffer, "int", 3)) {
92
0
        eOut = PLY::EDT_Int;
93
0
    } else if (PLY::DOM::TokenMatch(buffer, "uint32", 6) || PLY::DOM::TokenMatch(buffer, "uint", 4)) {
94
0
        eOut = PLY::EDT_UInt;
95
0
    } else if (PLY::DOM::TokenMatch(buffer, "float", 5) || PLY::DOM::TokenMatch(buffer, "float32", 7)) {
96
0
        eOut = PLY::EDT_Float;
97
0
    } else if (PLY::DOM::TokenMatch(buffer, "double64", 8) || PLY::DOM::TokenMatch(buffer, "double", 6) ||
98
0
               PLY::DOM::TokenMatch(buffer, "float64", 7)) {
99
0
        eOut = PLY::EDT_Double;
100
0
    }
101
0
    if (PLY::EDT_INVALID == eOut) {
102
0
        ASSIMP_LOG_INFO("Found unknown data type in PLY file. This is OK");
103
0
    }
104
105
0
    return eOut;
106
0
}
107
108
// ------------------------------------------------------------------------------------------------
109
0
PLY::ESemantic PLY::Property::ParseSemantic(std::vector<char> &buffer) {
110
0
    ai_assert(!buffer.empty());
111
112
0
    PLY::ESemantic eOut = PLY::EST_INVALID;
113
0
    if (PLY::DOM::TokenMatch(buffer, "red", 3)) {
114
0
        eOut = PLY::EST_Red;
115
0
    } else if (PLY::DOM::TokenMatch(buffer, "green", 5)) {
116
0
        eOut = PLY::EST_Green;
117
0
    } else if (PLY::DOM::TokenMatch(buffer, "blue", 4)) {
118
0
        eOut = PLY::EST_Blue;
119
0
    } else if (PLY::DOM::TokenMatch(buffer, "alpha", 5)) {
120
0
        eOut = PLY::EST_Alpha;
121
0
    } else if (PLY::DOM::TokenMatch(buffer, "vertex_index", 12) || PLY::DOM::TokenMatch(buffer, "vertex_indices", 14)) {
122
0
        eOut = PLY::EST_VertexIndex;
123
0
    } else if (PLY::DOM::TokenMatch(buffer, "texcoord", 8)) // Manage uv coords on faces
124
0
    {
125
0
        eOut = PLY::EST_TextureCoordinates;
126
0
    } else if (PLY::DOM::TokenMatch(buffer, "material_index", 14)) {
127
0
        eOut = PLY::EST_MaterialIndex;
128
0
    } else if (PLY::DOM::TokenMatch(buffer, "ambient_red", 11)) {
129
0
        eOut = PLY::EST_AmbientRed;
130
0
    } else if (PLY::DOM::TokenMatch(buffer, "ambient_green", 13)) {
131
0
        eOut = PLY::EST_AmbientGreen;
132
0
    } else if (PLY::DOM::TokenMatch(buffer, "ambient_blue", 12)) {
133
0
        eOut = PLY::EST_AmbientBlue;
134
0
    } else if (PLY::DOM::TokenMatch(buffer, "ambient_alpha", 13)) {
135
0
        eOut = PLY::EST_AmbientAlpha;
136
0
    } else if (PLY::DOM::TokenMatch(buffer, "diffuse_red", 11)) {
137
0
        eOut = PLY::EST_DiffuseRed;
138
0
    } else if (PLY::DOM::TokenMatch(buffer, "diffuse_green", 13)) {
139
0
        eOut = PLY::EST_DiffuseGreen;
140
0
    } else if (PLY::DOM::TokenMatch(buffer, "diffuse_blue", 12)) {
141
0
        eOut = PLY::EST_DiffuseBlue;
142
0
    } else if (PLY::DOM::TokenMatch(buffer, "diffuse_alpha", 13)) {
143
0
        eOut = PLY::EST_DiffuseAlpha;
144
0
    } else if (PLY::DOM::TokenMatch(buffer, "specular_red", 12)) {
145
0
        eOut = PLY::EST_SpecularRed;
146
0
    } else if (PLY::DOM::TokenMatch(buffer, "specular_green", 14)) {
147
0
        eOut = PLY::EST_SpecularGreen;
148
0
    } else if (PLY::DOM::TokenMatch(buffer, "specular_blue", 13)) {
149
0
        eOut = PLY::EST_SpecularBlue;
150
0
    } else if (PLY::DOM::TokenMatch(buffer, "specular_alpha", 14)) {
151
0
        eOut = PLY::EST_SpecularAlpha;
152
0
    } else if (PLY::DOM::TokenMatch(buffer, "opacity", 7)) {
153
0
        eOut = PLY::EST_Opacity;
154
0
    } else if (PLY::DOM::TokenMatch(buffer, "specular_power", 14)) {
155
0
        eOut = PLY::EST_PhongPower;
156
0
    } else if (PLY::DOM::TokenMatch(buffer, "r", 1)) {
157
0
        eOut = PLY::EST_Red;
158
0
    } else if (PLY::DOM::TokenMatch(buffer, "g", 1)) {
159
0
        eOut = PLY::EST_Green;
160
0
    } else if (PLY::DOM::TokenMatch(buffer, "b", 1)) {
161
0
        eOut = PLY::EST_Blue;
162
0
    }
163
164
    // NOTE: Blender3D exports texture coordinates as s,t tuples
165
0
    else if (PLY::DOM::TokenMatch(buffer, "u", 1) || PLY::DOM::TokenMatch(buffer, "s", 1) || PLY::DOM::TokenMatch(buffer, "tx", 2) || PLY::DOM::TokenMatch(buffer, "texture_u", 9)) {
166
0
        eOut = PLY::EST_UTextureCoord;
167
0
    } else if (PLY::DOM::TokenMatch(buffer, "v", 1) || PLY::DOM::TokenMatch(buffer, "t", 1) || PLY::DOM::TokenMatch(buffer, "ty", 2) || PLY::DOM::TokenMatch(buffer, "texture_v", 9)) {
168
0
        eOut = PLY::EST_VTextureCoord;
169
0
    } else if (PLY::DOM::TokenMatch(buffer, "x", 1)) {
170
0
        eOut = PLY::EST_XCoord;
171
0
    } else if (PLY::DOM::TokenMatch(buffer, "y", 1)) {
172
0
        eOut = PLY::EST_YCoord;
173
0
    } else if (PLY::DOM::TokenMatch(buffer, "z", 1)) {
174
0
        eOut = PLY::EST_ZCoord;
175
0
    } else if (PLY::DOM::TokenMatch(buffer, "nx", 2)) {
176
0
        eOut = PLY::EST_XNormal;
177
0
    } else if (PLY::DOM::TokenMatch(buffer, "ny", 2)) {
178
0
        eOut = PLY::EST_YNormal;
179
0
    } else if (PLY::DOM::TokenMatch(buffer, "nz", 2)) {
180
0
        eOut = PLY::EST_ZNormal;
181
0
    } else {
182
0
        ASSIMP_LOG_INFO("Found unknown property semantic in file. This is ok");
183
0
        PLY::DOM::SkipLine(buffer);
184
0
    }
185
0
    return eOut;
186
0
}
187
188
// ------------------------------------------------------------------------------------------------
189
8
bool PLY::Property::ParseProperty(std::vector<char> &buffer, PLY::Property *pOut) {
190
8
    ai_assert(!buffer.empty());
191
192
    // Forms supported:
193
    // "property float x"
194
    // "property list uchar int vertex_index"
195
196
    // skip leading spaces
197
8
    if (!PLY::DOM::SkipSpaces(buffer)) {
198
0
        return false;
199
0
    }
200
201
    // skip the "property" string at the beginning
202
8
    if (!PLY::DOM::TokenMatch(buffer, "property", 8)) {
203
        // seems not to be a valid property entry
204
8
        return false;
205
8
    }
206
    // get next word
207
0
    if (!PLY::DOM::SkipSpaces(buffer)) {
208
0
        return false;
209
0
    }
210
0
    if (PLY::DOM::TokenMatch(buffer, "list", 4)) {
211
0
        pOut->bIsList = true;
212
213
        // seems to be a list.
214
0
        if (EDT_INVALID == (pOut->eFirstType = PLY::Property::ParseDataType(buffer))) {
215
            // unable to parse list size data type
216
0
            PLY::DOM::SkipLine(buffer);
217
0
            return false;
218
0
        }
219
0
        if (!PLY::DOM::SkipSpaces(buffer)) return false;
220
0
        if (EDT_INVALID == (pOut->eType = PLY::Property::ParseDataType(buffer))) {
221
            // unable to parse list data type
222
0
            PLY::DOM::SkipLine(buffer);
223
0
            return false;
224
0
        }
225
0
    } else {
226
0
        if (EDT_INVALID == (pOut->eType = PLY::Property::ParseDataType(buffer))) {
227
            // unable to parse data type. Skip the property
228
0
            PLY::DOM::SkipLine(buffer);
229
0
            return false;
230
0
        }
231
0
    }
232
233
0
    if (!PLY::DOM::SkipSpaces(buffer))
234
0
        return false;
235
236
0
    pOut->Semantic = PLY::Property::ParseSemantic(buffer);
237
238
0
    if (PLY::EST_INVALID == pOut->Semantic) {
239
0
        ASSIMP_LOG_INFO("Found unknown semantic in PLY file. This is OK");
240
0
        std::string(&buffer[0], &buffer[0] + strlen(&buffer[0]));
241
0
    }
242
243
0
    PLY::DOM::SkipSpacesAndLineEnd(buffer);
244
0
    return true;
245
0
}
246
247
// ------------------------------------------------------------------------------------------------
248
8
PLY::EElementSemantic PLY::Element::ParseSemantic(std::vector<char> &buffer) {
249
8
    ai_assert(!buffer.empty());
250
251
8
    PLY::EElementSemantic eOut = PLY::EEST_INVALID;
252
8
    if (PLY::DOM::TokenMatch(buffer, "vertex", 6)) {
253
0
        eOut = PLY::EEST_Vertex;
254
8
    } else if (PLY::DOM::TokenMatch(buffer, "face", 4)) {
255
0
        eOut = PLY::EEST_Face;
256
8
    } else if (PLY::DOM::TokenMatch(buffer, "tristrips", 9)) {
257
0
        eOut = PLY::EEST_TriStrip;
258
0
    }
259
#if 0
260
  // TODO: maybe implement this?
261
  else if (PLY::DOM::TokenMatch(buffer,"range_grid",10))
262
  {
263
    eOut = PLY::EEST_Face;
264
  }
265
#endif
266
8
    else if (PLY::DOM::TokenMatch(buffer, "edge", 4)) {
267
0
        eOut = PLY::EEST_Edge;
268
8
    } else if (PLY::DOM::TokenMatch(buffer, "material", 8)) {
269
0
        eOut = PLY::EEST_Material;
270
8
    } else if (PLY::DOM::TokenMatch(buffer, "TextureFile", 11)) {
271
0
        eOut = PLY::EEST_TextureFile;
272
0
    }
273
274
8
    return eOut;
275
8
}
276
277
// ------------------------------------------------------------------------------------------------
278
771
bool PLY::Element::ParseElement(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer, PLY::Element *pOut) {
279
771
    ai_assert(nullptr != pOut);
280
    // Example format: "element vertex 8"
281
282
    // skip leading spaces
283
771
    if (!PLY::DOM::SkipSpaces(buffer)) {
284
326
        return false;
285
326
    }
286
287
    // skip the "element" string at the beginning
288
445
    if (!PLY::DOM::TokenMatch(buffer, "element", 7) && !PLY::DOM::TokenMatch(buffer, "comment", 7)) {
289
        // seems not to be a valid property entry
290
437
        return false;
291
437
    }
292
    // get next word
293
8
    if (!PLY::DOM::SkipSpaces(buffer))
294
0
        return false;
295
296
    // parse the semantic of the element
297
8
    pOut->eSemantic = PLY::Element::ParseSemantic(buffer);
298
8
    if (PLY::EEST_INVALID == pOut->eSemantic) {
299
        // if the exact semantic can't be determined, just store
300
        // the original string identifier
301
8
        pOut->szName = std::string(&buffer[0], &buffer[0] + strlen(&buffer[0]));
302
8
        auto pos = pOut->szName.find_last_of(' ');
303
8
        if (pos != std::string::npos) {
304
8
            pOut->szName.erase(pos, pOut->szName.size());
305
8
        }
306
8
    }
307
308
8
    if (!PLY::DOM::SkipSpaces(buffer))
309
0
        return false;
310
311
8
    if (PLY::EEST_TextureFile == pOut->eSemantic) {
312
0
        char *endPos = &buffer[0] + (strlen(&buffer[0]) - 1);
313
0
        pOut->szName = std::string(&buffer[0], endPos);
314
315
        // go to the next line
316
0
        PLY::DOM::SkipSpacesAndLineEnd(buffer);
317
318
0
        return true;
319
0
    }
320
321
    // parse the number of occurrences of this element
322
8
    const char *pCur = (char *)&buffer[0];
323
8
    pOut->NumOccur = strtoul10(pCur, &pCur);
324
325
    // go to the next line
326
8
    PLY::DOM::SkipSpacesAndLineEnd(buffer);
327
328
    // now parse all properties of the element
329
8
    while (true) {
330
8
        streamBuffer.getNextLine(buffer);
331
8
        pCur = (char *)&buffer[0];
332
333
        // skip all comments and go to next line
334
8
        if (PLY::DOM::SkipComments(buffer)) continue;
335
336
8
        PLY::Property prop;
337
8
        if (!PLY::Property::ParseProperty(buffer, &prop))
338
8
            break;
339
340
0
        pOut->alProperties.push_back(prop);
341
0
    }
342
343
8
    return true;
344
8
}
345
346
1.57k
bool PLY::DOM::SkipSpaces(std::vector<char> &buffer) {
347
1.57k
    const char *pCur = buffer.empty() ? nullptr : (char *)&buffer[0];
348
1.57k
    const char *end = pCur + buffer.size();
349
1.57k
    bool ret = false;
350
1.57k
    if (pCur) {
351
1.57k
        const char *szCur = pCur;
352
1.57k
        ret = Assimp::SkipSpaces(pCur, &pCur, end);
353
354
1.57k
        uintptr_t iDiff = (uintptr_t)pCur - (uintptr_t)szCur;
355
1.57k
        buffer.erase(buffer.begin(), buffer.begin() + iDiff);
356
1.57k
        return ret;
357
1.57k
    }
358
359
0
    return ret;
360
1.57k
}
361
362
0
bool PLY::DOM::SkipLine(std::vector<char> &buffer) {
363
0
    const char *pCur = buffer.empty() ? nullptr : (char *)&buffer[0];
364
0
    const char *end = pCur + buffer.size();
365
0
    bool ret = false;
366
0
    if (pCur) {
367
0
        const char *szCur = pCur;
368
0
        ret = Assimp::SkipLine(pCur, &pCur, end);
369
370
0
        uintptr_t iDiff = (uintptr_t)pCur - (uintptr_t)szCur;
371
0
        buffer.erase(buffer.begin(), buffer.begin() + iDiff);
372
0
        return ret;
373
0
    }
374
375
0
    return ret;
376
0
}
377
378
2.15k
bool PLY::DOM::TokenMatch(std::vector<char> &buffer, const char *token, unsigned int len) {
379
2.15k
    const char *pCur = buffer.empty() ? nullptr : (char *)&buffer[0];
380
2.15k
    bool ret = false;
381
2.15k
    if (pCur) {
382
2.15k
        const char *szCur = pCur;
383
2.15k
        ret = Assimp::TokenMatch(pCur, token, len);
384
385
2.15k
        uintptr_t iDiff = (uintptr_t)pCur - (uintptr_t)szCur;
386
2.15k
        buffer.erase(buffer.begin(), buffer.begin() + iDiff);
387
2.15k
        return ret;
388
2.15k
    }
389
390
0
    return ret;
391
2.15k
}
392
393
8
bool PLY::DOM::SkipSpacesAndLineEnd(std::vector<char> &buffer) {
394
8
    const char *pCur = buffer.empty() ? nullptr : (char *)&buffer[0];
395
8
    const char *end = pCur + buffer.size();
396
8
    bool ret = false;
397
8
    if (pCur) {
398
8
        const char *szCur = pCur;
399
8
        ret = Assimp::SkipSpacesAndLineEnd(pCur, &pCur, end);
400
401
8
        uintptr_t iDiff = (uintptr_t)pCur - (uintptr_t)szCur;
402
8
        buffer.erase(buffer.begin(), buffer.begin() + iDiff);
403
8
        return ret;
404
8
    }
405
406
0
    return ret;
407
8
}
408
409
779
bool PLY::DOM::SkipComments(std::vector<char> buffer) {
410
779
    ai_assert(!buffer.empty());
411
412
779
    std::vector<char> nbuffer = std::move(buffer);
413
    // skip spaces
414
779
    if (!SkipSpaces(nbuffer)) {
415
326
        return false;
416
326
    }
417
418
453
    if (TokenMatch(nbuffer, "comment", 7)) {
419
0
        if (!SkipSpaces(nbuffer))
420
0
            SkipLine(nbuffer);
421
422
0
        if (!TokenMatch(nbuffer, "TextureFile", 11)) {
423
0
            SkipLine(nbuffer);
424
0
            buffer = nbuffer;
425
0
            return true;
426
0
        }
427
428
0
        return true;
429
0
    }
430
431
453
    return false;
432
453
}
433
434
// ------------------------------------------------------------------------------------------------
435
2
bool PLY::DOM::ParseHeader(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer, bool isBinary) {
436
2
    ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseHeader() begin");
437
438
2
    std::unordered_set<std::string> definedAlElements;
439
    // parse all elements
440
771
    while (!buffer.empty()) {
441
        // skip all comments
442
771
        PLY::DOM::SkipComments(buffer);
443
444
771
        PLY::Element out;
445
771
        if (PLY::Element::ParseElement(streamBuffer, buffer, &out)) {
446
            // add the element to the list of elements
447
448
8
            const auto propertyName = (out.szName.empty()) ? to_string(out.eSemantic) : out.szName;
449
8
            auto alreadyDefined = definedAlElements.find(propertyName);
450
8
            if (alreadyDefined != definedAlElements.end()) {
451
0
                throw DeadlyImportError("Property '" + propertyName + "' in header already defined ");
452
0
            }
453
8
            definedAlElements.insert(propertyName);
454
8
            alElements.push_back(out);
455
763
        } else if (TokenMatch(buffer, "end_header", 10)) {
456
            // we have reached the end of the header
457
0
            break;
458
763
        } else {
459
            // ignore unknown header elements
460
763
            if (!streamBuffer.getNextLine(buffer))
461
2
                return false;
462
763
        }
463
771
    }
464
465
0
    if (!isBinary) // it would occur an error, if binary data start with values as space or line end.
466
0
        SkipSpacesAndLineEnd(buffer);
467
468
0
    ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseHeader() succeeded");
469
0
    return true;
470
2
}
471
472
// ------------------------------------------------------------------------------------------------
473
0
bool PLY::DOM::ParseElementInstanceLists(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer, PLYImporter *loader) {
474
0
    ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseElementInstanceLists() begin");
475
0
    alElementData.resize(alElements.size());
476
477
0
    std::vector<PLY::Element>::const_iterator i = alElements.begin();
478
0
    std::vector<PLY::ElementInstanceList>::iterator a = alElementData.begin();
479
480
    // parse all element instances
481
    // construct vertices and faces
482
0
    for (; i != alElements.end(); ++i, ++a) {
483
0
        if ((*i).eSemantic == EEST_Vertex || (*i).eSemantic == EEST_Face || (*i).eSemantic == EEST_TriStrip) {
484
0
            PLY::ElementInstanceList::ParseInstanceList(streamBuffer, buffer, &(*i), nullptr, loader);
485
0
        } else {
486
0
            (*a).alInstances.resize((*i).NumOccur);
487
0
            PLY::ElementInstanceList::ParseInstanceList(streamBuffer, buffer, &(*i), &(*a), nullptr);
488
0
        }
489
0
    }
490
491
0
    ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseElementInstanceLists() succeeded");
492
0
    return true;
493
0
}
494
495
// ------------------------------------------------------------------------------------------------
496
bool PLY::DOM::ParseElementInstanceListsBinary(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer,
497
        const char *&pCur,
498
        unsigned int &bufferSize,
499
        PLYImporter *loader,
500
0
        bool p_bBE) {
501
0
    ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseElementInstanceListsBinary() begin");
502
0
    alElementData.resize(alElements.size());
503
504
0
    std::vector<PLY::Element>::const_iterator i = alElements.begin();
505
0
    std::vector<PLY::ElementInstanceList>::iterator a = alElementData.begin();
506
507
    // parse all element instances
508
0
    for (; i != alElements.end(); ++i, ++a) {
509
0
        if ((*i).eSemantic == EEST_Vertex || (*i).eSemantic == EEST_Face || (*i).eSemantic == EEST_TriStrip) {
510
0
            PLY::ElementInstanceList::ParseInstanceListBinary(streamBuffer, buffer, pCur, bufferSize, &(*i), nullptr, loader, p_bBE);
511
0
        } else {
512
0
            (*a).alInstances.resize((*i).NumOccur);
513
0
            PLY::ElementInstanceList::ParseInstanceListBinary(streamBuffer, buffer, pCur, bufferSize, &(*i), &(*a), nullptr, p_bBE);
514
0
        }
515
0
    }
516
517
0
    ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseElementInstanceListsBinary() succeeded");
518
0
    return true;
519
0
}
520
521
// ------------------------------------------------------------------------------------------------
522
1
bool PLY::DOM::ParseInstanceBinary(IOStreamBuffer<char> &streamBuffer, DOM *p_pcOut, PLYImporter *loader, bool p_bBE) {
523
1
    ai_assert(nullptr != p_pcOut);
524
1
    ai_assert(nullptr != loader);
525
526
1
    std::vector<char> buffer;
527
1
    streamBuffer.getNextLine(buffer);
528
529
1
    ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseInstanceBinary() begin");
530
531
1
    if (!p_pcOut->ParseHeader(streamBuffer, buffer, true)) {
532
1
        ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseInstanceBinary() failure");
533
1
        return false;
534
1
    }
535
536
0
    streamBuffer.getNextBlock(buffer);
537
538
0
    unsigned int bufferSize = static_cast<unsigned int>(buffer.size());
539
0
    const char *pCur = (char *)&buffer[0];
540
0
    if (!p_pcOut->ParseElementInstanceListsBinary(streamBuffer, buffer, pCur, bufferSize, loader, p_bBE)) {
541
0
        ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseInstanceBinary() failure");
542
0
        return false;
543
0
    }
544
0
    ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseInstanceBinary() succeeded");
545
0
    return true;
546
0
}
547
548
// ------------------------------------------------------------------------------------------------
549
1
bool PLY::DOM::ParseInstance(IOStreamBuffer<char> &streamBuffer, DOM *p_pcOut, PLYImporter *loader) {
550
1
    ai_assert(nullptr != p_pcOut);
551
1
    ai_assert(nullptr != loader);
552
553
1
    std::vector<char> buffer;
554
1
    streamBuffer.getNextLine(buffer);
555
556
1
    ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseInstance() begin");
557
558
1
    if (!p_pcOut->ParseHeader(streamBuffer, buffer, false)) {
559
1
        ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseInstance() failure");
560
1
        return false;
561
1
    }
562
563
    // get next line after header
564
0
    streamBuffer.getNextLine(buffer);
565
0
    if (!p_pcOut->ParseElementInstanceLists(streamBuffer, buffer, loader)) {
566
0
        ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseInstance() failure");
567
0
        return false;
568
0
    }
569
0
    ASSIMP_LOG_VERBOSE_DEBUG("PLY::DOM::ParseInstance() succeeded");
570
0
    return true;
571
0
}
572
573
// ------------------------------------------------------------------------------------------------
574
bool PLY::ElementInstanceList::ParseInstanceList(
575
        IOStreamBuffer<char> &streamBuffer,
576
        std::vector<char> &buffer,
577
        const PLY::Element *pcElement,
578
        PLY::ElementInstanceList *p_pcOut,
579
0
        PLYImporter *loader) {
580
0
    ai_assert(nullptr != pcElement);
581
582
    // parse all elements
583
0
    if (EEST_INVALID == pcElement->eSemantic || pcElement->alProperties.empty()) {
584
        // if the element has an unknown semantic we can skip all lines
585
        // However, there could be comments
586
0
        for (unsigned int i = 0; i < pcElement->NumOccur; ++i) {
587
0
            PLY::DOM::SkipComments(buffer);
588
0
            PLY::DOM::SkipLine(buffer);
589
0
            streamBuffer.getNextLine(buffer);
590
0
        }
591
0
    } else {
592
0
        const char *pCur = (const char *)&buffer[0];
593
0
        const char *end = pCur + buffer.size();
594
        // be sure to have enough storage
595
0
        for (unsigned int i = 0; i < pcElement->NumOccur; ++i) {
596
0
            if (p_pcOut)
597
0
                PLY::ElementInstance::ParseInstance(pCur, end, pcElement, &p_pcOut->alInstances[i]);
598
0
            else {
599
0
                ElementInstance elt;
600
0
                PLY::ElementInstance::ParseInstance(pCur, end, pcElement, &elt);
601
602
                // Create vertex or face
603
0
                if (pcElement->eSemantic == EEST_Vertex) {
604
                    // call loader instance from here
605
0
                    loader->LoadVertex(pcElement, &elt, i);
606
0
                } else if (pcElement->eSemantic == EEST_Face) {
607
                    // call loader instance from here
608
0
                    loader->LoadFace(pcElement, &elt, i);
609
0
                } else if (pcElement->eSemantic == EEST_TriStrip) {
610
                    // call loader instance from here
611
0
                    loader->LoadFace(pcElement, &elt, i);
612
0
                }
613
0
            }
614
615
0
            streamBuffer.getNextLine(buffer);
616
0
            pCur = (buffer.empty()) ? nullptr : (const char *)&buffer[0];
617
0
        }
618
0
    }
619
0
    return true;
620
0
}
621
622
// ------------------------------------------------------------------------------------------------
623
bool PLY::ElementInstanceList::ParseInstanceListBinary(
624
        IOStreamBuffer<char> &streamBuffer,
625
        std::vector<char> &buffer,
626
        const char *&pCur,
627
        unsigned int &bufferSize,
628
        const PLY::Element *pcElement,
629
        PLY::ElementInstanceList *p_pcOut,
630
        PLYImporter *loader,
631
0
        bool p_bBE /* = false */) {
632
0
    ai_assert(nullptr != pcElement);
633
634
    // we can add special handling code for unknown element semantics since
635
    // we can't skip it as a whole block (we don't know its exact size
636
    // due to the fact that lists could be contained in the property list
637
    // of the unknown element)
638
0
    for (unsigned int i = 0; i < pcElement->NumOccur; ++i) {
639
0
        if (p_pcOut)
640
0
            PLY::ElementInstance::ParseInstanceBinary(streamBuffer, buffer, pCur, bufferSize, pcElement, &p_pcOut->alInstances[i], p_bBE);
641
0
        else {
642
0
            ElementInstance elt;
643
0
            PLY::ElementInstance::ParseInstanceBinary(streamBuffer, buffer, pCur, bufferSize, pcElement, &elt, p_bBE);
644
645
            // Create vertex or face
646
0
            if (pcElement->eSemantic == EEST_Vertex) {
647
                // call loader instance from here
648
0
                loader->LoadVertex(pcElement, &elt, i);
649
0
            } else if (pcElement->eSemantic == EEST_Face) {
650
                // call loader instance from here
651
0
                loader->LoadFace(pcElement, &elt, i);
652
0
            } else if (pcElement->eSemantic == EEST_TriStrip) {
653
                // call loader instance from here
654
0
                loader->LoadFace(pcElement, &elt, i);
655
0
            }
656
0
        }
657
0
    }
658
0
    return true;
659
0
}
660
661
// ------------------------------------------------------------------------------------------------
662
bool PLY::ElementInstance::ParseInstance(const char *&pCur, const char *end,
663
        const PLY::Element *pcElement,
664
0
        PLY::ElementInstance *p_pcOut) {
665
0
    ai_assert(nullptr != pcElement);
666
0
    ai_assert(nullptr != p_pcOut);
667
668
    // allocate enough storage
669
0
    p_pcOut->alProperties.resize(pcElement->alProperties.size());
670
671
0
    std::vector<PLY::PropertyInstance>::iterator i = p_pcOut->alProperties.begin();
672
0
    std::vector<PLY::Property>::const_iterator a = pcElement->alProperties.begin();
673
0
    for (; i != p_pcOut->alProperties.end(); ++i, ++a) {
674
0
        if (!(PLY::PropertyInstance::ParseInstance(pCur, end, &(*a), &(*i)))) {
675
0
            ASSIMP_LOG_WARN("Unable to parse property instance. "
676
0
                            "Skipping this element instance");
677
678
0
            PLY::PropertyInstance::ValueUnion v = PLY::PropertyInstance::DefaultValue((*a).eType);
679
0
            (*i).avList.push_back(v);
680
0
        }
681
0
    }
682
0
    return true;
683
0
}
684
685
// ------------------------------------------------------------------------------------------------
686
bool PLY::ElementInstance::ParseInstanceBinary(
687
        IOStreamBuffer<char> &streamBuffer,
688
        std::vector<char> &buffer,
689
        const char *&pCur,
690
        unsigned int &bufferSize,
691
        const PLY::Element *pcElement,
692
        PLY::ElementInstance *p_pcOut,
693
0
        bool p_bBE /* = false */) {
694
0
    ai_assert(nullptr != pcElement);
695
0
    ai_assert(nullptr != p_pcOut);
696
697
    // allocate enough storage
698
0
    p_pcOut->alProperties.resize(pcElement->alProperties.size());
699
700
0
    std::vector<PLY::PropertyInstance>::iterator i = p_pcOut->alProperties.begin();
701
0
    std::vector<PLY::Property>::const_iterator a = pcElement->alProperties.begin();
702
0
    for (; i != p_pcOut->alProperties.end(); ++i, ++a) {
703
0
        if (!(PLY::PropertyInstance::ParseInstanceBinary(streamBuffer, buffer, pCur, bufferSize, &(*a), &(*i), p_bBE))) {
704
0
            ASSIMP_LOG_WARN("Unable to parse binary property instance. "
705
0
                            "Skipping this element instance");
706
707
0
            (*i).avList.push_back(PLY::PropertyInstance::DefaultValue((*a).eType));
708
0
        }
709
0
    }
710
0
    return true;
711
0
}
712
713
// ------------------------------------------------------------------------------------------------
714
bool PLY::PropertyInstance::ParseInstance(const char *&pCur, const char *end, const PLY::Property *prop,
715
0
        PLY::PropertyInstance *p_pcOut) {
716
0
    ai_assert(nullptr != prop);
717
0
    ai_assert(nullptr != p_pcOut);
718
719
    // skip spaces at the beginning
720
0
    if (!SkipSpaces(&pCur, end)) {
721
0
        return false;
722
0
    }
723
724
0
    if (prop->bIsList) {
725
        // parse the number of elements in the list
726
0
        PLY::PropertyInstance::ValueUnion v;
727
0
        PLY::PropertyInstance::ParseValue(pCur, prop->eFirstType, &v);
728
729
        // convert to unsigned int
730
0
        unsigned int iNum = PLY::PropertyInstance::ConvertTo<unsigned int>(v, prop->eFirstType);
731
732
        // parse all list elements
733
0
        p_pcOut->avList.resize(iNum);
734
0
        for (unsigned int i = 0; i < iNum; ++i) {
735
0
            if (!SkipSpaces(&pCur, end))
736
0
                return false;
737
738
0
            PLY::PropertyInstance::ParseValue(pCur, prop->eType, &p_pcOut->avList[i]);
739
0
        }
740
0
    } else {
741
        // parse the property
742
0
        PLY::PropertyInstance::ValueUnion v;
743
744
0
        PLY::PropertyInstance::ParseValue(pCur, prop->eType, &v);
745
0
        p_pcOut->avList.push_back(v);
746
0
    }
747
0
    SkipSpacesAndLineEnd(&pCur, end);
748
0
    return true;
749
0
}
750
751
// ------------------------------------------------------------------------------------------------
752
bool PLY::PropertyInstance::ParseInstanceBinary(IOStreamBuffer<char> &streamBuffer, std::vector<char> &buffer,
753
        const char *&pCur,
754
        unsigned int &bufferSize,
755
        const PLY::Property *prop,
756
        PLY::PropertyInstance *p_pcOut,
757
0
        bool p_bBE) {
758
0
    ai_assert(nullptr != prop);
759
0
    ai_assert(nullptr != p_pcOut);
760
761
    // parse all elements
762
0
    if (prop->bIsList) {
763
        // parse the number of elements in the list
764
0
        PLY::PropertyInstance::ValueUnion v;
765
0
        PLY::PropertyInstance::ParseValueBinary(streamBuffer, buffer, pCur, bufferSize, prop->eFirstType, &v, p_bBE);
766
767
        // convert to unsigned int
768
0
        unsigned int iNum = PLY::PropertyInstance::ConvertTo<unsigned int>(v, prop->eFirstType);
769
770
        // parse all list elements
771
0
        p_pcOut->avList.resize(iNum);
772
0
        for (unsigned int i = 0; i < iNum; ++i) {
773
0
            PLY::PropertyInstance::ParseValueBinary(streamBuffer, buffer, pCur, bufferSize, prop->eType, &p_pcOut->avList[i], p_bBE);
774
0
        }
775
0
    } else {
776
        // parse the property
777
0
        PLY::PropertyInstance::ValueUnion v;
778
0
        PLY::PropertyInstance::ParseValueBinary(streamBuffer, buffer, pCur, bufferSize, prop->eType, &v, p_bBE);
779
0
        p_pcOut->avList.push_back(v);
780
0
    }
781
0
    return true;
782
0
}
783
784
// ------------------------------------------------------------------------------------------------
785
0
PLY::PropertyInstance::ValueUnion PLY::PropertyInstance::DefaultValue(PLY::EDataType eType) {
786
0
    PLY::PropertyInstance::ValueUnion out;
787
788
0
    switch (eType) {
789
0
    case EDT_Float:
790
0
        out.fFloat = 0.f;
791
0
        return out;
792
793
0
    case EDT_Double:
794
0
        out.fDouble = 0.;
795
0
        return out;
796
797
0
    default:;
798
0
    };
799
0
    out.iUInt = 0;
800
0
    return out;
801
0
}
802
803
// ------------------------------------------------------------------------------------------------
804
bool PLY::PropertyInstance::ParseValue(const char *&pCur,
805
        PLY::EDataType eType,
806
0
        PLY::PropertyInstance::ValueUnion *out) {
807
0
    ai_assert(nullptr != pCur);
808
0
    ai_assert(nullptr != out);
809
810
    // calc element size
811
0
    bool ret = true;
812
0
    switch (eType) {
813
0
    case EDT_UInt:
814
0
    case EDT_UShort:
815
0
    case EDT_UChar:
816
817
0
        out->iUInt = (uint32_t)strtoul10(pCur, &pCur);
818
0
        break;
819
820
0
    case EDT_Int:
821
0
    case EDT_Short:
822
0
    case EDT_Char:
823
824
0
        out->iInt = (int32_t)strtol10(pCur, &pCur);
825
0
        break;
826
827
0
    case EDT_Float:
828
        // technically this should cast to float, but people tend to use float descriptors for double data
829
        // this is the best way to not risk losing precision on import and it doesn't hurt to do this
830
0
        ai_real f;
831
0
        pCur = fast_atoreal_move(pCur, f);
832
0
        out->fFloat = (ai_real)f;
833
0
        break;
834
835
0
    case EDT_Double:
836
0
        double d;
837
0
        pCur = fast_atoreal_move(pCur, d);
838
0
        out->fDouble = (double)d;
839
0
        break;
840
841
0
    case EDT_INVALID:
842
0
    default:
843
0
        ret = false;
844
0
        break;
845
0
    }
846
847
0
    return ret;
848
0
}
849
850
// ------------------------------------------------------------------------------------------------
851
bool PLY::PropertyInstance::ParseValueBinary(IOStreamBuffer<char> &streamBuffer,
852
        std::vector<char> &buffer,
853
        const char *&pCur,
854
        unsigned int &bufferSize,
855
        PLY::EDataType eType,
856
        PLY::PropertyInstance::ValueUnion *out,
857
0
        bool p_bBE) {
858
0
    ai_assert(nullptr != out);
859
860
    // calc element size
861
0
    unsigned int lsize = 0;
862
0
    switch (eType) {
863
0
    case EDT_Char:
864
0
    case EDT_UChar:
865
0
        lsize = 1;
866
0
        break;
867
868
0
    case EDT_UShort:
869
0
    case EDT_Short:
870
0
        lsize = 2;
871
0
        break;
872
873
0
    case EDT_UInt:
874
0
    case EDT_Int:
875
0
    case EDT_Float:
876
0
        lsize = 4;
877
0
        break;
878
879
0
    case EDT_Double:
880
0
        lsize = 8;
881
0
        break;
882
883
0
    case EDT_INVALID:
884
0
    default:
885
0
        break;
886
0
    }
887
888
    // read the next file block if needed
889
0
    if (bufferSize < lsize) {
890
0
        std::vector<char> nbuffer;
891
0
        if (streamBuffer.getNextBlock(nbuffer)) {
892
            // concat buffer contents
893
0
            buffer = std::vector<char>(buffer.end() - bufferSize, buffer.end());
894
0
            buffer.insert(buffer.end(), nbuffer.begin(), nbuffer.end());
895
0
            nbuffer.clear();
896
0
            bufferSize = static_cast<unsigned int>(buffer.size());
897
0
            pCur = (char *)&buffer[0];
898
0
        } else {
899
0
            throw DeadlyImportError("Invalid .ply file: File corrupted");
900
0
        }
901
0
    }
902
903
0
    bool ret = true;
904
0
    switch (eType) {
905
0
    case EDT_UInt: {
906
0
        uint32_t t;
907
0
        memcpy(&t, pCur, sizeof(uint32_t));
908
0
        pCur += sizeof(uint32_t);
909
910
        // Swap endianness
911
0
        if (p_bBE) ByteSwap::Swap(&t);
912
0
        out->iUInt = t;
913
0
        break;
914
0
    }
915
916
0
    case EDT_UShort: {
917
0
        uint16_t t;
918
0
        memcpy(&t, pCur, sizeof(uint16_t));
919
0
        pCur += sizeof(uint16_t);
920
921
        // Swap endianness
922
0
        if (p_bBE) ByteSwap::Swap(&t);
923
0
        out->iUInt = t;
924
0
        break;
925
0
    }
926
927
0
    case EDT_UChar: {
928
0
        uint8_t t;
929
0
        memcpy(&t, pCur, sizeof(uint8_t));
930
0
        pCur += sizeof(uint8_t);
931
0
        out->iUInt = t;
932
0
        break;
933
0
    }
934
935
0
    case EDT_Int: {
936
0
        int32_t t;
937
0
        memcpy(&t, pCur, sizeof(int32_t));
938
0
        pCur += sizeof(int32_t);
939
940
        // Swap endianness
941
0
        if (p_bBE) ByteSwap::Swap(&t);
942
0
        out->iInt = t;
943
0
        break;
944
0
    }
945
946
0
    case EDT_Short: {
947
0
        int16_t t;
948
0
        memcpy(&t, pCur, sizeof(int16_t));
949
0
        pCur += sizeof(int16_t);
950
951
        // Swap endianness
952
0
        if (p_bBE) ByteSwap::Swap(&t);
953
0
        out->iInt = t;
954
0
        break;
955
0
    }
956
957
0
    case EDT_Char: {
958
0
        int8_t t;
959
0
        memcpy(&t, pCur, sizeof(int8_t));
960
0
        pCur += sizeof(int8_t);
961
0
        out->iInt = t;
962
0
        break;
963
0
    }
964
965
0
    case EDT_Float: {
966
0
        float t;
967
0
        memcpy(&t, pCur, sizeof(float));
968
0
        pCur += sizeof(float);
969
970
        // Swap endianness
971
0
        if (p_bBE) ByteSwap::Swap(&t);
972
0
        out->fFloat = t;
973
0
        break;
974
0
    }
975
0
    case EDT_Double: {
976
0
        double t;
977
0
        memcpy(&t, pCur, sizeof(double));
978
0
        pCur += sizeof(double);
979
980
        // Swap endianness
981
0
        if (p_bBE) ByteSwap::Swap(&t);
982
0
        out->fDouble = t;
983
0
        break;
984
0
    }
985
0
    default:
986
0
        ret = false;
987
0
    }
988
989
0
    bufferSize -= lsize;
990
991
0
    return ret;
992
0
}
993
994
} // namespace Assimp
995
996
#endif // !! ASSIMP_BUILD_NO_PLY_IMPORTER