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 : GIntBig 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 : GIntBig 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 : GIntBig nOffset = static_cast<GIntBig>(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 : nMarkerSize =
1103 0 : (nBoxDataOffset + nBoxDataLength - 2) - nOffset - 2;
1104 0 : if (VSIFSeekL(fp, nBoxDataOffset + nBoxDataLength - 2,
1105 0 : SEEK_SET) != 0 ||
1106 0 : VSIFReadL(abyMarker, 2, 1, fp) != 1 ||
1107 0 : abyMarker[0] != 0xFF || abyMarker[1] != 0xD9)
1108 : {
1109 : /* autotest/gdrivers/data/rgb16_ecwsdk.jp2 does not end */
1110 : /* with a EOC... */
1111 0 : nMarkerSize += 2;
1112 0 : bBreak = true;
1113 : }
1114 : }
1115 43 : else if (nNextTileOffset >= nOffset + 2)
1116 43 : nMarkerSize = nNextTileOffset - nOffset - 2;
1117 :
1118 43 : if (bIncludeSOD)
1119 : {
1120 43 : CreateMarker(psCSBox, psLastChildCSBox, psDumpContext, "SOD",
1121 : nOffset, nMarkerSize);
1122 : }
1123 43 : if (bBreak || psDumpContext->bStopAtSOD)
1124 : {
1125 0 : psDumpContext->bSODEncountered = true;
1126 0 : break;
1127 : }
1128 :
1129 43 : if (nNextTileOffset && nNextTileOffset == nOffset)
1130 : {
1131 : /* Found with Pleiades images. openjpeg doesn't like it either
1132 : */
1133 0 : nNextTileOffset = 0;
1134 : }
1135 43 : else if (nNextTileOffset && nNextTileOffset >= nOffset + 2)
1136 : {
1137 43 : if (VSIFSeekL(fp, nNextTileOffset, SEEK_SET) != 0)
1138 0 : AddError(psCSBox, psLastChildCSBox, psDumpContext,
1139 : "Cannot seek to", nNextTileOffset);
1140 43 : nNextTileOffset = 0;
1141 : }
1142 : else
1143 : {
1144 : /* We have seek and check before we hit a EOC */
1145 0 : nOffset = nBoxDataOffset + nBoxDataLength - 2;
1146 0 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1147 0 : strstr(psDumpContext->pszCodestreamMarkers, "EOC"))
1148 : {
1149 0 : CreateMarker(psCSBox, psLastChildCSBox, psDumpContext,
1150 : "EOC", nOffset, 0);
1151 : }
1152 : }
1153 43 : continue;
1154 : }
1155 285 : if (abyMarker[1] == 0xD9)
1156 : {
1157 38 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1158 0 : strstr(psDumpContext->pszCodestreamMarkers, "EOC"))
1159 : {
1160 38 : CreateMarker(psCSBox, psLastChildCSBox, psDumpContext, "EOC",
1161 : nOffset, 0);
1162 : }
1163 38 : continue;
1164 : }
1165 : /* Reserved markers */
1166 247 : if (abyMarker[1] >= 0x30 && abyMarker[1] <= 0x3F)
1167 : {
1168 0 : if (psDumpContext->pszCodestreamMarkers == nullptr)
1169 : {
1170 0 : CreateMarker(psCSBox, psLastChildCSBox, psDumpContext,
1171 0 : CPLSPrintf("Unknown 0xFF%02X", abyMarker[1]),
1172 : nOffset, 0);
1173 : }
1174 0 : continue;
1175 : }
1176 :
1177 : GUInt16 nMarkerSize;
1178 247 : if (VSIFReadL(&nMarkerSize, 2, 1, fp) != 1)
1179 : {
1180 0 : AddError(psCSBox, psLastChildCSBox, psDumpContext,
1181 : CPLSPrintf("Cannot read marker size of %s",
1182 0 : GetMarkerName(abyMarker[1])),
1183 : nOffset);
1184 0 : break;
1185 : }
1186 247 : CPL_MSBPTR16(&nMarkerSize);
1187 247 : if (nMarkerSize < 2)
1188 : {
1189 0 : AddError(psCSBox, psLastChildCSBox, psDumpContext,
1190 : CPLSPrintf("Invalid marker size of %s",
1191 0 : GetMarkerName(abyMarker[1])),
1192 : nOffset);
1193 0 : break;
1194 : }
1195 :
1196 220 : const auto CreateCurrentMarker = [&]()
1197 : {
1198 220 : return CreateMarker(psCSBox, psLastChildCSBox, psDumpContext,
1199 220 : GetMarkerName(abyMarker[1]), nOffset,
1200 440 : nMarkerSize);
1201 247 : };
1202 247 : CPLXMLNode *psMarker = nullptr;
1203 247 : CPLXMLNode *psLastChild = nullptr;
1204 247 : if (VSIFReadL(pabyMarkerData, nMarkerSize - 2, 1, fp) != 1)
1205 : {
1206 0 : psMarker = CreateCurrentMarker();
1207 0 : AddError(psMarker, psLastChild, psDumpContext,
1208 : "Cannot read marker data", nOffset);
1209 0 : break;
1210 : }
1211 247 : GByte *pabyMarkerDataIter = pabyMarkerData;
1212 247 : GUInt16 nRemainingMarkerSize = nMarkerSize - 2;
1213 247 : bool bError = false;
1214 :
1215 : auto READ_MARKER_FIELD_UINT8 =
1216 674 : [&](const char *name, std::string (*commentFunc)(GByte) = nullptr)
1217 : {
1218 : GByte v;
1219 674 : if (nRemainingMarkerSize >= 1)
1220 : {
1221 2022 : v = *pabyMarkerDataIter;
1222 : const std::string comment(commentFunc ? commentFunc(v)
1223 674 : : std::string());
1224 674 : AddField(psMarker, psLastChild, psDumpContext, name,
1225 674 : *pabyMarkerDataIter,
1226 1054 : comment.empty() ? nullptr : comment.c_str());
1227 674 : pabyMarkerDataIter += 1;
1228 674 : nRemainingMarkerSize -= 1;
1229 : }
1230 : else
1231 : {
1232 0 : AddError(psMarker, psLastChild, psDumpContext,
1233 : CPLSPrintf("Cannot read field %s", name));
1234 0 : v = 0;
1235 0 : bError = true;
1236 : }
1237 674 : return v;
1238 247 : };
1239 :
1240 : auto READ_MARKER_FIELD_UINT16 =
1241 308 : [&](const char *name, std::string (*commentFunc)(GUInt16) = nullptr)
1242 : {
1243 : GUInt16 v;
1244 308 : if (nRemainingMarkerSize >= 2)
1245 : {
1246 616 : memcpy(&v, pabyMarkerDataIter, 2);
1247 308 : CPL_MSBPTR16(&v);
1248 : const std::string comment(commentFunc ? commentFunc(v)
1249 308 : : std::string());
1250 486 : AddField(psMarker, psLastChild, psDumpContext, name, v,
1251 486 : comment.empty() ? nullptr : comment.c_str());
1252 308 : pabyMarkerDataIter += 2;
1253 308 : nRemainingMarkerSize -= 2;
1254 : }
1255 : else
1256 : {
1257 0 : AddError(psMarker, psLastChild, psDumpContext,
1258 : CPLSPrintf("Cannot read field %s", name));
1259 0 : v = 0;
1260 0 : bError = true;
1261 : }
1262 308 : return v;
1263 247 : };
1264 :
1265 : auto READ_MARKER_FIELD_UINT32 =
1266 343 : [&](const char *name, std::string (*commentFunc)(GUInt32) = nullptr)
1267 : {
1268 : GUInt32 v;
1269 343 : if (nRemainingMarkerSize >= 4)
1270 : {
1271 686 : memcpy(&v, pabyMarkerDataIter, 4);
1272 343 : CPL_MSBPTR32(&v);
1273 : const std::string comment(commentFunc ? commentFunc(v)
1274 343 : : std::string());
1275 343 : AddField(psMarker, psLastChild, psDumpContext, name, v,
1276 343 : comment.empty() ? nullptr : comment.c_str());
1277 343 : pabyMarkerDataIter += 4;
1278 343 : nRemainingMarkerSize -= 4;
1279 : }
1280 : else
1281 : {
1282 0 : AddError(psMarker, psLastChild, psDumpContext,
1283 : CPLSPrintf("Cannot read field %s", name));
1284 0 : v = 0;
1285 0 : bError = true;
1286 : }
1287 343 : return v;
1288 247 : };
1289 :
1290 45 : const auto cblkstyleLamba = [](GByte v)
1291 : {
1292 45 : std::string osInterp;
1293 45 : if (v & 0x1)
1294 0 : osInterp += "Selective arithmetic coding bypass";
1295 : else
1296 45 : osInterp += "No selective arithmetic coding bypass";
1297 45 : osInterp += ", ";
1298 45 : if (v & 0x2)
1299 : osInterp +=
1300 0 : "Reset context probabilities on coding pass boundaries";
1301 : else
1302 : osInterp += "No reset of context probabilities on coding pass "
1303 45 : "boundaries";
1304 45 : osInterp += ", ";
1305 45 : if (v & 0x4)
1306 0 : osInterp += "Termination on each coding pass";
1307 : else
1308 45 : osInterp += "No termination on each coding pass";
1309 45 : osInterp += ", ";
1310 45 : if (v & 0x8)
1311 0 : osInterp += "Vertically causal context";
1312 : else
1313 45 : osInterp += "No vertically causal context";
1314 45 : osInterp += ", ";
1315 45 : if (v & 0x10)
1316 0 : osInterp += "Predictable termination";
1317 : else
1318 45 : osInterp += "No predictable termination";
1319 45 : osInterp += ", ";
1320 45 : if (v & 0x20)
1321 0 : osInterp += "Segmentation symbols are used";
1322 : else
1323 45 : osInterp += "No segmentation symbols are used";
1324 45 : if (v & 0x40)
1325 0 : osInterp += ", High Throughput algorithm";
1326 45 : if (v & 0x80)
1327 0 : osInterp += ", Mixed HT and Part1 code-block style";
1328 45 : return osInterp;
1329 : };
1330 :
1331 247 : if (abyMarker[1] == 0x90) /* SOT */
1332 : {
1333 51 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1334 8 : strstr(psDumpContext->pszCodestreamMarkers, "SOT"))
1335 : {
1336 43 : psMarker = CreateCurrentMarker();
1337 43 : if (!psMarker)
1338 0 : break;
1339 43 : READ_MARKER_FIELD_UINT16("Isot");
1340 43 : GUInt32 PSOT = READ_MARKER_FIELD_UINT32("Psot");
1341 43 : READ_MARKER_FIELD_UINT8("TPsot");
1342 43 : READ_MARKER_FIELD_UINT8("TNsot");
1343 43 : if (nRemainingMarkerSize > 0)
1344 0 : AddElement(
1345 : psMarker, psLastChild, psDumpContext,
1346 : CPLCreateXMLElementAndValue(
1347 : nullptr, "RemainingBytes",
1348 : CPLSPrintf(
1349 : "%d", static_cast<int>(nRemainingMarkerSize))));
1350 :
1351 43 : if (PSOT)
1352 43 : nNextTileOffset = nOffset + PSOT;
1353 : }
1354 : }
1355 196 : else if (abyMarker[1] == 0x50) /* CAP (HTJ2K) */
1356 : {
1357 0 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1358 0 : strstr(psDumpContext->pszCodestreamMarkers, "CAP"))
1359 : {
1360 0 : psMarker = CreateCurrentMarker();
1361 0 : if (!psMarker)
1362 0 : break;
1363 0 : const GUInt32 Pcap = READ_MARKER_FIELD_UINT32("Pcap");
1364 0 : for (int i = 0; i < 32; i++)
1365 : {
1366 0 : if ((Pcap >> (31 - i)) & 1)
1367 : {
1368 0 : if (i + 1 == 15)
1369 : {
1370 0 : READ_MARKER_FIELD_UINT16(
1371 : CPLSPrintf("Scap_P%d", i + 1),
1372 0 : [](GUInt16 v)
1373 : {
1374 0 : std::string ret;
1375 0 : if ((v >> 14) == 0)
1376 : ret = "All code-blocks are HT "
1377 0 : "code-blocks";
1378 0 : else if ((v >> 14) == 2)
1379 : ret = "Either all HT or all Part1 "
1380 0 : "code-blocks per tile component";
1381 0 : else if ((v >> 14) == 3)
1382 : ret = "Mixed HT or all Part1 "
1383 0 : "code-blocks per tile component";
1384 : else
1385 : ret =
1386 0 : "Reserved value for bit 14 and 15";
1387 0 : ret += ", ";
1388 0 : if ((v >> 13) & 1)
1389 : ret += "More than one HT set per "
1390 0 : "code-block";
1391 : else
1392 : ret +=
1393 0 : "Zero or one HT set per code-block";
1394 0 : ret += ", ";
1395 0 : if ((v >> 12) & 1)
1396 0 : ret += "ROI marker can be present";
1397 : else
1398 0 : ret += "No ROI marker";
1399 0 : ret += ", ";
1400 0 : if ((v >> 11) & 1)
1401 0 : ret += "Heterogeneous codestream";
1402 : else
1403 0 : ret += "Homogeneous codestream";
1404 0 : ret += ", ";
1405 0 : if ((v >> 5) & 1)
1406 : ret += "HT code-blocks can be used "
1407 0 : "with irreversible transforms";
1408 : else
1409 : ret += "HT code-blocks only used with "
1410 0 : "reversible transforms";
1411 0 : ret += ", ";
1412 0 : ret += "P=";
1413 0 : ret += CPLSPrintf("%d", v & 0x31);
1414 0 : return ret;
1415 : });
1416 : }
1417 : else
1418 : {
1419 0 : READ_MARKER_FIELD_UINT16(
1420 : CPLSPrintf("Scap_P%d", i + 1));
1421 : }
1422 : }
1423 : }
1424 0 : if (nRemainingMarkerSize > 0)
1425 0 : AddElement(
1426 : psMarker, psLastChild, psDumpContext,
1427 : CPLCreateXMLElementAndValue(
1428 : nullptr, "RemainingBytes",
1429 : CPLSPrintf(
1430 : "%d", static_cast<int>(nRemainingMarkerSize))));
1431 : }
1432 : }
1433 196 : else if (abyMarker[1] == 0x51) /* SIZ */
1434 : {
1435 45 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1436 8 : strstr(psDumpContext->pszCodestreamMarkers, "SIZ"))
1437 : {
1438 37 : psMarker = CreateCurrentMarker();
1439 37 : if (!psMarker)
1440 0 : break;
1441 37 : READ_MARKER_FIELD_UINT16(
1442 : "Rsiz",
1443 37 : [](GUInt16 v)
1444 : {
1445 : return std::string((v == 0) ? "Unrestricted profile"
1446 62 : : (v == 1) ? "Profile 0"
1447 31 : : (v == 2) ? "Profile 1"
1448 0 : : (v == 16384) ? "HTJ2K"
1449 68 : : "");
1450 : });
1451 37 : READ_MARKER_FIELD_UINT32("Xsiz");
1452 37 : READ_MARKER_FIELD_UINT32("Ysiz");
1453 37 : READ_MARKER_FIELD_UINT32("XOsiz");
1454 37 : READ_MARKER_FIELD_UINT32("YOsiz");
1455 37 : READ_MARKER_FIELD_UINT32("XTsiz");
1456 37 : READ_MARKER_FIELD_UINT32("YTsiz");
1457 37 : READ_MARKER_FIELD_UINT32("XTOSiz");
1458 37 : READ_MARKER_FIELD_UINT32("YTOSiz");
1459 37 : Csiz = READ_MARKER_FIELD_UINT16("Csiz");
1460 37 : bError = false;
1461 : // cppcheck-suppress knownConditionTrueFalse
1462 88 : for (int i = 0; i < Csiz && !bError; i++)
1463 : {
1464 51 : READ_MARKER_FIELD_UINT8(
1465 : CPLSPrintf("Ssiz%d", i),
1466 51 : [](GByte v)
1467 : {
1468 51 : const char *psz = GetInterpretationOfBPC(v);
1469 51 : return std::string(psz ? psz : "");
1470 : });
1471 51 : READ_MARKER_FIELD_UINT8(CPLSPrintf("XRsiz%d", i));
1472 51 : READ_MARKER_FIELD_UINT8(CPLSPrintf("YRsiz%d", i));
1473 : }
1474 37 : if (nRemainingMarkerSize > 0)
1475 0 : AddElement(
1476 : psMarker, psLastChild, psDumpContext,
1477 : CPLCreateXMLElementAndValue(
1478 : nullptr, "RemainingBytes",
1479 : CPLSPrintf(
1480 : "%d", static_cast<int>(nRemainingMarkerSize))));
1481 : }
1482 : }
1483 151 : else if (abyMarker[1] == 0x52) /* COD */
1484 : {
1485 45 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1486 8 : strstr(psDumpContext->pszCodestreamMarkers, "COD"))
1487 : {
1488 45 : psMarker = CreateCurrentMarker();
1489 45 : if (!psMarker)
1490 0 : break;
1491 45 : bool bHasPrecincts = false;
1492 45 : if (nRemainingMarkerSize >= 1)
1493 : {
1494 45 : auto nLastVal = *pabyMarkerDataIter;
1495 45 : CPLString osInterp;
1496 45 : if (nLastVal & 0x1)
1497 : {
1498 38 : bHasPrecincts = true;
1499 38 : osInterp += "User defined precincts";
1500 : }
1501 : else
1502 7 : osInterp += "Standard precincts";
1503 45 : osInterp += ", ";
1504 45 : if (nLastVal & 0x2)
1505 0 : osInterp += "SOP marker segments may be used";
1506 : else
1507 45 : osInterp += "No SOP marker segments";
1508 45 : osInterp += ", ";
1509 45 : if (nLastVal & 0x4)
1510 0 : osInterp += "EPH marker segments may be used";
1511 : else
1512 45 : osInterp += "No EPH marker segments";
1513 45 : AddField(psMarker, psLastChild, psDumpContext, "Scod",
1514 : nLastVal, osInterp.c_str());
1515 45 : pabyMarkerDataIter += 1;
1516 45 : nRemainingMarkerSize -= 1;
1517 : }
1518 : else
1519 : {
1520 0 : AddError(psMarker, psLastChild, psDumpContext,
1521 : CPLSPrintf("Cannot read field %s", "Scod"));
1522 : }
1523 45 : READ_MARKER_FIELD_UINT8("SGcod_Progress", lambdaPOCType);
1524 45 : READ_MARKER_FIELD_UINT16("SGcod_NumLayers");
1525 45 : READ_MARKER_FIELD_UINT8("SGcod_MCT");
1526 45 : READ_MARKER_FIELD_UINT8("SPcod_NumDecompositions");
1527 45 : READ_MARKER_FIELD_UINT8(
1528 : "SPcod_xcb_minus_2",
1529 45 : [](GByte v) {
1530 : return std::string(v <= 8
1531 45 : ? CPLSPrintf("%d", 1 << (2 + v))
1532 90 : : "invalid");
1533 : });
1534 45 : READ_MARKER_FIELD_UINT8(
1535 : "SPcod_ycb_minus_2",
1536 45 : [](GByte v) {
1537 : return std::string(v <= 8
1538 45 : ? CPLSPrintf("%d", 1 << (2 + v))
1539 90 : : "invalid");
1540 : });
1541 45 : READ_MARKER_FIELD_UINT8("SPcod_cbstyle", cblkstyleLamba);
1542 45 : READ_MARKER_FIELD_UINT8("SPcod_transformation",
1543 45 : [](GByte v)
1544 : {
1545 : return std::string(
1546 : (v == 0) ? "9-7 irreversible"
1547 16 : : (v == 1) ? "5-3 reversible"
1548 61 : : "");
1549 : });
1550 45 : if (bHasPrecincts)
1551 : {
1552 38 : int i = 0;
1553 116 : while (nRemainingMarkerSize >= 1)
1554 : {
1555 78 : auto nLastVal = *pabyMarkerDataIter;
1556 78 : AddField(psMarker, psLastChild, psDumpContext,
1557 : CPLSPrintf("SPcod_Precincts%d", i),
1558 78 : *pabyMarkerDataIter,
1559 : CPLSPrintf("PPx=%d PPy=%d: %dx%d",
1560 78 : nLastVal & 0xf, nLastVal >> 4,
1561 78 : 1 << (nLastVal & 0xf),
1562 78 : 1 << (nLastVal >> 4)));
1563 78 : pabyMarkerDataIter += 1;
1564 78 : nRemainingMarkerSize -= 1;
1565 78 : i++;
1566 : }
1567 : }
1568 45 : if (nRemainingMarkerSize > 0)
1569 0 : AddElement(
1570 : psMarker, psLastChild, psDumpContext,
1571 : CPLCreateXMLElementAndValue(
1572 : nullptr, "RemainingBytes",
1573 : CPLSPrintf(
1574 : "%d", static_cast<int>(nRemainingMarkerSize))));
1575 : }
1576 : }
1577 106 : else if (abyMarker[1] == 0x53) /* COC */
1578 : {
1579 0 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1580 0 : strstr(psDumpContext->pszCodestreamMarkers, "COC"))
1581 : {
1582 0 : psMarker = CreateCurrentMarker();
1583 0 : if (!psMarker)
1584 0 : break;
1585 0 : if (Csiz < 257)
1586 0 : READ_MARKER_FIELD_UINT8("Ccoc");
1587 : else
1588 0 : READ_MARKER_FIELD_UINT16("Ccoc");
1589 :
1590 0 : bool bHasPrecincts = false;
1591 0 : if (nRemainingMarkerSize >= 1)
1592 : {
1593 0 : auto nLastVal = *pabyMarkerDataIter;
1594 0 : CPLString osInterp;
1595 0 : if (nLastVal & 0x1)
1596 : {
1597 0 : bHasPrecincts = true;
1598 0 : osInterp += "User defined precincts";
1599 : }
1600 : else
1601 0 : osInterp += "Standard precincts";
1602 0 : AddField(psMarker, psLastChild, psDumpContext, "Scoc",
1603 : nLastVal, osInterp.c_str());
1604 0 : pabyMarkerDataIter += 1;
1605 0 : nRemainingMarkerSize -= 1;
1606 : }
1607 : else
1608 : {
1609 0 : AddError(psMarker, psLastChild, psDumpContext,
1610 : CPLSPrintf("Cannot read field %s", "Scoc"));
1611 : }
1612 0 : READ_MARKER_FIELD_UINT8("SPcoc_NumDecompositions");
1613 0 : READ_MARKER_FIELD_UINT8(
1614 : "SPcoc_xcb_minus_2",
1615 0 : [](GByte v) {
1616 : return std::string(v <= 8
1617 0 : ? CPLSPrintf("%d", 1 << (2 + v))
1618 0 : : "invalid");
1619 : });
1620 0 : READ_MARKER_FIELD_UINT8(
1621 : "SPcoc_ycb_minus_2",
1622 0 : [](GByte v) {
1623 : return std::string(v <= 8
1624 0 : ? CPLSPrintf("%d", 1 << (2 + v))
1625 0 : : "invalid");
1626 : });
1627 0 : READ_MARKER_FIELD_UINT8("SPcoc_cbstyle", cblkstyleLamba);
1628 0 : READ_MARKER_FIELD_UINT8("SPcoc_transformation",
1629 0 : [](GByte v)
1630 : {
1631 : return std::string(
1632 : (v == 0) ? "9-7 irreversible"
1633 0 : : (v == 1) ? "5-3 reversible"
1634 0 : : "");
1635 : });
1636 0 : if (bHasPrecincts)
1637 : {
1638 0 : int i = 0;
1639 0 : while (nRemainingMarkerSize >= 1)
1640 : {
1641 0 : auto nLastVal = *pabyMarkerDataIter;
1642 0 : AddField(psMarker, psLastChild, psDumpContext,
1643 : CPLSPrintf("SPcoc_Precincts%d", i),
1644 0 : *pabyMarkerDataIter,
1645 : CPLSPrintf("PPx=%d PPy=%d: %dx%d",
1646 0 : nLastVal & 0xf, nLastVal >> 4,
1647 0 : 1 << (nLastVal & 0xf),
1648 0 : 1 << (nLastVal >> 4)));
1649 0 : pabyMarkerDataIter += 1;
1650 0 : nRemainingMarkerSize -= 1;
1651 0 : i++;
1652 : }
1653 : }
1654 0 : if (nRemainingMarkerSize > 0)
1655 0 : AddElement(
1656 : psMarker, psLastChild, psDumpContext,
1657 : CPLCreateXMLElementAndValue(
1658 : nullptr, "RemainingBytes",
1659 : CPLSPrintf(
1660 : "%d", static_cast<int>(nRemainingMarkerSize))));
1661 : }
1662 : }
1663 106 : else if (abyMarker[1] == 0x55) /* TLM */
1664 : {
1665 1 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1666 0 : strstr(psDumpContext->pszCodestreamMarkers, "TLM"))
1667 : {
1668 1 : psMarker = CreateCurrentMarker();
1669 1 : if (!psMarker)
1670 0 : break;
1671 1 : READ_MARKER_FIELD_UINT8("Ztlm");
1672 1 : auto Stlm = READ_MARKER_FIELD_UINT8(
1673 : "Stlm",
1674 1 : [](GByte v) {
1675 : return std::string(CPLSPrintf(
1676 1 : "ST=%d SP=%d", (v >> 4) & 3, (v >> 6) & 1));
1677 : });
1678 1 : int ST = (Stlm >> 4) & 3;
1679 1 : int SP = (Stlm >> 6) & 1;
1680 1 : int nTilePartDescLength = ST + ((SP == 0) ? 2 : 4);
1681 1 : int i = 0;
1682 5 : while (nRemainingMarkerSize >= nTilePartDescLength)
1683 : {
1684 4 : if (ST == 1)
1685 0 : READ_MARKER_FIELD_UINT8(CPLSPrintf("Ttlm%d", i));
1686 4 : else if (ST == 2)
1687 4 : READ_MARKER_FIELD_UINT16(CPLSPrintf("Ttlm%d", i));
1688 4 : if (SP == 0)
1689 0 : READ_MARKER_FIELD_UINT16(CPLSPrintf("Ptlm%d", i));
1690 : else
1691 4 : READ_MARKER_FIELD_UINT32(CPLSPrintf("Ptlm%d", i));
1692 4 : i++;
1693 : }
1694 1 : if (nRemainingMarkerSize > 0)
1695 0 : AddElement(
1696 : psMarker, psLastChild, psDumpContext,
1697 : CPLCreateXMLElementAndValue(
1698 : nullptr, "RemainingBytes",
1699 : CPLSPrintf(
1700 : "%d", static_cast<int>(nRemainingMarkerSize))));
1701 : }
1702 : }
1703 105 : else if (abyMarker[1] == 0x57) /* PLM */
1704 : {
1705 0 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1706 0 : strstr(psDumpContext->pszCodestreamMarkers, "PLM"))
1707 : {
1708 0 : psMarker = CreateCurrentMarker();
1709 0 : if (!psMarker)
1710 0 : break;
1711 : }
1712 : }
1713 105 : else if (abyMarker[1] == 0x58) /* PLT */
1714 : {
1715 13 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1716 3 : strstr(psDumpContext->pszCodestreamMarkers, "PLT"))
1717 : {
1718 10 : psMarker = CreateCurrentMarker();
1719 10 : if (!psMarker)
1720 0 : break;
1721 10 : READ_MARKER_FIELD_UINT8("Zplt");
1722 10 : int i = 0;
1723 10 : unsigned nPacketLength = 0;
1724 196 : while (nRemainingMarkerSize >= 1)
1725 : {
1726 186 : auto nLastVal = *pabyMarkerDataIter;
1727 186 : nPacketLength |= (nLastVal & 0x7f);
1728 186 : if (nLastVal & 0x80)
1729 : {
1730 16 : nPacketLength <<= 7;
1731 : }
1732 : else
1733 : {
1734 170 : AddField(psMarker, psLastChild, psDumpContext,
1735 : CPLSPrintf("Iplt%d", i), nPacketLength);
1736 170 : nPacketLength = 0;
1737 170 : i++;
1738 : }
1739 186 : pabyMarkerDataIter += 1;
1740 186 : nRemainingMarkerSize -= 1;
1741 : }
1742 10 : if (nPacketLength != 0)
1743 : {
1744 0 : AddError(psMarker, psLastChild, psDumpContext,
1745 : "Incorrect PLT marker");
1746 : }
1747 : }
1748 : }
1749 92 : else if (abyMarker[1] == 0x59) /* CPF (HTJ2K) */
1750 : {
1751 0 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1752 0 : strstr(psDumpContext->pszCodestreamMarkers, "CPF"))
1753 : {
1754 0 : psMarker = CreateCurrentMarker();
1755 0 : if (!psMarker)
1756 0 : break;
1757 0 : const GUInt16 Lcpf = nMarkerSize;
1758 0 : if (Lcpf > 2 && (Lcpf % 2) == 0)
1759 : {
1760 0 : for (int i = 0; i < (Lcpf - 2) / 2; i++)
1761 : {
1762 0 : READ_MARKER_FIELD_UINT16(CPLSPrintf("Pcpf%d", i + 1));
1763 : }
1764 : }
1765 0 : if (nRemainingMarkerSize > 0)
1766 0 : AddElement(
1767 : psMarker, psLastChild, psDumpContext,
1768 : CPLCreateXMLElementAndValue(
1769 : nullptr, "RemainingBytes",
1770 : CPLSPrintf(
1771 : "%d", static_cast<int>(nRemainingMarkerSize))));
1772 : }
1773 : }
1774 92 : else if (abyMarker[1] == 0x5C) /* QCD */
1775 : {
1776 45 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1777 8 : strstr(psDumpContext->pszCodestreamMarkers, "QCD"))
1778 : {
1779 37 : psMarker = CreateCurrentMarker();
1780 37 : if (!psMarker)
1781 0 : break;
1782 37 : const int Sqcd = READ_MARKER_FIELD_UINT8(
1783 : "Sqcd",
1784 37 : [](GByte v)
1785 : {
1786 37 : std::string ret;
1787 37 : if ((v & 31) == 0)
1788 10 : ret = "No quantization";
1789 27 : else if ((v & 31) == 1)
1790 0 : ret = "Scalar derived";
1791 27 : else if ((v & 31) == 2)
1792 27 : ret = "Scalar expounded";
1793 37 : ret += ", ";
1794 37 : ret += CPLSPrintf("guard bits = %d", v >> 5);
1795 37 : return ret;
1796 37 : });
1797 37 : if ((Sqcd & 31) == 0)
1798 : {
1799 : // Reversible
1800 10 : int i = 0;
1801 74 : while (nRemainingMarkerSize >= 1)
1802 : {
1803 64 : READ_MARKER_FIELD_UINT8(
1804 : CPLSPrintf("SPqcd%d", i),
1805 64 : [](GByte v) {
1806 : return std::string(
1807 64 : CPLSPrintf("epsilon_b = %d", v >> 3));
1808 : });
1809 64 : ++i;
1810 : }
1811 : }
1812 : else
1813 : {
1814 27 : int i = 0;
1815 123 : while (nRemainingMarkerSize >= 2)
1816 : {
1817 96 : READ_MARKER_FIELD_UINT16(
1818 : CPLSPrintf("SPqcd%d", i),
1819 96 : [](GUInt16 v)
1820 : {
1821 : return std::string(CPLSPrintf(
1822 : "mantissa_b = %d, epsilon_b = %d",
1823 96 : v & ((1 << 11) - 1), v >> 11));
1824 : });
1825 96 : ++i;
1826 : }
1827 : }
1828 : }
1829 : }
1830 47 : else if (abyMarker[1] == 0x5D) /* QCC */
1831 : {
1832 1 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1833 0 : strstr(psDumpContext->pszCodestreamMarkers, "QCC"))
1834 : {
1835 1 : psMarker = CreateCurrentMarker();
1836 1 : if (!psMarker)
1837 0 : break;
1838 1 : if (Csiz < 257)
1839 1 : READ_MARKER_FIELD_UINT8("Cqcc");
1840 : else
1841 0 : READ_MARKER_FIELD_UINT16("Cqcc");
1842 :
1843 1 : const int Sqcc = READ_MARKER_FIELD_UINT8(
1844 : "Sqcc",
1845 1 : [](GByte v)
1846 : {
1847 1 : std::string ret;
1848 1 : if ((v & 31) == 0)
1849 0 : ret = "No quantization";
1850 1 : else if ((v & 31) == 1)
1851 0 : ret = "Scalar derived";
1852 1 : else if ((v & 31) == 2)
1853 1 : ret = "Scalar expounded";
1854 1 : ret += ", ";
1855 1 : ret += CPLSPrintf("guard bits = %d", v >> 5);
1856 1 : return ret;
1857 1 : });
1858 1 : if ((Sqcc & 31) == 0)
1859 : {
1860 : // Reversible
1861 0 : int i = 0;
1862 0 : while (nRemainingMarkerSize >= 1)
1863 : {
1864 0 : READ_MARKER_FIELD_UINT8(
1865 : CPLSPrintf("SPqcc%d", i),
1866 0 : [](GByte v) {
1867 : return std::string(
1868 0 : CPLSPrintf("epsilon_b = %d", v >> 3));
1869 : });
1870 0 : ++i;
1871 : }
1872 : }
1873 : else
1874 : {
1875 1 : int i = 0;
1876 2 : while (nRemainingMarkerSize >= 2)
1877 : {
1878 1 : READ_MARKER_FIELD_UINT16(
1879 : CPLSPrintf("SPqcc%d", i),
1880 1 : [](GUInt16 v)
1881 : {
1882 : return std::string(CPLSPrintf(
1883 : "mantissa_b = %d, epsilon_b = %d",
1884 1 : v & ((1 << 11) - 1), v >> 11));
1885 : });
1886 1 : ++i;
1887 : }
1888 : }
1889 : }
1890 : }
1891 46 : else if (abyMarker[1] == 0x5E) /* RGN */
1892 : {
1893 1 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1894 0 : strstr(psDumpContext->pszCodestreamMarkers, "RGN"))
1895 : {
1896 1 : psMarker = CreateCurrentMarker();
1897 1 : if (!psMarker)
1898 0 : break;
1899 : }
1900 : }
1901 45 : else if (abyMarker[1] == 0x5F) /* POC */
1902 : {
1903 1 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1904 0 : strstr(psDumpContext->pszCodestreamMarkers, "POC"))
1905 : {
1906 1 : psMarker = CreateCurrentMarker();
1907 1 : if (!psMarker)
1908 0 : break;
1909 1 : const int nPOCEntrySize = Csiz < 257 ? 7 : 9;
1910 1 : int i = 0;
1911 2 : while (nRemainingMarkerSize >= nPOCEntrySize)
1912 : {
1913 1 : READ_MARKER_FIELD_UINT8(CPLSPrintf("RSpoc%d", i));
1914 1 : if (nPOCEntrySize == 7)
1915 : {
1916 1 : READ_MARKER_FIELD_UINT8(CPLSPrintf("CSpoc%d", i));
1917 : }
1918 : else
1919 : {
1920 0 : READ_MARKER_FIELD_UINT16(CPLSPrintf("CSpoc%d", i));
1921 : }
1922 1 : READ_MARKER_FIELD_UINT16(CPLSPrintf("LYEpoc%d", i));
1923 1 : READ_MARKER_FIELD_UINT8(CPLSPrintf("REpoc%d", i));
1924 1 : if (nPOCEntrySize == 7)
1925 : {
1926 1 : READ_MARKER_FIELD_UINT8(CPLSPrintf("CEpoc%d", i));
1927 : }
1928 : else
1929 : {
1930 0 : READ_MARKER_FIELD_UINT16(CPLSPrintf("CEpoc%d", i));
1931 : }
1932 1 : READ_MARKER_FIELD_UINT8(CPLSPrintf("Ppoc%d", i),
1933 : lambdaPOCType);
1934 1 : i++;
1935 : }
1936 1 : if (nRemainingMarkerSize > 0)
1937 : {
1938 0 : AddElement(
1939 : psMarker, psLastChild, psDumpContext,
1940 : CPLCreateXMLElementAndValue(
1941 : nullptr, "RemainingBytes",
1942 : CPLSPrintf(
1943 : "%d", static_cast<int>(nRemainingMarkerSize))));
1944 : }
1945 : }
1946 : }
1947 44 : else if (abyMarker[1] == 0x60) /* PPM */
1948 : {
1949 0 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1950 0 : strstr(psDumpContext->pszCodestreamMarkers, "PPM"))
1951 : {
1952 0 : psMarker = CreateCurrentMarker();
1953 0 : if (!psMarker)
1954 0 : break;
1955 : }
1956 : }
1957 44 : else if (abyMarker[1] == 0x61) /* PPT */
1958 : {
1959 0 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1960 0 : strstr(psDumpContext->pszCodestreamMarkers, "PPT"))
1961 : {
1962 0 : psMarker = CreateCurrentMarker();
1963 0 : if (!psMarker)
1964 0 : break;
1965 : }
1966 : }
1967 44 : else if (abyMarker[1] == 0x63) /* CRG */
1968 : {
1969 0 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1970 0 : strstr(psDumpContext->pszCodestreamMarkers, "CRG"))
1971 : {
1972 0 : psMarker = CreateCurrentMarker();
1973 0 : if (!psMarker)
1974 0 : break;
1975 : }
1976 : }
1977 44 : else if (abyMarker[1] == 0x64) /* COM */
1978 : {
1979 44 : if (psDumpContext->pszCodestreamMarkers == nullptr ||
1980 11 : strstr(psDumpContext->pszCodestreamMarkers, "COM"))
1981 : {
1982 44 : psMarker = CreateCurrentMarker();
1983 44 : if (!psMarker)
1984 0 : break;
1985 44 : auto RCom = READ_MARKER_FIELD_UINT16(
1986 : "Rcom",
1987 44 : [](GUInt16 v) {
1988 : return std::string((v == 0) ? "Binary"
1989 44 : : (v == 1) ? "LATIN1"
1990 88 : : "");
1991 : });
1992 44 : if (RCom == 1)
1993 : {
1994 44 : GByte abyBackup = pabyMarkerDataIter[nRemainingMarkerSize];
1995 44 : pabyMarkerDataIter[nRemainingMarkerSize] = 0;
1996 44 : AddField(
1997 : psMarker, psLastChild, psDumpContext, "COM",
1998 : static_cast<int>(nRemainingMarkerSize),
1999 : reinterpret_cast<const char *>(pabyMarkerDataIter));
2000 44 : pabyMarkerDataIter[nRemainingMarkerSize] = abyBackup;
2001 : }
2002 : }
2003 : }
2004 :
2005 247 : if (VSIFSeekL(fp, nOffset + 2 + nMarkerSize, SEEK_SET) != 0)
2006 : {
2007 0 : AddError(psCSBox, psLastChildCSBox, psDumpContext,
2008 0 : "Cannot seek to next marker", nOffset + 2 + nMarkerSize);
2009 0 : break;
2010 : }
2011 :
2012 247 : CPL_IGNORE_RET_VAL(bError);
2013 : }
2014 47 : CPLFree(pabyMarkerData);
2015 47 : return psCSBox;
2016 : }
2017 :
2018 : /************************************************************************/
2019 : /* GDALGetJPEG2000StructureInternal() */
2020 : /************************************************************************/
2021 :
2022 121 : static void GDALGetJPEG2000StructureInternal(CPLXMLNode *psParent, VSILFILE *fp,
2023 : GDALJP2Box *poParentBox,
2024 : int nRecLevel,
2025 : vsi_l_offset nFileOrParentBoxSize,
2026 : DumpContext *psDumpContext)
2027 : {
2028 : // Limit recursion to a reasonable level. I believe that in practice 2
2029 : // should be sufficient, but just in case someone creates deeply
2030 : // nested "super-boxes", allow up to 5.
2031 121 : if (nRecLevel == 5)
2032 0 : return;
2033 :
2034 : static const char *const szHex = "0123456789ABCDEF";
2035 242 : GDALJP2Box oBox(fp);
2036 121 : oBox.SetAllowGetFileSize(psDumpContext->bAllowGetFileSize);
2037 121 : CPLXMLNode *psLastChild = nullptr;
2038 121 : if (oBox.ReadFirstChild(poParentBox))
2039 : {
2040 806 : while (strlen(oBox.GetType()) > 0 &&
2041 403 : psDumpContext->nCurLineCount <= psDumpContext->nMaxLineCount + 1)
2042 : {
2043 401 : GIntBig nBoxDataLength = oBox.GetDataLength();
2044 401 : const char *pszBoxType = oBox.GetType();
2045 401 : CPLXMLNode *psBox = nullptr;
2046 758 : const auto CreateBox = [&]()
2047 : {
2048 758 : if (psBox != nullptr)
2049 401 : return true;
2050 357 : psBox = CPLCreateXMLNode(nullptr, CXT_Element, "JP2Box");
2051 357 : psBox = AddElement(psParent, psLastChild, psDumpContext, psBox);
2052 357 : if (!psBox)
2053 0 : return false;
2054 357 : CPLAddXMLAttributeAndValue(psBox, "name", pszBoxType);
2055 357 : CPLAddXMLAttributeAndValue(
2056 : psBox, "box_offset",
2057 357 : CPLSPrintf(CPL_FRMT_GIB, oBox.GetBoxOffset()));
2058 357 : const auto nBoxLength = oBox.GetBoxLength();
2059 711 : CPLAddXMLAttributeAndValue(
2060 : psBox, "box_length",
2061 354 : nBoxLength > 0 ? CPLSPrintf(CPL_FRMT_GIB, nBoxLength)
2062 : : "unknown");
2063 357 : CPLAddXMLAttributeAndValue(
2064 : psBox, "data_offset",
2065 : CPLSPrintf(CPL_FRMT_GIB, oBox.GetDataOffset()));
2066 357 : CPLAddXMLAttributeAndValue(
2067 : psBox, "data_length",
2068 357 : nBoxDataLength > 0
2069 352 : ? CPLSPrintf(CPL_FRMT_GIB, nBoxDataLength)
2070 : : "unknown");
2071 :
2072 357 : if (nBoxDataLength > GINTBIG_MAX - oBox.GetDataOffset())
2073 : {
2074 0 : CPLXMLNode *psLastChildBox = nullptr;
2075 0 : AddError(psBox, psLastChildBox, psDumpContext,
2076 : "Invalid box_length");
2077 0 : return false;
2078 : }
2079 357 : return true;
2080 401 : };
2081 :
2082 : // Check large non-jp2c boxes against filesize
2083 401 : if (strcmp(pszBoxType, "jp2c") != 0 && nBoxDataLength > 100 * 1024)
2084 : {
2085 0 : if (nFileOrParentBoxSize == 0)
2086 : {
2087 0 : CPL_IGNORE_RET_VAL(VSIFSeekL(fp, 0, SEEK_END));
2088 0 : nFileOrParentBoxSize = VSIFTellL(fp);
2089 : }
2090 : }
2091 585 : if (nFileOrParentBoxSize > 0 && nBoxDataLength > 0 &&
2092 184 : (static_cast<vsi_l_offset>(oBox.GetDataOffset()) >
2093 184 : nFileOrParentBoxSize ||
2094 184 : static_cast<vsi_l_offset>(nBoxDataLength) >
2095 184 : nFileOrParentBoxSize - oBox.GetDataOffset()))
2096 : {
2097 0 : CPLXMLNode *psLastChildBox = nullptr;
2098 0 : if (!CreateBox())
2099 0 : break;
2100 0 : AddError(psBox, psLastChildBox, psDumpContext,
2101 : "Invalid box_length");
2102 0 : break;
2103 : }
2104 :
2105 401 : if (oBox.IsSuperBox())
2106 : {
2107 80 : if (!CreateBox())
2108 0 : break;
2109 80 : if (nBoxDataLength <= 0)
2110 0 : break;
2111 160 : GDALGetJPEG2000StructureInternal(
2112 : psBox, fp, &oBox, nRecLevel + 1,
2113 80 : oBox.GetDataOffset() +
2114 80 : static_cast<vsi_l_offset>(nBoxDataLength),
2115 : psDumpContext);
2116 : }
2117 : else
2118 : {
2119 321 : if (strcmp(pszBoxType, "uuid") == 0 &&
2120 23 : psDumpContext->bDumpJP2Boxes)
2121 : {
2122 17 : if (!CreateBox())
2123 0 : break;
2124 : char *pszBinaryContent =
2125 17 : static_cast<char *>(VSIMalloc(2 * 16 + 1));
2126 17 : const GByte *pabyUUID = oBox.GetUUID();
2127 289 : for (int i = 0; i < 16; i++)
2128 : {
2129 272 : pszBinaryContent[2 * i] = szHex[pabyUUID[i] >> 4];
2130 272 : pszBinaryContent[2 * i + 1] = szHex[pabyUUID[i] & 0xf];
2131 : }
2132 17 : pszBinaryContent[2 * 16] = '\0';
2133 : CPLXMLNode *psUUIDNode =
2134 17 : CPLCreateXMLNode(nullptr, CXT_Element, "UUID");
2135 17 : if (GDALJP2Metadata::IsUUID_MSI(pabyUUID))
2136 16 : CPLAddXMLAttributeAndValue(psUUIDNode, "description",
2137 : "GeoTIFF");
2138 1 : else if (GDALJP2Metadata::IsUUID_XMP(pabyUUID))
2139 1 : CPLAddXMLAttributeAndValue(psUUIDNode, "description",
2140 : "XMP");
2141 17 : CPLCreateXMLNode(psUUIDNode, CXT_Text, pszBinaryContent);
2142 17 : VSIFree(pszBinaryContent);
2143 :
2144 17 : CPLXMLNode *psLastChildBox = nullptr;
2145 17 : AddElement(psBox, psLastChildBox, psDumpContext,
2146 : psUUIDNode);
2147 : }
2148 :
2149 321 : if (psDumpContext->bDumpBinaryContent &&
2150 271 : strcmp(pszBoxType, "jp2c") != 0 &&
2151 236 : nBoxDataLength < 100 * 1024)
2152 : {
2153 236 : if (!CreateBox())
2154 0 : break;
2155 : CPLXMLNode *psBinaryContent =
2156 236 : CPLCreateXMLNode(nullptr, CXT_Element, "BinaryContent");
2157 236 : GByte *pabyBoxData = oBox.ReadBoxData();
2158 : const int nBoxLength = static_cast<int>(
2159 236 : std::min<GIntBig>(nBoxDataLength, INT_MAX / 2 - 1));
2160 : char *pszBinaryContent =
2161 236 : static_cast<char *>(VSIMalloc(2 * nBoxLength + 1));
2162 236 : if (pabyBoxData && pszBinaryContent)
2163 : {
2164 47688 : for (int i = 0; i < nBoxLength; i++)
2165 : {
2166 47452 : pszBinaryContent[2 * i] =
2167 47452 : szHex[pabyBoxData[i] >> 4];
2168 47452 : pszBinaryContent[2 * i + 1] =
2169 47452 : szHex[pabyBoxData[i] & 0xf];
2170 : }
2171 236 : pszBinaryContent[2 * nBoxLength] = '\0';
2172 236 : CPLCreateXMLNode(psBinaryContent, CXT_Text,
2173 : pszBinaryContent);
2174 : }
2175 236 : CPLFree(pabyBoxData);
2176 236 : VSIFree(pszBinaryContent);
2177 :
2178 236 : CPLXMLNode *psLastChildBox = nullptr;
2179 236 : AddElement(psBox, psLastChildBox, psDumpContext,
2180 : psBinaryContent);
2181 : }
2182 :
2183 321 : if (psDumpContext->bDumpTextContent &&
2184 271 : strcmp(pszBoxType, "jp2c") != 0 &&
2185 236 : nBoxDataLength < 100 * 1024)
2186 : {
2187 236 : if (!CreateBox())
2188 0 : break;
2189 236 : GByte *pabyBoxData = oBox.ReadBoxData();
2190 236 : if (pabyBoxData)
2191 : {
2192 236 : const char *pszBoxData =
2193 : reinterpret_cast<const char *>(pabyBoxData);
2194 427 : if (CPLIsUTF8(pszBoxData, -1) &&
2195 191 : static_cast<int>(strlen(pszBoxData)) + 2 >=
2196 : nBoxDataLength)
2197 : {
2198 58 : CPLXMLNode *psXMLContentBox = nullptr;
2199 58 : if (pszBoxData[0] == '<')
2200 : {
2201 22 : CPLPushErrorHandler(CPLQuietErrorHandler);
2202 22 : psXMLContentBox = CPLParseXMLString(pszBoxData);
2203 22 : CPLPopErrorHandler();
2204 : }
2205 58 : if (psXMLContentBox)
2206 : {
2207 22 : CPLXMLNode *psXMLContentNode = CPLCreateXMLNode(
2208 : nullptr, CXT_Element, "XMLContent");
2209 22 : psXMLContentNode->psChild = psXMLContentBox;
2210 :
2211 22 : CPLXMLNode *psLastChildBox = nullptr;
2212 22 : AddElement(psBox, psLastChildBox, psDumpContext,
2213 : psXMLContentNode);
2214 : }
2215 : else
2216 : {
2217 36 : auto psTextElement = CPLCreateXMLNode(
2218 : nullptr, CXT_Element, "TextContent");
2219 36 : CPLCreateXMLNode(psTextElement, CXT_Text,
2220 : pszBoxData);
2221 :
2222 36 : CPLXMLNode *psLastChildBox = nullptr;
2223 36 : AddElement(psBox, psLastChildBox, psDumpContext,
2224 : psTextElement);
2225 : }
2226 : }
2227 : }
2228 236 : CPLFree(pabyBoxData);
2229 : }
2230 :
2231 321 : if (strcmp(pszBoxType, "jp2c") == 0)
2232 : {
2233 41 : if (psDumpContext->bDumpCodestream ||
2234 6 : psDumpContext->pszCodestreamMarkers)
2235 : {
2236 41 : if (!CreateBox())
2237 0 : break;
2238 41 : DumpJPK2CodeStream(psBox, fp, oBox.GetDataOffset(),
2239 : nBoxDataLength, psDumpContext);
2240 41 : if (psDumpContext->bStopAtSOD &&
2241 6 : psDumpContext->bSODEncountered)
2242 : {
2243 6 : break;
2244 : }
2245 : }
2246 : }
2247 280 : else if (!psDumpContext->bDumpJP2Boxes)
2248 : {
2249 : // do nothing
2250 : }
2251 253 : else if (strcmp(pszBoxType, "uuid") == 0 &&
2252 17 : GDALJP2Metadata::IsUUID_MSI(oBox.GetUUID()))
2253 : {
2254 16 : if (!CreateBox())
2255 0 : break;
2256 16 : DumpGeoTIFFBox(psBox, oBox, psDumpContext);
2257 : }
2258 220 : else if (strcmp(pszBoxType, "ftyp") == 0)
2259 : {
2260 35 : if (!CreateBox())
2261 0 : break;
2262 35 : DumpFTYPBox(psBox, oBox, psDumpContext);
2263 : }
2264 185 : else if (strcmp(pszBoxType, "ihdr") == 0)
2265 : {
2266 35 : if (!CreateBox())
2267 0 : break;
2268 35 : DumpIHDRBox(psBox, oBox, psDumpContext);
2269 : }
2270 150 : else if (strcmp(pszBoxType, "bpcc") == 0)
2271 : {
2272 3 : if (!CreateBox())
2273 0 : break;
2274 3 : DumpBPCCBox(psBox, oBox, psDumpContext);
2275 : }
2276 147 : else if (strcmp(pszBoxType, "colr") == 0)
2277 : {
2278 33 : if (!CreateBox())
2279 0 : break;
2280 33 : DumpCOLRBox(psBox, oBox, psDumpContext);
2281 : }
2282 114 : else if (strcmp(pszBoxType, "pclr") == 0)
2283 : {
2284 6 : if (!CreateBox())
2285 0 : break;
2286 6 : DumpPCLRBox(psBox, oBox, psDumpContext);
2287 : }
2288 108 : else if (strcmp(pszBoxType, "cmap") == 0)
2289 : {
2290 6 : if (!CreateBox())
2291 0 : break;
2292 6 : DumpCMAPBox(psBox, oBox, psDumpContext);
2293 : }
2294 102 : else if (strcmp(pszBoxType, "cdef") == 0)
2295 : {
2296 3 : if (!CreateBox())
2297 0 : break;
2298 3 : DumpCDEFBox(psBox, oBox, psDumpContext);
2299 : }
2300 99 : else if (strcmp(pszBoxType, "resc") == 0 ||
2301 98 : strcmp(pszBoxType, "resd") == 0)
2302 : {
2303 1 : if (!CreateBox())
2304 0 : break;
2305 1 : DumpRESxBox(psBox, oBox, psDumpContext);
2306 : }
2307 98 : else if (strcmp(pszBoxType, "rreq") == 0)
2308 : {
2309 10 : if (!CreateBox())
2310 0 : break;
2311 10 : DumpRREQBox(psBox, oBox, psDumpContext);
2312 : }
2313 : }
2314 :
2315 395 : if (!oBox.ReadNextChild(poParentBox))
2316 113 : break;
2317 : }
2318 : }
2319 : }
2320 :
2321 : /************************************************************************/
2322 : /* GDALGetJPEG2000Structure() */
2323 : /************************************************************************/
2324 :
2325 : constexpr unsigned char jpc_header[] = {0xff, 0x4f};
2326 : constexpr unsigned char jp2_box_jp[] = {0x6a, 0x50, 0x20, 0x20}; /* 'jP ' */
2327 :
2328 : /** Dump the structure of a JPEG2000 file as a XML tree.
2329 : *
2330 : * @param pszFilename filename.
2331 : * @param papszOptions NULL terminated list of options, or NULL.
2332 : * Allowed options are BINARY_CONTENT=YES, TEXT_CONTENT=YES,
2333 : * CODESTREAM=YES, ALL=YES, JP2_BOXES=YES,
2334 : * CODESTREAM_MARKERS=list_of_marker_names_comma_separated,
2335 : * STOP_AT_SOD=YES, ALLOW_GET_FILE_SIZE=NO.
2336 : * @return XML tree (to be freed with CPLDestroyXMLNode()) or NULL in case
2337 : * of error
2338 : * @since GDAL 2.0
2339 : */
2340 :
2341 39 : CPLXMLNode *GDALGetJPEG2000Structure(const char *pszFilename,
2342 : CSLConstList papszOptions)
2343 : {
2344 39 : VSILFILE *fp = VSIFOpenL(pszFilename, "rb");
2345 39 : if (fp == nullptr)
2346 : {
2347 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s", pszFilename);
2348 0 : return nullptr;
2349 : }
2350 39 : auto psRet = GDALGetJPEG2000Structure(pszFilename, fp, papszOptions);
2351 39 : CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
2352 39 : return psRet;
2353 : }
2354 :
2355 : #ifndef DOXYGEN_SKIP
2356 :
2357 : /************************************************************************/
2358 : /* GDALGetJPEG2000Structure() */
2359 : /************************************************************************/
2360 :
2361 47 : CPLXMLNode *GDALGetJPEG2000Structure(const char *pszFilename, VSILFILE *fp,
2362 : CSLConstList papszOptions)
2363 : {
2364 47 : if (fp == nullptr)
2365 0 : return GDALGetJPEG2000Structure(pszFilename, papszOptions);
2366 :
2367 : GByte abyHeader[16];
2368 47 : if (VSIFSeekL(fp, 0, SEEK_SET) != 0 ||
2369 94 : VSIFReadL(abyHeader, 16, 1, fp) != 1 ||
2370 47 : (memcmp(abyHeader, jpc_header, sizeof(jpc_header)) != 0 &&
2371 41 : memcmp(abyHeader + 4, jp2_box_jp, sizeof(jp2_box_jp)) != 0))
2372 : {
2373 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s is not a JPEG2000 file",
2374 : pszFilename);
2375 0 : return nullptr;
2376 : }
2377 :
2378 47 : CPLXMLNode *psParent = nullptr;
2379 47 : DumpContext dc;
2380 47 : dc.nCurLineCount = 0;
2381 47 : dc.nMaxLineCount = atoi(CSLFetchNameValueDef(
2382 : papszOptions, "MAX_LINES",
2383 : CPLGetConfigOption("GDAL_JPEG2000_STRUCTURE_MAX_LINES", "500000")));
2384 47 : if (dc.nMaxLineCount > INT_MAX - 1)
2385 0 : dc.nMaxLineCount = INT_MAX - 1;
2386 47 : dc.bDumpAll = CPLFetchBool(papszOptions, "ALL", false);
2387 47 : dc.bDumpCodestream =
2388 47 : dc.bDumpAll || CPLFetchBool(papszOptions, "CODESTREAM", false);
2389 47 : dc.bDumpBinaryContent =
2390 47 : dc.bDumpAll || CPLFetchBool(papszOptions, "BINARY_CONTENT", false);
2391 47 : dc.bDumpTextContent =
2392 47 : dc.bDumpAll || CPLFetchBool(papszOptions, "TEXT_CONTENT", false);
2393 47 : dc.pszCodestreamMarkers =
2394 47 : CSLFetchNameValue(papszOptions, "CODESTREAM_MARKERS");
2395 102 : dc.bDumpJP2Boxes = dc.bDumpAll ||
2396 55 : CPLFetchBool(papszOptions, "JP2_BOXES", false) ||
2397 8 : dc.pszCodestreamMarkers == nullptr;
2398 47 : dc.bStopAtSOD = CPLFetchBool(papszOptions, "STOP_AT_SOD", false);
2399 47 : dc.bAllowGetFileSize =
2400 47 : CPLFetchBool(papszOptions, "ALLOW_GET_FILE_SIZE", true);
2401 :
2402 47 : if (memcmp(abyHeader, jpc_header, sizeof(jpc_header)) == 0)
2403 : {
2404 6 : if (dc.bDumpCodestream || dc.pszCodestreamMarkers != nullptr)
2405 : {
2406 6 : GIntBig nBoxDataLength = -1;
2407 6 : if (dc.bAllowGetFileSize && VSIFSeekL(fp, 0, SEEK_END) == 0)
2408 : {
2409 4 : nBoxDataLength = static_cast<GIntBig>(VSIFTellL(fp));
2410 : }
2411 6 : psParent = DumpJPK2CodeStream(nullptr, fp, 0, nBoxDataLength, &dc);
2412 6 : CPLAddXMLAttributeAndValue(psParent, "filename", pszFilename);
2413 : }
2414 : }
2415 : else
2416 : {
2417 41 : psParent = CPLCreateXMLNode(nullptr, CXT_Element, "JP2File");
2418 41 : CPLAddXMLAttributeAndValue(psParent, "filename", pszFilename);
2419 41 : vsi_l_offset nFileSize = 0;
2420 41 : GDALGetJPEG2000StructureInternal(psParent, fp, nullptr, 0, nFileSize,
2421 : &dc);
2422 : }
2423 :
2424 47 : if (dc.nCurLineCount > dc.nMaxLineCount)
2425 : {
2426 2 : CPLError(CE_Failure, CPLE_AppDefined,
2427 : "Maximum number of lines in JPEG2000 structure dump reached. "
2428 : "Increase GDAL_JPEG2000_STRUCTURE_MAX_LINES beyond %d.",
2429 : dc.nMaxLineCount);
2430 : }
2431 :
2432 47 : return psParent;
2433 : }
2434 :
2435 : /************************************************************************/
2436 : /* GDALGetJPEG2000Reversibility() */
2437 : /************************************************************************/
2438 :
2439 8 : const char *GDALGetJPEG2000Reversibility(const char *pszFilename, VSILFILE *fp)
2440 : {
2441 8 : const char *const apszOptions[] = {"ALLOW_GET_FILE_SIZE=NO",
2442 : "STOP_AT_SOD=YES",
2443 : "CODESTREAM_MARKERS=COD,COM", nullptr};
2444 8 : CPLXMLNode *psRes = GDALGetJPEG2000Structure(pszFilename, fp, apszOptions);
2445 8 : if (psRes == nullptr)
2446 0 : return nullptr;
2447 8 : const char *pszReversibility = nullptr;
2448 8 : const CPLXMLNode *psJP2C = CPLSearchXMLNode(psRes, "JP2KCodeStream");
2449 8 : if (psJP2C)
2450 : {
2451 8 : const char *pszTransformation = nullptr;
2452 8 : const char *pszCOM = nullptr;
2453 29 : for (const CPLXMLNode *psMarker = psJP2C->psChild; psMarker;
2454 21 : psMarker = psMarker->psNext)
2455 : {
2456 61 : if (psMarker->eType == CXT_Element &&
2457 40 : strcmp(psMarker->pszValue, "Marker") == 0 &&
2458 19 : strcmp(CPLGetXMLValue(psMarker, "name", ""), "COD") == 0)
2459 : {
2460 96 : for (const CPLXMLNode *psField = psMarker->psChild; psField;
2461 88 : psField = psField->psNext)
2462 : {
2463 264 : if (psField->eType == CXT_Element &&
2464 168 : strcmp(psField->pszValue, "Field") == 0 &&
2465 72 : strcmp(CPLGetXMLValue(psField, "name", ""),
2466 : "SPcod_transformation") == 0)
2467 : {
2468 : pszTransformation =
2469 8 : CPLGetXMLValue(psField, nullptr, nullptr);
2470 8 : break;
2471 : }
2472 : }
2473 : }
2474 37 : else if (psMarker->eType == CXT_Element &&
2475 24 : strcmp(psMarker->pszValue, "Marker") == 0 &&
2476 11 : strcmp(CPLGetXMLValue(psMarker, "name", ""), "COM") == 0)
2477 : {
2478 55 : for (const CPLXMLNode *psField = psMarker->psChild; psField;
2479 44 : psField = psField->psNext)
2480 : {
2481 132 : if (psField->eType == CXT_Element &&
2482 77 : strcmp(psField->pszValue, "Field") == 0 &&
2483 22 : strcmp(CPLGetXMLValue(psField, "name", ""), "COM") == 0)
2484 : {
2485 11 : pszCOM = CPLGetXMLValue(psField, nullptr, nullptr);
2486 11 : break;
2487 : }
2488 : }
2489 : }
2490 : }
2491 :
2492 8 : if (pszTransformation != nullptr &&
2493 8 : strcmp(pszTransformation, "0") ==
2494 : 0) // 0 = 9x7 irreversible wavelet
2495 : {
2496 2 : pszReversibility = "LOSSY";
2497 : }
2498 6 : else if (pszTransformation != nullptr &&
2499 6 : strcmp(pszTransformation, "1") ==
2500 : 0) // 1 = 5x3 reversible wavelet
2501 : {
2502 : // 5x3 wavelet by itself doesn't guarantee full lossless mode
2503 : // if quality layers are discarded. hence the "possibly"
2504 6 : pszReversibility = "LOSSLESS (possibly)";
2505 :
2506 6 : if (pszCOM &&
2507 6 : STARTS_WITH(
2508 : pszCOM,
2509 : "Kdu-Layer-Info: "
2510 : "log_2{Delta-D(squared-error)/Delta-L(bytes)}, L(bytes)"))
2511 : {
2512 0 : if (strstr(pszCOM, "-192.0,") != nullptr)
2513 : {
2514 : // Not really sure to understand this fully, but
2515 : // experimentaly I've found that if the last row in the
2516 : // Kdu-Layer-Info includes a line starting with "-192.0", it
2517 : // means that the last layer includes everything to be
2518 : // lossless.
2519 0 : pszReversibility = "LOSSLESS";
2520 : }
2521 : else
2522 : {
2523 0 : pszReversibility = "LOSSY";
2524 : }
2525 : }
2526 : // Kakadu < 6.4
2527 6 : else if (pszCOM &&
2528 6 : STARTS_WITH(
2529 : pszCOM,
2530 : "Kdu-Layer-Info: "
2531 : "log_2{Delta-D(MSE)/[2^16*Delta-L(bytes)]}, L(bytes)"))
2532 : {
2533 2 : if (strstr(pszCOM, "-256.0,") != nullptr)
2534 : {
2535 : // Not really sure to understand this fully, but
2536 : // experimentaly I've found that if the last row in the
2537 : // Kdu-Layer-Info includes a line starting with "-256.0", it
2538 : // means that the last layer includes everything to be
2539 : // lossless.
2540 2 : pszReversibility = "LOSSLESS";
2541 : }
2542 : else
2543 : {
2544 0 : pszReversibility = "LOSSY";
2545 : }
2546 : }
2547 4 : else if (pszCOM && STARTS_WITH(pszCOM, "Created by OpenJPEG"))
2548 : {
2549 : // Starting with GDAL 3.6, the JP2OpenJPEG driver will write
2550 : // if the encoding parameters are lossless/lossy (for 5x3
2551 : // wavelets)
2552 4 : if (strstr(pszCOM, "LOSSLESS settings used"))
2553 : {
2554 3 : pszReversibility = "LOSSLESS";
2555 : }
2556 1 : else if (strstr(pszCOM, "LOSSY settings used"))
2557 : {
2558 1 : pszReversibility = "LOSSY";
2559 : }
2560 : }
2561 : }
2562 : }
2563 8 : CPLDestroyXMLNode(psRes);
2564 8 : return pszReversibility;
2565 : }
2566 :
2567 : #endif /* #ifndef DOXYGEN_SKIP */
|