Coverage Report

Created: 2025-07-23 09:13

/src/gdal/gcore/gdaljp2box.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  GDALJP2Box Implementation - Low level JP2 box reader.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
9
 * Copyright (c) 2010-2012, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_port.h"
15
#include "gdaljp2metadata.h"
16
17
#include <cstddef>
18
#include <cstdio>
19
#include <cstring>
20
#if HAVE_FCNTL_H
21
#include <fcntl.h>
22
#endif
23
24
#include <algorithm>
25
26
#include "cpl_conv.h"
27
#include "cpl_error.h"
28
#include "cpl_string.h"
29
#include "cpl_vsi.h"
30
31
/*! @cond Doxygen_Suppress */
32
33
/************************************************************************/
34
/*                             GDALJP2Box()                             */
35
/************************************************************************/
36
37
// GDALJP2Box does *not* take ownership of fpIn
38
22.2k
GDALJP2Box::GDALJP2Box(VSILFILE *fpIn) : fpVSIL(fpIn)
39
22.2k
{
40
22.2k
}
41
42
/************************************************************************/
43
/*                            ~GDALJP2Box()                             */
44
/************************************************************************/
45
46
GDALJP2Box::~GDALJP2Box()
47
48
22.2k
{
49
    // Do not close fpVSIL. Ownership remains to the caller of GDALJP2Box
50
    // constructor
51
22.2k
    CPLFree(pabyData);
52
22.2k
}
53
54
/************************************************************************/
55
/*                             SetOffset()                              */
56
/************************************************************************/
57
58
int GDALJP2Box::SetOffset(GIntBig nNewOffset)
59
60
197k
{
61
197k
    szBoxType[0] = '\0';
62
197k
    return VSIFSeekL(fpVSIL, nNewOffset, SEEK_SET) == 0;
63
197k
}
64
65
/************************************************************************/
66
/*                             ReadFirst()                              */
67
/************************************************************************/
68
69
int GDALJP2Box::ReadFirst()
70
71
17.5k
{
72
17.5k
    return SetOffset(0) && ReadBox();
73
17.5k
}
74
75
/************************************************************************/
76
/*                              ReadNext()                              */
77
/************************************************************************/
78
79
int GDALJP2Box::ReadNext()
80
81
175k
{
82
175k
    return SetOffset(nBoxOffset + nBoxLength) && ReadBox();
83
175k
}
84
85
/************************************************************************/
86
/*                           ReadFirstChild()                           */
87
/************************************************************************/
88
89
int GDALJP2Box::ReadFirstChild(GDALJP2Box *poSuperBox)
90
91
22.2k
{
92
22.2k
    if (poSuperBox == nullptr)
93
17.5k
        return ReadFirst();
94
95
4.65k
    szBoxType[0] = '\0';
96
4.65k
    if (!poSuperBox->IsSuperBox())
97
0
        return FALSE;
98
99
4.65k
    return SetOffset(poSuperBox->nDataOffset) && ReadBox();
100
4.65k
}
101
102
/************************************************************************/
103
/*                           ReadNextChild()                            */
104
/************************************************************************/
105
106
int GDALJP2Box::ReadNextChild(GDALJP2Box *poSuperBox)
107
108
175k
{
109
175k
    if (poSuperBox == nullptr)
110
166k
        return ReadNext();
111
112
8.22k
    if (!ReadNext())
113
128
        return FALSE;
114
115
8.09k
    if (nBoxOffset >= poSuperBox->nBoxOffset + poSuperBox->nBoxLength)
116
2.78k
    {
117
2.78k
        szBoxType[0] = '\0';
118
2.78k
        return FALSE;
119
2.78k
    }
120
121
5.30k
    return TRUE;
122
8.09k
}
123
124
/************************************************************************/
125
/*                              ReadBox()                               */
126
/************************************************************************/
127
128
int GDALJP2Box::ReadBox()
129
130
197k
{
131
197k
    GUInt32 nLBox = 0;
132
197k
    GUInt32 nTBox = 0;
133
134
197k
    nBoxOffset = VSIFTellL(fpVSIL);
135
136
197k
    if (VSIFReadL(&nLBox, 4, 1, fpVSIL) != 1 ||
137
197k
        VSIFReadL(&nTBox, 4, 1, fpVSIL) != 1)
138
14.1k
    {
139
14.1k
        return FALSE;
140
14.1k
    }
141
142
183k
    memcpy(szBoxType, &nTBox, 4);
143
183k
    szBoxType[4] = '\0';
144
145
183k
    nLBox = CPL_MSBWORD32(nLBox);
146
147
183k
    if (nLBox != 1)
148
182k
    {
149
182k
        nBoxLength = nLBox;
150
182k
        nDataOffset = nBoxOffset + 8;
151
182k
    }
152
301
    else
153
301
    {
154
301
        GByte abyXLBox[8] = {0};
155
301
        if (VSIFReadL(abyXLBox, 8, 1, fpVSIL) != 1)
156
15
            return FALSE;
157
158
286
        CPL_MSBPTR64(abyXLBox);
159
286
        memcpy(&nBoxLength, abyXLBox, 8);
160
161
286
        if (nBoxLength < 0)
162
72
        {
163
72
            CPLDebug("GDALJP2", "Invalid length for box %s", szBoxType);
164
72
            return FALSE;
165
72
        }
166
214
        nDataOffset = nBoxOffset + 16;
167
214
    }
168
169
183k
    if (nBoxLength == 0 && m_bAllowGetFileSize)
170
10.1k
    {
171
10.1k
        if (VSIFSeekL(fpVSIL, 0, SEEK_END) != 0)
172
0
            return FALSE;
173
10.1k
        nBoxLength = VSIFTellL(fpVSIL) - nBoxOffset;
174
10.1k
        if (VSIFSeekL(fpVSIL, nDataOffset, SEEK_SET) != 0)
175
0
            return FALSE;
176
10.1k
    }
177
178
183k
    if (EQUAL(szBoxType, "uuid"))
179
77.8k
    {
180
77.8k
        if (VSIFReadL(abyUUID, 16, 1, fpVSIL) != 1)
181
78
            return FALSE;
182
77.7k
        nDataOffset += 16;
183
77.7k
    }
184
185
182k
    if (m_bAllowGetFileSize && GetDataLength() < 0)
186
53
    {
187
53
        CPLDebug("GDALJP2", "Invalid length for box %s", szBoxType);
188
53
        return FALSE;
189
53
    }
190
191
182k
    return TRUE;
192
182k
}
193
194
/************************************************************************/
195
/*                             IsSuperBox()                             */
196
/************************************************************************/
197
198
int GDALJP2Box::IsSuperBox()
199
200
179k
{
201
179k
    if (EQUAL(GetType(), "asoc") || EQUAL(GetType(), "jp2h") ||
202
179k
        EQUAL(GetType(), "res ") || EQUAL(GetType(), "jumb"))
203
9.38k
        return TRUE;
204
205
170k
    return FALSE;
206
179k
}
207
208
/************************************************************************/
209
/*                            ReadBoxData()                             */
210
/************************************************************************/
211
212
GByte *GDALJP2Box::ReadBoxData()
213
214
399k
{
215
399k
    GIntBig nDataLength = GetDataLength();
216
399k
    if (nDataLength > 100 * 1024 * 1024)
217
0
    {
218
0
        CPLError(CE_Failure, CPLE_AppDefined,
219
0
                 "Too big box : " CPL_FRMT_GIB " bytes", nDataLength);
220
0
        return nullptr;
221
0
    }
222
223
399k
    if (VSIFSeekL(fpVSIL, nDataOffset, SEEK_SET) != 0)
224
0
        return nullptr;
225
226
399k
    char *pszData = static_cast<char *>(
227
399k
        VSI_MALLOC_VERBOSE(static_cast<int>(nDataLength) + 1));
228
399k
    if (pszData == nullptr)
229
0
        return nullptr;
230
231
399k
    if (static_cast<GIntBig>(VSIFReadL(
232
399k
            pszData, 1, static_cast<int>(nDataLength), fpVSIL)) != nDataLength)
233
2.39k
    {
234
2.39k
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot read box content");
235
2.39k
        CPLFree(pszData);
236
2.39k
        return nullptr;
237
2.39k
    }
238
239
397k
    pszData[nDataLength] = '\0';
240
241
397k
    return reinterpret_cast<GByte *>(pszData);
242
399k
}
243
244
/************************************************************************/
245
/*                           GetDataLength()                            */
246
/************************************************************************/
247
248
GIntBig GDALJP2Box::GetDataLength() const
249
861k
{
250
861k
    return nBoxLength - (nDataOffset - nBoxOffset);
251
861k
}
252
253
/************************************************************************/
254
/*                            DumpReadable()                            */
255
/************************************************************************/
256
257
int GDALJP2Box::DumpReadable(FILE *fpOut, int nIndentLevel)
258
259
0
{
260
0
    if (fpOut == nullptr)
261
0
        fpOut = stdout;
262
263
0
    for (int i = 0; i < nIndentLevel; ++i)
264
0
        fprintf(fpOut, "  ");
265
266
0
    char szBuffer[128];
267
0
    CPLsnprintf(szBuffer, sizeof(szBuffer),
268
0
                "  Type=%s, Offset=" CPL_FRMT_GIB "/" CPL_FRMT_GIB
269
0
                ", Data Size=" CPL_FRMT_GIB,
270
0
                szBoxType, nBoxOffset, nDataOffset, GetDataLength());
271
0
    fprintf(fpOut, "%s", szBuffer);
272
273
0
    if (IsSuperBox())
274
0
    {
275
0
        fprintf(fpOut, " (super)");
276
0
    }
277
278
0
    fprintf(fpOut, "\n");
279
280
0
    if (IsSuperBox())
281
0
    {
282
0
        GDALJP2Box oSubBox(GetFILE());
283
284
0
        for (oSubBox.ReadFirstChild(this); strlen(oSubBox.GetType()) > 0;
285
0
             oSubBox.ReadNextChild(this))
286
0
        {
287
0
            oSubBox.DumpReadable(fpOut, nIndentLevel + 1);
288
0
        }
289
0
    }
290
291
0
    if (EQUAL(GetType(), "uuid"))
292
0
    {
293
0
        char *pszHex = CPLBinaryToHex(16, GetUUID());
294
0
        for (int i = 0; i < nIndentLevel; ++i)
295
0
            fprintf(fpOut, "  ");
296
297
0
        fprintf(fpOut, "    UUID=%s", pszHex);
298
299
0
        if (EQUAL(pszHex, "B14BF8BD083D4B43A5AE8CD7D5A6CE03"))
300
0
            fprintf(fpOut, " (GeoTIFF)");
301
0
        if (EQUAL(pszHex, "96A9F1F1DC98402DA7AED68E34451809"))
302
0
            fprintf(fpOut, " (MSI Worldfile)");
303
0
        if (EQUAL(pszHex, "BE7ACFCB97A942E89C71999491E3AFAC"))
304
0
            fprintf(fpOut, " (XMP)");
305
0
        CPLFree(pszHex);
306
307
0
        fprintf(fpOut, "\n");
308
0
    }
309
310
0
    return 0;
311
0
}
312
313
/************************************************************************/
314
/*                              SetType()                               */
315
/************************************************************************/
316
317
void GDALJP2Box::SetType(const char *pszType)
318
319
0
{
320
0
    CPLAssert(strlen(pszType) == 4);
321
322
0
    memcpy(szBoxType, pszType, 4);
323
0
    szBoxType[4] = '\0';
324
0
}
325
326
/************************************************************************/
327
/*                          GetWritableBoxData()                        */
328
/************************************************************************/
329
330
GByte *GDALJP2Box::GetWritableBoxData() const
331
0
{
332
0
    GByte *pabyRet =
333
0
        static_cast<GByte *>(CPLMalloc(static_cast<GUInt32>(nBoxLength)));
334
0
    const GUInt32 nLBox = CPL_MSBWORD32(static_cast<GUInt32>(nBoxLength));
335
0
    memcpy(pabyRet, &nLBox, sizeof(GUInt32));
336
0
    memcpy(pabyRet + 4, szBoxType, 4);
337
0
    memcpy(pabyRet + 8, pabyData, static_cast<GUInt32>(nBoxLength) - 8);
338
0
    return pabyRet;
339
0
}
340
341
/************************************************************************/
342
/*                          SetWritableData()                           */
343
/************************************************************************/
344
345
void GDALJP2Box::SetWritableData(int nLength, const GByte *pabyDataIn)
346
347
0
{
348
0
    CPLFree(pabyData);
349
350
0
    pabyData = static_cast<GByte *>(CPLMalloc(nLength));
351
0
    memcpy(pabyData, pabyDataIn, nLength);
352
353
0
    nBoxOffset = -9;  // Virtual offsets for data length computation.
354
0
    nDataOffset = -1;
355
356
0
    nBoxLength = 8 + nLength;
357
0
}
358
359
/************************************************************************/
360
/*                          AppendWritableData()                        */
361
/************************************************************************/
362
363
void GDALJP2Box::AppendWritableData(int nLength, const void *pabyDataIn)
364
365
0
{
366
0
    if (pabyData == nullptr)
367
0
    {
368
0
        nBoxOffset = -9;  // Virtual offsets for data length computation.
369
0
        nDataOffset = -1;
370
0
        nBoxLength = 8;
371
0
    }
372
373
0
    pabyData = static_cast<GByte *>(
374
0
        CPLRealloc(pabyData, static_cast<size_t>(GetDataLength() + nLength)));
375
0
    memcpy(pabyData + GetDataLength(), pabyDataIn, nLength);
376
377
0
    nBoxLength += nLength;
378
0
}
379
380
/************************************************************************/
381
/*                              AppendUInt32()                          */
382
/************************************************************************/
383
384
void GDALJP2Box::AppendUInt32(GUInt32 nVal)
385
0
{
386
0
    CPL_MSBPTR32(&nVal);
387
0
    AppendWritableData(4, &nVal);
388
0
}
389
390
/************************************************************************/
391
/*                              AppendUInt16()                          */
392
/************************************************************************/
393
394
void GDALJP2Box::AppendUInt16(GUInt16 nVal)
395
0
{
396
0
    CPL_MSBPTR16(&nVal);
397
0
    AppendWritableData(2, &nVal);
398
0
}
399
400
/************************************************************************/
401
/*                              AppendUInt8()                           */
402
/************************************************************************/
403
404
void GDALJP2Box::AppendUInt8(GByte nVal)
405
0
{
406
0
    AppendWritableData(1, &nVal);
407
0
}
408
409
/************************************************************************/
410
/*                           CreateUUIDBox()                            */
411
/************************************************************************/
412
413
GDALJP2Box *GDALJP2Box::CreateUUIDBox(const GByte *pabyUUID, int nDataSize,
414
                                      const GByte *pabyDataIn)
415
416
0
{
417
0
    GDALJP2Box *const poBox = new GDALJP2Box();
418
0
    poBox->SetType("uuid");
419
420
0
    poBox->AppendWritableData(16, pabyUUID);
421
0
    poBox->AppendWritableData(nDataSize, pabyDataIn);
422
423
0
    return poBox;
424
0
}
425
426
/************************************************************************/
427
/*                           CreateAsocBox()                            */
428
/************************************************************************/
429
430
GDALJP2Box *GDALJP2Box::CreateAsocBox(int nCount,
431
                                      const GDALJP2Box *const *papoBoxes)
432
0
{
433
0
    return CreateSuperBox("asoc", nCount, papoBoxes);
434
0
}
435
436
/************************************************************************/
437
/*                           CreateAsocBox()                            */
438
/************************************************************************/
439
440
GDALJP2Box *GDALJP2Box::CreateSuperBox(const char *pszType, int nCount,
441
                                       const GDALJP2Box *const *papoBoxes)
442
0
{
443
0
    int nDataSize = 0;
444
445
    /* -------------------------------------------------------------------- */
446
    /*      Compute size of data area of asoc box.                          */
447
    /* -------------------------------------------------------------------- */
448
0
    for (int iBox = 0; iBox < nCount; ++iBox)
449
0
        nDataSize += 8 + static_cast<int>(papoBoxes[iBox]->GetDataLength());
450
451
0
    GByte *pabyNext = static_cast<GByte *>(CPLMalloc(nDataSize));
452
0
    GByte *pabyCompositeData = pabyNext;
453
454
    /* -------------------------------------------------------------------- */
455
    /*      Copy subboxes headers and data into buffer.                     */
456
    /* -------------------------------------------------------------------- */
457
0
    for (int iBox = 0; iBox < nCount; ++iBox)
458
0
    {
459
0
        GUInt32 nLBox =
460
0
            CPL_MSBWORD32(static_cast<GUInt32>(papoBoxes[iBox]->nBoxLength));
461
0
        memcpy(pabyNext, &nLBox, 4);
462
0
        pabyNext += 4;
463
464
0
        memcpy(pabyNext, papoBoxes[iBox]->szBoxType, 4);
465
0
        pabyNext += 4;
466
467
0
        memcpy(pabyNext, papoBoxes[iBox]->pabyData,
468
0
               static_cast<int>(papoBoxes[iBox]->GetDataLength()));
469
0
        pabyNext += papoBoxes[iBox]->GetDataLength();
470
0
    }
471
472
    /* -------------------------------------------------------------------- */
473
    /*      Create asoc box.                                                */
474
    /* -------------------------------------------------------------------- */
475
0
    GDALJP2Box *const poAsoc = new GDALJP2Box();
476
477
0
    poAsoc->SetType(pszType);
478
0
    poAsoc->SetWritableData(nDataSize, pabyCompositeData);
479
480
0
    CPLFree(pabyCompositeData);
481
482
0
    return poAsoc;
483
0
}
484
485
/************************************************************************/
486
/*                            CreateLblBox()                            */
487
/************************************************************************/
488
489
GDALJP2Box *GDALJP2Box::CreateLblBox(const char *pszLabel)
490
491
0
{
492
0
    GDALJP2Box *const poBox = new GDALJP2Box();
493
0
    poBox->SetType("lbl ");
494
0
    poBox->SetWritableData(static_cast<int>(strlen(pszLabel) + 1),
495
0
                           reinterpret_cast<const GByte *>(pszLabel));
496
497
0
    return poBox;
498
0
}
499
500
/************************************************************************/
501
/*                       CreateLabelledXMLAssoc()                       */
502
/************************************************************************/
503
504
GDALJP2Box *GDALJP2Box::CreateLabelledXMLAssoc(const char *pszLabel,
505
                                               const char *pszXML)
506
507
0
{
508
0
    GDALJP2Box oLabel;
509
0
    oLabel.SetType("lbl ");
510
0
    oLabel.SetWritableData(static_cast<int>(strlen(pszLabel) + 1),
511
0
                           reinterpret_cast<const GByte *>(pszLabel));
512
513
0
    GDALJP2Box oXML;
514
0
    oXML.SetType("xml ");
515
0
    oXML.SetWritableData(static_cast<int>(strlen(pszXML) + 1),
516
0
                         reinterpret_cast<const GByte *>(pszXML));
517
518
0
    GDALJP2Box *aoList[2] = {&oLabel, &oXML};
519
520
0
    return CreateAsocBox(2, aoList);
521
0
}
522
523
/************************************************************************/
524
/*                    CreateJUMBFDescriptionBox()                       */
525
/************************************************************************/
526
527
GDALJP2Box *GDALJP2Box::CreateJUMBFDescriptionBox(const GByte *pabyUUIDType,
528
                                                  const char *pszLabel)
529
530
0
{
531
0
    GDALJP2Box *const poBox = new GDALJP2Box();
532
0
    poBox->SetType("jumd");
533
534
0
    poBox->AppendWritableData(16, pabyUUIDType);
535
0
    poBox->AppendUInt8(3);  // requestable field
536
0
    const size_t nLabelLen = strlen(pszLabel) + 1;
537
0
    poBox->AppendWritableData(static_cast<int>(nLabelLen), pszLabel);
538
539
0
    return poBox;
540
0
}
541
542
/************************************************************************/
543
/*                         CreateJUMBFBox()                             */
544
/************************************************************************/
545
546
GDALJP2Box *GDALJP2Box::CreateJUMBFBox(const GDALJP2Box *poJUMBFDescriptionBox,
547
                                       int nCount,
548
                                       const GDALJP2Box *const *papoBoxes)
549
0
{
550
0
    std::vector<const GDALJP2Box *> apoBoxes;
551
0
    apoBoxes.push_back(poJUMBFDescriptionBox);
552
0
    apoBoxes.insert(apoBoxes.end(), papoBoxes, papoBoxes + nCount);
553
0
    return CreateSuperBox("jumb", static_cast<int>(apoBoxes.size()),
554
0
                          apoBoxes.data());
555
0
}
556
557
/*! @endcond */