Coverage Report

Created: 2026-09-14 07:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/assimp/code/AssetLib/LWO/LWOBLoader.cpp
Line
Count
Source
1
/*
2
---------------------------------------------------------------------------
3
Open Asset Import Library (assimp)
4
---------------------------------------------------------------------------
5
6
Copyright (c) 2006-2026, assimp team
7
8
9
10
All rights reserved.
11
12
Redistribution and use of this software in source and binary forms,
13
with or without modification, are permitted provided that the following
14
conditions are met:
15
16
* Redistributions of source code must retain the above
17
  copyright notice, this list of conditions and the
18
  following disclaimer.
19
20
* Redistributions in binary form must reproduce the above
21
  copyright notice, this list of conditions and the
22
  following disclaimer in the documentation and/or other
23
  materials provided with the distribution.
24
25
* Neither the name of the assimp team, nor the names of its
26
  contributors may be used to endorse or promote products
27
  derived from this software without specific prior
28
  written permission of the assimp team.
29
30
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
31
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
32
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
33
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
34
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
36
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
37
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
38
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
40
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41
---------------------------------------------------------------------------
42
*/
43
44
/** @file Implementation of the LWO importer class for the older LWOB
45
    file formats, including materials */
46
47
48
#ifndef ASSIMP_BUILD_NO_LWO_IMPORTER
49
50
// Internal headers
51
#include "LWOLoader.h"
52
using namespace Assimp;
53
54
// ------------------------------------------------------------------------------------------------
55
607
void LWOImporter::LoadLWOBFile() {
56
607
    LE_NCONST uint8_t* const end = mFileBuffer + fileSize;
57
607
    bool running = true;
58
2.83k
    while (running) {
59
2.83k
        if (mFileBuffer + sizeof(IFF::ChunkHeader) > end)
60
431
            break;
61
2.40k
        const IFF::ChunkHeader head = IFF::LoadChunk(mFileBuffer);
62
63
2.40k
        if (mFileBuffer + head.length > end) {
64
133
            throw DeadlyImportError("LWOB: Invalid chunk length");
65
133
        }
66
2.27k
        uint8_t* const next = mFileBuffer+head.length;
67
2.27k
        switch (head.type) {
68
            // vertex list
69
537
            case AI_LWO_PNTS: {
70
537
                if (!mCurLayer->mTempPoints.empty())
71
537
                    ASSIMP_LOG_WARN("LWO: PNTS chunk encountered twice");
72
530
                else
73
530
                    LoadLWOPoints(head.length);
74
537
                } break;
75
519
            case AI_LWO_POLS: {                 // face list
76
519
                    if (!mCurLayer->mFaces.empty())
77
519
                        ASSIMP_LOG_WARN("LWO: POLS chunk encountered twice");
78
514
                    else
79
514
                        LoadLWOBPolygons(head.length);
80
519
                } break;
81
82
586
            case AI_LWO_SRFS: // list of tags
83
586
                {
84
586
                    if (!mTags->empty())
85
586
                        ASSIMP_LOG_WARN("LWO: SRFS chunk encountered twice");
86
579
                    else
87
579
                        LoadLWOTags(head.length);
88
586
                } break;
89
90
446
            case AI_LWO_SURF: // surface chunk
91
446
                {
92
446
                    LoadLWOBSurface(head.length);
93
446
                } break;
94
95
185
            default:
96
185
                break;
97
2.27k
        }
98
2.23k
        mFileBuffer = next;
99
2.23k
    }
100
607
}
101
102
// ------------------------------------------------------------------------------------------------
103
514
void LWOImporter::LoadLWOBPolygons(unsigned int length) {
104
    // first find out how many faces and vertices we'll finally need
105
514
    LE_NCONST uint16_t* const end   = (LE_NCONST uint16_t*)(mFileBuffer+length);
106
514
    LE_NCONST uint16_t* cursor      = (LE_NCONST uint16_t*)mFileBuffer;
107
108
    // perform endianness conversions
109
514
#ifndef AI_BUILD_BIG_ENDIAN
110
77.6k
    while (cursor < end)ByteSwap::Swap2(cursor++);
111
514
    cursor = (LE_NCONST uint16_t*)mFileBuffer;
112
514
#endif
113
114
514
    unsigned int iNumFaces = 0,iNumVertices = 0;
115
514
    CountVertsAndFacesLWOB(iNumVertices,iNumFaces,cursor,end);
116
117
    // allocate the output array and copy face indices
118
514
    if (iNumFaces) {
119
487
        cursor = (LE_NCONST uint16_t*)mFileBuffer;
120
121
487
        mCurLayer->mFaces.resize(iNumFaces);
122
487
        FaceList::iterator it = mCurLayer->mFaces.begin();
123
487
        CopyFaceIndicesLWOB(it,cursor,end);
124
487
    }
125
514
}
126
127
// ------------------------------------------------------------------------------------------------
128
void LWOImporter::CountVertsAndFacesLWOB(unsigned int& verts, unsigned int& faces,
129
539
    LE_NCONST uint16_t*& cursor, const uint16_t* const end, unsigned int max) {
130
11.3k
    while (cursor < end && max--) {
131
10.8k
        uint16_t numIndices;
132
        // must have 2 shorts left for numIndices and surface
133
10.8k
        if (end - cursor < 2) {
134
3
            throw DeadlyImportError("LWOB: Unexpected end of file");
135
3
        }
136
10.8k
        ::memcpy(&numIndices, cursor++, 2);
137
        // must have enough left for indices and surface
138
10.8k
        if (end - cursor < (1 + numIndices)) {
139
22
            throw DeadlyImportError("LWOB: Unexpected end of file");
140
22
        }
141
10.8k
        verts += numIndices;
142
10.8k
        faces++;
143
10.8k
        cursor += numIndices;
144
10.8k
        int16_t surface;
145
10.8k
        ::memcpy(&surface, cursor++, 2);
146
10.8k
        if (surface < 0) {
147
            // there are detail polygons
148
25
            ::memcpy(&numIndices, cursor++, 2);
149
25
            CountVertsAndFacesLWOB(verts,faces,cursor,end,numIndices);
150
25
        }
151
10.8k
    }
152
539
}
153
154
// ------------------------------------------------------------------------------------------------
155
void LWOImporter::CopyFaceIndicesLWOB(FaceList::iterator &it,
156
    LE_NCONST uint16_t*& cursor,
157
    const uint16_t* const end,
158
502
    unsigned int max) {
159
10.1k
    while (cursor < end && max--) {
160
9.67k
        LWO::Face& face = *it;++it;
161
9.67k
        uint16_t numIndices;
162
9.67k
        ::memcpy(&numIndices, cursor++, 2);
163
9.67k
        face.mNumIndices = numIndices;
164
9.67k
        if(face.mNumIndices) {
165
9.54k
            if (cursor + face.mNumIndices >= end) {
166
0
                break;
167
0
            }
168
9.54k
            face.mIndices = new unsigned int[face.mNumIndices];
169
56.8k
            for (unsigned int i = 0; i < face.mNumIndices;++i) {
170
47.3k
                unsigned int & mi = face.mIndices[i];
171
47.3k
                uint16_t index;
172
47.3k
                ::memcpy(&index, cursor++, 2);
173
47.3k
                mi = index;
174
47.3k
                if (mi >= mCurLayer->mTempPoints.size()) {
175
1.20k
                    ASSIMP_LOG_WARN("LWOB: face index is out of range");
176
1.20k
                    mi = mCurLayer->mTempPoints.empty() ? 0 : (unsigned int)(mCurLayer->mTempPoints.size() - 1);
177
1.20k
                }
178
47.3k
            }
179
9.54k
        } else {
180
130
            ASSIMP_LOG_WARN("LWOB: Face has 0 indices");
181
130
        }
182
9.67k
        int16_t surface;
183
9.67k
        ::memcpy(&surface, cursor++, 2);
184
9.67k
        if (surface < 0) {
185
19
            surface = -surface;
186
187
            // there are detail polygons.
188
19
            uint16_t numPolygons;
189
19
            ::memcpy(&numPolygons, cursor++, 2);
190
19
            if (cursor < end) {
191
15
                CopyFaceIndicesLWOB(it,cursor,end,numPolygons);
192
15
            }
193
19
        }
194
9.67k
        face.surfaceIndex = surface-1;
195
9.67k
    }
196
502
}
197
198
// ------------------------------------------------------------------------------------------------
199
85
LWO::Texture* LWOImporter::SetupNewTextureLWOB(LWO::TextureList& list,unsigned int size) {
200
85
    list.emplace_back();
201
85
    LWO::Texture* tex = &list.back();
202
203
85
    std::string type;
204
85
    GetS0(type,size);
205
85
    const char* s = type.c_str();
206
207
85
    if(strstr(s, "Image Map")) {
208
        // Determine mapping type
209
74
        if(strstr(s, "Planar"))
210
0
            tex->mapMode = LWO::Texture::Planar;
211
74
        else if(strstr(s, "Cylindrical"))
212
69
            tex->mapMode = LWO::Texture::Cylindrical;
213
5
        else if(strstr(s, "Spherical"))
214
0
            tex->mapMode = LWO::Texture::Spherical;
215
5
        else if(strstr(s, "Cubic"))
216
0
            tex->mapMode = LWO::Texture::Cubic;
217
5
        else if(strstr(s, "Front"))
218
0
            tex->mapMode = LWO::Texture::FrontProjection;
219
74
    } else {
220
        // procedural or gradient, not supported
221
11
        ASSIMP_LOG_ERROR("LWOB: Unsupported legacy texture: ", type);
222
11
    }
223
224
85
    return tex;
225
85
}
226
227
// ------------------------------------------------------------------------------------------------
228
446
void LWOImporter::LoadLWOBSurface(unsigned int size) {
229
446
    LE_NCONST uint8_t* const end = mFileBuffer + size;
230
231
446
    mSurfaces->push_back( LWO::Surface () );
232
446
    LWO::Surface& surf = mSurfaces->back();
233
446
    LWO::Texture *pTex = nullptr;
234
235
446
    GetS0(surf.mName,size);
236
446
    bool running = true;
237
6.28k
    while (running)    {
238
6.28k
        if (mFileBuffer + 6 >= end)
239
430
            break;
240
241
5.85k
        IFF::SubChunkHeader head = IFF::LoadSubChunk(mFileBuffer);
242
243
        /*  A single test file (sonycam.lwo) seems to have invalid surface chunks.
244
         *  I'm assuming it's the fault of a single, unknown exporter so there are
245
         *  probably THOUSANDS of them. Here's a dirty workaround:
246
         *
247
         *  We don't break if the chunk limit is exceeded. Instead, we're computing
248
         *  how much storage is actually left and work with this value from now on.
249
         */
250
5.85k
        if (mFileBuffer + head.length > end) {
251
294
            ASSIMP_LOG_ERROR("LWOB: Invalid surface chunk length. Trying to continue.");
252
294
            head.length = (uint16_t) (end - mFileBuffer);
253
294
        }
254
255
5.85k
        uint8_t* const next = mFileBuffer+head.length;
256
5.85k
        switch (head.type) {
257
            // diffuse color
258
419
            case AI_LWO_COLR:
259
419
                {
260
419
                    AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,COLR,3);
261
417
                    surf.mColor.r = GetU1() / 255.0f;
262
417
                    surf.mColor.g = GetU1() / 255.0f;
263
417
                    surf.mColor.b = GetU1() / 255.0f;
264
417
                    break;
265
419
                }
266
            // diffuse strength ...
267
340
            case AI_LWO_DIFF:
268
340
                {
269
340
                    AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,DIFF,2);
270
338
                    surf.mDiffuseValue = GetU2() / 255.0f;
271
338
                    break;
272
340
                }
273
            // specular strength ...
274
330
            case AI_LWO_SPEC:
275
330
                {
276
330
                    AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,SPEC,2);
277
328
                    surf.mSpecularValue = GetU2() / 255.0f;
278
328
                    break;
279
330
                }
280
            // luminosity ...
281
322
            case AI_LWO_LUMI:
282
322
                {
283
322
                    AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,LUMI,2);
284
321
                    surf.mLuminosity = GetU2() / 255.0f;
285
321
                    break;
286
322
                }
287
            // transparency
288
314
            case AI_LWO_TRAN:
289
314
                {
290
314
                    AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,TRAN,2);
291
312
                    surf.mTransparency = GetU2() / 255.0f;
292
312
                    break;
293
314
                }
294
            // surface flags
295
401
            case AI_LWO_FLAG:
296
401
                {
297
401
                    AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,FLAG,2);
298
399
                    uint16_t flag = GetU2();
299
399
                    if (flag & 0x4 )   surf.mMaximumSmoothAngle = 1.56207f;
300
399
                    if (flag & 0x8 )   surf.mColorHighlights = 1.f;
301
399
                    if (flag & 0x100)  surf.bDoubleSided = true;
302
399
                    break;
303
401
                }
304
            // maximum smoothing angle
305
103
            case AI_LWO_SMAN:
306
103
                {
307
103
                    AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,SMAN,4);
308
102
                    surf.mMaximumSmoothAngle = std::fabs( GetF4() );
309
102
                    break;
310
103
                }
311
            // glossiness
312
218
            case AI_LWO_GLOS:
313
218
                {
314
218
                    AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,GLOS,2);
315
216
                    surf.mGlossiness = (float)GetU2();
316
216
                    break;
317
218
                }
318
            // color texture
319
75
            case AI_LWO_CTEX:
320
75
                {
321
75
                    pTex = SetupNewTextureLWOB(surf.mColorTextures,
322
75
                        head.length);
323
75
                    break;
324
218
                }
325
            // diffuse texture
326
2
            case AI_LWO_DTEX:
327
2
                {
328
2
                    pTex = SetupNewTextureLWOB(surf.mDiffuseTextures,
329
2
                        head.length);
330
2
                    break;
331
218
                }
332
            // specular texture
333
2
            case AI_LWO_STEX:
334
2
                {
335
2
                    pTex = SetupNewTextureLWOB(surf.mSpecularTextures,
336
2
                        head.length);
337
2
                    break;
338
218
                }
339
            // bump texture
340
2
            case AI_LWO_BTEX:
341
2
                {
342
2
                    pTex = SetupNewTextureLWOB(surf.mBumpTextures,
343
2
                        head.length);
344
2
                    break;
345
218
                }
346
            // transparency texture
347
4
            case AI_LWO_TTEX:
348
4
                {
349
4
                    pTex = SetupNewTextureLWOB(surf.mOpacityTextures,
350
4
                        head.length);
351
4
                    break;
352
218
                }
353
            // texture path
354
47
            case AI_LWO_TIMG:
355
47
                {
356
47
                    if (pTex)   {
357
46
                        GetS0(pTex->mFileName,head.length);
358
46
                    } else {
359
1
                        ASSIMP_LOG_WARN("LWOB: Unexpected TIMG chunk");
360
1
                    }
361
47
                    break;
362
218
                }
363
            // texture strength
364
1
            case AI_LWO_TVAL:
365
1
                {
366
1
                    AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,TVAL,1);
367
1
                    if (pTex)   {
368
1
                        pTex->mStrength = (float)GetU1()/ 255.f;
369
1
                    } else {
370
0
                        ASSIMP_LOG_ERROR("LWOB: Unexpected TVAL chunk");
371
0
                    }
372
1
                    break;
373
1
                }
374
            // texture flags
375
71
            case AI_LWO_TFLG:
376
71
                {
377
71
                    AI_LWO_VALIDATE_CHUNK_LENGTH(head.length,TFLG,2);
378
379
69
                    if (nullptr != pTex) {
380
67
                        const uint16_t s = GetU2();
381
67
                        if (s & 1)
382
3
                            pTex->majorAxis = LWO::Texture::AXIS_X;
383
64
                        else if (s & 2)
384
2
                            pTex->majorAxis = LWO::Texture::AXIS_Y;
385
62
                        else if (s & 4)
386
60
                            pTex->majorAxis = LWO::Texture::AXIS_Z;
387
388
67
                        if (s & 16) {
389
2
                            ASSIMP_LOG_WARN("LWOB: Ignoring \'negate\' flag on texture");
390
2
                        }
391
67
                    }
392
2
                    else {
393
2
                        ASSIMP_LOG_WARN("LWOB: Unexpected TFLG chunk");
394
2
                    }
395
69
                    break;
396
71
                }
397
5.85k
        }
398
5.83k
        mFileBuffer = next;
399
5.83k
    }
400
446
}
401
402
#endif // !! ASSIMP_BUILD_NO_LWO_IMPORTER