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