Coverage Report

Created: 2025-08-11 09:23

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