Coverage Report

Created: 2025-12-03 08:24

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