Coverage Report

Created: 2026-08-11 08:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/gcore/gdaljp2structure.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  GDALJP2Stucture - Dump structure of a JP2/J2K file
5
 * Author:   Even Rouault, <even dot rouault at spatialys dot com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2015, European Union (European Environment Agency)
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "cpl_port.h"
14
#include "gdaljp2metadata.h"
15
16
#include <algorithm>
17
#include <cmath>
18
#include <cstring>
19
20
#include <string>
21
22
#include "cpl_conv.h"
23
#include "cpl_error.h"
24
#include "cpl_minixml.h"
25
#include "cpl_string.h"
26
#include "cpl_vsi.h"
27
#include "gdal.h"
28
#include "gdal_priv.h"
29
30
constexpr int knbMaxJPEG2000Components = 16384;  // per the JPEG2000 standard
31
32
namespace
33
{
34
struct DumpContext
35
{
36
    int nCurLineCount = 0;
37
    int nMaxLineCount = 0;
38
    const char *pszCodestreamMarkers = nullptr;
39
    bool bDumpAll = false;
40
    bool bDumpCodestream = false;
41
    bool bDumpBinaryContent = false;
42
    bool bDumpTextContent = false;
43
    bool bDumpJP2Boxes = false;
44
    bool bStopAtSOD = false;
45
    bool bSODEncountered = false;
46
    bool bAllowGetFileSize = true;
47
};
48
}  // namespace
49
50
static CPLXMLNode *GetLastChild(CPLXMLNode *psParent)
51
1.52M
{
52
1.52M
    CPLXMLNode *psChild = psParent->psChild;
53
5.22M
    while (psChild && psChild->psNext)
54
3.69M
        psChild = psChild->psNext;
55
1.52M
    return psChild;
56
1.52M
}
57
58
static CPLXMLNode *_AddError(CPLXMLNode *psParent, const char *pszErrorMsg,
59
                             GIntBig nOffset = 0)
60
2.40M
{
61
2.40M
    CPLXMLNode *psError = CPLCreateXMLNode(psParent, CXT_Element, "Error");
62
2.40M
    CPLAddXMLAttributeAndValue(psError, "message", pszErrorMsg);
63
2.40M
    if (nOffset)
64
16.2k
    {
65
16.2k
        CPLAddXMLAttributeAndValue(psError, "offset",
66
16.2k
                                   CPLSPrintf(CPL_FRMT_GIB, nOffset));
67
16.2k
    }
68
2.40M
    return psError;
69
2.40M
}
70
71
static CPLXMLNode *AddElement(CPLXMLNode *psParent, CPLXMLNode *&psLastChild,
72
                              DumpContext *psDumpContext, CPLXMLNode *psNewElt)
73
18.8M
{
74
18.8M
    if (psDumpContext->nCurLineCount > psDumpContext->nMaxLineCount)
75
30
    {
76
30
        CPLDestroyXMLNode(psNewElt);
77
78
30
        if (psDumpContext->nCurLineCount == psDumpContext->nMaxLineCount + 1)
79
29
        {
80
29
            _AddError(psParent, "Too many lines in dump");
81
29
            psDumpContext->nCurLineCount++;
82
29
        }
83
30
        return nullptr;
84
30
    }
85
18.8M
    psDumpContext->nCurLineCount++;
86
87
18.8M
    if (psLastChild == nullptr)
88
1.52M
        psLastChild = GetLastChild(psParent);
89
18.8M
    if (psLastChild == nullptr)
90
49.5k
        psParent->psChild = psNewElt;
91
18.8M
    else
92
18.8M
        psLastChild->psNext = psNewElt;
93
18.8M
    psLastChild = psNewElt;
94
18.8M
    return psNewElt;
95
18.8M
}
96
97
static void AddField(CPLXMLNode *psParent, CPLXMLNode *&psLastChild,
98
                     DumpContext *psDumpContext, const char *pszFieldName,
99
                     int nFieldSize, const char *pszValue,
100
                     const char *pszDescription = nullptr)
101
21.7k
{
102
21.7k
    if (psDumpContext->nCurLineCount > psDumpContext->nMaxLineCount + 1)
103
0
    {
104
0
        return;
105
0
    }
106
107
21.7k
    CPLXMLNode *psField =
108
21.7k
        CPLCreateXMLElementAndValue(nullptr, "Field", pszValue);
109
21.7k
    CPLAddXMLAttributeAndValue(psField, "name", pszFieldName);
110
21.7k
    CPLAddXMLAttributeAndValue(psField, "type", "string");
111
21.7k
    CPLAddXMLAttributeAndValue(psField, "size", CPLSPrintf("%d", nFieldSize));
112
21.7k
    if (pszDescription)
113
0
        CPLAddXMLAttributeAndValue(psField, "description", pszDescription);
114
21.7k
    AddElement(psParent, psLastChild, psDumpContext, psField);
115
21.7k
}
116
117
static void AddHexField(CPLXMLNode *psParent, CPLXMLNode *&psLastChild,
118
                        DumpContext *psDumpContext, const char *pszFieldName,
119
                        int nFieldSize, const char *pszValue,
120
                        const char *pszDescription = nullptr)
121
400k
{
122
400k
    if (psDumpContext->nCurLineCount > psDumpContext->nMaxLineCount + 1)
123
0
    {
124
0
        return;
125
0
    }
126
127
400k
    CPLXMLNode *psField =
128
400k
        CPLCreateXMLElementAndValue(nullptr, "Field", pszValue);
129
400k
    CPLAddXMLAttributeAndValue(psField, "name", pszFieldName);
130
400k
    CPLAddXMLAttributeAndValue(psField, "type", "hexint");
131
400k
    CPLAddXMLAttributeAndValue(psField, "size", CPLSPrintf("%d", nFieldSize));
132
400k
    if (pszDescription)
133
0
        CPLAddXMLAttributeAndValue(psField, "description", pszDescription);
134
400k
    AddElement(psParent, psLastChild, psDumpContext, psField);
135
400k
}
136
137
static void AddField(CPLXMLNode *psParent, CPLXMLNode *&psLastChild,
138
                     DumpContext *psDumpContext, const char *pszFieldName,
139
                     GByte nVal, const char *pszDescription = nullptr)
140
6.68M
{
141
6.68M
    if (psDumpContext->nCurLineCount > psDumpContext->nMaxLineCount + 1)
142
266
    {
143
266
        return;
144
266
    }
145
146
6.68M
    CPLXMLNode *psField =
147
6.68M
        CPLCreateXMLElementAndValue(nullptr, "Field", CPLSPrintf("%d", nVal));
148
6.68M
    CPLAddXMLAttributeAndValue(psField, "name", pszFieldName);
149
6.68M
    CPLAddXMLAttributeAndValue(psField, "type", "uint8");
150
6.68M
    if (pszDescription)
151
4.67M
        CPLAddXMLAttributeAndValue(psField, "description", pszDescription);
152
6.68M
    AddElement(psParent, psLastChild, psDumpContext, psField);
153
6.68M
}
154
155
static void AddField(CPLXMLNode *psParent, CPLXMLNode *&psLastChild,
156
                     DumpContext *psDumpContext, const char *pszFieldName,
157
                     GUInt16 nVal, const char *pszDescription = nullptr)
158
1.57M
{
159
1.57M
    if (psDumpContext->nCurLineCount > psDumpContext->nMaxLineCount + 1)
160
10
    {
161
10
        return;
162
10
    }
163
164
1.57M
    CPLXMLNode *psField =
165
1.57M
        CPLCreateXMLElementAndValue(nullptr, "Field", CPLSPrintf("%d", nVal));
166
1.57M
    CPLAddXMLAttributeAndValue(psField, "name", pszFieldName);
167
1.57M
    CPLAddXMLAttributeAndValue(psField, "type", "uint16");
168
1.57M
    if (pszDescription)
169
144k
        CPLAddXMLAttributeAndValue(psField, "description", pszDescription);
170
1.57M
    AddElement(psParent, psLastChild, psDumpContext, psField);
171
1.57M
}
172
173
static void AddField(CPLXMLNode *psParent, CPLXMLNode *&psLastChild,
174
                     DumpContext *psDumpContext, const char *pszFieldName,
175
                     GUInt32 nVal, const char *pszDescription = nullptr)
176
6.19M
{
177
6.19M
    if (psDumpContext->nCurLineCount - 1 >= psDumpContext->nMaxLineCount)
178
76.3k
    {
179
76.3k
        return;
180
76.3k
    }
181
182
6.12M
    CPLXMLNode *psField =
183
6.12M
        CPLCreateXMLElementAndValue(nullptr, "Field", CPLSPrintf("%u", nVal));
184
6.12M
    CPLAddXMLAttributeAndValue(psField, "name", pszFieldName);
185
6.12M
    CPLAddXMLAttributeAndValue(psField, "type", "uint32");
186
6.12M
    if (pszDescription)
187
11.8k
        CPLAddXMLAttributeAndValue(psField, "description", pszDescription);
188
6.12M
    AddElement(psParent, psLastChild, psDumpContext, psField);
189
6.12M
}
190
191
static const char *GetInterpretationOfBPC(GByte bpc)
192
34.2k
{
193
34.2k
    if (bpc == 255)
194
1.26k
        return nullptr;
195
32.9k
    if ((bpc & 0x80))
196
8.32k
        return CPLSPrintf("Signed %d bits", 1 + (bpc & 0x7F));
197
24.6k
    else
198
24.6k
        return CPLSPrintf("Unsigned %d bits", 1 + bpc);
199
32.9k
}
200
201
static const char *GetStandardFieldString(GUInt16 nVal)
202
82.1k
{
203
82.1k
    switch (nVal)
204
82.1k
    {
205
2.53k
        case 1:
206
2.53k
            return "Codestream contains no extensions";
207
497
        case 2:
208
497
            return "Contains multiple composition layers";
209
811
        case 3:
210
811
            return "Codestream is compressed using JPEG 2000 and requires at "
211
811
                   "least a Profile 0 decoder";
212
1.22k
        case 4:
213
1.22k
            return "Codestream is compressed using JPEG 2000 and requires at "
214
1.22k
                   "least a Profile 1 decoder";
215
324
        case 5:
216
324
            return "Codestream is compressed using JPEG 2000 unrestricted";
217
357
        case 35:
218
357
            return "Contains IPR metadata";
219
1.10k
        case 67:
220
1.10k
            return "Contains GMLJP2 metadata";
221
75.2k
        default:
222
75.2k
            return nullptr;
223
82.1k
    }
224
82.1k
}
225
226
static void DumpGeoTIFFBox(CPLXMLNode *psBox, GDALJP2Box &oBox,
227
                           DumpContext *psDumpContext)
228
93.2k
{
229
93.2k
    GIntBig nBoxDataLength = oBox.GetDataLength();
230
93.2k
    GByte *pabyBoxData = oBox.ReadBoxData();
231
93.2k
    GDALDriver *poVRTDriver =
232
93.2k
        static_cast<GDALDriver *>(GDALGetDriverByName("VRT"));
233
93.2k
    if (pabyBoxData && poVRTDriver)
234
92.7k
    {
235
92.7k
        const CPLString osTmpFilename(VSIMemGenerateHiddenFilename("tmp.tif"));
236
92.7k
        CPL_IGNORE_RET_VAL(VSIFCloseL(VSIFileFromMemBuffer(
237
92.7k
            osTmpFilename, pabyBoxData, nBoxDataLength, FALSE)));
238
92.7k
        CPLPushErrorHandler(CPLQuietErrorHandler);
239
92.7k
        GDALDataset *poDS =
240
92.7k
            GDALDataset::FromHandle(GDALOpen(osTmpFilename, GA_ReadOnly));
241
92.7k
        CPLPopErrorHandler();
242
        // Reject GeoJP2 boxes with a TIFF with band_count > 1.
243
92.7k
        if (poDS && poDS->GetRasterCount() > 1)
244
866
        {
245
866
            GDALClose(poDS);
246
866
            poDS = nullptr;
247
866
        }
248
92.7k
        if (poDS)
249
67.8k
        {
250
67.8k
            const CPLString osTmpVRTFilename(
251
67.8k
                CPLResetExtensionSafe(osTmpFilename.c_str(), "vrt"));
252
67.8k
            GDALDataset *poVRTDS = poVRTDriver->CreateCopy(
253
67.8k
                osTmpVRTFilename, poDS, FALSE, nullptr, nullptr, nullptr);
254
67.8k
            GDALClose(poVRTDS);
255
67.8k
            CPLXMLNode *psXMLVRT = CPLParseXMLFile(osTmpVRTFilename.c_str());
256
67.8k
            if (psXMLVRT)
257
67.8k
            {
258
67.8k
                ++psDumpContext->nCurLineCount;
259
260
67.8k
                CPLXMLNode *psXMLContentNode =
261
67.8k
                    CPLCreateXMLNode(psBox, CXT_Element, "DecodedGeoTIFF");
262
67.8k
                psXMLContentNode->psChild = psXMLVRT;
263
67.8k
                CPLXMLNode *psPrev = nullptr;
264
417k
                for (CPLXMLNode *psIter = psXMLVRT->psChild; psIter;
265
349k
                     psIter = psIter->psNext)
266
349k
                {
267
349k
                    if (psIter->eType == CXT_Element &&
268
213k
                        strcmp(psIter->pszValue, "VRTRasterBand") == 0)
269
67.8k
                    {
270
67.8k
                        CPLXMLNode *psNext = psIter->psNext;
271
67.8k
                        psIter->psNext = nullptr;
272
67.8k
                        CPLDestroyXMLNode(psIter);
273
67.8k
                        if (psPrev)
274
67.8k
                            psPrev->psNext = psNext;
275
0
                        else
276
0
                            break;
277
67.8k
                        psIter = psPrev;
278
67.8k
                    }
279
349k
                    psPrev = psIter;
280
349k
                }
281
67.8k
                CPLCreateXMLNode(psXMLVRT, CXT_Element, "VRTRasterBand");
282
67.8k
            }
283
284
67.8k
            VSIUnlink(osTmpVRTFilename);
285
67.8k
            GDALClose(poDS);
286
67.8k
        }
287
92.7k
        VSIUnlink(osTmpFilename);
288
92.7k
    }
289
93.2k
    CPLFree(pabyBoxData);
290
93.2k
}
291
292
static void DumpFTYPBox(CPLXMLNode *psBox, GDALJP2Box &oBox,
293
                        DumpContext *psDumpContext)
294
3.08k
{
295
3.08k
    GIntBig nBoxDataLength = oBox.GetDataLength();
296
3.08k
    GByte *pabyBoxData = oBox.ReadBoxData();
297
3.08k
    if (pabyBoxData)
298
3.08k
    {
299
3.08k
        CPLXMLNode *psDecodedContent =
300
3.08k
            CPLCreateXMLNode(psBox, CXT_Element, "DecodedContent");
301
3.08k
        GIntBig nRemainingLength = nBoxDataLength;
302
3.08k
        GByte *pabyIter = pabyBoxData;
303
3.08k
        CPLXMLNode *psLastChild = nullptr;
304
3.08k
        if (nRemainingLength >= 4)
305
2.99k
        {
306
2.99k
            char szBranding[5];
307
2.99k
            memcpy(szBranding, pabyIter, 4);
308
2.99k
            szBranding[4] = 0;
309
2.99k
            AddField(psDecodedContent, psLastChild, psDumpContext, "BR", 4,
310
2.99k
                     szBranding);
311
2.99k
            pabyIter += 4;
312
2.99k
            nRemainingLength -= 4;
313
2.99k
        }
314
3.08k
        if (nRemainingLength >= 4)
315
2.85k
        {
316
2.85k
            GUInt32 nVal;
317
2.85k
            memcpy(&nVal, pabyIter, 4);
318
2.85k
            CPL_MSBPTR32(&nVal);
319
2.85k
            AddField(psDecodedContent, psLastChild, psDumpContext, "MinV",
320
2.85k
                     nVal);
321
2.85k
            pabyIter += 4;
322
2.85k
            nRemainingLength -= 4;
323
2.85k
        }
324
3.08k
        int nCLIndex = 0;
325
21.7k
        while (nRemainingLength >= 4)
326
18.6k
        {
327
18.6k
            char szBranding[5];
328
18.6k
            memcpy(szBranding, pabyIter, 4);
329
18.6k
            szBranding[4] = 0;
330
18.6k
            AddField(psDecodedContent, psLastChild, psDumpContext,
331
18.6k
                     CPLSPrintf("CL%d", nCLIndex), 4, szBranding);
332
18.6k
            pabyIter += 4;
333
18.6k
            nRemainingLength -= 4;
334
18.6k
            nCLIndex++;
335
18.6k
        }
336
3.08k
        if (nRemainingLength > 0)
337
35
            AddElement(
338
35
                psDecodedContent, psLastChild, psDumpContext,
339
35
                CPLCreateXMLElementAndValue(
340
35
                    nullptr, "RemainingBytes",
341
35
                    CPLSPrintf("%d", static_cast<int>(nRemainingLength))));
342
3.08k
    }
343
3.08k
    CPLFree(pabyBoxData);
344
3.08k
}
345
346
static void DumpIHDRBox(CPLXMLNode *psBox, GDALJP2Box &oBox,
347
                        DumpContext *psDumpContext)
348
8.75k
{
349
8.75k
    GIntBig nBoxDataLength = oBox.GetDataLength();
350
8.75k
    GByte *pabyBoxData = oBox.ReadBoxData();
351
8.75k
    if (pabyBoxData)
352
8.74k
    {
353
8.74k
        CPLXMLNode *psDecodedContent =
354
8.74k
            CPLCreateXMLNode(psBox, CXT_Element, "DecodedContent");
355
8.74k
        GIntBig nRemainingLength = nBoxDataLength;
356
8.74k
        GByte *pabyIter = pabyBoxData;
357
8.74k
        CPLXMLNode *psLastChild = nullptr;
358
8.74k
        if (nRemainingLength >= 4)
359
8.62k
        {
360
8.62k
            GUInt32 nVal;
361
8.62k
            memcpy(&nVal, pabyIter, 4);
362
8.62k
            CPL_MSBPTR32(&nVal);
363
8.62k
            AddField(psDecodedContent, psLastChild, psDumpContext, "HEIGHT",
364
8.62k
                     nVal);
365
8.62k
            pabyIter += 4;
366
8.62k
            nRemainingLength -= 4;
367
8.62k
        }
368
8.74k
        if (nRemainingLength >= 4)
369
8.61k
        {
370
8.61k
            GUInt32 nVal;
371
8.61k
            memcpy(&nVal, pabyIter, 4);
372
8.61k
            CPL_MSBPTR32(&nVal);
373
8.61k
            AddField(psDecodedContent, psLastChild, psDumpContext, "WIDTH",
374
8.61k
                     nVal);
375
8.61k
            pabyIter += 4;
376
8.61k
            nRemainingLength -= 4;
377
8.61k
        }
378
8.74k
        if (nRemainingLength >= 2)
379
8.62k
        {
380
8.62k
            GUInt16 nVal;
381
8.62k
            memcpy(&nVal, pabyIter, 2);
382
8.62k
            CPL_MSBPTR16(&nVal);
383
8.62k
            AddField(psDecodedContent, psLastChild, psDumpContext, "NC", nVal);
384
8.62k
            pabyIter += 2;
385
8.62k
            nRemainingLength -= 2;
386
8.62k
        }
387
8.74k
        if (nRemainingLength >= 1)
388
8.72k
        {
389
8.72k
            AddField(psDecodedContent, psLastChild, psDumpContext, "BPC",
390
8.72k
                     *pabyIter, GetInterpretationOfBPC(*pabyIter));
391
8.72k
            pabyIter += 1;
392
8.72k
            nRemainingLength -= 1;
393
8.72k
        }
394
8.74k
        if (nRemainingLength >= 1)
395
8.60k
        {
396
8.60k
            AddField(psDecodedContent, psLastChild, psDumpContext, "C",
397
8.60k
                     *pabyIter);
398
8.60k
            pabyIter += 1;
399
8.60k
            nRemainingLength -= 1;
400
8.60k
        }
401
8.74k
        if (nRemainingLength >= 1)
402
8.59k
        {
403
8.59k
            AddField(psDecodedContent, psLastChild, psDumpContext, "UnkC",
404
8.59k
                     *pabyIter);
405
8.59k
            pabyIter += 1;
406
8.59k
            nRemainingLength -= 1;
407
8.59k
        }
408
8.74k
        if (nRemainingLength >= 1)
409
8.58k
        {
410
8.58k
            AddField(psDecodedContent, psLastChild, psDumpContext, "IPR",
411
8.58k
                     *pabyIter);
412
            /*pabyIter += 1;*/
413
8.58k
            nRemainingLength -= 1;
414
8.58k
        }
415
8.74k
        if (nRemainingLength > 0)
416
39
            AddElement(
417
39
                psDecodedContent, psLastChild, psDumpContext,
418
39
                CPLCreateXMLElementAndValue(
419
39
                    nullptr, "RemainingBytes",
420
39
                    CPLSPrintf("%d", static_cast<int>(nRemainingLength))));
421
8.74k
    }
422
8.75k
    CPLFree(pabyBoxData);
423
8.75k
}
424
425
static void DumpBPCCBox(CPLXMLNode *psBox, GDALJP2Box &oBox,
426
                        DumpContext *psDumpContext)
427
380
{
428
380
    GIntBig nBoxDataLength = oBox.GetDataLength();
429
380
    GByte *pabyBoxData = oBox.ReadBoxData();
430
380
    if (pabyBoxData)
431
379
    {
432
379
        CPLXMLNode *psDecodedContent =
433
379
            CPLCreateXMLNode(psBox, CXT_Element, "DecodedContent");
434
379
        GIntBig nRemainingLength = nBoxDataLength;
435
379
        GByte *pabyIter = pabyBoxData;
436
379
        int nBPCIndex = 0;
437
379
        CPLXMLNode *psLastChild = nullptr;
438
23.1k
        while (nRemainingLength >= 1 && nBPCIndex < knbMaxJPEG2000Components)
439
22.7k
        {
440
22.7k
            AddField(psDecodedContent, psLastChild, psDumpContext,
441
22.7k
                     CPLSPrintf("BPC%d", nBPCIndex), *pabyIter,
442
22.7k
                     GetInterpretationOfBPC(*pabyIter));
443
22.7k
            nBPCIndex++;
444
22.7k
            pabyIter += 1;
445
22.7k
            nRemainingLength -= 1;
446
22.7k
        }
447
379
        if (nRemainingLength > 0)
448
1
            AddElement(
449
1
                psDecodedContent, psLastChild, psDumpContext,
450
1
                CPLCreateXMLElementAndValue(
451
1
                    nullptr, "RemainingBytes",
452
1
                    CPLSPrintf("%d", static_cast<int>(nRemainingLength))));
453
379
    }
454
380
    CPLFree(pabyBoxData);
455
380
}
456
457
static void DumpCOLRBox(CPLXMLNode *psBox, GDALJP2Box &oBox,
458
                        DumpContext *psDumpContext)
459
15.4k
{
460
15.4k
    GIntBig nBoxDataLength = oBox.GetDataLength();
461
15.4k
    GByte *pabyBoxData = oBox.ReadBoxData();
462
15.4k
    if (pabyBoxData)
463
15.4k
    {
464
15.4k
        CPLXMLNode *psDecodedContent =
465
15.4k
            CPLCreateXMLNode(psBox, CXT_Element, "DecodedContent");
466
15.4k
        GIntBig nRemainingLength = nBoxDataLength;
467
15.4k
        GByte *pabyIter = pabyBoxData;
468
15.4k
        GByte nMeth;
469
15.4k
        CPLXMLNode *psLastChild = nullptr;
470
15.4k
        if (nRemainingLength >= 1)
471
15.2k
        {
472
15.2k
            nMeth = *pabyIter;
473
15.2k
            AddField(psDecodedContent, psLastChild, psDumpContext, "METH",
474
15.2k
                     nMeth,
475
15.2k
                     (nMeth == 1)   ? "Enumerated Colourspace"
476
15.2k
                     : (nMeth == 2) ? "Restricted ICC profile"
477
1.88k
                                    : nullptr);
478
15.2k
            pabyIter += 1;
479
15.2k
            nRemainingLength -= 1;
480
15.2k
        }
481
15.4k
        if (nRemainingLength >= 1)
482
15.2k
        {
483
15.2k
            AddField(psDecodedContent, psLastChild, psDumpContext, "PREC",
484
15.2k
                     *pabyIter);
485
15.2k
            pabyIter += 1;
486
15.2k
            nRemainingLength -= 1;
487
15.2k
        }
488
15.4k
        if (nRemainingLength >= 1)
489
15.2k
        {
490
15.2k
            AddField(psDecodedContent, psLastChild, psDumpContext, "APPROX",
491
15.2k
                     *pabyIter);
492
15.2k
            pabyIter += 1;
493
15.2k
            nRemainingLength -= 1;
494
15.2k
        }
495
15.4k
        if (nRemainingLength >= 4)
496
14.9k
        {
497
14.9k
            GUInt32 nVal;
498
14.9k
            memcpy(&nVal, pabyIter, 4);
499
14.9k
            CPL_MSBPTR32(&nVal);
500
14.9k
            AddField(psDecodedContent, psLastChild, psDumpContext, "EnumCS",
501
14.9k
                     nVal,
502
14.9k
                     (nVal == 16)   ? "sRGB"
503
14.9k
                     : (nVal == 17) ? "greyscale"
504
13.4k
                     : (nVal == 18) ? "sYCC"
505
3.16k
                                    : nullptr);
506
            /*pabyIter += 4;*/
507
14.9k
            nRemainingLength -= 4;
508
14.9k
        }
509
15.4k
        if (nRemainingLength > 0)
510
101
            AddElement(
511
101
                psDecodedContent, psLastChild, psDumpContext,
512
101
                CPLCreateXMLElementAndValue(
513
101
                    nullptr, "RemainingBytes",
514
101
                    CPLSPrintf("%d", static_cast<int>(nRemainingLength))));
515
15.4k
    }
516
15.4k
    CPLFree(pabyBoxData);
517
15.4k
}
518
519
static void DumpPCLRBox(CPLXMLNode *psBox, GDALJP2Box &oBox,
520
                        DumpContext *psDumpContext)
521
1.33k
{
522
1.33k
    GIntBig nBoxDataLength = oBox.GetDataLength();
523
1.33k
    GByte *pabyBoxData = oBox.ReadBoxData();
524
1.33k
    if (pabyBoxData)
525
1.33k
    {
526
1.33k
        CPLXMLNode *psDecodedContent =
527
1.33k
            CPLCreateXMLNode(psBox, CXT_Element, "DecodedContent");
528
1.33k
        GIntBig nRemainingLength = nBoxDataLength;
529
1.33k
        GByte *pabyIter = pabyBoxData;
530
1.33k
        GUInt16 NE = 0;
531
1.33k
        CPLXMLNode *psLastChild = nullptr;
532
1.33k
        if (nRemainingLength >= 2)
533
1.31k
        {
534
1.31k
            GUInt16 nVal;
535
1.31k
            memcpy(&nVal, pabyIter, 2);
536
1.31k
            CPL_MSBPTR16(&nVal);
537
1.31k
            NE = nVal;
538
1.31k
            AddField(psDecodedContent, psLastChild, psDumpContext, "NE", nVal);
539
1.31k
            pabyIter += 2;
540
1.31k
            nRemainingLength -= 2;
541
1.31k
        }
542
1.33k
        GByte NPC = 0;
543
1.33k
        if (nRemainingLength >= 1)
544
1.31k
        {
545
1.31k
            NPC = *pabyIter;
546
1.31k
            AddField(psDecodedContent, psLastChild, psDumpContext, "NPC", NPC);
547
1.31k
            pabyIter += 1;
548
1.31k
            nRemainingLength -= 1;
549
1.31k
        }
550
1.33k
        int b8BitOnly = TRUE;
551
11.8k
        for (int i = 0; i < NPC; i++)
552
10.4k
        {
553
10.4k
            if (nRemainingLength >= 1)
554
1.88k
            {
555
1.88k
                b8BitOnly &= (*pabyIter <= 7);
556
1.88k
                AddField(psDecodedContent, psLastChild, psDumpContext,
557
1.88k
                         CPLSPrintf("B%d", i), *pabyIter,
558
1.88k
                         GetInterpretationOfBPC(*pabyIter));
559
1.88k
                pabyIter += 1;
560
1.88k
                nRemainingLength -= 1;
561
1.88k
            }
562
10.4k
        }
563
1.33k
        if (b8BitOnly)
564
1.26k
        {
565
2.39M
            for (int j = 0; j < NE; j++)
566
2.39M
            {
567
35.5M
                for (int i = 0; i < NPC; i++)
568
33.1M
                {
569
33.1M
                    if (nRemainingLength >= 1)
570
556
                    {
571
556
                        AddField(psDecodedContent, psLastChild, psDumpContext,
572
556
                                 CPLSPrintf("C_%d_%d", j, i), *pabyIter);
573
556
                        pabyIter += 1;
574
556
                        nRemainingLength -= 1;
575
556
                    }
576
33.1M
                }
577
2.39M
            }
578
1.26k
        }
579
1.33k
        if (nRemainingLength > 0)
580
1.20k
            AddElement(
581
1.20k
                psDecodedContent, psLastChild, psDumpContext,
582
1.20k
                CPLCreateXMLElementAndValue(
583
1.20k
                    nullptr, "RemainingBytes",
584
1.20k
                    CPLSPrintf("%d", static_cast<int>(nRemainingLength))));
585
1.33k
    }
586
1.33k
    CPLFree(pabyBoxData);
587
1.33k
}
588
589
static void DumpCMAPBox(CPLXMLNode *psBox, GDALJP2Box &oBox,
590
                        DumpContext *psDumpContext)
591
186
{
592
186
    GIntBig nBoxDataLength = oBox.GetDataLength();
593
186
    GByte *pabyBoxData = oBox.ReadBoxData();
594
186
    if (pabyBoxData)
595
185
    {
596
185
        CPLXMLNode *psDecodedContent =
597
185
            CPLCreateXMLNode(psBox, CXT_Element, "DecodedContent");
598
185
        GIntBig nRemainingLength = nBoxDataLength;
599
185
        GByte *pabyIter = pabyBoxData;
600
185
        int nIndex = 0;
601
185
        CPLXMLNode *psLastChild = nullptr;
602
5.26k
        while (nRemainingLength >= 2 + 1 + 1 &&
603
5.08k
               nIndex < knbMaxJPEG2000Components)
604
5.08k
        {
605
5.08k
            GUInt16 nVal;
606
5.08k
            memcpy(&nVal, pabyIter, 2);
607
5.08k
            CPL_MSBPTR16(&nVal);
608
5.08k
            AddField(psDecodedContent, psLastChild, psDumpContext,
609
5.08k
                     CPLSPrintf("CMP%d", nIndex), nVal);
610
5.08k
            pabyIter += 2;
611
5.08k
            nRemainingLength -= 2;
612
613
5.08k
            AddField(psDecodedContent, psLastChild, psDumpContext,
614
5.08k
                     CPLSPrintf("MTYP%d", nIndex), *pabyIter,
615
5.08k
                     (*pabyIter == 0)   ? "Direct use"
616
5.08k
                     : (*pabyIter == 1) ? "Palette mapping"
617
2.97k
                                        : nullptr);
618
5.08k
            pabyIter += 1;
619
5.08k
            nRemainingLength -= 1;
620
621
5.08k
            AddField(psDecodedContent, psLastChild, psDumpContext,
622
5.08k
                     CPLSPrintf("PCOL%d", nIndex), *pabyIter);
623
5.08k
            pabyIter += 1;
624
5.08k
            nRemainingLength -= 1;
625
626
5.08k
            nIndex++;
627
5.08k
        }
628
185
        if (nRemainingLength > 0)
629
112
            AddElement(
630
112
                psDecodedContent, psLastChild, psDumpContext,
631
112
                CPLCreateXMLElementAndValue(
632
112
                    nullptr, "RemainingBytes",
633
112
                    CPLSPrintf("%d", static_cast<int>(nRemainingLength))));
634
185
    }
635
186
    CPLFree(pabyBoxData);
636
186
}
637
638
static void DumpCDEFBox(CPLXMLNode *psBox, GDALJP2Box &oBox,
639
                        DumpContext *psDumpContext)
640
474
{
641
474
    GIntBig nBoxDataLength = oBox.GetDataLength();
642
474
    GByte *pabyBoxData = oBox.ReadBoxData();
643
474
    if (pabyBoxData)
644
473
    {
645
473
        CPLXMLNode *psDecodedContent =
646
473
            CPLCreateXMLNode(psBox, CXT_Element, "DecodedContent");
647
473
        GIntBig nRemainingLength = nBoxDataLength;
648
473
        GByte *pabyIter = pabyBoxData;
649
473
        GUInt16 nChannels = 0;
650
473
        CPLXMLNode *psLastChild = nullptr;
651
473
        if (nRemainingLength >= 2)
652
452
        {
653
452
            GUInt16 nVal;
654
452
            memcpy(&nVal, pabyIter, 2);
655
452
            nChannels = nVal;
656
452
            CPL_MSBPTR16(&nVal);
657
452
            AddField(psDecodedContent, psLastChild, psDumpContext, "N", nVal);
658
452
            pabyIter += 2;
659
452
            nRemainingLength -= 2;
660
452
        }
661
2.40M
        for (int i = 0; i < nChannels; i++)
662
2.40M
        {
663
2.40M
            if (nRemainingLength >= 2)
664
23.2k
            {
665
23.2k
                GUInt16 nVal;
666
23.2k
                memcpy(&nVal, pabyIter, 2);
667
23.2k
                CPL_MSBPTR16(&nVal);
668
23.2k
                AddField(psDecodedContent, psLastChild, psDumpContext,
669
23.2k
                         CPLSPrintf("Cn%d", i), nVal);
670
23.2k
                pabyIter += 2;
671
23.2k
                nRemainingLength -= 2;
672
23.2k
            }
673
2.40M
            if (nRemainingLength >= 2)
674
23.2k
            {
675
23.2k
                GUInt16 nVal;
676
23.2k
                memcpy(&nVal, pabyIter, 2);
677
23.2k
                CPL_MSBPTR16(&nVal);
678
23.2k
                AddField(psDecodedContent, psLastChild, psDumpContext,
679
23.2k
                         CPLSPrintf("Typ%d", i), nVal,
680
23.2k
                         (nVal == 0)       ? "Colour channel"
681
23.2k
                         : (nVal == 1)     ? "Opacity channel"
682
18.7k
                         : (nVal == 2)     ? "Premultiplied opacity"
683
17.8k
                         : (nVal == 65535) ? "Not specified"
684
17.5k
                                           : nullptr);
685
23.2k
                pabyIter += 2;
686
23.2k
                nRemainingLength -= 2;
687
23.2k
            }
688
2.40M
            if (nRemainingLength >= 2)
689
23.2k
            {
690
23.2k
                GUInt16 nVal;
691
23.2k
                memcpy(&nVal, pabyIter, 2);
692
23.2k
                CPL_MSBPTR16(&nVal);
693
23.2k
                AddField(psDecodedContent, psLastChild, psDumpContext,
694
23.2k
                         CPLSPrintf("Asoc%d", i), nVal,
695
23.2k
                         (nVal == 0) ? "Associated to the whole image"
696
23.2k
                         : (nVal == 65535)
697
19.4k
                             ? "Not associated with a particular colour"
698
19.4k
                             : "Associated with a particular colour");
699
23.2k
                pabyIter += 2;
700
23.2k
                nRemainingLength -= 2;
701
23.2k
            }
702
2.40M
        }
703
473
        if (nRemainingLength > 0)
704
47
            AddElement(
705
47
                psDecodedContent, psLastChild, psDumpContext,
706
47
                CPLCreateXMLElementAndValue(
707
47
                    nullptr, "RemainingBytes",
708
47
                    CPLSPrintf("%d", static_cast<int>(nRemainingLength))));
709
473
    }
710
474
    CPLFree(pabyBoxData);
711
474
}
712
713
static void DumpRESxBox(CPLXMLNode *psBox, GDALJP2Box &oBox,
714
                        DumpContext *psDumpContext)
715
2.05k
{
716
2.05k
    GIntBig nBoxDataLength = oBox.GetDataLength();
717
2.05k
    GByte *pabyBoxData = oBox.ReadBoxData();
718
2.05k
    char chC = oBox.GetType()[3];
719
2.05k
    if (pabyBoxData)
720
2.05k
    {
721
2.05k
        CPLXMLNode *psDecodedContent =
722
2.05k
            CPLCreateXMLNode(psBox, CXT_Element, "DecodedContent");
723
2.05k
        GIntBig nRemainingLength = nBoxDataLength;
724
2.05k
        GByte *pabyIter = pabyBoxData;
725
2.05k
        GUInt16 nNumV = 0;
726
2.05k
        GUInt16 nNumH = 0;
727
2.05k
        GUInt16 nDenomV = 1;
728
2.05k
        GUInt16 nDenomH = 1;
729
2.05k
        GUInt16 nExpV = 0;
730
2.05k
        GUInt16 nExpH = 0;
731
2.05k
        CPLXMLNode *psLastChild = nullptr;
732
2.05k
        if (nRemainingLength >= 2)
733
2.01k
        {
734
2.01k
            GUInt16 nVal;
735
2.01k
            memcpy(&nVal, pabyIter, 2);
736
2.01k
            CPL_MSBPTR16(&nVal);
737
2.01k
            nNumV = nVal;
738
2.01k
            AddField(psDecodedContent, psLastChild, psDumpContext,
739
2.01k
                     CPLSPrintf("VR%cN", chC), nVal);
740
2.01k
            pabyIter += 2;
741
2.01k
            nRemainingLength -= 2;
742
2.01k
        }
743
2.05k
        if (nRemainingLength >= 2)
744
989
        {
745
989
            GUInt16 nVal;
746
989
            memcpy(&nVal, pabyIter, 2);
747
989
            CPL_MSBPTR16(&nVal);
748
989
            nDenomV = nVal;
749
989
            AddField(psDecodedContent, psLastChild, psDumpContext,
750
989
                     CPLSPrintf("VR%cD", chC), nVal);
751
989
            pabyIter += 2;
752
989
            nRemainingLength -= 2;
753
989
        }
754
2.05k
        if (nRemainingLength >= 2)
755
648
        {
756
648
            GUInt16 nVal;
757
648
            memcpy(&nVal, pabyIter, 2);
758
648
            CPL_MSBPTR16(&nVal);
759
648
            nNumH = nVal;
760
648
            AddField(psDecodedContent, psLastChild, psDumpContext,
761
648
                     CPLSPrintf("HR%cN", chC), nVal);
762
648
            pabyIter += 2;
763
648
            nRemainingLength -= 2;
764
648
        }
765
2.05k
        if (nRemainingLength >= 2)
766
645
        {
767
645
            GUInt16 nVal;
768
645
            memcpy(&nVal, pabyIter, 2);
769
645
            CPL_MSBPTR16(&nVal);
770
645
            nDenomH = nVal;
771
645
            AddField(psDecodedContent, psLastChild, psDumpContext,
772
645
                     CPLSPrintf("HR%cD", chC), nVal);
773
645
            pabyIter += 2;
774
645
            nRemainingLength -= 2;
775
645
        }
776
2.05k
        if (nRemainingLength >= 1)
777
666
        {
778
666
            AddField(psDecodedContent, psLastChild, psDumpContext,
779
666
                     CPLSPrintf("VR%cE", chC), *pabyIter);
780
666
            nExpV = *pabyIter;
781
666
            pabyIter += 1;
782
666
            nRemainingLength -= 1;
783
666
        }
784
2.05k
        if (nRemainingLength >= 1)
785
634
        {
786
634
            AddField(psDecodedContent, psLastChild, psDumpContext,
787
634
                     CPLSPrintf("HR%cE", chC), *pabyIter);
788
634
            nExpH = *pabyIter;
789
            /*pabyIter += 1;*/
790
634
            nRemainingLength -= 1;
791
634
        }
792
2.05k
        if (nRemainingLength == 0)
793
2.02k
        {
794
2.02k
            const char *pszVRes =
795
2.02k
                (nDenomV == 0) ? "invalid"
796
2.02k
                               : CPLSPrintf("%.03f", 1.0 * nNumV / nDenomV *
797
1.67k
                                                         pow(10.0, nExpV));
798
2.02k
            AddElement(psDecodedContent, psLastChild, psDumpContext,
799
2.02k
                       CPLCreateXMLElementAndValue(nullptr, "VRes", pszVRes));
800
2.02k
            const char *pszHRes =
801
2.02k
                (nDenomH == 0) ? "invalid"
802
2.02k
                               : CPLSPrintf("%.03f", 1.0 * nNumH / nDenomH *
803
1.95k
                                                         pow(10.0, nExpH));
804
2.02k
            AddElement(psDecodedContent, psLastChild, psDumpContext,
805
2.02k
                       CPLCreateXMLElementAndValue(nullptr, "HRes", pszHRes));
806
2.02k
        }
807
23
        else if (nRemainingLength > 0)
808
23
            AddElement(
809
23
                psDecodedContent, psLastChild, psDumpContext,
810
23
                CPLCreateXMLElementAndValue(
811
23
                    nullptr, "RemainingBytes",
812
23
                    CPLSPrintf("%d", static_cast<int>(nRemainingLength))));
813
2.05k
    }
814
2.05k
    CPLFree(pabyBoxData);
815
2.05k
}
816
817
static void DumpRREQBox(CPLXMLNode *psBox, GDALJP2Box &oBox,
818
                        DumpContext *psDumpContext)
819
1.71k
{
820
1.71k
    GIntBig nBoxDataLength = oBox.GetDataLength();
821
1.71k
    GByte *pabyBoxData = oBox.ReadBoxData();
822
1.71k
    if (pabyBoxData)
823
1.71k
    {
824
1.71k
        CPLXMLNode *psDecodedContent =
825
1.71k
            CPLCreateXMLNode(psBox, CXT_Element, "DecodedContent");
826
1.71k
        GIntBig nRemainingLength = nBoxDataLength;
827
1.71k
        GByte *pabyIter = pabyBoxData;
828
1.71k
        GByte ML = 0;
829
1.71k
        CPLXMLNode *psLastChild = nullptr;
830
1.71k
        if (nRemainingLength >= 1)
831
1.68k
        {
832
1.68k
            ML = *pabyIter;
833
1.68k
            AddField(psDecodedContent, psLastChild, psDumpContext, "ML",
834
1.68k
                     *pabyIter);
835
1.68k
            pabyIter += 1;
836
1.68k
            nRemainingLength -= 1;
837
1.68k
        }
838
1.71k
        if (nRemainingLength >= ML)
839
1.51k
        {
840
1.51k
            CPLString osHex("0x");
841
11.9k
            for (int i = 0; i < ML; i++)
842
10.4k
            {
843
10.4k
                osHex += CPLSPrintf("%02X", *pabyIter);
844
10.4k
                pabyIter += 1;
845
10.4k
                nRemainingLength -= 1;
846
10.4k
            }
847
1.51k
            AddHexField(psDecodedContent, psLastChild, psDumpContext, "FUAM",
848
1.51k
                        static_cast<int>(ML), osHex.c_str());
849
1.51k
        }
850
1.71k
        if (nRemainingLength >= ML)
851
1.41k
        {
852
1.41k
            CPLString osHex("0x");
853
10.8k
            for (int i = 0; i < ML; i++)
854
9.40k
            {
855
9.40k
                osHex += CPLSPrintf("%02X", *pabyIter);
856
9.40k
                pabyIter += 1;
857
9.40k
                nRemainingLength -= 1;
858
9.40k
            }
859
1.41k
            AddHexField(psDecodedContent, psLastChild, psDumpContext, "DCM",
860
1.41k
                        static_cast<int>(ML), osHex.c_str());
861
1.41k
        }
862
1.71k
        GUInt16 NSF = 0;
863
1.71k
        if (nRemainingLength >= 2)
864
1.55k
        {
865
1.55k
            GUInt16 nVal;
866
1.55k
            memcpy(&nVal, pabyIter, 2);
867
1.55k
            CPL_MSBPTR16(&nVal);
868
1.55k
            NSF = nVal;
869
1.55k
            AddField(psDecodedContent, psLastChild, psDumpContext, "NSF", nVal);
870
1.55k
            pabyIter += 2;
871
1.55k
            nRemainingLength -= 2;
872
1.55k
        }
873
83.5k
        for (int iNSF = 0; iNSF < NSF; iNSF++)
874
82.3k
        {
875
82.3k
            if (nRemainingLength >= 2)
876
82.1k
            {
877
82.1k
                GUInt16 nVal;
878
82.1k
                memcpy(&nVal, pabyIter, 2);
879
82.1k
                CPL_MSBPTR16(&nVal);
880
82.1k
                AddField(psDecodedContent, psLastChild, psDumpContext,
881
82.1k
                         CPLSPrintf("SF%d", iNSF), nVal,
882
82.1k
                         GetStandardFieldString(nVal));
883
82.1k
                pabyIter += 2;
884
82.1k
                nRemainingLength -= 2;
885
82.1k
            }
886
234
            else
887
234
                break;
888
82.1k
            if (nRemainingLength >= ML)
889
81.8k
            {
890
81.8k
                CPLString osHex("0x");
891
118k
                for (int i = 0; i < ML; i++)
892
36.8k
                {
893
36.8k
                    osHex += CPLSPrintf("%02X", *pabyIter);
894
36.8k
                    pabyIter += 1;
895
36.8k
                    nRemainingLength -= 1;
896
36.8k
                }
897
81.8k
                AddHexField(psDecodedContent, psLastChild, psDumpContext,
898
81.8k
                            CPLSPrintf("SM%d", iNSF), static_cast<int>(ML),
899
81.8k
                            osHex.c_str());
900
81.8k
            }
901
265
            else
902
265
                break;
903
82.1k
        }
904
1.71k
        GUInt16 NVF = 0;
905
1.71k
        if (nRemainingLength >= 2)
906
1.22k
        {
907
1.22k
            GUInt16 nVal;
908
1.22k
            memcpy(&nVal, pabyIter, 2);
909
1.22k
            CPL_MSBPTR16(&nVal);
910
1.22k
            NVF = nVal;
911
1.22k
            AddField(psDecodedContent, psLastChild, psDumpContext, "NVF", nVal);
912
1.22k
            pabyIter += 2;
913
1.22k
            nRemainingLength -= 2;
914
1.22k
        }
915
159k
        for (int iNVF = 0; iNVF < NVF; iNVF++)
916
158k
        {
917
158k
            if (nRemainingLength >= 16)
918
157k
            {
919
157k
                CPLString osHex("0x");
920
2.68M
                for (int i = 0; i < 16; i++)
921
2.52M
                {
922
2.52M
                    osHex += CPLSPrintf("%02X", *pabyIter);
923
2.52M
                    pabyIter += 1;
924
2.52M
                    nRemainingLength -= 1;
925
2.52M
                }
926
157k
                AddHexField(psDecodedContent, psLastChild, psDumpContext,
927
157k
                            CPLSPrintf("VF%d", iNVF), static_cast<int>(ML),
928
157k
                            osHex.c_str());
929
157k
            }
930
601
            else
931
601
                break;
932
157k
            if (nRemainingLength >= ML)
933
157k
            {
934
157k
                CPLString osHex("0x");
935
218k
                for (int i = 0; i < ML; i++)
936
60.5k
                {
937
60.5k
                    osHex += CPLSPrintf("%02X", *pabyIter);
938
60.5k
                    pabyIter += 1;
939
60.5k
                    nRemainingLength -= 1;
940
60.5k
                }
941
157k
                AddHexField(psDecodedContent, psLastChild, psDumpContext,
942
157k
                            CPLSPrintf("VM%d", iNVF), static_cast<int>(ML),
943
157k
                            osHex.c_str());
944
157k
            }
945
15
            else
946
15
                break;
947
157k
        }
948
1.71k
        if (nRemainingLength > 0)
949
289
            AddElement(
950
289
                psDecodedContent, psLastChild, psDumpContext,
951
289
                CPLCreateXMLElementAndValue(
952
289
                    nullptr, "RemainingBytes",
953
289
                    CPLSPrintf("%d", static_cast<int>(nRemainingLength))));
954
1.71k
    }
955
1.71k
    CPLFree(pabyBoxData);
956
1.71k
}
957
958
static CPLXMLNode *CreateMarker(CPLXMLNode *psCSBox,
959
                                CPLXMLNode *&psLastChildCSBox,
960
                                DumpContext *psDumpContext, const char *pszName,
961
                                GIntBig nOffset, GIntBig nLength)
962
1.14M
{
963
1.14M
    CPLXMLNode *psMarker = CPLCreateXMLNode(nullptr, CXT_Element, "Marker");
964
1.14M
    CPLAddXMLAttributeAndValue(psMarker, "name", pszName);
965
1.14M
    CPLAddXMLAttributeAndValue(psMarker, "offset",
966
1.14M
                               CPLSPrintf(CPL_FRMT_GIB, nOffset));
967
1.14M
    CPLAddXMLAttributeAndValue(psMarker, "length",
968
1.14M
                               CPLSPrintf(CPL_FRMT_GIB, 2 + nLength));
969
1.14M
    return AddElement(psCSBox, psLastChildCSBox, psDumpContext, psMarker);
970
1.14M
}
971
972
static void AddError(CPLXMLNode *psParent, CPLXMLNode *&psLastChild,
973
                     DumpContext *psDumpContext, const char *pszErrorMsg,
974
                     GIntBig nOffset = 0)
975
2.40M
{
976
2.40M
    if (psDumpContext->nCurLineCount > psDumpContext->nMaxLineCount + 1)
977
32
    {
978
32
        return;
979
32
    }
980
981
2.40M
    AddElement(psParent, psLastChild, psDumpContext,
982
2.40M
               _AddError(nullptr, pszErrorMsg, nOffset));
983
2.40M
}
984
985
static const char *GetMarkerName(GByte byVal)
986
1.14M
{
987
1.14M
    switch (byVal)
988
1.14M
    {
989
868
        case 0x90:
990
868
            return "SOT";
991
1.72k
        case 0x50:
992
1.72k
            return "CAP";
993
105k
        case 0x51:
994
105k
            return "SIZ";
995
667k
        case 0x52:
996
667k
            return "COD";
997
305k
        case 0x53:
998
305k
            return "COC";
999
460
        case 0x55:
1000
460
            return "TLM";
1001
966
        case 0x57:
1002
966
            return "PLM";
1003
1.64k
        case 0x58:
1004
1.64k
            return "PLT";
1005
710
        case 0x5C:
1006
710
            return "QCD";
1007
1.81k
        case 0x5D:
1008
1.81k
            return "QCC";
1009
199
        case 0x5E:
1010
199
            return "RGN";
1011
1.06k
        case 0x5F:
1012
1.06k
            return "POC";
1013
51.9k
        case 0x59:
1014
51.9k
            return "CPF";  // HTJ2K
1015
3.14k
        case 0x60:
1016
3.14k
            return "PPM";
1017
227
        case 0x61:
1018
227
            return "PPT";
1019
407
        case 0x63:
1020
407
            return "CRG";
1021
436
        case 0x64:
1022
436
            return "COM";
1023
656
        default:
1024
656
            return CPLSPrintf("Unknown 0xFF%02X", byVal);
1025
1.14M
    }
1026
1.14M
}
1027
1028
/************************************************************************/
1029
/*                         DumpJPK2CodeStream()                         */
1030
/************************************************************************/
1031
1032
static CPLXMLNode *DumpJPK2CodeStream(CPLXMLNode *psBox, VSILFILE *fp,
1033
                                      vsi_l_offset nBoxDataOffset,
1034
                                      GIntBig nBoxDataLength,
1035
                                      DumpContext *psDumpContext)
1036
16.6k
{
1037
16.6k
    GByte abyMarker[2];
1038
16.6k
    CPLXMLNode *psCSBox =
1039
16.6k
        CPLCreateXMLNode(psBox, CXT_Element, "JP2KCodeStream");
1040
16.6k
    CPLXMLNode *psLastChildCSBox = nullptr;
1041
16.6k
    if (VSIFSeekL(fp, nBoxDataOffset, SEEK_SET) != 0)
1042
0
    {
1043
0
        AddError(psCSBox, psLastChildCSBox, psDumpContext,
1044
0
                 "Cannot read codestream", 0);
1045
0
        return psCSBox;
1046
0
    }
1047
16.6k
    GByte *pabyMarkerData = static_cast<GByte *>(CPLMalloc(65535 + 1));
1048
16.6k
    vsi_l_offset nNextTileOffset = 0;
1049
16.6k
    int Csiz = -1;
1050
16.6k
    const auto lambdaPOCType = [](GByte v)
1051
666k
    {
1052
666k
        return std::string((v == 0)   ? "LRCP"
1053
666k
                           : (v == 1) ? "RLCP"
1054
5.61k
                           : (v == 2) ? "RPCL"
1055
5.34k
                           : (v == 3) ? "PCRL"
1056
5.10k
                           : (v == 4) ? "CPRL"
1057
4.83k
                                      : "");
1058
666k
    };
1059
1060
1.16M
    while (psDumpContext->nCurLineCount <= psDumpContext->nMaxLineCount + 1)
1061
1.16M
    {
1062
1.16M
        auto nOffset = VSIFTellL(fp);
1063
1.16M
        if (nBoxDataLength > 0 && nOffset == nBoxDataOffset + nBoxDataLength)
1064
331
            break;
1065
1.16M
        if (VSIFReadL(abyMarker, 2, 1, fp) != 1)
1066
1.31k
        {
1067
1.31k
            AddError(psCSBox, psLastChildCSBox, psDumpContext,
1068
1.31k
                     "Cannot read marker", nOffset);
1069
1.31k
            break;
1070
1.31k
        }
1071
1.16M
        if (abyMarker[0] != 0xFF)
1072
7.95k
        {
1073
7.95k
            AddError(psCSBox, psLastChildCSBox, psDumpContext, "Not a marker",
1074
7.95k
                     nOffset);
1075
7.95k
            break;
1076
7.95k
        }
1077
1.15M
        if (abyMarker[1] == 0x4F)  // SOC
1078
2.11k
        {
1079
2.11k
            if (psDumpContext->pszCodestreamMarkers == nullptr ||
1080
0
                strstr(psDumpContext->pszCodestreamMarkers, "SOC"))
1081
2.11k
            {
1082
2.11k
                CreateMarker(psCSBox, psLastChildCSBox, psDumpContext, "SOC",
1083
2.11k
                             nOffset, 0);
1084
2.11k
            }
1085
2.11k
            continue;
1086
2.11k
        }
1087
1.14M
        if (abyMarker[1] == 0x93)  // SOD
1088
859
        {
1089
859
            const bool bIncludeSOD =
1090
859
                (psDumpContext->pszCodestreamMarkers == nullptr ||
1091
0
                 strstr(psDumpContext->pszCodestreamMarkers, "SOD"));
1092
859
            if (psDumpContext->bStopAtSOD && !bIncludeSOD)
1093
0
            {
1094
0
                psDumpContext->bSODEncountered = true;
1095
0
                break;
1096
0
            }
1097
1098
859
            GIntBig nMarkerSize = 0;
1099
859
            bool bBreak = false;
1100
859
            if (nNextTileOffset == 0)
1101
239
            {
1102
239
                const auto nPos = nBoxDataOffset + nBoxDataLength - 2;
1103
239
                if (nPos >= nOffset + 2)
1104
70
                {
1105
70
                    nMarkerSize =
1106
70
                        (nBoxDataOffset + nBoxDataLength - 2) - (nOffset + 2);
1107
70
                    if (VSIFSeekL(fp, nBoxDataOffset + nBoxDataLength - 2,
1108
70
                                  SEEK_SET) != 0 ||
1109
70
                        VSIFReadL(abyMarker, 2, 1, fp) != 1 ||
1110
34
                        abyMarker[0] != 0xFF || abyMarker[1] != 0xD9)
1111
68
                    {
1112
                        /* autotest/gdrivers/data/rgb16_ecwsdk.jp2 does not end */
1113
                        /* with a EOC... */
1114
68
                        nMarkerSize += 2;
1115
68
                        bBreak = true;
1116
68
                    }
1117
70
                }
1118
239
            }
1119
620
            else if (nNextTileOffset >= nOffset + 2)
1120
293
                nMarkerSize = nNextTileOffset - nOffset - 2;
1121
1122
859
            if (bIncludeSOD)
1123
859
            {
1124
859
                CreateMarker(psCSBox, psLastChildCSBox, psDumpContext, "SOD",
1125
859
                             nOffset, nMarkerSize);
1126
859
            }
1127
859
            if (bBreak || psDumpContext->bStopAtSOD)
1128
68
            {
1129
68
                psDumpContext->bSODEncountered = true;
1130
68
                break;
1131
68
            }
1132
1133
791
            if (nNextTileOffset && nNextTileOffset == nOffset)
1134
93
            {
1135
                /* Found with Pleiades images. openjpeg doesn't like it either
1136
                 */
1137
93
                nNextTileOffset = 0;
1138
93
            }
1139
698
            else if (nNextTileOffset && nNextTileOffset >= nOffset + 2)
1140
293
            {
1141
293
                if (VSIFSeekL(fp, nNextTileOffset, SEEK_SET) != 0)
1142
0
                    AddError(psCSBox, psLastChildCSBox, psDumpContext,
1143
0
                             "Cannot seek to", nNextTileOffset);
1144
293
                nNextTileOffset = 0;
1145
293
            }
1146
405
            else
1147
405
            {
1148
                /* We have seek and check before we hit a EOC */
1149
405
                nOffset = nBoxDataOffset + nBoxDataLength - 2;
1150
405
                if (psDumpContext->pszCodestreamMarkers == nullptr ||
1151
0
                    strstr(psDumpContext->pszCodestreamMarkers, "EOC"))
1152
405
                {
1153
405
                    CreateMarker(psCSBox, psLastChildCSBox, psDumpContext,
1154
405
                                 "EOC", nOffset, 0);
1155
405
                }
1156
405
            }
1157
791
            continue;
1158
859
        }
1159
1.14M
        if (abyMarker[1] == 0xD9)
1160
438
        {
1161
438
            if (psDumpContext->pszCodestreamMarkers == nullptr ||
1162
0
                strstr(psDumpContext->pszCodestreamMarkers, "EOC"))
1163
438
            {
1164
438
                CreateMarker(psCSBox, psLastChildCSBox, psDumpContext, "EOC",
1165
438
                             nOffset, 0);
1166
438
            }
1167
438
            continue;
1168
438
        }
1169
        /* Reserved markers */
1170
1.14M
        if (abyMarker[1] >= 0x30 && abyMarker[1] <= 0x3F)
1171
624
        {
1172
624
            if (psDumpContext->pszCodestreamMarkers == nullptr)
1173
624
            {
1174
624
                CreateMarker(psCSBox, psLastChildCSBox, psDumpContext,
1175
624
                             CPLSPrintf("Unknown 0xFF%02X", abyMarker[1]),
1176
624
                             nOffset, 0);
1177
624
            }
1178
624
            continue;
1179
624
        }
1180
1181
1.14M
        GUInt16 nMarkerSize;
1182
1.14M
        if (VSIFReadL(&nMarkerSize, 2, 1, fp) != 1)
1183
400
        {
1184
400
            AddError(psCSBox, psLastChildCSBox, psDumpContext,
1185
400
                     CPLSPrintf("Cannot read marker size of %s",
1186
400
                                GetMarkerName(abyMarker[1])),
1187
400
                     nOffset);
1188
400
            break;
1189
400
        }
1190
1.14M
        CPL_MSBPTR16(&nMarkerSize);
1191
1.14M
        if (nMarkerSize < 2)
1192
2.79k
        {
1193
2.79k
            AddError(psCSBox, psLastChildCSBox, psDumpContext,
1194
2.79k
                     CPLSPrintf("Invalid marker size of %s",
1195
2.79k
                                GetMarkerName(abyMarker[1])),
1196
2.79k
                     nOffset);
1197
2.79k
            break;
1198
2.79k
        }
1199
1200
1.14M
        const auto CreateCurrentMarker = [&]()
1201
1.14M
        {
1202
1.14M
            return CreateMarker(psCSBox, psLastChildCSBox, psDumpContext,
1203
1.14M
                                GetMarkerName(abyMarker[1]), nOffset,
1204
1.14M
                                nMarkerSize);
1205
1.14M
        };
1206
1.14M
        CPLXMLNode *psMarker = nullptr;
1207
1.14M
        CPLXMLNode *psLastChild = nullptr;
1208
1.14M
        if (VSIFReadL(pabyMarkerData, nMarkerSize - 2, 1, fp) != 1)
1209
3.81k
        {
1210
3.81k
            psMarker = CreateCurrentMarker();
1211
3.81k
            AddError(psMarker, psLastChild, psDumpContext,
1212
3.81k
                     "Cannot read marker data", nOffset);
1213
3.81k
            break;
1214
3.81k
        }
1215
1.14M
        GByte *pabyMarkerDataIter = pabyMarkerData;
1216
1.14M
        GUInt16 nRemainingMarkerSize = nMarkerSize - 2;
1217
1.14M
        bool bError = false;
1218
1219
1.14M
        auto READ_MARKER_FIELD_UINT8 =
1220
1.14M
            [&](const char *name, std::string (*commentFunc)(GByte) = nullptr)
1221
6.52M
        {
1222
6.52M
            GByte v;
1223
6.52M
            if (nRemainingMarkerSize >= 1)
1224
4.88M
            {
1225
4.88M
                v = *pabyMarkerDataIter;
1226
4.88M
                const std::string comment(commentFunc ? commentFunc(v)
1227
4.88M
                                                      : std::string());
1228
4.88M
                AddField(psMarker, psLastChild, psDumpContext, name,
1229
4.88M
                         *pabyMarkerDataIter,
1230
4.88M
                         comment.empty() ? nullptr : comment.c_str());
1231
4.88M
                pabyMarkerDataIter += 1;
1232
4.88M
                nRemainingMarkerSize -= 1;
1233
4.88M
            }
1234
1.64M
            else
1235
1.64M
            {
1236
1.64M
                AddError(psMarker, psLastChild, psDumpContext,
1237
1.64M
                         CPLSPrintf("Cannot read field %s", name));
1238
1.64M
                v = 0;
1239
1.64M
                bError = true;
1240
1.64M
            }
1241
6.52M
            return v;
1242
6.52M
        };
1243
1244
1.14M
        auto READ_MARKER_FIELD_UINT16 =
1245
1.14M
            [&](const char *name, std::string (*commentFunc)(GUInt16) = nullptr)
1246
1.40M
        {
1247
1.40M
            GUInt16 v;
1248
1.40M
            if (nRemainingMarkerSize >= 2)
1249
1.39M
            {
1250
1.39M
                memcpy(&v, pabyMarkerDataIter, 2);
1251
1.39M
                CPL_MSBPTR16(&v);
1252
1.39M
                const std::string comment(commentFunc ? commentFunc(v)
1253
1.39M
                                                      : std::string());
1254
1.39M
                AddField(psMarker, psLastChild, psDumpContext, name, v,
1255
1.39M
                         comment.empty() ? nullptr : comment.c_str());
1256
1.39M
                pabyMarkerDataIter += 2;
1257
1.39M
                nRemainingMarkerSize -= 2;
1258
1.39M
            }
1259
4.24k
            else
1260
4.24k
            {
1261
4.24k
                AddError(psMarker, psLastChild, psDumpContext,
1262
4.24k
                         CPLSPrintf("Cannot read field %s", name));
1263
4.24k
                v = 0;
1264
4.24k
                bError = true;
1265
4.24k
            }
1266
1.40M
            return v;
1267
1.40M
        };
1268
1269
1.14M
        auto READ_MARKER_FIELD_UINT32 =
1270
1.14M
            [&](const char *name, std::string (*commentFunc)(GUInt32) = nullptr)
1271
1.14M
        {
1272
849k
            GUInt32 v;
1273
849k
            if (nRemainingMarkerSize >= 4)
1274
108k
            {
1275
108k
                memcpy(&v, pabyMarkerDataIter, 4);
1276
108k
                CPL_MSBPTR32(&v);
1277
108k
                const std::string comment(commentFunc ? commentFunc(v)
1278
108k
                                                      : std::string());
1279
108k
                AddField(psMarker, psLastChild, psDumpContext, name, v,
1280
108k
                         comment.empty() ? nullptr : comment.c_str());
1281
108k
                pabyMarkerDataIter += 4;
1282
108k
                nRemainingMarkerSize -= 4;
1283
108k
            }
1284
741k
            else
1285
741k
            {
1286
741k
                AddError(psMarker, psLastChild, psDumpContext,
1287
741k
                         CPLSPrintf("Cannot read field %s", name));
1288
741k
                v = 0;
1289
741k
                bError = true;
1290
741k
            }
1291
849k
            return v;
1292
849k
        };
1293
1294
1.14M
        const auto cblkstyleLamba = [](GByte v)
1295
1.14M
        {
1296
308k
            std::string osInterp;
1297
308k
            if (v & 0x1)
1298
91.8k
                osInterp += "Selective arithmetic coding bypass";
1299
216k
            else
1300
216k
                osInterp += "No selective arithmetic coding bypass";
1301
308k
            osInterp += ", ";
1302
308k
            if (v & 0x2)
1303
195k
                osInterp +=
1304
195k
                    "Reset context probabilities on coding pass boundaries";
1305
112k
            else
1306
112k
                osInterp += "No reset of context probabilities on coding pass "
1307
112k
                            "boundaries";
1308
308k
            osInterp += ", ";
1309
308k
            if (v & 0x4)
1310
2.85k
                osInterp += "Termination on each coding pass";
1311
305k
            else
1312
305k
                osInterp += "No termination on each coding pass";
1313
308k
            osInterp += ", ";
1314
308k
            if (v & 0x8)
1315
20.9k
                osInterp += "Vertically causal context";
1316
287k
            else
1317
287k
                osInterp += "No vertically causal context";
1318
308k
            osInterp += ", ";
1319
308k
            if (v & 0x10)
1320
290k
                osInterp += "Predictable termination";
1321
17.9k
            else
1322
17.9k
                osInterp += "No predictable termination";
1323
308k
            osInterp += ", ";
1324
308k
            if (v & 0x20)
1325
272k
                osInterp += "Segmentation symbols are used";
1326
35.8k
            else
1327
35.8k
                osInterp += "No segmentation symbols are used";
1328
308k
            if (v & 0x40)
1329
26.7k
                osInterp += ", High Throughput algorithm";
1330
308k
            if (v & 0x80)
1331
2.69k
                osInterp += ", Mixed HT and Part1 code-block style";
1332
308k
            return osInterp;
1333
308k
        };
1334
1335
1.14M
        if (abyMarker[1] == 0x90) /* SOT */
1336
845
        {
1337
845
            if (psDumpContext->pszCodestreamMarkers == nullptr ||
1338
0
                strstr(psDumpContext->pszCodestreamMarkers, "SOT"))
1339
845
            {
1340
845
                psMarker = CreateCurrentMarker();
1341
845
                if (!psMarker)
1342
0
                    break;
1343
845
                READ_MARKER_FIELD_UINT16("Isot");
1344
845
                GUInt32 PSOT = READ_MARKER_FIELD_UINT32("Psot");
1345
845
                READ_MARKER_FIELD_UINT8("TPsot");
1346
845
                READ_MARKER_FIELD_UINT8("TNsot");
1347
845
                if (nRemainingMarkerSize > 0)
1348
40
                    AddElement(
1349
40
                        psMarker, psLastChild, psDumpContext,
1350
40
                        CPLCreateXMLElementAndValue(
1351
40
                            nullptr, "RemainingBytes",
1352
40
                            CPLSPrintf(
1353
40
                                "%d", static_cast<int>(nRemainingMarkerSize))));
1354
1355
845
                if (PSOT)
1356
571
                    nNextTileOffset = nOffset + PSOT;
1357
845
            }
1358
845
        }
1359
1.14M
        else if (abyMarker[1] == 0x50) /* CAP (HTJ2K) */
1360
1.71k
        {
1361
1.71k
            if (psDumpContext->pszCodestreamMarkers == nullptr ||
1362
0
                strstr(psDumpContext->pszCodestreamMarkers, "CAP"))
1363
1.71k
            {
1364
1.71k
                psMarker = CreateCurrentMarker();
1365
1.71k
                if (!psMarker)
1366
0
                    break;
1367
1.71k
                const GUInt32 Pcap = READ_MARKER_FIELD_UINT32("Pcap");
1368
56.6k
                for (int i = 0; i < 32; i++)
1369
54.9k
                {
1370
54.9k
                    if ((Pcap >> (31 - i)) & 1)
1371
11.4k
                    {
1372
11.4k
                        if (i + 1 == 15)
1373
519
                        {
1374
519
                            READ_MARKER_FIELD_UINT16(
1375
519
                                CPLSPrintf("Scap_P%d", i + 1),
1376
519
                                [](GUInt16 v)
1377
519
                                {
1378
438
                                    std::string ret;
1379
438
                                    if ((v >> 14) == 0)
1380
120
                                        ret = "All code-blocks are HT "
1381
120
                                              "code-blocks";
1382
318
                                    else if ((v >> 14) == 2)
1383
68
                                        ret = "Either all HT or all Part1 "
1384
68
                                              "code-blocks per tile component";
1385
250
                                    else if ((v >> 14) == 3)
1386
75
                                        ret = "Mixed HT or all Part1 "
1387
75
                                              "code-blocks per tile component";
1388
175
                                    else
1389
175
                                        ret =
1390
175
                                            "Reserved value for bit 14 and 15";
1391
438
                                    ret += ", ";
1392
438
                                    if ((v >> 13) & 1)
1393
245
                                        ret += "More than one HT set per "
1394
245
                                               "code-block";
1395
193
                                    else
1396
193
                                        ret +=
1397
193
                                            "Zero or one HT set per code-block";
1398
438
                                    ret += ", ";
1399
438
                                    if ((v >> 12) & 1)
1400
136
                                        ret += "ROI marker can be present";
1401
302
                                    else
1402
302
                                        ret += "No ROI marker";
1403
438
                                    ret += ", ";
1404
438
                                    if ((v >> 11) & 1)
1405
252
                                        ret += "Heterogeneous codestream";
1406
186
                                    else
1407
186
                                        ret += "Homogeneous codestream";
1408
438
                                    ret += ", ";
1409
438
                                    if ((v >> 5) & 1)
1410
191
                                        ret += "HT code-blocks can be used "
1411
191
                                               "with irreversible transforms";
1412
247
                                    else
1413
247
                                        ret += "HT code-blocks only used with "
1414
247
                                               "reversible transforms";
1415
438
                                    ret += ", ";
1416
438
                                    ret += "P=";
1417
438
                                    ret += CPLSPrintf("%d", v & 0x31);
1418
438
                                    return ret;
1419
438
                                });
1420
519
                        }
1421
10.8k
                        else
1422
10.8k
                        {
1423
10.8k
                            READ_MARKER_FIELD_UINT16(
1424
10.8k
                                CPLSPrintf("Scap_P%d", i + 1));
1425
10.8k
                        }
1426
11.4k
                    }
1427
54.9k
                }
1428
1.71k
                if (nRemainingMarkerSize > 0)
1429
311
                    AddElement(
1430
311
                        psMarker, psLastChild, psDumpContext,
1431
311
                        CPLCreateXMLElementAndValue(
1432
311
                            nullptr, "RemainingBytes",
1433
311
                            CPLSPrintf(
1434
311
                                "%d", static_cast<int>(nRemainingMarkerSize))));
1435
1.71k
            }
1436
1.71k
        }
1437
1.13M
        else if (abyMarker[1] == 0x51) /* SIZ */
1438
105k
        {
1439
105k
            if (psDumpContext->pszCodestreamMarkers == nullptr ||
1440
0
                strstr(psDumpContext->pszCodestreamMarkers, "SIZ"))
1441
105k
            {
1442
105k
                psMarker = CreateCurrentMarker();
1443
105k
                if (!psMarker)
1444
1
                    break;
1445
105k
                READ_MARKER_FIELD_UINT16(
1446
105k
                    "Rsiz",
1447
105k
                    [](GUInt16 v)
1448
105k
                    {
1449
105k
                        return std::string((v == 0)   ? "Unrestricted profile"
1450
105k
                                           : (v == 1) ? "Profile 0"
1451
727
                                           : (v == 2) ? "Profile 1"
1452
635
                                           : (v == 16384) ? "HTJ2K"
1453
595
                                                          : "");
1454
105k
                    });
1455
105k
                READ_MARKER_FIELD_UINT32("Xsiz");
1456
105k
                READ_MARKER_FIELD_UINT32("Ysiz");
1457
105k
                READ_MARKER_FIELD_UINT32("XOsiz");
1458
105k
                READ_MARKER_FIELD_UINT32("YOsiz");
1459
105k
                READ_MARKER_FIELD_UINT32("XTsiz");
1460
105k
                READ_MARKER_FIELD_UINT32("YTsiz");
1461
105k
                READ_MARKER_FIELD_UINT32("XTOSiz");
1462
105k
                READ_MARKER_FIELD_UINT32("YTOSiz");
1463
105k
                Csiz = READ_MARKER_FIELD_UINT16("Csiz");
1464
105k
                bError = false;
1465
                // cppcheck-suppress knownConditionTrueFalse
1466
212k
                for (int i = 0; i < Csiz && !bError; i++)
1467
106k
                {
1468
106k
                    READ_MARKER_FIELD_UINT8(
1469
106k
                        CPLSPrintf("Ssiz%d", i),
1470
106k
                        [](GByte v)
1471
106k
                        {
1472
889
                            const char *psz = GetInterpretationOfBPC(v);
1473
889
                            return std::string(psz ? psz : "");
1474
889
                        });
1475
106k
                    READ_MARKER_FIELD_UINT8(CPLSPrintf("XRsiz%d", i));
1476
106k
                    READ_MARKER_FIELD_UINT8(CPLSPrintf("YRsiz%d", i));
1477
106k
                }
1478
105k
                if (nRemainingMarkerSize > 0)
1479
227
                    AddElement(
1480
227
                        psMarker, psLastChild, psDumpContext,
1481
227
                        CPLCreateXMLElementAndValue(
1482
227
                            nullptr, "RemainingBytes",
1483
227
                            CPLSPrintf(
1484
227
                                "%d", static_cast<int>(nRemainingMarkerSize))));
1485
105k
            }
1486
105k
        }
1487
1.03M
        else if (abyMarker[1] == 0x52) /* COD */
1488
664k
        {
1489
664k
            if (psDumpContext->pszCodestreamMarkers == nullptr ||
1490
0
                strstr(psDumpContext->pszCodestreamMarkers, "COD"))
1491
664k
            {
1492
664k
                psMarker = CreateCurrentMarker();
1493
664k
                if (!psMarker)
1494
1
                    break;
1495
664k
                bool bHasPrecincts = false;
1496
664k
                if (nRemainingMarkerSize >= 1)
1497
664k
                {
1498
664k
                    auto nLastVal = *pabyMarkerDataIter;
1499
664k
                    CPLString osInterp;
1500
664k
                    if (nLastVal & 0x1)
1501
2.85k
                    {
1502
2.85k
                        bHasPrecincts = true;
1503
2.85k
                        osInterp += "User defined precincts";
1504
2.85k
                    }
1505
661k
                    else
1506
661k
                        osInterp += "Standard precincts";
1507
664k
                    osInterp += ", ";
1508
664k
                    if (nLastVal & 0x2)
1509
3.17k
                        osInterp += "SOP marker segments may be used";
1510
660k
                    else
1511
660k
                        osInterp += "No SOP marker segments";
1512
664k
                    osInterp += ", ";
1513
664k
                    if (nLastVal & 0x4)
1514
2.78k
                        osInterp += "EPH marker segments may be used";
1515
661k
                    else
1516
661k
                        osInterp += "No EPH marker segments";
1517
664k
                    AddField(psMarker, psLastChild, psDumpContext, "Scod",
1518
664k
                             nLastVal, osInterp.c_str());
1519
664k
                    pabyMarkerDataIter += 1;
1520
664k
                    nRemainingMarkerSize -= 1;
1521
664k
                }
1522
0
                else
1523
0
                {
1524
0
                    AddError(psMarker, psLastChild, psDumpContext,
1525
0
                             CPLSPrintf("Cannot read field %s", "Scod"));
1526
0
                }
1527
664k
                READ_MARKER_FIELD_UINT8("SGcod_Progress", lambdaPOCType);
1528
664k
                READ_MARKER_FIELD_UINT16("SGcod_NumLayers");
1529
664k
                READ_MARKER_FIELD_UINT8("SGcod_MCT");
1530
664k
                READ_MARKER_FIELD_UINT8("SPcod_NumDecompositions");
1531
664k
                READ_MARKER_FIELD_UINT8(
1532
664k
                    "SPcod_xcb_minus_2",
1533
664k
                    [](GByte v)
1534
664k
                    {
1535
664k
                        return std::string(v <= 8
1536
664k
                                               ? CPLSPrintf("%d", 1 << (2 + v))
1537
664k
                                               : "invalid");
1538
664k
                    });
1539
664k
                READ_MARKER_FIELD_UINT8(
1540
664k
                    "SPcod_ycb_minus_2",
1541
664k
                    [](GByte v)
1542
664k
                    {
1543
664k
                        return std::string(v <= 8
1544
664k
                                               ? CPLSPrintf("%d", 1 << (2 + v))
1545
664k
                                               : "invalid");
1546
664k
                    });
1547
664k
                READ_MARKER_FIELD_UINT8("SPcod_cbstyle", cblkstyleLamba);
1548
664k
                READ_MARKER_FIELD_UINT8("SPcod_transformation",
1549
664k
                                        [](GByte v)
1550
664k
                                        {
1551
3.17k
                                            return std::string(
1552
3.17k
                                                (v == 0)   ? "9-7 irreversible"
1553
3.17k
                                                : (v == 1) ? "5-3 reversible"
1554
2.90k
                                                           : "");
1555
3.17k
                                        });
1556
664k
                if (bHasPrecincts)
1557
2.85k
                {
1558
2.85k
                    int i = 0;
1559
541k
                    while (nRemainingMarkerSize >= 1)
1560
538k
                    {
1561
538k
                        auto nLastVal = *pabyMarkerDataIter;
1562
538k
                        AddField(psMarker, psLastChild, psDumpContext,
1563
538k
                                 CPLSPrintf("SPcod_Precincts%d", i),
1564
538k
                                 *pabyMarkerDataIter,
1565
538k
                                 CPLSPrintf("PPx=%d PPy=%d: %dx%d",
1566
538k
                                            nLastVal & 0xf, nLastVal >> 4,
1567
538k
                                            1 << (nLastVal & 0xf),
1568
538k
                                            1 << (nLastVal >> 4)));
1569
538k
                        pabyMarkerDataIter += 1;
1570
538k
                        nRemainingMarkerSize -= 1;
1571
538k
                        i++;
1572
538k
                    }
1573
2.85k
                }
1574
664k
                if (nRemainingMarkerSize > 0)
1575
653
                    AddElement(
1576
653
                        psMarker, psLastChild, psDumpContext,
1577
653
                        CPLCreateXMLElementAndValue(
1578
653
                            nullptr, "RemainingBytes",
1579
653
                            CPLSPrintf(
1580
653
                                "%d", static_cast<int>(nRemainingMarkerSize))));
1581
664k
            }
1582
664k
        }
1583
368k
        else if (abyMarker[1] == 0x53) /* COC */
1584
305k
        {
1585
305k
            if (psDumpContext->pszCodestreamMarkers == nullptr ||
1586
0
                strstr(psDumpContext->pszCodestreamMarkers, "COC"))
1587
305k
            {
1588
305k
                psMarker = CreateCurrentMarker();
1589
305k
                if (!psMarker)
1590
1
                    break;
1591
305k
                if (Csiz < 257)
1592
6.48k
                    READ_MARKER_FIELD_UINT8("Ccoc");
1593
298k
                else
1594
298k
                    READ_MARKER_FIELD_UINT16("Ccoc");
1595
1596
305k
                bool bHasPrecincts = false;
1597
305k
                if (nRemainingMarkerSize >= 1)
1598
305k
                {
1599
305k
                    auto nLastVal = *pabyMarkerDataIter;
1600
305k
                    CPLString osInterp;
1601
305k
                    if (nLastVal & 0x1)
1602
17.1k
                    {
1603
17.1k
                        bHasPrecincts = true;
1604
17.1k
                        osInterp += "User defined precincts";
1605
17.1k
                    }
1606
287k
                    else
1607
287k
                        osInterp += "Standard precincts";
1608
305k
                    AddField(psMarker, psLastChild, psDumpContext, "Scoc",
1609
305k
                             nLastVal, osInterp.c_str());
1610
305k
                    pabyMarkerDataIter += 1;
1611
305k
                    nRemainingMarkerSize -= 1;
1612
305k
                }
1613
94
                else
1614
94
                {
1615
94
                    AddError(psMarker, psLastChild, psDumpContext,
1616
94
                             CPLSPrintf("Cannot read field %s", "Scoc"));
1617
94
                }
1618
305k
                READ_MARKER_FIELD_UINT8("SPcoc_NumDecompositions");
1619
305k
                READ_MARKER_FIELD_UINT8(
1620
305k
                    "SPcoc_xcb_minus_2",
1621
305k
                    [](GByte v)
1622
305k
                    {
1623
305k
                        return std::string(v <= 8
1624
305k
                                               ? CPLSPrintf("%d", 1 << (2 + v))
1625
305k
                                               : "invalid");
1626
305k
                    });
1627
305k
                READ_MARKER_FIELD_UINT8(
1628
305k
                    "SPcoc_ycb_minus_2",
1629
305k
                    [](GByte v)
1630
305k
                    {
1631
305k
                        return std::string(v <= 8
1632
305k
                                               ? CPLSPrintf("%d", 1 << (2 + v))
1633
305k
                                               : "invalid");
1634
305k
                    });
1635
305k
                READ_MARKER_FIELD_UINT8("SPcoc_cbstyle", cblkstyleLamba);
1636
305k
                READ_MARKER_FIELD_UINT8("SPcoc_transformation",
1637
305k
                                        [](GByte v)
1638
305k
                                        {
1639
305k
                                            return std::string(
1640
305k
                                                (v == 0)   ? "9-7 irreversible"
1641
305k
                                                : (v == 1) ? "5-3 reversible"
1642
271k
                                                           : "");
1643
305k
                                        });
1644
305k
                if (bHasPrecincts)
1645
17.1k
                {
1646
17.1k
                    int i = 0;
1647
187k
                    while (nRemainingMarkerSize >= 1)
1648
170k
                    {
1649
170k
                        auto nLastVal = *pabyMarkerDataIter;
1650
170k
                        AddField(psMarker, psLastChild, psDumpContext,
1651
170k
                                 CPLSPrintf("SPcoc_Precincts%d", i),
1652
170k
                                 *pabyMarkerDataIter,
1653
170k
                                 CPLSPrintf("PPx=%d PPy=%d: %dx%d",
1654
170k
                                            nLastVal & 0xf, nLastVal >> 4,
1655
170k
                                            1 << (nLastVal & 0xf),
1656
170k
                                            1 << (nLastVal >> 4)));
1657
170k
                        pabyMarkerDataIter += 1;
1658
170k
                        nRemainingMarkerSize -= 1;
1659
170k
                        i++;
1660
170k
                    }
1661
17.1k
                }
1662
305k
                if (nRemainingMarkerSize > 0)
1663
6.20k
                    AddElement(
1664
6.20k
                        psMarker, psLastChild, psDumpContext,
1665
6.20k
                        CPLCreateXMLElementAndValue(
1666
6.20k
                            nullptr, "RemainingBytes",
1667
6.20k
                            CPLSPrintf(
1668
6.20k
                                "%d", static_cast<int>(nRemainingMarkerSize))));
1669
305k
            }
1670
305k
        }
1671
63.2k
        else if (abyMarker[1] == 0x55) /* TLM */
1672
458
        {
1673
458
            if (psDumpContext->pszCodestreamMarkers == nullptr ||
1674
0
                strstr(psDumpContext->pszCodestreamMarkers, "TLM"))
1675
458
            {
1676
458
                psMarker = CreateCurrentMarker();
1677
458
                if (!psMarker)
1678
0
                    break;
1679
458
                READ_MARKER_FIELD_UINT8("Ztlm");
1680
458
                auto Stlm = READ_MARKER_FIELD_UINT8(
1681
458
                    "Stlm",
1682
458
                    [](GByte v)
1683
458
                    {
1684
457
                        return std::string(CPLSPrintf(
1685
457
                            "ST=%d SP=%d", (v >> 4) & 3, (v >> 6) & 1));
1686
457
                    });
1687
458
                int ST = (Stlm >> 4) & 3;
1688
458
                int SP = (Stlm >> 6) & 1;
1689
458
                int nTilePartDescLength = ST + ((SP == 0) ? 2 : 4);
1690
458
                int i = 0;
1691
2.05k
                while (nRemainingMarkerSize >= nTilePartDescLength)
1692
1.59k
                {
1693
1.59k
                    if (ST == 1)
1694
206
                        READ_MARKER_FIELD_UINT8(CPLSPrintf("Ttlm%d", i));
1695
1.38k
                    else if (ST == 2)
1696
207
                        READ_MARKER_FIELD_UINT16(CPLSPrintf("Ttlm%d", i));
1697
1.59k
                    if (SP == 0)
1698
1.36k
                        READ_MARKER_FIELD_UINT16(CPLSPrintf("Ptlm%d", i));
1699
225
                    else
1700
225
                        READ_MARKER_FIELD_UINT32(CPLSPrintf("Ptlm%d", i));
1701
1.59k
                    i++;
1702
1.59k
                }
1703
458
                if (nRemainingMarkerSize > 0)
1704
114
                    AddElement(
1705
114
                        psMarker, psLastChild, psDumpContext,
1706
114
                        CPLCreateXMLElementAndValue(
1707
114
                            nullptr, "RemainingBytes",
1708
114
                            CPLSPrintf(
1709
114
                                "%d", static_cast<int>(nRemainingMarkerSize))));
1710
458
            }
1711
458
        }
1712
62.7k
        else if (abyMarker[1] == 0x57) /* PLM */
1713
930
        {
1714
930
            if (psDumpContext->pszCodestreamMarkers == nullptr ||
1715
0
                strstr(psDumpContext->pszCodestreamMarkers, "PLM"))
1716
930
            {
1717
930
                psMarker = CreateCurrentMarker();
1718
930
                if (!psMarker)
1719
0
                    break;
1720
930
            }
1721
930
        }
1722
61.8k
        else if (abyMarker[1] == 0x58) /* PLT */
1723
1.61k
        {
1724
1.61k
            if (psDumpContext->pszCodestreamMarkers == nullptr ||
1725
0
                strstr(psDumpContext->pszCodestreamMarkers, "PLT"))
1726
1.61k
            {
1727
1.61k
                psMarker = CreateCurrentMarker();
1728
1.61k
                if (!psMarker)
1729
0
                    break;
1730
1.61k
                READ_MARKER_FIELD_UINT8("Zplt");
1731
1.61k
                int i = 0;
1732
1.61k
                unsigned nPacketLength = 0;
1733
6.98M
                while (nRemainingMarkerSize >= 1)
1734
6.98M
                {
1735
6.98M
                    auto nLastVal = *pabyMarkerDataIter;
1736
6.98M
                    nPacketLength |= (nLastVal & 0x7f);
1737
6.98M
                    if (nLastVal & 0x80)
1738
932k
                    {
1739
932k
                        nPacketLength <<= 7;
1740
932k
                    }
1741
6.05M
                    else
1742
6.05M
                    {
1743
6.05M
                        AddField(psMarker, psLastChild, psDumpContext,
1744
6.05M
                                 CPLSPrintf("Iplt%d", i), nPacketLength);
1745
6.05M
                        nPacketLength = 0;
1746
6.05M
                        i++;
1747
6.05M
                    }
1748
6.98M
                    pabyMarkerDataIter += 1;
1749
6.98M
                    nRemainingMarkerSize -= 1;
1750
6.98M
                }
1751
1.61k
                if (nPacketLength != 0)
1752
257
                {
1753
257
                    AddError(psMarker, psLastChild, psDumpContext,
1754
257
                             "Incorrect PLT marker");
1755
257
                }
1756
1.61k
            }
1757
1.61k
        }
1758
60.2k
        else if (abyMarker[1] == 0x59) /* CPF (HTJ2K) */
1759
51.9k
        {
1760
51.9k
            if (psDumpContext->pszCodestreamMarkers == nullptr ||
1761
0
                strstr(psDumpContext->pszCodestreamMarkers, "CPF"))
1762
51.9k
            {
1763
51.9k
                psMarker = CreateCurrentMarker();
1764
51.9k
                if (!psMarker)
1765
1
                    break;
1766
51.9k
                const GUInt16 Lcpf = nMarkerSize;
1767
51.9k
                if (Lcpf > 2 && (Lcpf % 2) == 0)
1768
51.7k
                {
1769
258k
                    for (int i = 0; i < (Lcpf - 2) / 2; i++)
1770
207k
                    {
1771
207k
                        READ_MARKER_FIELD_UINT16(CPLSPrintf("Pcpf%d", i + 1));
1772
207k
                    }
1773
51.7k
                }
1774
51.9k
                if (nRemainingMarkerSize > 0)
1775
218
                    AddElement(
1776
218
                        psMarker, psLastChild, psDumpContext,
1777
218
                        CPLCreateXMLElementAndValue(
1778
218
                            nullptr, "RemainingBytes",
1779
218
                            CPLSPrintf(
1780
218
                                "%d", static_cast<int>(nRemainingMarkerSize))));
1781
51.9k
            }
1782
51.9k
        }
1783
8.30k
        else if (abyMarker[1] == 0x5C) /* QCD */
1784
695
        {
1785
695
            if (psDumpContext->pszCodestreamMarkers == nullptr ||
1786
0
                strstr(psDumpContext->pszCodestreamMarkers, "QCD"))
1787
695
            {
1788
695
                psMarker = CreateCurrentMarker();
1789
695
                if (!psMarker)
1790
0
                    break;
1791
695
                const int Sqcd = READ_MARKER_FIELD_UINT8(
1792
695
                    "Sqcd",
1793
695
                    [](GByte v)
1794
695
                    {
1795
695
                        std::string ret;
1796
695
                        if ((v & 31) == 0)
1797
361
                            ret = "No quantization";
1798
334
                        else if ((v & 31) == 1)
1799
118
                            ret = "Scalar derived";
1800
216
                        else if ((v & 31) == 2)
1801
42
                            ret = "Scalar expounded";
1802
695
                        ret += ", ";
1803
695
                        ret += CPLSPrintf("guard bits = %d", v >> 5);
1804
695
                        return ret;
1805
695
                    });
1806
695
                if ((Sqcd & 31) == 0)
1807
361
                {
1808
                    // Reversible
1809
361
                    int i = 0;
1810
2.40k
                    while (nRemainingMarkerSize >= 1)
1811
2.04k
                    {
1812
2.04k
                        READ_MARKER_FIELD_UINT8(
1813
2.04k
                            CPLSPrintf("SPqcd%d", i),
1814
2.04k
                            [](GByte v)
1815
2.04k
                            {
1816
2.04k
                                return std::string(
1817
2.04k
                                    CPLSPrintf("epsilon_b = %d", v >> 3));
1818
2.04k
                            });
1819
2.04k
                        ++i;
1820
2.04k
                    }
1821
361
                }
1822
334
                else
1823
334
                {
1824
334
                    int i = 0;
1825
1.38k
                    while (nRemainingMarkerSize >= 2)
1826
1.05k
                    {
1827
1.05k
                        READ_MARKER_FIELD_UINT16(
1828
1.05k
                            CPLSPrintf("SPqcd%d", i),
1829
1.05k
                            [](GUInt16 v)
1830
1.05k
                            {
1831
1.05k
                                return std::string(CPLSPrintf(
1832
1.05k
                                    "mantissa_b = %d, epsilon_b = %d",
1833
1.05k
                                    v & ((1 << 11) - 1), v >> 11));
1834
1.05k
                            });
1835
1.05k
                        ++i;
1836
1.05k
                    }
1837
334
                }
1838
695
            }
1839
695
        }
1840
7.60k
        else if (abyMarker[1] == 0x5D) /* QCC */
1841
1.79k
        {
1842
1.79k
            if (psDumpContext->pszCodestreamMarkers == nullptr ||
1843
0
                strstr(psDumpContext->pszCodestreamMarkers, "QCC"))
1844
1.79k
            {
1845
1.79k
                psMarker = CreateCurrentMarker();
1846
1.79k
                if (!psMarker)
1847
0
                    break;
1848
1.79k
                if (Csiz < 257)
1849
1.42k
                    READ_MARKER_FIELD_UINT8("Cqcc");
1850
377
                else
1851
377
                    READ_MARKER_FIELD_UINT16("Cqcc");
1852
1853
1.79k
                const int Sqcc = READ_MARKER_FIELD_UINT8(
1854
1.79k
                    "Sqcc",
1855
1.79k
                    [](GByte v)
1856
1.79k
                    {
1857
1.71k
                        std::string ret;
1858
1.71k
                        if ((v & 31) == 0)
1859
970
                            ret = "No quantization";
1860
749
                        else if ((v & 31) == 1)
1861
71
                            ret = "Scalar derived";
1862
678
                        else if ((v & 31) == 2)
1863
286
                            ret = "Scalar expounded";
1864
1.71k
                        ret += ", ";
1865
1.71k
                        ret += CPLSPrintf("guard bits = %d", v >> 5);
1866
1.71k
                        return ret;
1867
1.71k
                    });
1868
1.79k
                if ((Sqcc & 31) == 0)
1869
1.04k
                {
1870
                    // Reversible
1871
1.04k
                    int i = 0;
1872
5.61k
                    while (nRemainingMarkerSize >= 1)
1873
4.57k
                    {
1874
4.57k
                        READ_MARKER_FIELD_UINT8(
1875
4.57k
                            CPLSPrintf("SPqcc%d", i),
1876
4.57k
                            [](GByte v)
1877
4.57k
                            {
1878
4.57k
                                return std::string(
1879
4.57k
                                    CPLSPrintf("epsilon_b = %d", v >> 3));
1880
4.57k
                            });
1881
4.57k
                        ++i;
1882
4.57k
                    }
1883
1.04k
                }
1884
749
                else
1885
749
                {
1886
749
                    int i = 0;
1887
1.61k
                    while (nRemainingMarkerSize >= 2)
1888
862
                    {
1889
862
                        READ_MARKER_FIELD_UINT16(
1890
862
                            CPLSPrintf("SPqcc%d", i),
1891
862
                            [](GUInt16 v)
1892
862
                            {
1893
862
                                return std::string(CPLSPrintf(
1894
862
                                    "mantissa_b = %d, epsilon_b = %d",
1895
862
                                    v & ((1 << 11) - 1), v >> 11));
1896
862
                            });
1897
862
                        ++i;
1898
862
                    }
1899
749
                }
1900
1.79k
            }
1901
1.79k
        }
1902
5.80k
        else if (abyMarker[1] == 0x5E) /* RGN */
1903
197
        {
1904
197
            if (psDumpContext->pszCodestreamMarkers == nullptr ||
1905
0
                strstr(psDumpContext->pszCodestreamMarkers, "RGN"))
1906
197
            {
1907
197
                psMarker = CreateCurrentMarker();
1908
197
                if (!psMarker)
1909
0
                    break;
1910
197
            }
1911
197
        }
1912
5.61k
        else if (abyMarker[1] == 0x5F) /* POC */
1913
949
        {
1914
949
            if (psDumpContext->pszCodestreamMarkers == nullptr ||
1915
0
                strstr(psDumpContext->pszCodestreamMarkers, "POC"))
1916
949
            {
1917
949
                psMarker = CreateCurrentMarker();
1918
949
                if (!psMarker)
1919
0
                    break;
1920
949
                const int nPOCEntrySize = Csiz < 257 ? 7 : 9;
1921
949
                int i = 0;
1922
2.96k
                while (nRemainingMarkerSize >= nPOCEntrySize)
1923
2.01k
                {
1924
2.01k
                    READ_MARKER_FIELD_UINT8(CPLSPrintf("RSpoc%d", i));
1925
2.01k
                    if (nPOCEntrySize == 7)
1926
1.22k
                    {
1927
1.22k
                        READ_MARKER_FIELD_UINT8(CPLSPrintf("CSpoc%d", i));
1928
1.22k
                    }
1929
796
                    else
1930
796
                    {
1931
796
                        READ_MARKER_FIELD_UINT16(CPLSPrintf("CSpoc%d", i));
1932
796
                    }
1933
2.01k
                    READ_MARKER_FIELD_UINT16(CPLSPrintf("LYEpoc%d", i));
1934
2.01k
                    READ_MARKER_FIELD_UINT8(CPLSPrintf("REpoc%d", i));
1935
2.01k
                    if (nPOCEntrySize == 7)
1936
1.22k
                    {
1937
1.22k
                        READ_MARKER_FIELD_UINT8(CPLSPrintf("CEpoc%d", i));
1938
1.22k
                    }
1939
796
                    else
1940
796
                    {
1941
796
                        READ_MARKER_FIELD_UINT16(CPLSPrintf("CEpoc%d", i));
1942
796
                    }
1943
2.01k
                    READ_MARKER_FIELD_UINT8(CPLSPrintf("Ppoc%d", i),
1944
2.01k
                                            lambdaPOCType);
1945
2.01k
                    i++;
1946
2.01k
                }
1947
949
                if (nRemainingMarkerSize > 0)
1948
857
                {
1949
857
                    AddElement(
1950
857
                        psMarker, psLastChild, psDumpContext,
1951
857
                        CPLCreateXMLElementAndValue(
1952
857
                            nullptr, "RemainingBytes",
1953
857
                            CPLSPrintf(
1954
857
                                "%d", static_cast<int>(nRemainingMarkerSize))));
1955
857
                }
1956
949
            }
1957
949
        }
1958
4.66k
        else if (abyMarker[1] == 0x60) /* PPM */
1959
700
        {
1960
700
            if (psDumpContext->pszCodestreamMarkers == nullptr ||
1961
0
                strstr(psDumpContext->pszCodestreamMarkers, "PPM"))
1962
700
            {
1963
700
                psMarker = CreateCurrentMarker();
1964
700
                if (!psMarker)
1965
0
                    break;
1966
700
            }
1967
700
        }
1968
3.96k
        else if (abyMarker[1] == 0x61) /* PPT */
1969
222
        {
1970
222
            if (psDumpContext->pszCodestreamMarkers == nullptr ||
1971
0
                strstr(psDumpContext->pszCodestreamMarkers, "PPT"))
1972
222
            {
1973
222
                psMarker = CreateCurrentMarker();
1974
222
                if (!psMarker)
1975
0
                    break;
1976
222
            }
1977
222
        }
1978
3.74k
        else if (abyMarker[1] == 0x63) /* CRG */
1979
252
        {
1980
252
            if (psDumpContext->pszCodestreamMarkers == nullptr ||
1981
0
                strstr(psDumpContext->pszCodestreamMarkers, "CRG"))
1982
252
            {
1983
252
                psMarker = CreateCurrentMarker();
1984
252
                if (!psMarker)
1985
0
                    break;
1986
252
            }
1987
252
        }
1988
3.48k
        else if (abyMarker[1] == 0x64) /* COM */
1989
409
        {
1990
409
            if (psDumpContext->pszCodestreamMarkers == nullptr ||
1991
0
                strstr(psDumpContext->pszCodestreamMarkers, "COM"))
1992
409
            {
1993
409
                psMarker = CreateCurrentMarker();
1994
409
                if (!psMarker)
1995
0
                    break;
1996
409
                auto RCom = READ_MARKER_FIELD_UINT16(
1997
409
                    "Rcom",
1998
409
                    [](GUInt16 v)
1999
409
                    {
2000
409
                        return std::string((v == 0)   ? "Binary"
2001
409
                                           : (v == 1) ? "LATIN1"
2002
320
                                                      : "");
2003
409
                    });
2004
409
                if (RCom == 1)
2005
50
                {
2006
50
                    GByte abyBackup = pabyMarkerDataIter[nRemainingMarkerSize];
2007
50
                    pabyMarkerDataIter[nRemainingMarkerSize] = 0;
2008
50
                    AddField(
2009
50
                        psMarker, psLastChild, psDumpContext, "COM",
2010
50
                        static_cast<int>(nRemainingMarkerSize),
2011
50
                        reinterpret_cast<const char *>(pabyMarkerDataIter));
2012
50
                    pabyMarkerDataIter[nRemainingMarkerSize] = abyBackup;
2013
50
                }
2014
409
            }
2015
409
        }
2016
2017
1.14M
        if (VSIFSeekL(fp, nOffset + 2 + nMarkerSize, SEEK_SET) != 0)
2018
0
        {
2019
0
            AddError(psCSBox, psLastChildCSBox, psDumpContext,
2020
0
                     "Cannot seek to next marker", nOffset + 2 + nMarkerSize);
2021
0
            break;
2022
0
        }
2023
2024
1.14M
        CPL_IGNORE_RET_VAL(bError);
2025
1.14M
    }
2026
16.6k
    CPLFree(pabyMarkerData);
2027
16.6k
    return psCSBox;
2028
16.6k
}
2029
2030
/************************************************************************/
2031
/*                  GDALGetJPEG2000StructureInternal()                  */
2032
/************************************************************************/
2033
2034
static void GDALGetJPEG2000StructureInternal(CPLXMLNode *psParent, VSILFILE *fp,
2035
                                             GDALJP2Box *poParentBox,
2036
                                             int nRecLevel,
2037
                                             vsi_l_offset nFileOrParentBoxSize,
2038
                                             DumpContext *psDumpContext)
2039
26.4k
{
2040
    // Limit recursion to a reasonable level. I believe that in practice 2
2041
    // should be sufficient, but just in case someone creates deeply
2042
    // nested "super-boxes", allow up to 5.
2043
26.4k
    if (nRecLevel == 5)
2044
18
        return;
2045
2046
26.4k
    static const char *const szHex = "0123456789ABCDEF";
2047
26.4k
    GDALJP2Box oBox(fp);
2048
26.4k
    oBox.SetAllowGetFileSize(psDumpContext->bAllowGetFileSize);
2049
26.4k
    CPLXMLNode *psLastChild = nullptr;
2050
26.4k
    if (oBox.ReadFirstChild(poParentBox))
2051
26.3k
    {
2052
214k
        while (strlen(oBox.GetType()) > 0 &&
2053
212k
               psDumpContext->nCurLineCount <= psDumpContext->nMaxLineCount + 1)
2054
212k
        {
2055
212k
            GIntBig nBoxDataLength = oBox.GetDataLength();
2056
212k
            const char *pszBoxType = oBox.GetType();
2057
212k
            CPLXMLNode *psBox = nullptr;
2058
212k
            const auto CreateBox = [&]()
2059
620k
            {
2060
620k
                if (psBox != nullptr)
2061
408k
                    return true;
2062
212k
                psBox = CPLCreateXMLNode(nullptr, CXT_Element, "JP2Box");
2063
212k
                psBox = AddElement(psParent, psLastChild, psDumpContext, psBox);
2064
212k
                if (!psBox)
2065
1
                    return false;
2066
212k
                CPLAddXMLAttributeAndValue(psBox, "name", pszBoxType);
2067
212k
                CPLAddXMLAttributeAndValue(
2068
212k
                    psBox, "box_offset",
2069
212k
                    CPLSPrintf(CPL_FRMT_GIB, oBox.GetBoxOffset()));
2070
212k
                const auto nBoxLength = oBox.GetBoxLength();
2071
212k
                CPLAddXMLAttributeAndValue(
2072
212k
                    psBox, "box_length",
2073
212k
                    nBoxLength > 0 ? CPLSPrintf(CPL_FRMT_GIB, nBoxLength)
2074
212k
                                   : "unknown");
2075
212k
                CPLAddXMLAttributeAndValue(
2076
212k
                    psBox, "data_offset",
2077
212k
                    CPLSPrintf(CPL_FRMT_GIB, oBox.GetDataOffset()));
2078
212k
                CPLAddXMLAttributeAndValue(
2079
212k
                    psBox, "data_length",
2080
212k
                    nBoxDataLength > 0
2081
212k
                        ? CPLSPrintf(CPL_FRMT_GIB, nBoxDataLength)
2082
212k
                        : "unknown");
2083
2084
212k
                if (nBoxDataLength > GINTBIG_MAX - oBox.GetDataOffset())
2085
3
                {
2086
3
                    CPLXMLNode *psLastChildBox = nullptr;
2087
3
                    AddError(psBox, psLastChildBox, psDumpContext,
2088
3
                             "Invalid box_length");
2089
3
                    return false;
2090
3
                }
2091
212k
                return true;
2092
212k
            };
2093
2094
            // Check large non-jp2c boxes against filesize
2095
212k
            if (strcmp(pszBoxType, "jp2c") != 0 && nBoxDataLength > 100 * 1024)
2096
4.31k
            {
2097
4.31k
                if (nFileOrParentBoxSize == 0)
2098
2.21k
                {
2099
2.21k
                    CPL_IGNORE_RET_VAL(VSIFSeekL(fp, 0, SEEK_END));
2100
2.21k
                    nFileOrParentBoxSize = VSIFTellL(fp);
2101
2.21k
                }
2102
4.31k
            }
2103
212k
            if (nFileOrParentBoxSize > 0 && nBoxDataLength > 0 &&
2104
13.6k
                (static_cast<vsi_l_offset>(oBox.GetDataOffset()) >
2105
13.6k
                     nFileOrParentBoxSize ||
2106
13.3k
                 static_cast<vsi_l_offset>(nBoxDataLength) >
2107
13.3k
                     nFileOrParentBoxSize - oBox.GetDataOffset()))
2108
4.53k
            {
2109
4.53k
                CPLXMLNode *psLastChildBox = nullptr;
2110
4.53k
                if (!CreateBox())
2111
2
                    break;
2112
4.53k
                AddError(psBox, psLastChildBox, psDumpContext,
2113
4.53k
                         "Invalid box_length");
2114
4.53k
                break;
2115
4.53k
            }
2116
2117
207k
            if (oBox.IsSuperBox())
2118
6.69k
            {
2119
6.69k
                if (!CreateBox())
2120
0
                    break;
2121
6.69k
                if (nBoxDataLength <= 0)
2122
70
                    break;
2123
6.62k
                GDALGetJPEG2000StructureInternal(
2124
6.62k
                    psBox, fp, &oBox, nRecLevel + 1,
2125
6.62k
                    oBox.GetDataOffset() +
2126
6.62k
                        static_cast<vsi_l_offset>(nBoxDataLength),
2127
6.62k
                    psDumpContext);
2128
6.62k
            }
2129
201k
            else
2130
201k
            {
2131
201k
                if (strcmp(pszBoxType, "uuid") == 0 &&
2132
97.0k
                    psDumpContext->bDumpJP2Boxes)
2133
97.0k
                {
2134
97.0k
                    if (!CreateBox())
2135
0
                        break;
2136
97.0k
                    char *pszBinaryContent =
2137
97.0k
                        static_cast<char *>(VSIMalloc(2 * 16 + 1));
2138
97.0k
                    const GByte *pabyUUID = oBox.GetUUID();
2139
1.64M
                    for (int i = 0; i < 16; i++)
2140
1.55M
                    {
2141
1.55M
                        pszBinaryContent[2 * i] = szHex[pabyUUID[i] >> 4];
2142
1.55M
                        pszBinaryContent[2 * i + 1] = szHex[pabyUUID[i] & 0xf];
2143
1.55M
                    }
2144
97.0k
                    pszBinaryContent[2 * 16] = '\0';
2145
97.0k
                    CPLXMLNode *psUUIDNode =
2146
97.0k
                        CPLCreateXMLNode(nullptr, CXT_Element, "UUID");
2147
97.0k
                    if (GDALJP2Metadata::IsUUID_MSI(pabyUUID))
2148
93.2k
                        CPLAddXMLAttributeAndValue(psUUIDNode, "description",
2149
93.2k
                                                   "GeoTIFF");
2150
3.81k
                    else if (GDALJP2Metadata::IsUUID_XMP(pabyUUID))
2151
3
                        CPLAddXMLAttributeAndValue(psUUIDNode, "description",
2152
3
                                                   "XMP");
2153
97.0k
                    CPLCreateXMLNode(psUUIDNode, CXT_Text, pszBinaryContent);
2154
97.0k
                    VSIFree(pszBinaryContent);
2155
2156
97.0k
                    CPLXMLNode *psLastChildBox = nullptr;
2157
97.0k
                    AddElement(psBox, psLastChildBox, psDumpContext,
2158
97.0k
                               psUUIDNode);
2159
97.0k
                }
2160
2161
201k
                if (psDumpContext->bDumpBinaryContent &&
2162
201k
                    strcmp(pszBoxType, "jp2c") != 0 &&
2163
185k
                    nBoxDataLength < 100 * 1024)
2164
185k
                {
2165
185k
                    if (!CreateBox())
2166
0
                        break;
2167
185k
                    CPLXMLNode *psBinaryContent =
2168
185k
                        CPLCreateXMLNode(nullptr, CXT_Element, "BinaryContent");
2169
185k
                    GByte *pabyBoxData = oBox.ReadBoxData();
2170
185k
                    const int nBoxLength = static_cast<int>(
2171
185k
                        std::min<GIntBig>(nBoxDataLength, INT_MAX / 2 - 1));
2172
185k
                    char *pszBinaryContent =
2173
185k
                        static_cast<char *>(VSIMalloc(2 * nBoxLength + 1));
2174
185k
                    if (pabyBoxData && pszBinaryContent)
2175
183k
                    {
2176
35.2M
                        for (int i = 0; i < nBoxLength; i++)
2177
35.0M
                        {
2178
35.0M
                            pszBinaryContent[2 * i] =
2179
35.0M
                                szHex[pabyBoxData[i] >> 4];
2180
35.0M
                            pszBinaryContent[2 * i + 1] =
2181
35.0M
                                szHex[pabyBoxData[i] & 0xf];
2182
35.0M
                        }
2183
183k
                        pszBinaryContent[2 * nBoxLength] = '\0';
2184
183k
                        CPLCreateXMLNode(psBinaryContent, CXT_Text,
2185
183k
                                         pszBinaryContent);
2186
183k
                    }
2187
185k
                    CPLFree(pabyBoxData);
2188
185k
                    VSIFree(pszBinaryContent);
2189
2190
185k
                    CPLXMLNode *psLastChildBox = nullptr;
2191
185k
                    AddElement(psBox, psLastChildBox, psDumpContext,
2192
185k
                               psBinaryContent);
2193
185k
                }
2194
2195
201k
                if (psDumpContext->bDumpTextContent &&
2196
201k
                    strcmp(pszBoxType, "jp2c") != 0 &&
2197
185k
                    nBoxDataLength < 100 * 1024)
2198
185k
                {
2199
185k
                    if (!CreateBox())
2200
0
                        break;
2201
185k
                    GByte *pabyBoxData = oBox.ReadBoxData();
2202
185k
                    if (pabyBoxData)
2203
183k
                    {
2204
183k
                        const char *pszBoxData =
2205
183k
                            reinterpret_cast<const char *>(pabyBoxData);
2206
183k
                        if (CPLIsUTF8(pszBoxData, -1) &&
2207
158k
                            static_cast<int>(strlen(pszBoxData)) + 2 >=
2208
158k
                                nBoxDataLength)
2209
27.6k
                        {
2210
27.6k
                            CPLXMLNode *psXMLContentBox = nullptr;
2211
27.6k
                            if (pszBoxData[0] == '<')
2212
5.41k
                            {
2213
5.41k
                                CPLPushErrorHandler(CPLQuietErrorHandler);
2214
5.41k
                                psXMLContentBox = CPLParseXMLString(pszBoxData);
2215
5.41k
                                CPLPopErrorHandler();
2216
5.41k
                            }
2217
27.6k
                            if (psXMLContentBox)
2218
1.34k
                            {
2219
1.34k
                                CPLXMLNode *psXMLContentNode = CPLCreateXMLNode(
2220
1.34k
                                    nullptr, CXT_Element, "XMLContent");
2221
1.34k
                                psXMLContentNode->psChild = psXMLContentBox;
2222
2223
1.34k
                                CPLXMLNode *psLastChildBox = nullptr;
2224
1.34k
                                AddElement(psBox, psLastChildBox, psDumpContext,
2225
1.34k
                                           psXMLContentNode);
2226
1.34k
                            }
2227
26.3k
                            else
2228
26.3k
                            {
2229
26.3k
                                auto psTextElement = CPLCreateXMLNode(
2230
26.3k
                                    nullptr, CXT_Element, "TextContent");
2231
26.3k
                                CPLCreateXMLNode(psTextElement, CXT_Text,
2232
26.3k
                                                 pszBoxData);
2233
2234
26.3k
                                CPLXMLNode *psLastChildBox = nullptr;
2235
26.3k
                                AddElement(psBox, psLastChildBox, psDumpContext,
2236
26.3k
                                           psTextElement);
2237
26.3k
                            }
2238
27.6k
                        }
2239
183k
                    }
2240
185k
                    CPLFree(pabyBoxData);
2241
185k
                }
2242
2243
201k
                if (strcmp(pszBoxType, "jp2c") == 0)
2244
16.0k
                {
2245
16.0k
                    if (psDumpContext->bDumpCodestream ||
2246
0
                        psDumpContext->pszCodestreamMarkers)
2247
16.0k
                    {
2248
16.0k
                        if (!CreateBox())
2249
2
                            break;
2250
16.0k
                        DumpJPK2CodeStream(psBox, fp, oBox.GetDataOffset(),
2251
16.0k
                                           nBoxDataLength, psDumpContext);
2252
16.0k
                        if (psDumpContext->bStopAtSOD &&
2253
0
                            psDumpContext->bSODEncountered)
2254
0
                        {
2255
0
                            break;
2256
0
                        }
2257
16.0k
                    }
2258
16.0k
                }
2259
185k
                else if (!psDumpContext->bDumpJP2Boxes)
2260
0
                {
2261
                    // do nothing
2262
0
                }
2263
185k
                else if (strcmp(pszBoxType, "uuid") == 0 &&
2264
97.0k
                         GDALJP2Metadata::IsUUID_MSI(oBox.GetUUID()))
2265
93.2k
                {
2266
93.2k
                    if (!CreateBox())
2267
0
                        break;
2268
93.2k
                    DumpGeoTIFFBox(psBox, oBox, psDumpContext);
2269
93.2k
                }
2270
91.8k
                else if (strcmp(pszBoxType, "ftyp") == 0)
2271
3.08k
                {
2272
3.08k
                    if (!CreateBox())
2273
0
                        break;
2274
3.08k
                    DumpFTYPBox(psBox, oBox, psDumpContext);
2275
3.08k
                }
2276
88.7k
                else if (strcmp(pszBoxType, "ihdr") == 0)
2277
8.75k
                {
2278
8.75k
                    if (!CreateBox())
2279
0
                        break;
2280
8.75k
                    DumpIHDRBox(psBox, oBox, psDumpContext);
2281
8.75k
                }
2282
80.0k
                else if (strcmp(pszBoxType, "bpcc") == 0)
2283
380
                {
2284
380
                    if (!CreateBox())
2285
0
                        break;
2286
380
                    DumpBPCCBox(psBox, oBox, psDumpContext);
2287
380
                }
2288
79.6k
                else if (strcmp(pszBoxType, "colr") == 0)
2289
15.4k
                {
2290
15.4k
                    if (!CreateBox())
2291
0
                        break;
2292
15.4k
                    DumpCOLRBox(psBox, oBox, psDumpContext);
2293
15.4k
                }
2294
64.1k
                else if (strcmp(pszBoxType, "pclr") == 0)
2295
1.33k
                {
2296
1.33k
                    if (!CreateBox())
2297
0
                        break;
2298
1.33k
                    DumpPCLRBox(psBox, oBox, psDumpContext);
2299
1.33k
                }
2300
62.8k
                else if (strcmp(pszBoxType, "cmap") == 0)
2301
186
                {
2302
186
                    if (!CreateBox())
2303
0
                        break;
2304
186
                    DumpCMAPBox(psBox, oBox, psDumpContext);
2305
186
                }
2306
62.6k
                else if (strcmp(pszBoxType, "cdef") == 0)
2307
474
                {
2308
474
                    if (!CreateBox())
2309
0
                        break;
2310
474
                    DumpCDEFBox(psBox, oBox, psDumpContext);
2311
474
                }
2312
62.1k
                else if (strcmp(pszBoxType, "resc") == 0 ||
2313
60.5k
                         strcmp(pszBoxType, "resd") == 0)
2314
2.05k
                {
2315
2.05k
                    if (!CreateBox())
2316
0
                        break;
2317
2.05k
                    DumpRESxBox(psBox, oBox, psDumpContext);
2318
2.05k
                }
2319
60.1k
                else if (strcmp(pszBoxType, "rreq") == 0)
2320
1.71k
                {
2321
1.71k
                    if (!CreateBox())
2322
0
                        break;
2323
1.71k
                    DumpRREQBox(psBox, oBox, psDumpContext);
2324
1.71k
                }
2325
201k
            }
2326
2327
207k
            if (!oBox.ReadNextChild(poParentBox))
2328
20.0k
                break;
2329
207k
        }
2330
26.3k
    }
2331
26.4k
}
2332
2333
/************************************************************************/
2334
/*                      GDALGetJPEG2000Structure()                      */
2335
/************************************************************************/
2336
2337
constexpr unsigned char jpc_header[] = {0xff, 0x4f};
2338
constexpr unsigned char jp2_box_jp[] = {0x6a, 0x50, 0x20, 0x20}; /* 'jP  ' */
2339
2340
/** Dump the structure of a JPEG2000 file as a XML tree.
2341
 *
2342
 * @param pszFilename filename.
2343
 * @param papszOptions NULL terminated list of options, or NULL.
2344
 *                     Allowed options are BINARY_CONTENT=YES, TEXT_CONTENT=YES,
2345
 *                     CODESTREAM=YES, ALL=YES, JP2_BOXES=YES,
2346
 *                     CODESTREAM_MARKERS=list_of_marker_names_comma_separated,
2347
 *                     STOP_AT_SOD=YES, ALLOW_GET_FILE_SIZE=NO.
2348
 * @return XML tree (to be freed with CPLDestroyXMLNode()) or NULL in case
2349
 *         of error
2350
 */
2351
2352
CPLXMLNode *GDALGetJPEG2000Structure(const char *pszFilename,
2353
                                     CSLConstList papszOptions)
2354
20.5k
{
2355
20.5k
    VSILFILE *fp = VSIFOpenL(pszFilename, "rb");
2356
20.5k
    if (fp == nullptr)
2357
0
    {
2358
0
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s", pszFilename);
2359
0
        return nullptr;
2360
0
    }
2361
20.5k
    auto psRet = GDALGetJPEG2000Structure(pszFilename, fp, papszOptions);
2362
20.5k
    CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
2363
20.5k
    return psRet;
2364
20.5k
}
2365
2366
#ifndef DOXYGEN_SKIP
2367
2368
/************************************************************************/
2369
/*                      GDALGetJPEG2000Structure()                      */
2370
/************************************************************************/
2371
2372
CPLXMLNode *GDALGetJPEG2000Structure(const char *pszFilename, VSILFILE *fp,
2373
                                     CSLConstList papszOptions)
2374
20.5k
{
2375
20.5k
    if (fp == nullptr)
2376
0
        return GDALGetJPEG2000Structure(pszFilename, papszOptions);
2377
2378
20.5k
    GByte abyHeader[16];
2379
20.5k
    if (VSIFSeekL(fp, 0, SEEK_SET) != 0 ||
2380
20.5k
        VSIFReadL(abyHeader, 16, 1, fp) != 1 ||
2381
20.5k
        (memcmp(abyHeader, jpc_header, sizeof(jpc_header)) != 0 &&
2382
19.8k
         memcmp(abyHeader + 4, jp2_box_jp, sizeof(jp2_box_jp)) != 0))
2383
32
    {
2384
32
        CPLError(CE_Failure, CPLE_AppDefined, "%s is not a JPEG2000 file",
2385
32
                 pszFilename);
2386
32
        return nullptr;
2387
32
    }
2388
2389
20.5k
    CPLXMLNode *psParent = nullptr;
2390
20.5k
    DumpContext dc;
2391
20.5k
    dc.nCurLineCount = 0;
2392
20.5k
    dc.nMaxLineCount = atoi(CSLFetchNameValueDef(
2393
20.5k
        papszOptions, "MAX_LINES",
2394
20.5k
        CPLGetConfigOption("GDAL_JPEG2000_STRUCTURE_MAX_LINES", "500000")));
2395
20.5k
    if (dc.nMaxLineCount > INT_MAX - 1)
2396
0
        dc.nMaxLineCount = INT_MAX - 1;
2397
20.5k
    dc.bDumpAll = CPLFetchBool(papszOptions, "ALL", false);
2398
20.5k
    dc.bDumpCodestream =
2399
20.5k
        dc.bDumpAll || CPLFetchBool(papszOptions, "CODESTREAM", false);
2400
20.5k
    dc.bDumpBinaryContent =
2401
20.5k
        dc.bDumpAll || CPLFetchBool(papszOptions, "BINARY_CONTENT", false);
2402
20.5k
    dc.bDumpTextContent =
2403
20.5k
        dc.bDumpAll || CPLFetchBool(papszOptions, "TEXT_CONTENT", false);
2404
20.5k
    dc.pszCodestreamMarkers =
2405
20.5k
        CSLFetchNameValue(papszOptions, "CODESTREAM_MARKERS");
2406
20.5k
    dc.bDumpJP2Boxes = dc.bDumpAll ||
2407
0
                       CPLFetchBool(papszOptions, "JP2_BOXES", false) ||
2408
0
                       dc.pszCodestreamMarkers == nullptr;
2409
20.5k
    dc.bStopAtSOD = CPLFetchBool(papszOptions, "STOP_AT_SOD", false);
2410
20.5k
    dc.bAllowGetFileSize =
2411
20.5k
        CPLFetchBool(papszOptions, "ALLOW_GET_FILE_SIZE", true);
2412
2413
20.5k
    if (memcmp(abyHeader, jpc_header, sizeof(jpc_header)) == 0)
2414
656
    {
2415
656
        if (dc.bDumpCodestream || dc.pszCodestreamMarkers != nullptr)
2416
656
        {
2417
656
            GIntBig nBoxDataLength = -1;
2418
656
            if (dc.bAllowGetFileSize && VSIFSeekL(fp, 0, SEEK_END) == 0)
2419
656
            {
2420
656
                nBoxDataLength = static_cast<GIntBig>(VSIFTellL(fp));
2421
656
            }
2422
656
            psParent = DumpJPK2CodeStream(nullptr, fp, 0, nBoxDataLength, &dc);
2423
656
            CPLAddXMLAttributeAndValue(psParent, "filename", pszFilename);
2424
656
        }
2425
656
    }
2426
19.8k
    else
2427
19.8k
    {
2428
19.8k
        psParent = CPLCreateXMLNode(nullptr, CXT_Element, "JP2File");
2429
19.8k
        CPLAddXMLAttributeAndValue(psParent, "filename", pszFilename);
2430
19.8k
        vsi_l_offset nFileSize = 0;
2431
19.8k
        GDALGetJPEG2000StructureInternal(psParent, fp, nullptr, 0, nFileSize,
2432
19.8k
                                         &dc);
2433
19.8k
    }
2434
2435
20.5k
    if (dc.nCurLineCount > dc.nMaxLineCount)
2436
29
    {
2437
29
        CPLError(CE_Failure, CPLE_AppDefined,
2438
29
                 "Maximum number of lines in JPEG2000 structure dump reached. "
2439
29
                 "Increase GDAL_JPEG2000_STRUCTURE_MAX_LINES beyond %d.",
2440
29
                 dc.nMaxLineCount);
2441
29
    }
2442
2443
20.5k
    return psParent;
2444
20.5k
}
2445
2446
/************************************************************************/
2447
/*                    GDALGetJPEG2000Reversibility()                    */
2448
/************************************************************************/
2449
2450
const char *GDALGetJPEG2000Reversibility(const char *pszFilename, VSILFILE *fp)
2451
0
{
2452
0
    const char *const apszOptions[] = {"ALLOW_GET_FILE_SIZE=NO",
2453
0
                                       "STOP_AT_SOD=YES",
2454
0
                                       "CODESTREAM_MARKERS=COD,COM", nullptr};
2455
0
    CPLXMLNode *psRes = GDALGetJPEG2000Structure(pszFilename, fp, apszOptions);
2456
0
    if (psRes == nullptr)
2457
0
        return nullptr;
2458
0
    const char *pszReversibility = nullptr;
2459
0
    const CPLXMLNode *psJP2C = CPLSearchXMLNode(psRes, "JP2KCodeStream");
2460
0
    if (psJP2C)
2461
0
    {
2462
0
        const char *pszTransformation = nullptr;
2463
0
        const char *pszCOM = nullptr;
2464
0
        for (const CPLXMLNode *psMarker = psJP2C->psChild; psMarker;
2465
0
             psMarker = psMarker->psNext)
2466
0
        {
2467
0
            if (psMarker->eType == CXT_Element &&
2468
0
                strcmp(psMarker->pszValue, "Marker") == 0 &&
2469
0
                strcmp(CPLGetXMLValue(psMarker, "name", ""), "COD") == 0)
2470
0
            {
2471
0
                for (const CPLXMLNode *psField = psMarker->psChild; psField;
2472
0
                     psField = psField->psNext)
2473
0
                {
2474
0
                    if (psField->eType == CXT_Element &&
2475
0
                        strcmp(psField->pszValue, "Field") == 0 &&
2476
0
                        strcmp(CPLGetXMLValue(psField, "name", ""),
2477
0
                               "SPcod_transformation") == 0)
2478
0
                    {
2479
0
                        pszTransformation =
2480
0
                            CPLGetXMLValue(psField, nullptr, nullptr);
2481
0
                        break;
2482
0
                    }
2483
0
                }
2484
0
            }
2485
0
            else if (psMarker->eType == CXT_Element &&
2486
0
                     strcmp(psMarker->pszValue, "Marker") == 0 &&
2487
0
                     strcmp(CPLGetXMLValue(psMarker, "name", ""), "COM") == 0)
2488
0
            {
2489
0
                for (const CPLXMLNode *psField = psMarker->psChild; psField;
2490
0
                     psField = psField->psNext)
2491
0
                {
2492
0
                    if (psField->eType == CXT_Element &&
2493
0
                        strcmp(psField->pszValue, "Field") == 0 &&
2494
0
                        strcmp(CPLGetXMLValue(psField, "name", ""), "COM") == 0)
2495
0
                    {
2496
0
                        pszCOM = CPLGetXMLValue(psField, nullptr, nullptr);
2497
0
                        break;
2498
0
                    }
2499
0
                }
2500
0
            }
2501
0
        }
2502
2503
0
        if (pszTransformation != nullptr &&
2504
0
            strcmp(pszTransformation, "0") ==
2505
0
                0)  // 0 = 9x7 irreversible wavelet
2506
0
        {
2507
0
            pszReversibility = "LOSSY";
2508
0
        }
2509
0
        else if (pszTransformation != nullptr &&
2510
0
                 strcmp(pszTransformation, "1") ==
2511
0
                     0)  // 1 = 5x3 reversible wavelet
2512
0
        {
2513
            // 5x3 wavelet by itself doesn't guarantee full lossless mode
2514
            // if quality layers are discarded. hence the "possibly"
2515
0
            pszReversibility = "LOSSLESS (possibly)";
2516
2517
0
            if (pszCOM &&
2518
0
                STARTS_WITH(
2519
0
                    pszCOM,
2520
0
                    "Kdu-Layer-Info: "
2521
0
                    "log_2{Delta-D(squared-error)/Delta-L(bytes)}, L(bytes)"))
2522
0
            {
2523
0
                if (strstr(pszCOM, "-192.0,") != nullptr)
2524
0
                {
2525
                    // Not really sure to understand this fully, but
2526
                    // experimentaly I've found that if the last row in the
2527
                    // Kdu-Layer-Info includes a line starting with "-192.0", it
2528
                    // means that the last layer includes everything to be
2529
                    // lossless.
2530
0
                    pszReversibility = "LOSSLESS";
2531
0
                }
2532
0
                else
2533
0
                {
2534
0
                    pszReversibility = "LOSSY";
2535
0
                }
2536
0
            }
2537
            // Kakadu < 6.4
2538
0
            else if (pszCOM &&
2539
0
                     STARTS_WITH(
2540
0
                         pszCOM,
2541
0
                         "Kdu-Layer-Info: "
2542
0
                         "log_2{Delta-D(MSE)/[2^16*Delta-L(bytes)]}, L(bytes)"))
2543
0
            {
2544
0
                if (strstr(pszCOM, "-256.0,") != nullptr)
2545
0
                {
2546
                    // Not really sure to understand this fully, but
2547
                    // experimentaly I've found that if the last row in the
2548
                    // Kdu-Layer-Info includes a line starting with "-256.0", it
2549
                    // means that the last layer includes everything to be
2550
                    // lossless.
2551
0
                    pszReversibility = "LOSSLESS";
2552
0
                }
2553
0
                else
2554
0
                {
2555
0
                    pszReversibility = "LOSSY";
2556
0
                }
2557
0
            }
2558
0
            else if ((pszCOM && STARTS_WITH(pszCOM, "Created by OpenJPEG")) ||
2559
0
                     (pszCOM && STARTS_WITH(pszCOM, "Created by Grok")))
2560
0
            {
2561
                // Starting with GDAL 3.6, the JP2OpenJPEG driver will write
2562
                // if the encoding parameters are lossless/lossy (for 5x3
2563
                // wavelets)
2564
0
                if (strstr(pszCOM, "LOSSLESS settings used"))
2565
0
                {
2566
0
                    pszReversibility = "LOSSLESS";
2567
0
                }
2568
0
                else if (strstr(pszCOM, "LOSSY settings used"))
2569
0
                {
2570
0
                    pszReversibility = "LOSSY";
2571
0
                }
2572
0
            }
2573
0
        }
2574
0
    }
2575
0
    CPLDestroyXMLNode(psRes);
2576
0
    return pszReversibility;
2577
0
}
2578
2579
#endif /* #ifndef DOXYGEN_SKIP */