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