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

Generated by: LCOV version 1.14