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