LCOV - code coverage report
Current view: top level - gcore - gdaljp2structure.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1111 1438 77.3 %
Date: 2024-11-21 22:18:42 Functions: 46 52 88.5 %

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

Generated by: LCOV version 1.14