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 : return std::string(v <= 8
1535 45 : ? CPLSPrintf("%d", 1 << (2 + v))
1536 90 : : "invalid");
1537 : });
1538 45 : READ_MARKER_FIELD_UINT8(
1539 : "SPcod_ycb_minus_2",
1540 45 : [](GByte v) {
1541 : return std::string(v <= 8
1542 45 : ? CPLSPrintf("%d", 1 << (2 + v))
1543 90 : : "invalid");
1544 : });
1545 45 : READ_MARKER_FIELD_UINT8("SPcod_cbstyle", cblkstyleLamba);
1546 45 : READ_MARKER_FIELD_UINT8("SPcod_transformation",
1547 45 : [](GByte v)
1548 : {
1549 : return std::string(
1550 : (v == 0) ? "9-7 irreversible"
1551 16 : : (v == 1) ? "5-3 reversible"
1552 61 : : "");
1553 : });
1554 45 : if (bHasPrecincts)
1555 : {
1556 38 : int i = 0;
1557 116 : while (nRemainingMarkerSize >= 1)
1558 : {
1559 78 : auto nLastVal = *pabyMarkerDataIter;
1560 78 : AddField(psMarker, psLastChild, psDumpContext,
1561 : CPLSPrintf("SPcod_Precincts%d", i),
1562 78 : *pabyMarkerDataIter,
1563 : CPLSPrintf("PPx=%d PPy=%d: %dx%d",
1564 78 : nLastVal & 0xf, nLastVal >> 4,
1565 78 : 1 << (nLastVal & 0xf),
1566 78 : 1 << (nLastVal >> 4)));
1567 78 : pabyMarkerDataIter += 1;
1568 78 : nRemainingMarkerSize -= 1;
1569 78 : i++;
1570 : }
1571 : }
1572 45 : if (nRemainingMarkerSize > 0)
1573 0 : AddElement(
1574 : psMarker, psLastChild, psDumpContext,
1575 : CPLCreateXMLElementAndValue(
1576 : nullptr, "RemainingBytes",
1577 : CPLSPrintf(
1578 : "%d", static_cast<int>(nRemainingMarkerSize))));
1579 : }
1580 : }
1581 106 : else if (abyMarker[1] == 0x53) /* COC */
1582 : {
1583 0 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1584 0 : strstr(psDumpContext->pszCodestreamMarkers, "COC"))
1585 : {
1586 0 : psMarker = CreateCurrentMarker();
1587 0 : if (!psMarker)
1588 0 : break;
1589 0 : if (Csiz < 257)
1590 0 : READ_MARKER_FIELD_UINT8("Ccoc");
1591 : else
1592 0 : READ_MARKER_FIELD_UINT16("Ccoc");
1593 :
1594 0 : bool bHasPrecincts = false;
1595 0 : if (nRemainingMarkerSize >= 1)
1596 : {
1597 0 : auto nLastVal = *pabyMarkerDataIter;
1598 0 : CPLString osInterp;
1599 0 : if (nLastVal & 0x1)
1600 : {
1601 0 : bHasPrecincts = true;
1602 0 : osInterp += "User defined precincts";
1603 : }
1604 : else
1605 0 : osInterp += "Standard precincts";
1606 0 : AddField(psMarker, psLastChild, psDumpContext, "Scoc",
1607 : nLastVal, osInterp.c_str());
1608 0 : pabyMarkerDataIter += 1;
1609 0 : nRemainingMarkerSize -= 1;
1610 : }
1611 : else
1612 : {
1613 0 : AddError(psMarker, psLastChild, psDumpContext,
1614 : CPLSPrintf("Cannot read field %s", "Scoc"));
1615 : }
1616 0 : READ_MARKER_FIELD_UINT8("SPcoc_NumDecompositions");
1617 0 : READ_MARKER_FIELD_UINT8(
1618 : "SPcoc_xcb_minus_2",
1619 0 : [](GByte v) {
1620 : return std::string(v <= 8
1621 0 : ? CPLSPrintf("%d", 1 << (2 + v))
1622 0 : : "invalid");
1623 : });
1624 0 : READ_MARKER_FIELD_UINT8(
1625 : "SPcoc_ycb_minus_2",
1626 0 : [](GByte v) {
1627 : return std::string(v <= 8
1628 0 : ? CPLSPrintf("%d", 1 << (2 + v))
1629 0 : : "invalid");
1630 : });
1631 0 : READ_MARKER_FIELD_UINT8("SPcoc_cbstyle", cblkstyleLamba);
1632 0 : READ_MARKER_FIELD_UINT8("SPcoc_transformation",
1633 0 : [](GByte v)
1634 : {
1635 : return std::string(
1636 : (v == 0) ? "9-7 irreversible"
1637 0 : : (v == 1) ? "5-3 reversible"
1638 0 : : "");
1639 : });
1640 0 : if (bHasPrecincts)
1641 : {
1642 0 : int i = 0;
1643 0 : while (nRemainingMarkerSize >= 1)
1644 : {
1645 0 : auto nLastVal = *pabyMarkerDataIter;
1646 0 : AddField(psMarker, psLastChild, psDumpContext,
1647 : CPLSPrintf("SPcoc_Precincts%d", i),
1648 0 : *pabyMarkerDataIter,
1649 : CPLSPrintf("PPx=%d PPy=%d: %dx%d",
1650 0 : nLastVal & 0xf, nLastVal >> 4,
1651 0 : 1 << (nLastVal & 0xf),
1652 0 : 1 << (nLastVal >> 4)));
1653 0 : pabyMarkerDataIter += 1;
1654 0 : nRemainingMarkerSize -= 1;
1655 0 : i++;
1656 : }
1657 : }
1658 0 : if (nRemainingMarkerSize > 0)
1659 0 : AddElement(
1660 : psMarker, psLastChild, psDumpContext,
1661 : CPLCreateXMLElementAndValue(
1662 : nullptr, "RemainingBytes",
1663 : CPLSPrintf(
1664 : "%d", static_cast<int>(nRemainingMarkerSize))));
1665 : }
1666 : }
1667 106 : else if (abyMarker[1] == 0x55) /* TLM */
1668 : {
1669 1 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1670 0 : strstr(psDumpContext->pszCodestreamMarkers, "TLM"))
1671 : {
1672 1 : psMarker = CreateCurrentMarker();
1673 1 : if (!psMarker)
1674 0 : break;
1675 1 : READ_MARKER_FIELD_UINT8("Ztlm");
1676 1 : auto Stlm = READ_MARKER_FIELD_UINT8(
1677 : "Stlm",
1678 1 : [](GByte v) {
1679 : return std::string(CPLSPrintf(
1680 1 : "ST=%d SP=%d", (v >> 4) & 3, (v >> 6) & 1));
1681 : });
1682 1 : int ST = (Stlm >> 4) & 3;
1683 1 : int SP = (Stlm >> 6) & 1;
1684 1 : int nTilePartDescLength = ST + ((SP == 0) ? 2 : 4);
1685 1 : int i = 0;
1686 5 : while (nRemainingMarkerSize >= nTilePartDescLength)
1687 : {
1688 4 : if (ST == 1)
1689 0 : READ_MARKER_FIELD_UINT8(CPLSPrintf("Ttlm%d", i));
1690 4 : else if (ST == 2)
1691 4 : READ_MARKER_FIELD_UINT16(CPLSPrintf("Ttlm%d", i));
1692 4 : if (SP == 0)
1693 0 : READ_MARKER_FIELD_UINT16(CPLSPrintf("Ptlm%d", i));
1694 : else
1695 4 : READ_MARKER_FIELD_UINT32(CPLSPrintf("Ptlm%d", i));
1696 4 : i++;
1697 : }
1698 1 : if (nRemainingMarkerSize > 0)
1699 0 : AddElement(
1700 : psMarker, psLastChild, psDumpContext,
1701 : CPLCreateXMLElementAndValue(
1702 : nullptr, "RemainingBytes",
1703 : CPLSPrintf(
1704 : "%d", static_cast<int>(nRemainingMarkerSize))));
1705 : }
1706 : }
1707 105 : else if (abyMarker[1] == 0x57) /* PLM */
1708 : {
1709 0 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1710 0 : strstr(psDumpContext->pszCodestreamMarkers, "PLM"))
1711 : {
1712 0 : psMarker = CreateCurrentMarker();
1713 0 : if (!psMarker)
1714 0 : break;
1715 : }
1716 : }
1717 105 : else if (abyMarker[1] == 0x58) /* PLT */
1718 : {
1719 13 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1720 3 : strstr(psDumpContext->pszCodestreamMarkers, "PLT"))
1721 : {
1722 10 : psMarker = CreateCurrentMarker();
1723 10 : if (!psMarker)
1724 0 : break;
1725 10 : READ_MARKER_FIELD_UINT8("Zplt");
1726 10 : int i = 0;
1727 10 : unsigned nPacketLength = 0;
1728 196 : while (nRemainingMarkerSize >= 1)
1729 : {
1730 186 : auto nLastVal = *pabyMarkerDataIter;
1731 186 : nPacketLength |= (nLastVal & 0x7f);
1732 186 : if (nLastVal & 0x80)
1733 : {
1734 16 : nPacketLength <<= 7;
1735 : }
1736 : else
1737 : {
1738 170 : AddField(psMarker, psLastChild, psDumpContext,
1739 : CPLSPrintf("Iplt%d", i), nPacketLength);
1740 170 : nPacketLength = 0;
1741 170 : i++;
1742 : }
1743 186 : pabyMarkerDataIter += 1;
1744 186 : nRemainingMarkerSize -= 1;
1745 : }
1746 10 : if (nPacketLength != 0)
1747 : {
1748 0 : AddError(psMarker, psLastChild, psDumpContext,
1749 : "Incorrect PLT marker");
1750 : }
1751 : }
1752 : }
1753 92 : else if (abyMarker[1] == 0x59) /* CPF (HTJ2K) */
1754 : {
1755 0 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1756 0 : strstr(psDumpContext->pszCodestreamMarkers, "CPF"))
1757 : {
1758 0 : psMarker = CreateCurrentMarker();
1759 0 : if (!psMarker)
1760 0 : break;
1761 0 : const GUInt16 Lcpf = nMarkerSize;
1762 0 : if (Lcpf > 2 && (Lcpf % 2) == 0)
1763 : {
1764 0 : for (int i = 0; i < (Lcpf - 2) / 2; i++)
1765 : {
1766 0 : READ_MARKER_FIELD_UINT16(CPLSPrintf("Pcpf%d", i + 1));
1767 : }
1768 : }
1769 0 : if (nRemainingMarkerSize > 0)
1770 0 : AddElement(
1771 : psMarker, psLastChild, psDumpContext,
1772 : CPLCreateXMLElementAndValue(
1773 : nullptr, "RemainingBytes",
1774 : CPLSPrintf(
1775 : "%d", static_cast<int>(nRemainingMarkerSize))));
1776 : }
1777 : }
1778 92 : else if (abyMarker[1] == 0x5C) /* QCD */
1779 : {
1780 45 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1781 8 : strstr(psDumpContext->pszCodestreamMarkers, "QCD"))
1782 : {
1783 37 : psMarker = CreateCurrentMarker();
1784 37 : if (!psMarker)
1785 0 : break;
1786 37 : const int Sqcd = READ_MARKER_FIELD_UINT8(
1787 : "Sqcd",
1788 37 : [](GByte v)
1789 : {
1790 37 : std::string ret;
1791 37 : if ((v & 31) == 0)
1792 10 : ret = "No quantization";
1793 27 : else if ((v & 31) == 1)
1794 0 : ret = "Scalar derived";
1795 27 : else if ((v & 31) == 2)
1796 27 : ret = "Scalar expounded";
1797 37 : ret += ", ";
1798 37 : ret += CPLSPrintf("guard bits = %d", v >> 5);
1799 37 : return ret;
1800 37 : });
1801 37 : if ((Sqcd & 31) == 0)
1802 : {
1803 : // Reversible
1804 10 : int i = 0;
1805 74 : while (nRemainingMarkerSize >= 1)
1806 : {
1807 64 : READ_MARKER_FIELD_UINT8(
1808 : CPLSPrintf("SPqcd%d", i),
1809 64 : [](GByte v) {
1810 : return std::string(
1811 64 : CPLSPrintf("epsilon_b = %d", v >> 3));
1812 : });
1813 64 : ++i;
1814 : }
1815 : }
1816 : else
1817 : {
1818 27 : int i = 0;
1819 123 : while (nRemainingMarkerSize >= 2)
1820 : {
1821 96 : READ_MARKER_FIELD_UINT16(
1822 : CPLSPrintf("SPqcd%d", i),
1823 96 : [](GUInt16 v)
1824 : {
1825 : return std::string(CPLSPrintf(
1826 : "mantissa_b = %d, epsilon_b = %d",
1827 96 : v & ((1 << 11) - 1), v >> 11));
1828 : });
1829 96 : ++i;
1830 : }
1831 : }
1832 : }
1833 : }
1834 47 : else if (abyMarker[1] == 0x5D) /* QCC */
1835 : {
1836 1 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1837 0 : strstr(psDumpContext->pszCodestreamMarkers, "QCC"))
1838 : {
1839 1 : psMarker = CreateCurrentMarker();
1840 1 : if (!psMarker)
1841 0 : break;
1842 1 : if (Csiz < 257)
1843 1 : READ_MARKER_FIELD_UINT8("Cqcc");
1844 : else
1845 0 : READ_MARKER_FIELD_UINT16("Cqcc");
1846 :
1847 1 : const int Sqcc = READ_MARKER_FIELD_UINT8(
1848 : "Sqcc",
1849 1 : [](GByte v)
1850 : {
1851 1 : std::string ret;
1852 1 : if ((v & 31) == 0)
1853 0 : ret = "No quantization";
1854 1 : else if ((v & 31) == 1)
1855 0 : ret = "Scalar derived";
1856 1 : else if ((v & 31) == 2)
1857 1 : ret = "Scalar expounded";
1858 1 : ret += ", ";
1859 1 : ret += CPLSPrintf("guard bits = %d", v >> 5);
1860 1 : return ret;
1861 1 : });
1862 1 : if ((Sqcc & 31) == 0)
1863 : {
1864 : // Reversible
1865 0 : int i = 0;
1866 0 : while (nRemainingMarkerSize >= 1)
1867 : {
1868 0 : READ_MARKER_FIELD_UINT8(
1869 : CPLSPrintf("SPqcc%d", i),
1870 0 : [](GByte v) {
1871 : return std::string(
1872 0 : CPLSPrintf("epsilon_b = %d", v >> 3));
1873 : });
1874 0 : ++i;
1875 : }
1876 : }
1877 : else
1878 : {
1879 1 : int i = 0;
1880 2 : while (nRemainingMarkerSize >= 2)
1881 : {
1882 1 : READ_MARKER_FIELD_UINT16(
1883 : CPLSPrintf("SPqcc%d", i),
1884 1 : [](GUInt16 v)
1885 : {
1886 : return std::string(CPLSPrintf(
1887 : "mantissa_b = %d, epsilon_b = %d",
1888 1 : v & ((1 << 11) - 1), v >> 11));
1889 : });
1890 1 : ++i;
1891 : }
1892 : }
1893 : }
1894 : }
1895 46 : else if (abyMarker[1] == 0x5E) /* RGN */
1896 : {
1897 1 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1898 0 : strstr(psDumpContext->pszCodestreamMarkers, "RGN"))
1899 : {
1900 1 : psMarker = CreateCurrentMarker();
1901 1 : if (!psMarker)
1902 0 : break;
1903 : }
1904 : }
1905 45 : else if (abyMarker[1] == 0x5F) /* POC */
1906 : {
1907 1 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1908 0 : strstr(psDumpContext->pszCodestreamMarkers, "POC"))
1909 : {
1910 1 : psMarker = CreateCurrentMarker();
1911 1 : if (!psMarker)
1912 0 : break;
1913 1 : const int nPOCEntrySize = Csiz < 257 ? 7 : 9;
1914 1 : int i = 0;
1915 2 : while (nRemainingMarkerSize >= nPOCEntrySize)
1916 : {
1917 1 : READ_MARKER_FIELD_UINT8(CPLSPrintf("RSpoc%d", i));
1918 1 : if (nPOCEntrySize == 7)
1919 : {
1920 1 : READ_MARKER_FIELD_UINT8(CPLSPrintf("CSpoc%d", i));
1921 : }
1922 : else
1923 : {
1924 0 : READ_MARKER_FIELD_UINT16(CPLSPrintf("CSpoc%d", i));
1925 : }
1926 1 : READ_MARKER_FIELD_UINT16(CPLSPrintf("LYEpoc%d", i));
1927 1 : READ_MARKER_FIELD_UINT8(CPLSPrintf("REpoc%d", i));
1928 1 : if (nPOCEntrySize == 7)
1929 : {
1930 1 : READ_MARKER_FIELD_UINT8(CPLSPrintf("CEpoc%d", i));
1931 : }
1932 : else
1933 : {
1934 0 : READ_MARKER_FIELD_UINT16(CPLSPrintf("CEpoc%d", i));
1935 : }
1936 1 : READ_MARKER_FIELD_UINT8(CPLSPrintf("Ppoc%d", i),
1937 : lambdaPOCType);
1938 1 : i++;
1939 : }
1940 1 : if (nRemainingMarkerSize > 0)
1941 : {
1942 0 : AddElement(
1943 : psMarker, psLastChild, psDumpContext,
1944 : CPLCreateXMLElementAndValue(
1945 : nullptr, "RemainingBytes",
1946 : CPLSPrintf(
1947 : "%d", static_cast<int>(nRemainingMarkerSize))));
1948 : }
1949 : }
1950 : }
1951 44 : else if (abyMarker[1] == 0x60) /* PPM */
1952 : {
1953 0 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1954 0 : strstr(psDumpContext->pszCodestreamMarkers, "PPM"))
1955 : {
1956 0 : psMarker = CreateCurrentMarker();
1957 0 : if (!psMarker)
1958 0 : break;
1959 : }
1960 : }
1961 44 : else if (abyMarker[1] == 0x61) /* PPT */
1962 : {
1963 0 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1964 0 : strstr(psDumpContext->pszCodestreamMarkers, "PPT"))
1965 : {
1966 0 : psMarker = CreateCurrentMarker();
1967 0 : if (!psMarker)
1968 0 : break;
1969 : }
1970 : }
1971 44 : else if (abyMarker[1] == 0x63) /* CRG */
1972 : {
1973 0 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1974 0 : strstr(psDumpContext->pszCodestreamMarkers, "CRG"))
1975 : {
1976 0 : psMarker = CreateCurrentMarker();
1977 0 : if (!psMarker)
1978 0 : break;
1979 : }
1980 : }
1981 44 : else if (abyMarker[1] == 0x64) /* COM */
1982 : {
1983 44 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1984 11 : strstr(psDumpContext->pszCodestreamMarkers, "COM"))
1985 : {
1986 44 : psMarker = CreateCurrentMarker();
1987 44 : if (!psMarker)
1988 0 : break;
1989 44 : auto RCom = READ_MARKER_FIELD_UINT16(
1990 : "Rcom",
1991 44 : [](GUInt16 v) {
1992 : return std::string((v == 0) ? "Binary"
1993 44 : : (v == 1) ? "LATIN1"
1994 88 : : "");
1995 : });
1996 44 : if (RCom == 1)
1997 : {
1998 44 : GByte abyBackup = pabyMarkerDataIter[nRemainingMarkerSize];
1999 44 : pabyMarkerDataIter[nRemainingMarkerSize] = 0;
2000 44 : AddField(
2001 : psMarker, psLastChild, psDumpContext, "COM",
2002 : static_cast<int>(nRemainingMarkerSize),
2003 : reinterpret_cast<const char *>(pabyMarkerDataIter));
2004 44 : pabyMarkerDataIter[nRemainingMarkerSize] = abyBackup;
2005 : }
2006 : }
2007 : }
2008 :
2009 247 : if (VSIFSeekL(fp, nOffset + 2 + nMarkerSize, SEEK_SET) != 0)
2010 : {
2011 0 : AddError(psCSBox, psLastChildCSBox, psDumpContext,
2012 0 : "Cannot seek to next marker", nOffset + 2 + nMarkerSize);
2013 0 : break;
2014 : }
2015 :
2016 247 : CPL_IGNORE_RET_VAL(bError);
2017 : }
2018 47 : CPLFree(pabyMarkerData);
2019 47 : return psCSBox;
2020 : }
2021 :
2022 : /************************************************************************/
2023 : /* GDALGetJPEG2000StructureInternal() */
2024 : /************************************************************************/
2025 :
2026 121 : static void GDALGetJPEG2000StructureInternal(CPLXMLNode *psParent, VSILFILE *fp,
2027 : GDALJP2Box *poParentBox,
2028 : int nRecLevel,
2029 : vsi_l_offset nFileOrParentBoxSize,
2030 : DumpContext *psDumpContext)
2031 : {
2032 : // Limit recursion to a reasonable level. I believe that in practice 2
2033 : // should be sufficient, but just in case someone creates deeply
2034 : // nested "super-boxes", allow up to 5.
2035 121 : if (nRecLevel == 5)
2036 0 : return;
2037 :
2038 : static const char *const szHex = "0123456789ABCDEF";
2039 242 : GDALJP2Box oBox(fp);
2040 121 : oBox.SetAllowGetFileSize(psDumpContext->bAllowGetFileSize);
2041 121 : CPLXMLNode *psLastChild = nullptr;
2042 121 : if (oBox.ReadFirstChild(poParentBox))
2043 : {
2044 806 : while (strlen(oBox.GetType()) > 0 &&
2045 403 : psDumpContext->nCurLineCount <= psDumpContext->nMaxLineCount + 1)
2046 : {
2047 401 : GIntBig nBoxDataLength = oBox.GetDataLength();
2048 401 : const char *pszBoxType = oBox.GetType();
2049 401 : CPLXMLNode *psBox = nullptr;
2050 758 : const auto CreateBox = [&]()
2051 : {
2052 758 : if (psBox != nullptr)
2053 401 : return true;
2054 357 : psBox = CPLCreateXMLNode(nullptr, CXT_Element, "JP2Box");
2055 357 : psBox = AddElement(psParent, psLastChild, psDumpContext, psBox);
2056 357 : if (!psBox)
2057 0 : return false;
2058 357 : CPLAddXMLAttributeAndValue(psBox, "name", pszBoxType);
2059 357 : CPLAddXMLAttributeAndValue(
2060 : psBox, "box_offset",
2061 357 : CPLSPrintf(CPL_FRMT_GIB, oBox.GetBoxOffset()));
2062 357 : const auto nBoxLength = oBox.GetBoxLength();
2063 711 : CPLAddXMLAttributeAndValue(
2064 : psBox, "box_length",
2065 354 : nBoxLength > 0 ? CPLSPrintf(CPL_FRMT_GIB, nBoxLength)
2066 : : "unknown");
2067 357 : CPLAddXMLAttributeAndValue(
2068 : psBox, "data_offset",
2069 : CPLSPrintf(CPL_FRMT_GIB, oBox.GetDataOffset()));
2070 357 : CPLAddXMLAttributeAndValue(
2071 : psBox, "data_length",
2072 357 : nBoxDataLength > 0
2073 352 : ? CPLSPrintf(CPL_FRMT_GIB, nBoxDataLength)
2074 : : "unknown");
2075 :
2076 357 : if (nBoxDataLength > GINTBIG_MAX - oBox.GetDataOffset())
2077 : {
2078 0 : CPLXMLNode *psLastChildBox = nullptr;
2079 0 : AddError(psBox, psLastChildBox, psDumpContext,
2080 : "Invalid box_length");
2081 0 : return false;
2082 : }
2083 357 : return true;
2084 401 : };
2085 :
2086 : // Check large non-jp2c boxes against filesize
2087 401 : if (strcmp(pszBoxType, "jp2c") != 0 && nBoxDataLength > 100 * 1024)
2088 : {
2089 0 : if (nFileOrParentBoxSize == 0)
2090 : {
2091 0 : CPL_IGNORE_RET_VAL(VSIFSeekL(fp, 0, SEEK_END));
2092 0 : nFileOrParentBoxSize = VSIFTellL(fp);
2093 : }
2094 : }
2095 585 : if (nFileOrParentBoxSize > 0 && nBoxDataLength > 0 &&
2096 184 : (static_cast<vsi_l_offset>(oBox.GetDataOffset()) >
2097 184 : nFileOrParentBoxSize ||
2098 184 : static_cast<vsi_l_offset>(nBoxDataLength) >
2099 184 : nFileOrParentBoxSize - oBox.GetDataOffset()))
2100 : {
2101 0 : CPLXMLNode *psLastChildBox = nullptr;
2102 0 : if (!CreateBox())
2103 0 : break;
2104 0 : AddError(psBox, psLastChildBox, psDumpContext,
2105 : "Invalid box_length");
2106 0 : break;
2107 : }
2108 :
2109 401 : if (oBox.IsSuperBox())
2110 : {
2111 80 : if (!CreateBox())
2112 0 : break;
2113 80 : if (nBoxDataLength <= 0)
2114 0 : break;
2115 160 : GDALGetJPEG2000StructureInternal(
2116 : psBox, fp, &oBox, nRecLevel + 1,
2117 80 : oBox.GetDataOffset() +
2118 80 : static_cast<vsi_l_offset>(nBoxDataLength),
2119 : psDumpContext);
2120 : }
2121 : else
2122 : {
2123 321 : if (strcmp(pszBoxType, "uuid") == 0 &&
2124 23 : psDumpContext->bDumpJP2Boxes)
2125 : {
2126 17 : if (!CreateBox())
2127 0 : break;
2128 : char *pszBinaryContent =
2129 17 : static_cast<char *>(VSIMalloc(2 * 16 + 1));
2130 17 : const GByte *pabyUUID = oBox.GetUUID();
2131 289 : for (int i = 0; i < 16; i++)
2132 : {
2133 272 : pszBinaryContent[2 * i] = szHex[pabyUUID[i] >> 4];
2134 272 : pszBinaryContent[2 * i + 1] = szHex[pabyUUID[i] & 0xf];
2135 : }
2136 17 : pszBinaryContent[2 * 16] = '\0';
2137 : CPLXMLNode *psUUIDNode =
2138 17 : CPLCreateXMLNode(nullptr, CXT_Element, "UUID");
2139 17 : if (GDALJP2Metadata::IsUUID_MSI(pabyUUID))
2140 16 : CPLAddXMLAttributeAndValue(psUUIDNode, "description",
2141 : "GeoTIFF");
2142 1 : else if (GDALJP2Metadata::IsUUID_XMP(pabyUUID))
2143 1 : CPLAddXMLAttributeAndValue(psUUIDNode, "description",
2144 : "XMP");
2145 17 : CPLCreateXMLNode(psUUIDNode, CXT_Text, pszBinaryContent);
2146 17 : VSIFree(pszBinaryContent);
2147 :
2148 17 : CPLXMLNode *psLastChildBox = nullptr;
2149 17 : AddElement(psBox, psLastChildBox, psDumpContext,
2150 : psUUIDNode);
2151 : }
2152 :
2153 321 : if (psDumpContext->bDumpBinaryContent &&
2154 271 : strcmp(pszBoxType, "jp2c") != 0 &&
2155 236 : nBoxDataLength < 100 * 1024)
2156 : {
2157 236 : if (!CreateBox())
2158 0 : break;
2159 : CPLXMLNode *psBinaryContent =
2160 236 : CPLCreateXMLNode(nullptr, CXT_Element, "BinaryContent");
2161 236 : GByte *pabyBoxData = oBox.ReadBoxData();
2162 : const int nBoxLength = static_cast<int>(
2163 236 : std::min<GIntBig>(nBoxDataLength, INT_MAX / 2 - 1));
2164 : char *pszBinaryContent =
2165 236 : static_cast<char *>(VSIMalloc(2 * nBoxLength + 1));
2166 236 : if (pabyBoxData && pszBinaryContent)
2167 : {
2168 47647 : for (int i = 0; i < nBoxLength; i++)
2169 : {
2170 47411 : pszBinaryContent[2 * i] =
2171 47411 : szHex[pabyBoxData[i] >> 4];
2172 47411 : pszBinaryContent[2 * i + 1] =
2173 47411 : szHex[pabyBoxData[i] & 0xf];
2174 : }
2175 236 : pszBinaryContent[2 * nBoxLength] = '\0';
2176 236 : CPLCreateXMLNode(psBinaryContent, CXT_Text,
2177 : pszBinaryContent);
2178 : }
2179 236 : CPLFree(pabyBoxData);
2180 236 : VSIFree(pszBinaryContent);
2181 :
2182 236 : CPLXMLNode *psLastChildBox = nullptr;
2183 236 : AddElement(psBox, psLastChildBox, psDumpContext,
2184 : psBinaryContent);
2185 : }
2186 :
2187 321 : if (psDumpContext->bDumpTextContent &&
2188 271 : strcmp(pszBoxType, "jp2c") != 0 &&
2189 236 : nBoxDataLength < 100 * 1024)
2190 : {
2191 236 : if (!CreateBox())
2192 0 : break;
2193 236 : GByte *pabyBoxData = oBox.ReadBoxData();
2194 236 : if (pabyBoxData)
2195 : {
2196 236 : const char *pszBoxData =
2197 : reinterpret_cast<const char *>(pabyBoxData);
2198 427 : if (CPLIsUTF8(pszBoxData, -1) &&
2199 191 : static_cast<int>(strlen(pszBoxData)) + 2 >=
2200 : nBoxDataLength)
2201 : {
2202 58 : CPLXMLNode *psXMLContentBox = nullptr;
2203 58 : if (pszBoxData[0] == '<')
2204 : {
2205 22 : CPLPushErrorHandler(CPLQuietErrorHandler);
2206 22 : psXMLContentBox = CPLParseXMLString(pszBoxData);
2207 22 : CPLPopErrorHandler();
2208 : }
2209 58 : if (psXMLContentBox)
2210 : {
2211 22 : CPLXMLNode *psXMLContentNode = CPLCreateXMLNode(
2212 : nullptr, CXT_Element, "XMLContent");
2213 22 : psXMLContentNode->psChild = psXMLContentBox;
2214 :
2215 22 : CPLXMLNode *psLastChildBox = nullptr;
2216 22 : AddElement(psBox, psLastChildBox, psDumpContext,
2217 : psXMLContentNode);
2218 : }
2219 : else
2220 : {
2221 36 : auto psTextElement = CPLCreateXMLNode(
2222 : nullptr, CXT_Element, "TextContent");
2223 36 : CPLCreateXMLNode(psTextElement, CXT_Text,
2224 : pszBoxData);
2225 :
2226 36 : CPLXMLNode *psLastChildBox = nullptr;
2227 36 : AddElement(psBox, psLastChildBox, psDumpContext,
2228 : psTextElement);
2229 : }
2230 : }
2231 : }
2232 236 : CPLFree(pabyBoxData);
2233 : }
2234 :
2235 321 : if (strcmp(pszBoxType, "jp2c") == 0)
2236 : {
2237 41 : if (psDumpContext->bDumpCodestream ||
2238 6 : psDumpContext->pszCodestreamMarkers)
2239 : {
2240 41 : if (!CreateBox())
2241 0 : break;
2242 41 : DumpJPK2CodeStream(psBox, fp, oBox.GetDataOffset(),
2243 : nBoxDataLength, psDumpContext);
2244 41 : if (psDumpContext->bStopAtSOD &&
2245 6 : psDumpContext->bSODEncountered)
2246 : {
2247 6 : break;
2248 : }
2249 : }
2250 : }
2251 280 : else if (!psDumpContext->bDumpJP2Boxes)
2252 : {
2253 : // do nothing
2254 : }
2255 253 : else if (strcmp(pszBoxType, "uuid") == 0 &&
2256 17 : GDALJP2Metadata::IsUUID_MSI(oBox.GetUUID()))
2257 : {
2258 16 : if (!CreateBox())
2259 0 : break;
2260 16 : DumpGeoTIFFBox(psBox, oBox, psDumpContext);
2261 : }
2262 220 : else if (strcmp(pszBoxType, "ftyp") == 0)
2263 : {
2264 35 : if (!CreateBox())
2265 0 : break;
2266 35 : DumpFTYPBox(psBox, oBox, psDumpContext);
2267 : }
2268 185 : else if (strcmp(pszBoxType, "ihdr") == 0)
2269 : {
2270 35 : if (!CreateBox())
2271 0 : break;
2272 35 : DumpIHDRBox(psBox, oBox, psDumpContext);
2273 : }
2274 150 : else if (strcmp(pszBoxType, "bpcc") == 0)
2275 : {
2276 3 : if (!CreateBox())
2277 0 : break;
2278 3 : DumpBPCCBox(psBox, oBox, psDumpContext);
2279 : }
2280 147 : else if (strcmp(pszBoxType, "colr") == 0)
2281 : {
2282 33 : if (!CreateBox())
2283 0 : break;
2284 33 : DumpCOLRBox(psBox, oBox, psDumpContext);
2285 : }
2286 114 : else if (strcmp(pszBoxType, "pclr") == 0)
2287 : {
2288 6 : if (!CreateBox())
2289 0 : break;
2290 6 : DumpPCLRBox(psBox, oBox, psDumpContext);
2291 : }
2292 108 : else if (strcmp(pszBoxType, "cmap") == 0)
2293 : {
2294 6 : if (!CreateBox())
2295 0 : break;
2296 6 : DumpCMAPBox(psBox, oBox, psDumpContext);
2297 : }
2298 102 : else if (strcmp(pszBoxType, "cdef") == 0)
2299 : {
2300 3 : if (!CreateBox())
2301 0 : break;
2302 3 : DumpCDEFBox(psBox, oBox, psDumpContext);
2303 : }
2304 99 : else if (strcmp(pszBoxType, "resc") == 0 ||
2305 98 : strcmp(pszBoxType, "resd") == 0)
2306 : {
2307 1 : if (!CreateBox())
2308 0 : break;
2309 1 : DumpRESxBox(psBox, oBox, psDumpContext);
2310 : }
2311 98 : else if (strcmp(pszBoxType, "rreq") == 0)
2312 : {
2313 10 : if (!CreateBox())
2314 0 : break;
2315 10 : DumpRREQBox(psBox, oBox, psDumpContext);
2316 : }
2317 : }
2318 :
2319 395 : if (!oBox.ReadNextChild(poParentBox))
2320 113 : break;
2321 : }
2322 : }
2323 : }
2324 :
2325 : /************************************************************************/
2326 : /* GDALGetJPEG2000Structure() */
2327 : /************************************************************************/
2328 :
2329 : constexpr unsigned char jpc_header[] = {0xff, 0x4f};
2330 : constexpr unsigned char jp2_box_jp[] = {0x6a, 0x50, 0x20, 0x20}; /* 'jP ' */
2331 :
2332 : /** Dump the structure of a JPEG2000 file as a XML tree.
2333 : *
2334 : * @param pszFilename filename.
2335 : * @param papszOptions NULL terminated list of options, or NULL.
2336 : * Allowed options are BINARY_CONTENT=YES, TEXT_CONTENT=YES,
2337 : * CODESTREAM=YES, ALL=YES, JP2_BOXES=YES,
2338 : * CODESTREAM_MARKERS=list_of_marker_names_comma_separated,
2339 : * STOP_AT_SOD=YES, ALLOW_GET_FILE_SIZE=NO.
2340 : * @return XML tree (to be freed with CPLDestroyXMLNode()) or NULL in case
2341 : * of error
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 */
|