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
GetLastChild(CPLXMLNode * psParent)50 static CPLXMLNode* GetLastChild(CPLXMLNode* psParent)
51 {
52 CPLXMLNode* psChild = psParent->psChild;
53 while( psChild && psChild->psNext )
54 psChild = psChild->psNext;
55 return psChild;
56 }
57
AddElement(CPLXMLNode * psParent,CPLXMLNode * & psLastChild,CPLXMLNode * psNewElt)58 static void AddElement(CPLXMLNode* psParent,
59 CPLXMLNode*& psLastChild,
60 CPLXMLNode* psNewElt)
61 {
62 if( psLastChild == nullptr )
63 psLastChild = GetLastChild(psParent);
64 if( psLastChild == nullptr )
65 psParent->psChild = psNewElt;
66 else
67 psLastChild->psNext = psNewElt;
68 psLastChild = psNewElt;
69 }
70
AddField(CPLXMLNode * psParent,CPLXMLNode * & psLastChild,const char * pszFieldName,int nFieldSize,const char * pszValue,const char * pszDescription=nullptr)71 static void AddField(CPLXMLNode* psParent,
72 CPLXMLNode*& psLastChild,
73 const char* pszFieldName,
74 int nFieldSize, const char* pszValue,
75 const char* pszDescription = nullptr)
76 {
77 CPLXMLNode* psField = CPLCreateXMLElementAndValue(
78 nullptr, "Field", pszValue );
79 CPLAddXMLAttributeAndValue(psField, "name", pszFieldName );
80 CPLAddXMLAttributeAndValue(psField, "type", "string" );
81 CPLAddXMLAttributeAndValue(psField, "size", CPLSPrintf("%d", nFieldSize ) );
82 if( pszDescription )
83 CPLAddXMLAttributeAndValue(psField, "description", pszDescription );
84 AddElement(psParent, psLastChild, psField);
85 }
86
AddHexField(CPLXMLNode * psParent,CPLXMLNode * & psLastChild,const char * pszFieldName,int nFieldSize,const char * pszValue,const char * pszDescription=nullptr)87 static void AddHexField(CPLXMLNode* psParent,
88 CPLXMLNode*& psLastChild,
89 const char* pszFieldName,
90 int nFieldSize, const char* pszValue,
91 const char* pszDescription = nullptr)
92 {
93 CPLXMLNode* psField = CPLCreateXMLElementAndValue(
94 nullptr, "Field", pszValue );
95 CPLAddXMLAttributeAndValue(psField, "name", pszFieldName );
96 CPLAddXMLAttributeAndValue(psField, "type", "hexint" );
97 CPLAddXMLAttributeAndValue(psField, "size", CPLSPrintf("%d", nFieldSize ) );
98 if( pszDescription )
99 CPLAddXMLAttributeAndValue(psField, "description", pszDescription );
100 AddElement(psParent, psLastChild, psField);
101 }
102
AddField(CPLXMLNode * psParent,CPLXMLNode * & psLastChild,const char * pszFieldName,GByte nVal,const char * pszDescription=nullptr)103 static void AddField(CPLXMLNode* psParent,
104 CPLXMLNode*& psLastChild,
105 const char* pszFieldName, GByte nVal,
106 const char* pszDescription = nullptr)
107 {
108 CPLXMLNode* psField = CPLCreateXMLElementAndValue(
109 nullptr, "Field", CPLSPrintf("%d", nVal) );
110 CPLAddXMLAttributeAndValue(psField, "name", pszFieldName );
111 CPLAddXMLAttributeAndValue(psField, "type", "uint8" );
112 if( pszDescription )
113 CPLAddXMLAttributeAndValue(psField, "description", pszDescription );
114 AddElement(psParent, psLastChild, psField);
115 }
116
AddField(CPLXMLNode * psParent,CPLXMLNode * & psLastChild,const char * pszFieldName,GUInt16 nVal,const char * pszDescription=nullptr)117 static void AddField(CPLXMLNode* psParent,
118 CPLXMLNode*& psLastChild,
119 const char* pszFieldName, GUInt16 nVal,
120 const char* pszDescription = nullptr)
121 {
122 CPLXMLNode* psField = CPLCreateXMLElementAndValue(
123 nullptr, "Field", CPLSPrintf("%d", nVal) );
124 CPLAddXMLAttributeAndValue(psField, "name", pszFieldName );
125 CPLAddXMLAttributeAndValue(psField, "type", "uint16" );
126 if( pszDescription )
127 CPLAddXMLAttributeAndValue(psField, "description", pszDescription );
128 AddElement(psParent, psLastChild, psField);
129 }
130
AddField(CPLXMLNode * psParent,CPLXMLNode * & psLastChild,const char * pszFieldName,GUInt32 nVal,const char * pszDescription=nullptr)131 static void AddField(CPLXMLNode* psParent,
132 CPLXMLNode*& psLastChild,
133 const char* pszFieldName, GUInt32 nVal,
134 const char* pszDescription = nullptr)
135 {
136 CPLXMLNode* psField = CPLCreateXMLElementAndValue(
137 nullptr, "Field", CPLSPrintf("%u", nVal) );
138 CPLAddXMLAttributeAndValue(psField, "name", pszFieldName );
139 CPLAddXMLAttributeAndValue(psField, "type", "uint32" );
140 if( pszDescription )
141 CPLAddXMLAttributeAndValue(psField, "description", pszDescription );
142 AddElement(psParent, psLastChild, psField);
143 }
144
GetInterpretationOfBPC(GByte bpc)145 static const char* GetInterpretationOfBPC(GByte bpc)
146 {
147 if( bpc == 255 )
148 return nullptr;
149 if( (bpc & 0x80) )
150 return CPLSPrintf("Signed %d bits", 1 + (bpc & 0x7F));
151 else
152 return CPLSPrintf("Unsigned %d bits", 1 + bpc);
153 }
154
GetStandardFieldString(GUInt16 nVal)155 static const char* GetStandardFieldString(GUInt16 nVal)
156 {
157 switch(nVal)
158 {
159 case 1: return "Codestream contains no extensions";
160 case 2: return "Contains multiple composition layers";
161 case 3: return "Codestream is compressed using JPEG 2000 and requires at least a Profile 0 decoder";
162 case 4: return "Codestream is compressed using JPEG 2000 and requires at least a Profile 1 decoder";
163 case 5: return "Codestream is compressed using JPEG 2000 unrestricted";
164 case 35: return "Contains IPR metadata";
165 case 67: return "Contains GMLJP2 metadata";
166 default: return nullptr;
167 }
168 }
169
DumpGeoTIFFBox(CPLXMLNode * psBox,GDALJP2Box & oBox)170 static void DumpGeoTIFFBox(CPLXMLNode* psBox,
171 GDALJP2Box& oBox)
172 {
173 GIntBig nBoxDataLength = oBox.GetDataLength();
174 GByte* pabyBoxData = oBox.ReadBoxData();
175 GDALDriver* poVRTDriver = static_cast<GDALDriver*>(GDALGetDriverByName("VRT"));
176 if( pabyBoxData && poVRTDriver)
177 {
178 CPLString osTmpFilename(CPLSPrintf("/vsimem/tmp_%p.tif", oBox.GetFILE()));
179 CPL_IGNORE_RET_VAL(VSIFCloseL(VSIFileFromMemBuffer(
180 osTmpFilename, pabyBoxData, nBoxDataLength, FALSE) ));
181 CPLPushErrorHandler(CPLQuietErrorHandler);
182 GDALDataset* poDS = GDALDataset::FromHandle(GDALOpen(osTmpFilename, GA_ReadOnly));
183 CPLPopErrorHandler();
184 // Reject GeoJP2 boxes with a TIFF with band_count > 1.
185 if( poDS && poDS->GetRasterCount() > 1 )
186 {
187 GDALClose(poDS);
188 poDS = nullptr;
189 }
190 if( poDS )
191 {
192 CPLString osTmpVRTFilename(CPLSPrintf("/vsimem/tmp_%p.vrt", oBox.GetFILE()));
193 GDALDataset* poVRTDS = poVRTDriver->CreateCopy(osTmpVRTFilename, poDS, FALSE, nullptr, nullptr, nullptr);
194 GDALClose(poVRTDS);
195 GByte* pabyXML = VSIGetMemFileBuffer( osTmpVRTFilename, nullptr, FALSE );
196 CPLXMLNode* psXMLVRT = CPLParseXMLString(reinterpret_cast<const char*>(pabyXML));
197 if( psXMLVRT )
198 {
199 CPLXMLNode* psXMLContentNode =
200 CPLCreateXMLNode( psBox, CXT_Element, "DecodedGeoTIFF" );
201 psXMLContentNode->psChild = psXMLVRT;
202 CPLXMLNode* psPrev = nullptr;
203 for(CPLXMLNode* psIter = psXMLVRT->psChild; psIter; psIter = psIter->psNext)
204 {
205 if( psIter->eType == CXT_Element &&
206 strcmp(psIter->pszValue, "VRTRasterBand") == 0 )
207 {
208 CPLXMLNode* psNext = psIter->psNext;
209 psIter->psNext = nullptr;
210 CPLDestroyXMLNode(psIter);
211 if( psPrev )
212 psPrev->psNext = psNext;
213 else
214 break;
215 psIter = psPrev;
216 }
217 psPrev = psIter;
218 }
219 CPLCreateXMLNode(psXMLVRT, CXT_Element, "VRTRasterBand");
220 }
221
222 VSIUnlink(osTmpVRTFilename);
223 GDALClose(poDS);
224 }
225 VSIUnlink(osTmpFilename);
226 }
227 CPLFree(pabyBoxData);
228 }
229
DumpFTYPBox(CPLXMLNode * psBox,GDALJP2Box & oBox)230 static void DumpFTYPBox(CPLXMLNode* psBox, GDALJP2Box& oBox)
231 {
232 GIntBig nBoxDataLength = oBox.GetDataLength();
233 GByte* pabyBoxData = oBox.ReadBoxData();
234 if( pabyBoxData )
235 {
236 CPLXMLNode* psDecodedContent =
237 CPLCreateXMLNode( psBox, CXT_Element, "DecodedContent" );
238 GIntBig nRemainingLength = nBoxDataLength;
239 GByte* pabyIter = pabyBoxData;
240 CPLXMLNode* psLastChild = nullptr;
241 if( nRemainingLength >= 4 )
242 {
243 char szBranding[5];
244 memcpy(szBranding, pabyIter, 4);
245 szBranding[4] = 0;
246 AddField(psDecodedContent, psLastChild, "BR", 4, szBranding);
247 pabyIter += 4;
248 nRemainingLength -= 4;
249 }
250 if( nRemainingLength >= 4 )
251 {
252 GUInt32 nVal;
253 memcpy(&nVal, pabyIter, 4);
254 CPL_MSBPTR32(&nVal);
255 AddField(psDecodedContent, psLastChild, "MinV", nVal);
256 pabyIter += 4;
257 nRemainingLength -= 4;
258 }
259 int nCLIndex = 0;
260 while( nRemainingLength >= 4 )
261 {
262 char szBranding[5];
263 memcpy(szBranding, pabyIter, 4);
264 szBranding[4] = 0;
265 AddField(psDecodedContent,
266 psLastChild,
267 CPLSPrintf("CL%d", nCLIndex),
268 4, szBranding);
269 pabyIter += 4;
270 nRemainingLength -= 4;
271 nCLIndex ++;
272 }
273 if( nRemainingLength > 0 )
274 AddElement( psDecodedContent, psLastChild,
275 CPLCreateXMLElementAndValue(
276 nullptr, "RemainingBytes",
277 CPLSPrintf("%d", static_cast<int>(nRemainingLength) )));
278 }
279 CPLFree(pabyBoxData);
280 }
281
DumpIHDRBox(CPLXMLNode * psBox,GDALJP2Box & oBox)282 static void DumpIHDRBox(CPLXMLNode* psBox, GDALJP2Box& oBox)
283 {
284 GIntBig nBoxDataLength = oBox.GetDataLength();
285 GByte* pabyBoxData = oBox.ReadBoxData();
286 if( pabyBoxData )
287 {
288 CPLXMLNode* psDecodedContent =
289 CPLCreateXMLNode( psBox, CXT_Element, "DecodedContent" );
290 GIntBig nRemainingLength = nBoxDataLength;
291 GByte* pabyIter = pabyBoxData;
292 CPLXMLNode* psLastChild = nullptr;
293 if( nRemainingLength >= 4 )
294 {
295 GUInt32 nVal;
296 memcpy(&nVal, pabyIter, 4);
297 CPL_MSBPTR32(&nVal);
298 AddField(psDecodedContent, psLastChild, "HEIGHT", nVal);
299 pabyIter += 4;
300 nRemainingLength -= 4;
301 }
302 if( nRemainingLength >= 4 )
303 {
304 GUInt32 nVal;
305 memcpy(&nVal, pabyIter, 4);
306 CPL_MSBPTR32(&nVal);
307 AddField(psDecodedContent, psLastChild, "WIDTH", nVal);
308 pabyIter += 4;
309 nRemainingLength -= 4;
310 }
311 if( nRemainingLength >= 2 )
312 {
313 GUInt16 nVal;
314 memcpy(&nVal, pabyIter, 2);
315 CPL_MSBPTR16(&nVal);
316 AddField(psDecodedContent, psLastChild, "NC", nVal);
317 pabyIter += 2;
318 nRemainingLength -= 2;
319 }
320 if( nRemainingLength >= 1 )
321 {
322 AddField(psDecodedContent, psLastChild, "BPC", *pabyIter,
323 GetInterpretationOfBPC(*pabyIter));
324 pabyIter += 1;
325 nRemainingLength -= 1;
326 }
327 if( nRemainingLength >= 1 )
328 {
329 AddField(psDecodedContent, psLastChild, "C", *pabyIter);
330 pabyIter += 1;
331 nRemainingLength -= 1;
332 }
333 if( nRemainingLength >= 1 )
334 {
335 AddField(psDecodedContent, psLastChild, "UnkC", *pabyIter);
336 pabyIter += 1;
337 nRemainingLength -= 1;
338 }
339 if( nRemainingLength >= 1 )
340 {
341 AddField(psDecodedContent, psLastChild, "IPR", *pabyIter);
342 /*pabyIter += 1;*/
343 nRemainingLength -= 1;
344 }
345 if( nRemainingLength > 0 )
346 AddElement( psDecodedContent, psLastChild,
347 CPLCreateXMLElementAndValue(
348 nullptr, "RemainingBytes",
349 CPLSPrintf("%d", static_cast<int>(nRemainingLength) )));
350 }
351 CPLFree(pabyBoxData);
352 }
353
DumpBPCCBox(CPLXMLNode * psBox,GDALJP2Box & oBox)354 static void DumpBPCCBox(CPLXMLNode* psBox, GDALJP2Box& oBox)
355 {
356 GIntBig nBoxDataLength = oBox.GetDataLength();
357 GByte* pabyBoxData = oBox.ReadBoxData();
358 if( pabyBoxData )
359 {
360 CPLXMLNode* psDecodedContent =
361 CPLCreateXMLNode( psBox, CXT_Element, "DecodedContent" );
362 GIntBig nRemainingLength = nBoxDataLength;
363 GByte* pabyIter = pabyBoxData;
364 int nBPCIndex = 0;
365 CPLXMLNode* psLastChild = nullptr;
366 while( nRemainingLength >= 1 && nBPCIndex < knbMaxJPEG2000Components )
367 {
368 AddField(psDecodedContent,
369 psLastChild,
370 CPLSPrintf("BPC%d", nBPCIndex),
371 *pabyIter,
372 GetInterpretationOfBPC(*pabyIter));
373 nBPCIndex ++;
374 pabyIter += 1;
375 nRemainingLength -= 1;
376 }
377 if( nRemainingLength > 0 )
378 AddElement( psDecodedContent, psLastChild,
379 CPLCreateXMLElementAndValue(
380 nullptr, "RemainingBytes",
381 CPLSPrintf("%d", static_cast<int>(nRemainingLength) )));
382 }
383 CPLFree(pabyBoxData);
384 }
385
DumpCOLRBox(CPLXMLNode * psBox,GDALJP2Box & oBox)386 static void DumpCOLRBox(CPLXMLNode* psBox, GDALJP2Box& oBox)
387 {
388 GIntBig nBoxDataLength = oBox.GetDataLength();
389 GByte* pabyBoxData = oBox.ReadBoxData();
390 if( pabyBoxData )
391 {
392 CPLXMLNode* psDecodedContent =
393 CPLCreateXMLNode( psBox, CXT_Element, "DecodedContent" );
394 GIntBig nRemainingLength = nBoxDataLength;
395 GByte* pabyIter = pabyBoxData;
396 GByte nMeth;
397 CPLXMLNode* psLastChild = nullptr;
398 if( nRemainingLength >= 1 )
399 {
400 nMeth = *pabyIter;
401 AddField(psDecodedContent, psLastChild, "METH", nMeth,
402 (nMeth == 0) ? "Enumerated Colourspace":
403 (nMeth == 1) ? "Restricted ICC profile": nullptr);
404 pabyIter += 1;
405 nRemainingLength -= 1;
406 }
407 if( nRemainingLength >= 1 )
408 {
409 AddField(psDecodedContent, psLastChild, "PREC", *pabyIter);
410 pabyIter += 1;
411 nRemainingLength -= 1;
412 }
413 if( nRemainingLength >= 1 )
414 {
415 AddField(psDecodedContent, psLastChild, "APPROX", *pabyIter);
416 pabyIter += 1;
417 nRemainingLength -= 1;
418 }
419 if( nRemainingLength >= 4 )
420 {
421 GUInt32 nVal;
422 memcpy(&nVal, pabyIter, 4);
423 CPL_MSBPTR32(&nVal);
424 AddField(psDecodedContent, psLastChild, "EnumCS", nVal,
425 (nVal == 16) ? "sRGB" :
426 (nVal == 17) ? "greyscale":
427 (nVal == 18) ? "sYCC" : nullptr);
428 /*pabyIter += 4;*/
429 nRemainingLength -= 4;
430 }
431 if( nRemainingLength > 0 )
432 AddElement(psDecodedContent, psLastChild,
433 CPLCreateXMLElementAndValue(
434 nullptr, "RemainingBytes",
435 CPLSPrintf("%d", static_cast<int>(nRemainingLength) )));
436 }
437 CPLFree(pabyBoxData);
438 }
439
DumpPCLRBox(CPLXMLNode * psBox,GDALJP2Box & oBox)440 static void DumpPCLRBox(CPLXMLNode* psBox, GDALJP2Box& oBox)
441 {
442 GIntBig nBoxDataLength = oBox.GetDataLength();
443 GByte* pabyBoxData = oBox.ReadBoxData();
444 if( pabyBoxData )
445 {
446 CPLXMLNode* psDecodedContent =
447 CPLCreateXMLNode( psBox, CXT_Element, "DecodedContent" );
448 GIntBig nRemainingLength = nBoxDataLength;
449 GByte* pabyIter = pabyBoxData;
450 GUInt16 NE = 0;
451 CPLXMLNode* psLastChild = nullptr;
452 if( nRemainingLength >= 2 )
453 {
454 GUInt16 nVal;
455 memcpy(&nVal, pabyIter, 2);
456 CPL_MSBPTR16(&nVal);
457 NE = nVal;
458 AddField(psDecodedContent, psLastChild, "NE", nVal);
459 pabyIter += 2;
460 nRemainingLength -= 2;
461 }
462 GByte NPC = 0;
463 if( nRemainingLength >= 1 )
464 {
465 NPC = *pabyIter;
466 AddField(psDecodedContent, psLastChild, "NPC", NPC);
467 pabyIter += 1;
468 nRemainingLength -= 1;
469 }
470 int b8BitOnly = TRUE;
471 for(int i=0;i<NPC;i++)
472 {
473 if( nRemainingLength >= 1 )
474 {
475 b8BitOnly &= (*pabyIter <= 7);
476 AddField(psDecodedContent, psLastChild,
477 CPLSPrintf("B%d", i),
478 *pabyIter,
479 GetInterpretationOfBPC(*pabyIter));
480 pabyIter += 1;
481 nRemainingLength -= 1;
482 }
483 }
484 if( b8BitOnly )
485 {
486 for(int j=0;j<NE;j++)
487 {
488 for(int i=0;i<NPC;i++)
489 {
490 if( nRemainingLength >= 1 )
491 {
492 AddField(psDecodedContent, psLastChild,
493 CPLSPrintf("C_%d_%d", j, i),
494 *pabyIter);
495 pabyIter += 1;
496 nRemainingLength -= 1;
497 }
498 }
499 }
500 }
501 if( nRemainingLength > 0 )
502 AddElement( psDecodedContent, psLastChild,
503 CPLCreateXMLElementAndValue(
504 nullptr, "RemainingBytes",
505 CPLSPrintf("%d", static_cast<int>(nRemainingLength) )));
506 }
507 CPLFree(pabyBoxData);
508 }
509
DumpCMAPBox(CPLXMLNode * psBox,GDALJP2Box & oBox)510 static void DumpCMAPBox(CPLXMLNode* psBox, GDALJP2Box& oBox)
511 {
512 GIntBig nBoxDataLength = oBox.GetDataLength();
513 GByte* pabyBoxData = oBox.ReadBoxData();
514 if( pabyBoxData )
515 {
516 CPLXMLNode* psDecodedContent =
517 CPLCreateXMLNode( psBox, CXT_Element, "DecodedContent" );
518 GIntBig nRemainingLength = nBoxDataLength;
519 GByte* pabyIter = pabyBoxData;
520 int nIndex = 0;
521 CPLXMLNode* psLastChild = nullptr;
522 while( nRemainingLength >= 2 + 1 + 1 && nIndex < knbMaxJPEG2000Components )
523 {
524 GUInt16 nVal;
525 memcpy(&nVal, pabyIter, 2);
526 CPL_MSBPTR16(&nVal);
527 AddField(psDecodedContent, psLastChild,
528 CPLSPrintf("CMP%d", nIndex),
529 nVal);
530 pabyIter += 2;
531 nRemainingLength -= 2;
532
533 AddField(psDecodedContent, psLastChild,
534 CPLSPrintf("MTYP%d", nIndex),
535 *pabyIter,
536 (*pabyIter == 0) ? "Direct use":
537 (*pabyIter == 1) ? "Palette mapping": nullptr);
538 pabyIter += 1;
539 nRemainingLength -= 1;
540
541 AddField(psDecodedContent, psLastChild,
542 CPLSPrintf("PCOL%d", nIndex),
543 *pabyIter);
544 pabyIter += 1;
545 nRemainingLength -= 1;
546
547 nIndex ++;
548 }
549 if( nRemainingLength > 0 )
550 AddElement( psDecodedContent, psLastChild,
551 CPLCreateXMLElementAndValue(
552 nullptr, "RemainingBytes",
553 CPLSPrintf("%d", static_cast<int>(nRemainingLength) )));
554 }
555 CPLFree(pabyBoxData);
556 }
557
DumpCDEFBox(CPLXMLNode * psBox,GDALJP2Box & oBox)558 static void DumpCDEFBox(CPLXMLNode* psBox, GDALJP2Box& oBox)
559 {
560 GIntBig nBoxDataLength = oBox.GetDataLength();
561 GByte* pabyBoxData = oBox.ReadBoxData();
562 if( pabyBoxData )
563 {
564 CPLXMLNode* psDecodedContent =
565 CPLCreateXMLNode( psBox, CXT_Element, "DecodedContent" );
566 GIntBig nRemainingLength = nBoxDataLength;
567 GByte* pabyIter = pabyBoxData;
568 GUInt16 nChannels = 0;
569 CPLXMLNode* psLastChild = nullptr;
570 if( nRemainingLength >= 2 )
571 {
572 GUInt16 nVal;
573 memcpy(&nVal, pabyIter, 2);
574 nChannels = nVal;
575 CPL_MSBPTR16(&nVal);
576 AddField(psDecodedContent, psLastChild, "N", nVal);
577 pabyIter += 2;
578 nRemainingLength -= 2;
579 }
580 for( int i=0; i < nChannels; i++ )
581 {
582 if( nRemainingLength >= 2 )
583 {
584 GUInt16 nVal;
585 memcpy(&nVal, pabyIter, 2);
586 CPL_MSBPTR16(&nVal);
587 AddField(psDecodedContent, psLastChild,
588 CPLSPrintf("Cn%d", i),
589 nVal);
590 pabyIter += 2;
591 nRemainingLength -= 2;
592 }
593 if( nRemainingLength >= 2 )
594 {
595 GUInt16 nVal;
596 memcpy(&nVal, pabyIter, 2);
597 CPL_MSBPTR16(&nVal);
598 AddField(psDecodedContent, psLastChild,
599 CPLSPrintf("Typ%d", i),
600 nVal,
601 (nVal == 0) ? "Colour channel":
602 (nVal == 1) ? "Opacity channel":
603 (nVal == 2) ? "Premultiplied opacity":
604 (nVal == 65535) ? "Not specified" : nullptr);
605 pabyIter += 2;
606 nRemainingLength -= 2;
607 }
608 if( nRemainingLength >= 2 )
609 {
610 GUInt16 nVal;
611 memcpy(&nVal, pabyIter, 2);
612 CPL_MSBPTR16(&nVal);
613 AddField(psDecodedContent, psLastChild,
614 CPLSPrintf("Asoc%d", i),
615 nVal,
616 (nVal == 0) ? "Associated to the whole image":
617 (nVal == 65535) ? "Not associated with a particular colour":
618 "Associated with a particular colour");
619 pabyIter += 2;
620 nRemainingLength -= 2;
621 }
622 }
623 if( nRemainingLength > 0 )
624 AddElement( psDecodedContent, psLastChild,
625 CPLCreateXMLElementAndValue(
626 nullptr, "RemainingBytes",
627 CPLSPrintf("%d", static_cast<int>(nRemainingLength) )));
628 }
629 CPLFree(pabyBoxData);
630 }
631
DumpRESxBox(CPLXMLNode * psBox,GDALJP2Box & oBox)632 static void DumpRESxBox(CPLXMLNode* psBox, GDALJP2Box& oBox)
633 {
634 GIntBig nBoxDataLength = oBox.GetDataLength();
635 GByte* pabyBoxData = oBox.ReadBoxData();
636 char chC = oBox.GetType()[3];
637 if( pabyBoxData )
638 {
639 CPLXMLNode* psDecodedContent =
640 CPLCreateXMLNode( psBox, CXT_Element, "DecodedContent" );
641 GIntBig nRemainingLength = nBoxDataLength;
642 GByte* pabyIter = pabyBoxData;
643 GUInt16 nNumV = 0;
644 GUInt16 nNumH = 0;
645 GUInt16 nDenomV = 1;
646 GUInt16 nDenomH = 1;
647 GUInt16 nExpV = 0;
648 GUInt16 nExpH = 0;
649 CPLXMLNode* psLastChild = nullptr;
650 if( nRemainingLength >= 2 )
651 {
652 GUInt16 nVal;
653 memcpy(&nVal, pabyIter, 2);
654 CPL_MSBPTR16(&nVal);
655 nNumV = nVal;
656 AddField(psDecodedContent, psLastChild, CPLSPrintf("VR%cN", chC), nVal);
657 pabyIter += 2;
658 nRemainingLength -= 2;
659 }
660 if( nRemainingLength >= 2 )
661 {
662 GUInt16 nVal;
663 memcpy(&nVal, pabyIter, 2);
664 CPL_MSBPTR16(&nVal);
665 nDenomV = nVal;
666 AddField(psDecodedContent, psLastChild, CPLSPrintf("VR%cD", chC), nVal);
667 pabyIter += 2;
668 nRemainingLength -= 2;
669 }
670 if( nRemainingLength >= 2 )
671 {
672 GUInt16 nVal;
673 memcpy(&nVal, pabyIter, 2);
674 CPL_MSBPTR16(&nVal);
675 nNumH = nVal;
676 AddField(psDecodedContent, psLastChild, CPLSPrintf("HR%cN", chC), nVal);
677 pabyIter += 2;
678 nRemainingLength -= 2;
679 }
680 if( nRemainingLength >= 2 )
681 {
682 GUInt16 nVal;
683 memcpy(&nVal, pabyIter, 2);
684 CPL_MSBPTR16(&nVal);
685 nDenomH = nVal;
686 AddField(psDecodedContent, psLastChild, CPLSPrintf("HR%cD", chC), nVal);
687 pabyIter += 2;
688 nRemainingLength -= 2;
689 }
690 if( nRemainingLength >= 1 )
691 {
692 AddField(psDecodedContent, psLastChild, CPLSPrintf("VR%cE", chC), *pabyIter);
693 nExpV = *pabyIter;
694 pabyIter += 1;
695 nRemainingLength -= 1;
696 }
697 if( nRemainingLength >= 1 )
698 {
699 AddField(psDecodedContent, psLastChild, CPLSPrintf("HR%cE", chC), *pabyIter);
700 nExpH = *pabyIter;
701 /*pabyIter += 1;*/
702 nRemainingLength -= 1;
703 }
704 if( nRemainingLength == 0 )
705 {
706 const char* pszVRes =
707 (nDenomV == 0) ? "invalid" :
708 CPLSPrintf("%.03f", 1.0 * nNumV / nDenomV * pow(10.0, nExpV));
709 AddElement(psDecodedContent, psLastChild,
710 CPLCreateXMLElementAndValue( nullptr, "VRes", pszVRes ));
711 const char* pszHRes =
712 (nDenomH == 0) ? "invalid" :
713 CPLSPrintf("%.03f", 1.0 * nNumH / nDenomH * pow(10.0, nExpH));
714 AddElement(psDecodedContent, psLastChild,
715 CPLCreateXMLElementAndValue( nullptr, "HRes", pszHRes ));
716 }
717 else if( nRemainingLength > 0 )
718 AddElement(psDecodedContent, psLastChild,
719 CPLCreateXMLElementAndValue(
720 nullptr, "RemainingBytes",
721 CPLSPrintf("%d", static_cast<int>(nRemainingLength) )));
722 }
723 CPLFree(pabyBoxData);
724 }
725
DumpRREQBox(CPLXMLNode * psBox,GDALJP2Box & oBox)726 static void DumpRREQBox(CPLXMLNode* psBox, GDALJP2Box& oBox)
727 {
728 GIntBig nBoxDataLength = oBox.GetDataLength();
729 GByte* pabyBoxData = oBox.ReadBoxData();
730 if( pabyBoxData )
731 {
732 CPLXMLNode* psDecodedContent =
733 CPLCreateXMLNode( psBox, CXT_Element, "DecodedContent" );
734 GIntBig nRemainingLength = nBoxDataLength;
735 GByte* pabyIter = pabyBoxData;
736 GByte ML = 0;
737 CPLXMLNode* psLastChild = nullptr;
738 if( nRemainingLength >= 1 )
739 {
740 ML = *pabyIter;
741 AddField(psDecodedContent, psLastChild, "ML", *pabyIter);
742 pabyIter += 1;
743 nRemainingLength -= 1;
744 }
745 if( nRemainingLength >= ML )
746 {
747 CPLString osHex("0x");
748 for(int i=0;i<ML;i++)
749 {
750 osHex += CPLSPrintf("%02X", *pabyIter);
751 pabyIter += 1;
752 nRemainingLength -= 1;
753 }
754 AddHexField(psDecodedContent, psLastChild, "FUAM", static_cast<int>(ML), osHex.c_str());
755 }
756 if( nRemainingLength >= ML )
757 {
758 CPLString osHex("0x");
759 for(int i=0;i<ML;i++)
760 {
761 osHex += CPLSPrintf("%02X", *pabyIter);
762 pabyIter += 1;
763 nRemainingLength -= 1;
764 }
765 AddHexField(psDecodedContent, psLastChild, "DCM", static_cast<int>(ML), osHex.c_str());
766 }
767 GUInt16 NSF = 0;
768 if( nRemainingLength >= 2 )
769 {
770 GUInt16 nVal;
771 memcpy(&nVal, pabyIter, 2);
772 CPL_MSBPTR16(&nVal);
773 NSF = nVal;
774 AddField(psDecodedContent, psLastChild, "NSF", nVal);
775 pabyIter += 2;
776 nRemainingLength -= 2;
777 }
778 for(int iNSF=0;iNSF<NSF;iNSF++)
779 {
780 if( nRemainingLength >= 2 )
781 {
782 GUInt16 nVal;
783 memcpy(&nVal, pabyIter, 2);
784 CPL_MSBPTR16(&nVal);
785 AddField(psDecodedContent, psLastChild,
786 CPLSPrintf("SF%d", iNSF), nVal,
787 GetStandardFieldString(nVal));
788 pabyIter += 2;
789 nRemainingLength -= 2;
790 }
791 else
792 break;
793 if( nRemainingLength >= ML )
794 {
795 CPLString osHex("0x");
796 for(int i=0;i<ML;i++)
797 {
798 osHex += CPLSPrintf("%02X", *pabyIter);
799 pabyIter += 1;
800 nRemainingLength -= 1;
801 }
802 AddHexField(psDecodedContent, psLastChild,
803 CPLSPrintf("SM%d", iNSF),
804 static_cast<int>(ML), osHex.c_str());
805 }
806 else
807 break;
808 }
809 GUInt16 NVF = 0;
810 if( nRemainingLength >= 2 )
811 {
812 GUInt16 nVal;
813 memcpy(&nVal, pabyIter, 2);
814 CPL_MSBPTR16(&nVal);
815 NVF = nVal;
816 AddField(psDecodedContent, psLastChild, "NVF", nVal);
817 pabyIter += 2;
818 nRemainingLength -= 2;
819 }
820 for(int iNVF=0;iNVF<NVF;iNVF++)
821 {
822 if( nRemainingLength >= 16 )
823 {
824 CPLString osHex("0x");
825 for(int i=0;i<16;i++)
826 {
827 osHex += CPLSPrintf("%02X", *pabyIter);
828 pabyIter += 1;
829 nRemainingLength -= 1;
830 }
831 AddHexField(psDecodedContent, psLastChild,
832 CPLSPrintf("VF%d", iNVF),
833 static_cast<int>(ML), osHex.c_str());
834 }
835 else
836 break;
837 if( nRemainingLength >= ML )
838 {
839 CPLString osHex("0x");
840 for(int i=0;i<ML;i++)
841 {
842 osHex += CPLSPrintf("%02X", *pabyIter);
843 pabyIter += 1;
844 nRemainingLength -= 1;
845 }
846 AddHexField(psDecodedContent, psLastChild,
847 CPLSPrintf("VM%d", iNVF),
848 static_cast<int>(ML), osHex.c_str());
849 }
850 else
851 break;
852 }
853 if( nRemainingLength > 0 )
854 AddElement( psDecodedContent, psLastChild,
855 CPLCreateXMLElementAndValue(
856 nullptr, "RemainingBytes",
857 CPLSPrintf("%d", static_cast<int>(nRemainingLength) )));
858 }
859 CPLFree(pabyBoxData);
860 }
861
CreateMarker(CPLXMLNode * psCSBox,CPLXMLNode * & psLastChildCSBox,const char * pszName,GIntBig nOffset,GIntBig nLength)862 static CPLXMLNode* CreateMarker(CPLXMLNode* psCSBox,
863 CPLXMLNode*& psLastChildCSBox,
864 const char* pszName,
865 GIntBig nOffset, GIntBig nLength)
866 {
867 CPLXMLNode* psMarker = CPLCreateXMLNode( nullptr, CXT_Element, "Marker" );
868 CPLAddXMLAttributeAndValue(psMarker, "name", pszName );
869 CPLAddXMLAttributeAndValue(psMarker, "offset",
870 CPLSPrintf(CPL_FRMT_GIB, nOffset ) );
871 CPLAddXMLAttributeAndValue(psMarker, "length",
872 CPLSPrintf(CPL_FRMT_GIB, 2 + nLength ) );
873 AddElement( psCSBox, psLastChildCSBox, psMarker );
874 return psMarker;
875 }
876
_AddError(CPLXMLNode * psParent,const char * pszErrorMsg,GIntBig nOffset=0)877 static CPLXMLNode* _AddError(CPLXMLNode* psParent, const char* pszErrorMsg,
878 GIntBig nOffset = 0)
879 {
880 CPLXMLNode* psError = CPLCreateXMLNode( psParent, CXT_Element, "Error" );
881 CPLAddXMLAttributeAndValue(psError, "message", pszErrorMsg );
882 if( nOffset )
883 {
884 CPLAddXMLAttributeAndValue(psError, "offset",
885 CPLSPrintf(CPL_FRMT_GIB, nOffset ) );
886 }
887 return psError;
888 }
889
890
AddError(CPLXMLNode * psParent,CPLXMLNode * & psLastChild,const char * pszErrorMsg,GIntBig nOffset=0)891 static void AddError(CPLXMLNode* psParent,
892 CPLXMLNode*& psLastChild,
893 const char* pszErrorMsg,
894 GIntBig nOffset = 0)
895 {
896 AddElement( psParent, psLastChild,
897 _AddError(nullptr, pszErrorMsg, nOffset) );
898 }
899
GetMarkerName(GByte byVal)900 static const char* GetMarkerName(GByte byVal)
901 {
902 switch(byVal)
903 {
904 case 0x90: return "SOT";
905 case 0x50: return "CAP";
906 case 0x51: return "SIZ";
907 case 0x52: return "COD";
908 case 0x53: return "COC";
909 case 0x55: return "TLM";
910 case 0x57: return "PLM";
911 case 0x58: return "PLT";
912 case 0x5C: return "QCD";
913 case 0x5D: return "QCC";
914 case 0x5E: return "RGN";
915 case 0x5F: return "POC";
916 case 0x60: return "PPM";
917 case 0x61: return "PPT";
918 case 0x63: return "CRG";
919 case 0x64: return "COM";
920 default: return CPLSPrintf("Unknown 0xFF%02X", byVal);
921 }
922 }
923
924 /************************************************************************/
925 /* DumpJPK2CodeStream() */
926 /************************************************************************/
927
DumpJPK2CodeStream(CPLXMLNode * psBox,VSILFILE * fp,GIntBig nBoxDataOffset,GIntBig nBoxDataLength)928 static CPLXMLNode* DumpJPK2CodeStream(CPLXMLNode* psBox,
929 VSILFILE* fp,
930 GIntBig nBoxDataOffset,
931 GIntBig nBoxDataLength)
932 {
933 GByte abyMarker[2];
934 CPLXMLNode* psCSBox = CPLCreateXMLNode( psBox, CXT_Element, "JP2KCodeStream" );
935 CPLXMLNode* psLastChildCSBox = nullptr;
936 if( VSIFSeekL(fp, nBoxDataOffset, SEEK_SET) != 0 )
937 {
938 AddError(psCSBox, psLastChildCSBox, "Cannot read codestream", 0);
939 return psCSBox;
940 }
941 GByte* pabyMarkerData = static_cast<GByte*>(CPLMalloc(65535+1));
942 GIntBig nNextTileOffset = 0;
943 int Csiz = -1;
944 const auto lambdaPOCType = [](GByte v) {
945 return (v == 0) ? "LRCP" :
946 (v == 1) ? "RLCP" :
947 (v == 2) ? "RPCL" :
948 (v == 3) ? "PCRL" :
949 (v == 4) ? "CPRL" : nullptr; };
950 while( true )
951 {
952 GIntBig nOffset = static_cast<GIntBig>(VSIFTellL(fp));
953 if( nOffset == nBoxDataOffset + nBoxDataLength )
954 break;
955 if( VSIFReadL(abyMarker, 2, 1, fp) != 1 )
956 {
957 AddError(psCSBox, psLastChildCSBox, "Cannot read marker", nOffset);
958 break;
959 }
960 if( abyMarker[0] != 0xFF )
961 {
962 AddError(psCSBox, psLastChildCSBox, "Not a marker", nOffset);
963 break;
964 }
965 if( abyMarker[1] == 0x4F )
966 {
967 CreateMarker( psCSBox, psLastChildCSBox, "SOC", nOffset, 0 );
968 continue;
969 }
970 if( abyMarker[1] == 0x93 )
971 {
972 GIntBig nMarkerSize = 0;
973 bool bBreak = false;
974 if( nNextTileOffset == 0 )
975 {
976 nMarkerSize = (nBoxDataOffset + nBoxDataLength - 2) - nOffset - 2;
977 if( VSIFSeekL(fp, nBoxDataOffset + nBoxDataLength - 2, SEEK_SET) != 0 ||
978 VSIFReadL(abyMarker, 2, 1, fp) != 1 ||
979 abyMarker[0] != 0xFF || abyMarker[1] != 0xD9 )
980 {
981 /* autotest/gdrivers/data/rgb16_ecwsdk.jp2 does not end */
982 /* with a EOC... */
983 nMarkerSize += 2;
984 bBreak = true;
985 }
986 }
987 else if( nNextTileOffset >= nOffset + 2 )
988 nMarkerSize = nNextTileOffset - nOffset - 2;
989
990 CreateMarker( psCSBox, psLastChildCSBox, "SOD", nOffset, nMarkerSize );
991 if( bBreak )
992 break;
993
994 if( nNextTileOffset && nNextTileOffset == nOffset )
995 {
996 /* Found with Pleiades images. openjpeg doesn't like it either */
997 nNextTileOffset = 0;
998 }
999 else if( nNextTileOffset && nNextTileOffset >= nOffset + 2 )
1000 {
1001 if( VSIFSeekL(fp, nNextTileOffset, SEEK_SET) != 0 )
1002 AddError(psCSBox, psLastChildCSBox,
1003 "Cannot seek to", nNextTileOffset);
1004 nNextTileOffset = 0;
1005 }
1006 else
1007 {
1008 /* We have seek and check before we hit a EOC */
1009 nOffset = nBoxDataOffset + nBoxDataLength - 2;
1010 CreateMarker( psCSBox, psLastChildCSBox, "EOC", nOffset, 0 );
1011 }
1012 continue;
1013 }
1014 if( abyMarker[1] == 0xD9 )
1015 {
1016 CreateMarker( psCSBox, psLastChildCSBox, "EOC", nOffset, 0 );
1017 continue;
1018 }
1019 /* Reserved markers */
1020 if( abyMarker[1] >= 0x30 && abyMarker[1] <= 0x3F )
1021 {
1022 CreateMarker( psCSBox, psLastChildCSBox,
1023 CPLSPrintf("Unknown 0xFF%02X", abyMarker[1]), nOffset, 0 );
1024 continue;
1025 }
1026
1027 GUInt16 nMarkerSize;
1028 if( VSIFReadL(&nMarkerSize, 2, 1, fp) != 1 )
1029 {
1030 AddError(psCSBox, psLastChildCSBox,
1031 CPLSPrintf("Cannot read marker size of %s", GetMarkerName(abyMarker[1])), nOffset);
1032 break;
1033 }
1034 CPL_MSBPTR16(&nMarkerSize);
1035 if( nMarkerSize < 2 )
1036 {
1037 AddError(psCSBox, psLastChildCSBox,
1038 CPLSPrintf("Invalid marker size of %s", GetMarkerName(abyMarker[1])), nOffset);
1039 break;
1040 }
1041
1042 CPLXMLNode* psMarker = CreateMarker( psCSBox, psLastChildCSBox,
1043 GetMarkerName(abyMarker[1]), nOffset, nMarkerSize );
1044 CPLXMLNode* psLastChild = nullptr;
1045 if( VSIFReadL(pabyMarkerData, nMarkerSize - 2, 1, fp) != 1 )
1046 {
1047 AddError(psMarker, psLastChild,
1048 "Cannot read marker data", nOffset);
1049 break;
1050 }
1051 GByte* pabyMarkerDataIter = pabyMarkerData;
1052 GUInt16 nRemainingMarkerSize = nMarkerSize - 2;
1053 bool bError = false;
1054
1055 auto READ_MARKER_FIELD_UINT8 = [&](const char* name,
1056 const char* (*commentFunc)(GByte) = nullptr) {
1057 GByte v;
1058 if( nRemainingMarkerSize >= 1 ) {
1059 v = *pabyMarkerDataIter;
1060 AddField(psMarker, psLastChild, name, *pabyMarkerDataIter,
1061 commentFunc ? commentFunc(v) : nullptr);
1062 pabyMarkerDataIter += 1;
1063 nRemainingMarkerSize -= 1;
1064 }
1065 else {
1066 AddError(psMarker, psLastChild, CPLSPrintf("Cannot read field %s", name));
1067 v = 0;
1068 bError = true;
1069 }
1070 return v;
1071 };
1072
1073 auto READ_MARKER_FIELD_UINT16 = [&](const char* name,
1074 const char* (*commentFunc)(GUInt16) = nullptr) {
1075 GUInt16 v;
1076 if( nRemainingMarkerSize >= 2 ) {
1077 memcpy(&v, pabyMarkerDataIter, 2);
1078 CPL_MSBPTR16(&v);
1079 AddField(psMarker, psLastChild, name, v,
1080 commentFunc ? commentFunc(v) : nullptr);
1081 pabyMarkerDataIter += 2;
1082 nRemainingMarkerSize -= 2;
1083 }
1084 else {
1085 AddError(psMarker, psLastChild, CPLSPrintf("Cannot read field %s", name));
1086 v = 0;
1087 bError = true;
1088 }
1089 return v;
1090 };
1091
1092 auto READ_MARKER_FIELD_UINT32 = [&](const char* name,
1093 const char* (*commentFunc)(GUInt32) = nullptr) {
1094 GUInt32 v;
1095 if( nRemainingMarkerSize >= 4 ) {
1096 memcpy(&v, pabyMarkerDataIter, 4);
1097 CPL_MSBPTR32(&v);
1098 AddField(psMarker, psLastChild, name, v,
1099 commentFunc ? commentFunc(v) : nullptr);
1100 pabyMarkerDataIter += 4;
1101 nRemainingMarkerSize -= 4;
1102 }
1103 else {
1104 AddError(psMarker, psLastChild, CPLSPrintf("Cannot read field %s", name));
1105 v = 0;
1106 bError = true;
1107 }
1108 return v;
1109 };
1110
1111 if( abyMarker[1] == 0x90 ) /* SOT */
1112 {
1113 READ_MARKER_FIELD_UINT16("Isot");
1114 GUInt32 PSOT = READ_MARKER_FIELD_UINT32("Psot");
1115 READ_MARKER_FIELD_UINT8("TPsot");
1116 READ_MARKER_FIELD_UINT8("TNsot");
1117 if( nRemainingMarkerSize > 0 )
1118 AddElement( psMarker, psLastChild,
1119 CPLCreateXMLElementAndValue(
1120 nullptr, "RemainingBytes",
1121 CPLSPrintf("%d", static_cast<int>(nRemainingMarkerSize) )));
1122
1123 if( PSOT )
1124 nNextTileOffset = nOffset + PSOT;
1125 }
1126 else if( abyMarker[1] == 0x50 ) /* CAP (HTJ2K) */
1127 {
1128 const GUInt32 Pcap = READ_MARKER_FIELD_UINT32("Pcap");
1129 for( int i = 0; i < 32; i++ )
1130 {
1131 if( (Pcap >> (31 - i)) & 1 )
1132 {
1133 READ_MARKER_FIELD_UINT16(CPLSPrintf("Scap_P%d", i+1));
1134 }
1135 }
1136 if( nRemainingMarkerSize > 0 )
1137 AddElement( psMarker, psLastChild,
1138 CPLCreateXMLElementAndValue(
1139 nullptr, "RemainingBytes",
1140 CPLSPrintf("%d", static_cast<int>(nRemainingMarkerSize) )));
1141 }
1142 else if( abyMarker[1] == 0x51 ) /* SIZ */
1143 {
1144 READ_MARKER_FIELD_UINT16("Rsiz", [](GUInt16 v) {
1145 return (v == 0) ? "Unrestricted profile":
1146 (v == 1) ? "Profile 0":
1147 (v == 2) ? "Profile 1":
1148 (v == 16384) ? "HTJ2K": nullptr; });
1149 READ_MARKER_FIELD_UINT32("Xsiz");
1150 READ_MARKER_FIELD_UINT32("Ysiz");
1151 READ_MARKER_FIELD_UINT32("XOsiz");
1152 READ_MARKER_FIELD_UINT32("YOsiz");
1153 READ_MARKER_FIELD_UINT32("XTsiz");
1154 READ_MARKER_FIELD_UINT32("YTsiz");
1155 READ_MARKER_FIELD_UINT32("XTOSiz");
1156 READ_MARKER_FIELD_UINT32("YTOSiz");
1157 Csiz = READ_MARKER_FIELD_UINT16("Csiz");
1158 bError = false;
1159 // cppcheck-suppress knownConditionTrueFalse
1160 for(int i=0;i<Csiz && !bError;i++)
1161 {
1162 READ_MARKER_FIELD_UINT8(CPLSPrintf("Ssiz%d", i), [](GByte v) {
1163 return GetInterpretationOfBPC(v); });
1164 READ_MARKER_FIELD_UINT8(CPLSPrintf("XRsiz%d", i));
1165 READ_MARKER_FIELD_UINT8(CPLSPrintf("YRsiz%d", i));
1166 }
1167 if( nRemainingMarkerSize > 0 )
1168 AddElement( psMarker, psLastChild,
1169 CPLCreateXMLElementAndValue(
1170 nullptr, "RemainingBytes",
1171 CPLSPrintf("%d", static_cast<int>(nRemainingMarkerSize) )));
1172 }
1173 else if( abyMarker[1] == 0x52 ) /* COD */
1174 {
1175 bool bHasPrecincts = false;
1176 if( nRemainingMarkerSize >= 1 ) {
1177 auto nLastVal = *pabyMarkerDataIter;
1178 CPLString osInterp;
1179 if( nLastVal & 0x1 )
1180 {
1181 bHasPrecincts = true;
1182 osInterp += "User defined precincts";
1183 }
1184 else
1185 osInterp += "Standard precincts";
1186 osInterp += ", ";
1187 if( nLastVal & 0x2 )
1188 osInterp += "SOP marker segments may be used";
1189 else
1190 osInterp += "No SOP marker segments";
1191 osInterp += ", ";
1192 if( nLastVal & 0x4 )
1193 osInterp += "EPH marker segments may be used";
1194 else
1195 osInterp += "No EPH marker segments";
1196 AddField(psMarker, psLastChild, "Scod", nLastVal, osInterp.c_str());
1197 pabyMarkerDataIter += 1;
1198 nRemainingMarkerSize -= 1;
1199 }
1200 else {
1201 AddError(psMarker, psLastChild,
1202 CPLSPrintf("Cannot read field %s", "Scod"));
1203 }
1204 READ_MARKER_FIELD_UINT8("SGcod_Progress", lambdaPOCType);
1205 READ_MARKER_FIELD_UINT16("SGcod_NumLayers");
1206 READ_MARKER_FIELD_UINT8("SGcod_MCT");
1207 READ_MARKER_FIELD_UINT8("SPcod_NumDecompositions");
1208 READ_MARKER_FIELD_UINT8("SPcod_xcb_minus_2", [](GByte v) {
1209 return v <= 8 ? CPLSPrintf("%d", 1 << (2+v)) : "invalid"; });
1210 READ_MARKER_FIELD_UINT8("SPcod_ycb_minus_2", [](GByte v) {
1211 return v <= 8 ? CPLSPrintf("%d", 1 << (2+v)) : "invalid"; });
1212 if( nRemainingMarkerSize >= 1 ) {
1213 auto nLastVal = *pabyMarkerDataIter;
1214 CPLString osInterp;
1215 if( nLastVal & 0x1 )
1216 osInterp += "Selective arithmetic coding bypass";
1217 else
1218 osInterp += "No selective arithmetic coding bypass";
1219 osInterp += ", ";
1220 if( nLastVal & 0x2 )
1221 osInterp += "Reset context probabilities on coding pass boundaries";
1222 else
1223 osInterp += "No reset of context probabilities on coding pass boundaries";
1224 osInterp += ", ";
1225 if( nLastVal & 0x4 )
1226 osInterp += "Termination on each coding pass";
1227 else
1228 osInterp += "No termination on each coding pass";
1229 osInterp += ", ";
1230 if( nLastVal & 0x8 )
1231 osInterp += "Vertically causal context";
1232 else
1233 osInterp += "No vertically causal context";
1234 osInterp += ", ";
1235 if( nLastVal & 0x10 )
1236 osInterp += "Predictable termination";
1237 else
1238 osInterp += "No predictable termination";
1239 osInterp += ", ";
1240 if( nLastVal & 0x20 )
1241 osInterp += "Segmentation symbols are used";
1242 else
1243 osInterp += "No segmentation symbols are used";
1244 if( nLastVal & 0x40 )
1245 osInterp += ", High Throughput algorithm";
1246 AddField(psMarker, psLastChild,
1247 "SPcod_cbstyle", static_cast<GByte>(nLastVal), osInterp.c_str());
1248 pabyMarkerDataIter += 1;
1249 nRemainingMarkerSize -= 1;
1250 }
1251 else {
1252 AddError(psMarker,
1253 psLastChild,
1254 CPLSPrintf("Cannot read field %s", "SPcod_cbstyle"));
1255 }
1256 READ_MARKER_FIELD_UINT8("SPcod_transformation", [](GByte v) {
1257 return (v == 0) ? "9-7 irreversible":
1258 (v == 1) ? "5-3 reversible": nullptr; });
1259 if( bHasPrecincts )
1260 {
1261 int i = 0;
1262 while( nRemainingMarkerSize >= 1 )
1263 {
1264 auto nLastVal = *pabyMarkerDataIter;
1265 AddField(psMarker, psLastChild,
1266 CPLSPrintf("SPcod_Precincts%d", i), *pabyMarkerDataIter,
1267 CPLSPrintf("PPx=%d PPy=%d: %dx%d",
1268 nLastVal & 0xf, nLastVal >> 4,
1269 1 << (nLastVal & 0xf), 1 << (nLastVal >> 4)));
1270 pabyMarkerDataIter += 1;
1271 nRemainingMarkerSize -= 1;
1272 i ++;
1273 }
1274 }
1275 if( nRemainingMarkerSize > 0 )
1276 AddElement( psMarker, psLastChild,
1277 CPLCreateXMLElementAndValue(
1278 nullptr, "RemainingBytes",
1279 CPLSPrintf("%d", static_cast<int>(nRemainingMarkerSize) )));
1280 }
1281 else if( abyMarker[1] == 0x53 ) /* COC */
1282 {
1283 }
1284 else if( abyMarker[1] == 0x55 ) /* TLM */
1285 {
1286 READ_MARKER_FIELD_UINT8("Ztlm");
1287 auto Stlm = READ_MARKER_FIELD_UINT8("Stlm", [](GByte v) {
1288 return CPLSPrintf("ST=%d SP=%d",
1289 (v >> 4) & 3,
1290 (v >> 6) & 1); });
1291 int ST = (Stlm >> 4) & 3;
1292 int SP = (Stlm >> 6) & 1;
1293 int nTilePartDescLength = ST + ((SP == 0) ? 2 : 4);
1294 int i = 0;
1295 while( nRemainingMarkerSize >= nTilePartDescLength )
1296 {
1297 if( ST == 1 )
1298 READ_MARKER_FIELD_UINT8(CPLSPrintf("Ttlm%d", i));
1299 else if( ST == 2 )
1300 READ_MARKER_FIELD_UINT16(CPLSPrintf("Ttlm%d", i));
1301 if( SP == 0 )
1302 READ_MARKER_FIELD_UINT16(CPLSPrintf("Ptlm%d", i));
1303 else
1304 READ_MARKER_FIELD_UINT32(CPLSPrintf("Ptlm%d", i));
1305 i ++;
1306 }
1307 if( nRemainingMarkerSize > 0 )
1308 AddElement( psMarker, psLastChild,
1309 CPLCreateXMLElementAndValue(
1310 nullptr, "RemainingBytes",
1311 CPLSPrintf("%d", static_cast<int>(nRemainingMarkerSize) )));
1312 }
1313 else if( abyMarker[1] == 0x57 ) /* PLM */
1314 {
1315 }
1316 else if( abyMarker[1] == 0x58 ) /* PLT */
1317 {
1318 READ_MARKER_FIELD_UINT8("Zplt");
1319 int i = 0;
1320 unsigned nPacketLength = 0;
1321 while( nRemainingMarkerSize >= 1 )
1322 {
1323 auto nLastVal = *pabyMarkerDataIter;
1324 nPacketLength |= (nLastVal & 0x7f);
1325 if (nLastVal & 0x80)
1326 {
1327 nPacketLength <<= 7;
1328 }
1329 else
1330 {
1331 AddField(psMarker, psLastChild,
1332 CPLSPrintf("Iplt%d", i), nPacketLength);
1333 nPacketLength = 0;
1334 i ++;
1335 }
1336 pabyMarkerDataIter += 1;
1337 nRemainingMarkerSize -= 1;
1338 }
1339 if( nPacketLength != 0 )
1340 {
1341 AddError(psMarker, psLastChild,
1342 "Incorrect PLT marker");
1343 }
1344 }
1345 else if( abyMarker[1] == 0x5C ) /* QCD */
1346 {
1347 }
1348 else if( abyMarker[1] == 0x5D ) /* QCC */
1349 {
1350 }
1351 else if( abyMarker[1] == 0x5E ) /* RGN */
1352 {
1353 }
1354 else if( abyMarker[1] == 0x5F ) /* POC */
1355 {
1356 const int nPOCEntrySize = Csiz < 257 ? 7 : 9;
1357 int i = 0;
1358 while( nRemainingMarkerSize >= nPOCEntrySize )
1359 {
1360 READ_MARKER_FIELD_UINT8(CPLSPrintf("RSpoc%d", i));
1361 if( nPOCEntrySize == 7 )
1362 {
1363 READ_MARKER_FIELD_UINT8(CPLSPrintf("CSpoc%d", i));
1364 }
1365 else
1366 {
1367 READ_MARKER_FIELD_UINT16(CPLSPrintf("CSpoc%d", i));
1368 }
1369 READ_MARKER_FIELD_UINT16(CPLSPrintf("LYEpoc%d", i));
1370 READ_MARKER_FIELD_UINT8(CPLSPrintf("REpoc%d", i));
1371 if( nPOCEntrySize == 7 )
1372 {
1373 READ_MARKER_FIELD_UINT8(CPLSPrintf("CEpoc%d", i));
1374 }
1375 else
1376 {
1377 READ_MARKER_FIELD_UINT16(CPLSPrintf("CEpoc%d", i));
1378 }
1379 READ_MARKER_FIELD_UINT8(CPLSPrintf("Ppoc%d", i), lambdaPOCType);
1380 i ++;
1381 }
1382 if( nRemainingMarkerSize > 0 )
1383 {
1384 AddElement( psMarker, psLastChild,
1385 CPLCreateXMLElementAndValue(
1386 nullptr, "RemainingBytes",
1387 CPLSPrintf("%d", static_cast<int>(nRemainingMarkerSize) )));
1388 }
1389 }
1390 else if( abyMarker[1] == 0x60 ) /* PPM */
1391 {
1392 }
1393 else if( abyMarker[1] == 0x61 ) /* PPT */
1394 {
1395 }
1396 else if( abyMarker[1] == 0x63 ) /* CRG */
1397 {
1398 }
1399 else if( abyMarker[1] == 0x64 ) /* COM */
1400 {
1401 auto RCom = READ_MARKER_FIELD_UINT16("Rcom", [](GUInt16 v) {
1402 return (v == 0 ) ? "Binary" : (v == 1) ? "LATIN1" : nullptr; });
1403 if( RCom == 1 )
1404 {
1405 GByte abyBackup = pabyMarkerDataIter[nRemainingMarkerSize];
1406 pabyMarkerDataIter[nRemainingMarkerSize] = 0;
1407 AddField(psMarker, psLastChild,
1408 "COM",
1409 static_cast<int>(nRemainingMarkerSize),
1410 reinterpret_cast<const char*>(pabyMarkerDataIter));
1411 pabyMarkerDataIter[nRemainingMarkerSize] = abyBackup;
1412 }
1413 }
1414
1415 if( VSIFSeekL(fp, nOffset + 2 + nMarkerSize, SEEK_SET) != 0 )
1416 {
1417 AddError(psCSBox, psLastChildCSBox,
1418 "Cannot seek to next marker", nOffset + 2 + nMarkerSize);
1419 break;
1420 }
1421
1422 CPL_IGNORE_RET_VAL(bError);
1423 }
1424 CPLFree(pabyMarkerData);
1425 return psCSBox;
1426 }
1427
1428 /************************************************************************/
1429 /* GDALGetJPEG2000StructureInternal() */
1430 /************************************************************************/
1431
1432 static
GDALGetJPEG2000StructureInternal(CPLXMLNode * psParent,VSILFILE * fp,GDALJP2Box * poParentBox,CSLConstList papszOptions,int nRecLevel,vsi_l_offset nFileOrParentBoxSize)1433 void GDALGetJPEG2000StructureInternal(CPLXMLNode* psParent,
1434 VSILFILE* fp,
1435 GDALJP2Box* poParentBox,
1436 CSLConstList papszOptions,
1437 int nRecLevel,
1438 vsi_l_offset nFileOrParentBoxSize)
1439 {
1440 // Limit recursion to a reasonable level. I believe that in practice 2
1441 // should be sufficient, but just in case someone creates deeply
1442 // nested "super-boxes", allow up to 5.
1443 if( nRecLevel == 5 )
1444 return;
1445
1446 static const char* const szHex = "0123456789ABCDEF";
1447 GDALJP2Box oBox( fp );
1448 CPLXMLNode* psLastChild = nullptr;
1449 if( oBox.ReadFirstChild(poParentBox) )
1450 {
1451 while( strlen(oBox.GetType()) > 0 )
1452 {
1453 GIntBig nBoxDataLength = oBox.GetDataLength();
1454 const char* pszBoxType = oBox.GetType();
1455
1456 CPLXMLNode* psBox = CPLCreateXMLNode( nullptr, CXT_Element, "JP2Box" );
1457 AddElement( psParent, psLastChild, psBox );
1458 CPLAddXMLAttributeAndValue(psBox, "name", pszBoxType );
1459 CPLAddXMLAttributeAndValue(psBox, "box_offset",
1460 CPLSPrintf(CPL_FRMT_GIB, oBox.GetBoxOffset() ) );
1461 CPLAddXMLAttributeAndValue(psBox, "box_length",
1462 CPLSPrintf(CPL_FRMT_GIB, oBox.GetBoxLength() ) );
1463 CPLAddXMLAttributeAndValue(psBox, "data_offset",
1464 CPLSPrintf(CPL_FRMT_GIB, oBox.GetDataOffset() ) );
1465 CPLAddXMLAttributeAndValue(psBox, "data_length",
1466 CPLSPrintf(CPL_FRMT_GIB, nBoxDataLength ) );
1467
1468 if( nBoxDataLength > GINTBIG_MAX - oBox.GetDataOffset() )
1469 {
1470 CPLXMLNode* psLastChildBox = nullptr;
1471 AddError(psBox, psLastChildBox, "Invalid box_length");
1472 break;
1473 }
1474
1475 // Check large non-jp2c boxes against filesize
1476 if( strcmp(pszBoxType, "jp2c") != 0 && nBoxDataLength > 100 * 1024 )
1477 {
1478 if( nFileOrParentBoxSize == 0 )
1479 {
1480 CPL_IGNORE_RET_VAL(VSIFSeekL(fp, 0, SEEK_END));
1481 nFileOrParentBoxSize = VSIFTellL(fp);
1482 }
1483 }
1484 if( nFileOrParentBoxSize > 0 &&
1485 (static_cast<vsi_l_offset>(oBox.GetDataOffset()) > nFileOrParentBoxSize ||
1486 static_cast<vsi_l_offset>(nBoxDataLength) > nFileOrParentBoxSize - oBox.GetDataOffset()) )
1487 {
1488 CPLXMLNode* psLastChildBox = nullptr;
1489 AddError(psBox, psLastChildBox, "Invalid box_length");
1490 break;
1491 }
1492
1493 if( oBox.IsSuperBox() )
1494 {
1495 GDALGetJPEG2000StructureInternal(psBox, fp, &oBox,
1496 papszOptions,
1497 nRecLevel + 1,
1498 oBox.GetDataOffset() +
1499 static_cast<vsi_l_offset>(nBoxDataLength));
1500 }
1501 else
1502 {
1503 if( strcmp(pszBoxType, "uuid") == 0 )
1504 {
1505 char* pszBinaryContent = static_cast<char*>(VSIMalloc( 2 * 16 + 1 ));
1506 const GByte* pabyUUID = oBox.GetUUID();
1507 for(int i=0;i<16;i++)
1508 {
1509 pszBinaryContent[2*i] = szHex[pabyUUID[i] >> 4];
1510 pszBinaryContent[2*i+1] = szHex[pabyUUID[i] & 0xf];
1511 }
1512 pszBinaryContent[2*16] = '\0';
1513 CPLXMLNode* psUUIDNode =
1514 CPLCreateXMLNode( psBox, CXT_Element, "UUID" );
1515 if( GDALJP2Metadata::IsUUID_MSI(pabyUUID) )
1516 CPLAddXMLAttributeAndValue(psUUIDNode, "description", "GeoTIFF" );
1517 else if( GDALJP2Metadata::IsUUID_XMP(pabyUUID) )
1518 CPLAddXMLAttributeAndValue(psUUIDNode, "description", "XMP" );
1519 CPLCreateXMLNode( psUUIDNode, CXT_Text, pszBinaryContent);
1520 VSIFree(pszBinaryContent);
1521 }
1522
1523 if( (CPLFetchBool(papszOptions, "BINARY_CONTENT", false) ||
1524 CPLFetchBool(papszOptions, "ALL", false) ) &&
1525 strcmp(pszBoxType, "jp2c") != 0 &&
1526 nBoxDataLength < 100 * 1024 )
1527 {
1528 CPLXMLNode* psBinaryContent = CPLCreateXMLNode( psBox, CXT_Element, "BinaryContent" );
1529 GByte* pabyBoxData = oBox.ReadBoxData();
1530 int nBoxLength = static_cast<int>(nBoxDataLength);
1531 char* pszBinaryContent = static_cast<char*>(VSIMalloc( 2 * nBoxLength + 1 ));
1532 if( pabyBoxData && pszBinaryContent )
1533 {
1534 for(int i=0;i<nBoxLength;i++)
1535 {
1536 pszBinaryContent[2*i] = szHex[pabyBoxData[i] >> 4];
1537 pszBinaryContent[2*i+1] = szHex[pabyBoxData[i] & 0xf];
1538 }
1539 pszBinaryContent[2*nBoxLength] = '\0';
1540 CPLCreateXMLNode( psBinaryContent, CXT_Text, pszBinaryContent );
1541 }
1542 CPLFree(pabyBoxData);
1543 VSIFree(pszBinaryContent);
1544 }
1545
1546 if( (CPLFetchBool(papszOptions, "TEXT_CONTENT", false) ||
1547 CPLFetchBool(papszOptions, "ALL", false) ) &&
1548 strcmp(pszBoxType, "jp2c") != 0 &&
1549 nBoxDataLength < 100 * 1024 )
1550 {
1551 GByte* pabyBoxData = oBox.ReadBoxData();
1552 if( pabyBoxData )
1553 {
1554 const char* pszBoxData = reinterpret_cast<const char*>(pabyBoxData);
1555 if( CPLIsUTF8(pszBoxData, -1) &&
1556 static_cast<int>(strlen(pszBoxData)) + 2 >= nBoxDataLength )
1557 {
1558 CPLXMLNode* psXMLContentBox = nullptr;
1559 if( pszBoxData[0] == '<' )
1560 {
1561 CPLPushErrorHandler(CPLQuietErrorHandler);
1562 psXMLContentBox = CPLParseXMLString(pszBoxData);
1563 CPLPopErrorHandler();
1564 }
1565 if( psXMLContentBox )
1566 {
1567 CPLXMLNode* psXMLContentNode =
1568 CPLCreateXMLNode( psBox, CXT_Element, "XMLContent" );
1569 psXMLContentNode->psChild = psXMLContentBox;
1570 }
1571 else
1572 {
1573 CPLCreateXMLNode(
1574 CPLCreateXMLNode( psBox, CXT_Element, "TextContent" ),
1575 CXT_Text, pszBoxData);
1576 }
1577 }
1578 }
1579 CPLFree(pabyBoxData);
1580 }
1581
1582 if( strcmp(pszBoxType, "jp2c") == 0 )
1583 {
1584 if( CPLFetchBool(papszOptions, "CODESTREAM", false) ||
1585 CPLFetchBool(papszOptions, "ALL", false) )
1586 {
1587 DumpJPK2CodeStream(psBox, fp,
1588 oBox.GetDataOffset(), nBoxDataLength);
1589 }
1590 }
1591 else if( strcmp(pszBoxType, "uuid") == 0 &&
1592 GDALJP2Metadata::IsUUID_MSI(oBox.GetUUID()) )
1593 {
1594 DumpGeoTIFFBox(psBox, oBox);
1595 }
1596 else if( strcmp(pszBoxType, "ftyp") == 0 )
1597 {
1598 DumpFTYPBox(psBox, oBox);
1599 }
1600 else if( strcmp(pszBoxType, "ihdr") == 0 )
1601 {
1602 DumpIHDRBox(psBox, oBox);
1603 }
1604 else if( strcmp(pszBoxType, "bpcc") == 0 )
1605 {
1606 DumpBPCCBox(psBox, oBox);
1607 }
1608 else if( strcmp(pszBoxType, "colr") == 0 )
1609 {
1610 DumpCOLRBox(psBox, oBox);
1611 }
1612 else if( strcmp(pszBoxType, "pclr") == 0 )
1613 {
1614 DumpPCLRBox(psBox, oBox);
1615 }
1616 else if( strcmp(pszBoxType, "cmap") == 0 )
1617 {
1618 DumpCMAPBox(psBox, oBox);
1619 }
1620 else if( strcmp(pszBoxType, "cdef") == 0 )
1621 {
1622 DumpCDEFBox(psBox, oBox);
1623 }
1624 else if( strcmp(pszBoxType, "resc") == 0 ||
1625 strcmp(pszBoxType, "resd") == 0)
1626 {
1627 DumpRESxBox(psBox, oBox);
1628 }
1629 else if( strcmp(pszBoxType, "rreq") == 0 )
1630 {
1631 DumpRREQBox(psBox, oBox);
1632 }
1633 }
1634
1635 if (!oBox.ReadNextChild(poParentBox))
1636 break;
1637 }
1638 }
1639 }
1640
1641 /************************************************************************/
1642 /* GDALGetJPEG2000Structure() */
1643 /************************************************************************/
1644
1645 constexpr unsigned char jpc_header[] = {0xff,0x4f};
1646 constexpr unsigned char jp2_box_jp[] = {0x6a,0x50,0x20,0x20}; /* 'jP ' */
1647
1648 /** Dump the structure of a JPEG2000 file as a XML tree.
1649 *
1650 * @param pszFilename filename.
1651 * @param papszOptions NULL terminated list of options, or NULL.
1652 * Allowed options are BINARY_CONTENT=YES, TEXT_CONTENT=YES,
1653 * CODESTREAM=YES, ALL=YES.
1654 * @return XML tree (to be freed with CPLDestroyXMLNode()) or NULL in case
1655 * of error
1656 * @since GDAL 2.0
1657 */
1658
GDALGetJPEG2000Structure(const char * pszFilename,CSLConstList papszOptions)1659 CPLXMLNode* GDALGetJPEG2000Structure(const char* pszFilename,
1660 CSLConstList papszOptions)
1661 {
1662 VSILFILE* fp = VSIFOpenL(pszFilename, "rb");
1663 if( fp == nullptr )
1664 {
1665 CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s", pszFilename);
1666 return nullptr;
1667 }
1668 GByte abyHeader[16];
1669 if( VSIFReadL(abyHeader, 16, 1, fp) != 1 ||
1670 (memcmp(abyHeader, jpc_header, sizeof(jpc_header)) != 0 &&
1671 memcmp(abyHeader + 4, jp2_box_jp, sizeof(jp2_box_jp)) != 0) )
1672 {
1673 CPLError(CE_Failure, CPLE_AppDefined, "%s is not a JPEG2000 file", pszFilename);
1674 CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
1675 return nullptr;
1676 }
1677
1678 CPLXMLNode* psParent = nullptr;
1679 if( memcmp(abyHeader, jpc_header, sizeof(jpc_header)) == 0 )
1680 {
1681 if( CPLFetchBool(papszOptions, "CODESTREAM", false) ||
1682 CPLFetchBool(papszOptions, "ALL", false) )
1683 {
1684 if( VSIFSeekL(fp, 0, SEEK_END) == 0 )
1685 {
1686 GIntBig nBoxDataLength = static_cast<GIntBig>(VSIFTellL(fp));
1687 psParent = DumpJPK2CodeStream(nullptr, fp, 0, nBoxDataLength);
1688 CPLAddXMLAttributeAndValue(psParent, "filename", pszFilename );
1689 }
1690 }
1691 }
1692 else
1693 {
1694 psParent = CPLCreateXMLNode( nullptr, CXT_Element, "JP2File" );
1695 CPLAddXMLAttributeAndValue(psParent, "filename", pszFilename );
1696 vsi_l_offset nFileSize = 0;
1697 GDALGetJPEG2000StructureInternal(psParent, fp, nullptr, papszOptions, 0, nFileSize);
1698 }
1699
1700 CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
1701 return psParent;
1702 }
1703