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