LCOV - code coverage report
Current view: top level - gcore - gdaljp2structure.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1111 1440 77.2 %
Date: 2026-03-13 03:16:58 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             :                                       vsi_l_offset 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 :     vsi_l_offset 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 :         auto nOffset = 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 :                 const auto nPos = nBoxDataOffset + nBoxDataLength - 2;
    1103           0 :                 if (nPos >= nOffset + 2)
    1104             :                 {
    1105           0 :                     nMarkerSize =
    1106           0 :                         (nBoxDataOffset + nBoxDataLength - 2) - (nOffset + 2);
    1107           0 :                     if (VSIFSeekL(fp, nBoxDataOffset + nBoxDataLength - 2,
    1108           0 :                                   SEEK_SET) != 0 ||
    1109           0 :                         VSIFReadL(abyMarker, 2, 1, fp) != 1 ||
    1110           0 :                         abyMarker[0] != 0xFF || abyMarker[1] != 0xD9)
    1111             :                     {
    1112             :                         /* autotest/gdrivers/data/rgb16_ecwsdk.jp2 does not end */
    1113             :                         /* with a EOC... */
    1114           0 :                         nMarkerSize += 2;
    1115           0 :                         bBreak = true;
    1116             :                     }
    1117             :                 }
    1118             :             }
    1119          43 :             else if (nNextTileOffset >= nOffset + 2)
    1120          43 :                 nMarkerSize = nNextTileOffset - nOffset - 2;
    1121             : 
    1122          43 :             if (bIncludeSOD)
    1123             :             {
    1124          43 :                 CreateMarker(psCSBox, psLastChildCSBox, psDumpContext, "SOD",
    1125             :                              nOffset, nMarkerSize);
    1126             :             }
    1127          43 :             if (bBreak || psDumpContext->bStopAtSOD)
    1128             :             {
    1129           0 :                 psDumpContext->bSODEncountered = true;
    1130           0 :                 break;
    1131             :             }
    1132             : 
    1133          43 :             if (nNextTileOffset && nNextTileOffset == nOffset)
    1134             :             {
    1135             :                 /* Found with Pleiades images. openjpeg doesn't like it either
    1136             :                  */
    1137           0 :                 nNextTileOffset = 0;
    1138             :             }
    1139          43 :             else if (nNextTileOffset && nNextTileOffset >= nOffset + 2)
    1140             :             {
    1141          43 :                 if (VSIFSeekL(fp, nNextTileOffset, SEEK_SET) != 0)
    1142           0 :                     AddError(psCSBox, psLastChildCSBox, psDumpContext,
    1143             :                              "Cannot seek to", nNextTileOffset);
    1144          43 :                 nNextTileOffset = 0;
    1145             :             }
    1146             :             else
    1147             :             {
    1148             :                 /* We have seek and check before we hit a EOC */
    1149           0 :                 nOffset = nBoxDataOffset + nBoxDataLength - 2;
    1150           0 :                 if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1151           0 :                     strstr(psDumpContext->pszCodestreamMarkers, "EOC"))
    1152             :                 {
    1153           0 :                     CreateMarker(psCSBox, psLastChildCSBox, psDumpContext,
    1154             :                                  "EOC", nOffset, 0);
    1155             :                 }
    1156             :             }
    1157          43 :             continue;
    1158             :         }
    1159         285 :         if (abyMarker[1] == 0xD9)
    1160             :         {
    1161          38 :             if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1162           0 :                 strstr(psDumpContext->pszCodestreamMarkers, "EOC"))
    1163             :             {
    1164          38 :                 CreateMarker(psCSBox, psLastChildCSBox, psDumpContext, "EOC",
    1165             :                              nOffset, 0);
    1166             :             }
    1167          38 :             continue;
    1168             :         }
    1169             :         /* Reserved markers */
    1170         247 :         if (abyMarker[1] >= 0x30 && abyMarker[1] <= 0x3F)
    1171             :         {
    1172           0 :             if (psDumpContext->pszCodestreamMarkers == nullptr)
    1173             :             {
    1174           0 :                 CreateMarker(psCSBox, psLastChildCSBox, psDumpContext,
    1175           0 :                              CPLSPrintf("Unknown 0xFF%02X", abyMarker[1]),
    1176             :                              nOffset, 0);
    1177             :             }
    1178           0 :             continue;
    1179             :         }
    1180             : 
    1181             :         GUInt16 nMarkerSize;
    1182         247 :         if (VSIFReadL(&nMarkerSize, 2, 1, fp) != 1)
    1183             :         {
    1184           0 :             AddError(psCSBox, psLastChildCSBox, psDumpContext,
    1185             :                      CPLSPrintf("Cannot read marker size of %s",
    1186           0 :                                 GetMarkerName(abyMarker[1])),
    1187             :                      nOffset);
    1188           0 :             break;
    1189             :         }
    1190         247 :         CPL_MSBPTR16(&nMarkerSize);
    1191         247 :         if (nMarkerSize < 2)
    1192             :         {
    1193           0 :             AddError(psCSBox, psLastChildCSBox, psDumpContext,
    1194             :                      CPLSPrintf("Invalid marker size of %s",
    1195           0 :                                 GetMarkerName(abyMarker[1])),
    1196             :                      nOffset);
    1197           0 :             break;
    1198             :         }
    1199             : 
    1200         220 :         const auto CreateCurrentMarker = [&]()
    1201             :         {
    1202         220 :             return CreateMarker(psCSBox, psLastChildCSBox, psDumpContext,
    1203         220 :                                 GetMarkerName(abyMarker[1]), nOffset,
    1204         440 :                                 nMarkerSize);
    1205         247 :         };
    1206         247 :         CPLXMLNode *psMarker = nullptr;
    1207         247 :         CPLXMLNode *psLastChild = nullptr;
    1208         247 :         if (VSIFReadL(pabyMarkerData, nMarkerSize - 2, 1, fp) != 1)
    1209             :         {
    1210           0 :             psMarker = CreateCurrentMarker();
    1211           0 :             AddError(psMarker, psLastChild, psDumpContext,
    1212             :                      "Cannot read marker data", nOffset);
    1213           0 :             break;
    1214             :         }
    1215         247 :         GByte *pabyMarkerDataIter = pabyMarkerData;
    1216         247 :         GUInt16 nRemainingMarkerSize = nMarkerSize - 2;
    1217         247 :         bool bError = false;
    1218             : 
    1219             :         auto READ_MARKER_FIELD_UINT8 =
    1220         674 :             [&](const char *name, std::string (*commentFunc)(GByte) = nullptr)
    1221             :         {
    1222             :             GByte v;
    1223         674 :             if (nRemainingMarkerSize >= 1)
    1224             :             {
    1225        2022 :                 v = *pabyMarkerDataIter;
    1226             :                 const std::string comment(commentFunc ? commentFunc(v)
    1227         674 :                                                       : std::string());
    1228         674 :                 AddField(psMarker, psLastChild, psDumpContext, name,
    1229         674 :                          *pabyMarkerDataIter,
    1230        1054 :                          comment.empty() ? nullptr : comment.c_str());
    1231         674 :                 pabyMarkerDataIter += 1;
    1232         674 :                 nRemainingMarkerSize -= 1;
    1233             :             }
    1234             :             else
    1235             :             {
    1236           0 :                 AddError(psMarker, psLastChild, psDumpContext,
    1237             :                          CPLSPrintf("Cannot read field %s", name));
    1238           0 :                 v = 0;
    1239           0 :                 bError = true;
    1240             :             }
    1241         674 :             return v;
    1242         247 :         };
    1243             : 
    1244             :         auto READ_MARKER_FIELD_UINT16 =
    1245         308 :             [&](const char *name, std::string (*commentFunc)(GUInt16) = nullptr)
    1246             :         {
    1247             :             GUInt16 v;
    1248         308 :             if (nRemainingMarkerSize >= 2)
    1249             :             {
    1250         616 :                 memcpy(&v, pabyMarkerDataIter, 2);
    1251         308 :                 CPL_MSBPTR16(&v);
    1252             :                 const std::string comment(commentFunc ? commentFunc(v)
    1253         308 :                                                       : std::string());
    1254         486 :                 AddField(psMarker, psLastChild, psDumpContext, name, v,
    1255         486 :                          comment.empty() ? nullptr : comment.c_str());
    1256         308 :                 pabyMarkerDataIter += 2;
    1257         308 :                 nRemainingMarkerSize -= 2;
    1258             :             }
    1259             :             else
    1260             :             {
    1261           0 :                 AddError(psMarker, psLastChild, psDumpContext,
    1262             :                          CPLSPrintf("Cannot read field %s", name));
    1263           0 :                 v = 0;
    1264           0 :                 bError = true;
    1265             :             }
    1266         308 :             return v;
    1267         247 :         };
    1268             : 
    1269             :         auto READ_MARKER_FIELD_UINT32 =
    1270         343 :             [&](const char *name, std::string (*commentFunc)(GUInt32) = nullptr)
    1271             :         {
    1272             :             GUInt32 v;
    1273         343 :             if (nRemainingMarkerSize >= 4)
    1274             :             {
    1275         686 :                 memcpy(&v, pabyMarkerDataIter, 4);
    1276         343 :                 CPL_MSBPTR32(&v);
    1277             :                 const std::string comment(commentFunc ? commentFunc(v)
    1278         343 :                                                       : std::string());
    1279         343 :                 AddField(psMarker, psLastChild, psDumpContext, name, v,
    1280         343 :                          comment.empty() ? nullptr : comment.c_str());
    1281         343 :                 pabyMarkerDataIter += 4;
    1282         343 :                 nRemainingMarkerSize -= 4;
    1283             :             }
    1284             :             else
    1285             :             {
    1286           0 :                 AddError(psMarker, psLastChild, psDumpContext,
    1287             :                          CPLSPrintf("Cannot read field %s", name));
    1288           0 :                 v = 0;
    1289           0 :                 bError = true;
    1290             :             }
    1291         343 :             return v;
    1292         247 :         };
    1293             : 
    1294          45 :         const auto cblkstyleLamba = [](GByte v)
    1295             :         {
    1296          45 :             std::string osInterp;
    1297          45 :             if (v & 0x1)
    1298           0 :                 osInterp += "Selective arithmetic coding bypass";
    1299             :             else
    1300          45 :                 osInterp += "No selective arithmetic coding bypass";
    1301          45 :             osInterp += ", ";
    1302          45 :             if (v & 0x2)
    1303             :                 osInterp +=
    1304           0 :                     "Reset context probabilities on coding pass boundaries";
    1305             :             else
    1306             :                 osInterp += "No reset of context probabilities on coding pass "
    1307          45 :                             "boundaries";
    1308          45 :             osInterp += ", ";
    1309          45 :             if (v & 0x4)
    1310           0 :                 osInterp += "Termination on each coding pass";
    1311             :             else
    1312          45 :                 osInterp += "No termination on each coding pass";
    1313          45 :             osInterp += ", ";
    1314          45 :             if (v & 0x8)
    1315           0 :                 osInterp += "Vertically causal context";
    1316             :             else
    1317          45 :                 osInterp += "No vertically causal context";
    1318          45 :             osInterp += ", ";
    1319          45 :             if (v & 0x10)
    1320           0 :                 osInterp += "Predictable termination";
    1321             :             else
    1322          45 :                 osInterp += "No predictable termination";
    1323          45 :             osInterp += ", ";
    1324          45 :             if (v & 0x20)
    1325           0 :                 osInterp += "Segmentation symbols are used";
    1326             :             else
    1327          45 :                 osInterp += "No segmentation symbols are used";
    1328          45 :             if (v & 0x40)
    1329           0 :                 osInterp += ", High Throughput algorithm";
    1330          45 :             if (v & 0x80)
    1331           0 :                 osInterp += ", Mixed HT and Part1 code-block style";
    1332          45 :             return osInterp;
    1333             :         };
    1334             : 
    1335         247 :         if (abyMarker[1] == 0x90) /* SOT */
    1336             :         {
    1337          51 :             if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1338           8 :                 strstr(psDumpContext->pszCodestreamMarkers, "SOT"))
    1339             :             {
    1340          43 :                 psMarker = CreateCurrentMarker();
    1341          43 :                 if (!psMarker)
    1342           0 :                     break;
    1343          43 :                 READ_MARKER_FIELD_UINT16("Isot");
    1344          43 :                 GUInt32 PSOT = READ_MARKER_FIELD_UINT32("Psot");
    1345          43 :                 READ_MARKER_FIELD_UINT8("TPsot");
    1346          43 :                 READ_MARKER_FIELD_UINT8("TNsot");
    1347          43 :                 if (nRemainingMarkerSize > 0)
    1348           0 :                     AddElement(
    1349             :                         psMarker, psLastChild, psDumpContext,
    1350             :                         CPLCreateXMLElementAndValue(
    1351             :                             nullptr, "RemainingBytes",
    1352             :                             CPLSPrintf(
    1353             :                                 "%d", static_cast<int>(nRemainingMarkerSize))));
    1354             : 
    1355          43 :                 if (PSOT)
    1356          43 :                     nNextTileOffset = nOffset + PSOT;
    1357             :             }
    1358             :         }
    1359         196 :         else if (abyMarker[1] == 0x50) /* CAP (HTJ2K) */
    1360             :         {
    1361           0 :             if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1362           0 :                 strstr(psDumpContext->pszCodestreamMarkers, "CAP"))
    1363             :             {
    1364           0 :                 psMarker = CreateCurrentMarker();
    1365           0 :                 if (!psMarker)
    1366           0 :                     break;
    1367           0 :                 const GUInt32 Pcap = READ_MARKER_FIELD_UINT32("Pcap");
    1368           0 :                 for (int i = 0; i < 32; i++)
    1369             :                 {
    1370           0 :                     if ((Pcap >> (31 - i)) & 1)
    1371             :                     {
    1372           0 :                         if (i + 1 == 15)
    1373             :                         {
    1374           0 :                             READ_MARKER_FIELD_UINT16(
    1375             :                                 CPLSPrintf("Scap_P%d", i + 1),
    1376           0 :                                 [](GUInt16 v)
    1377             :                                 {
    1378           0 :                                     std::string ret;
    1379           0 :                                     if ((v >> 14) == 0)
    1380             :                                         ret = "All code-blocks are HT "
    1381           0 :                                               "code-blocks";
    1382           0 :                                     else if ((v >> 14) == 2)
    1383             :                                         ret = "Either all HT or all Part1 "
    1384           0 :                                               "code-blocks per tile component";
    1385           0 :                                     else if ((v >> 14) == 3)
    1386             :                                         ret = "Mixed HT or all Part1 "
    1387           0 :                                               "code-blocks per tile component";
    1388             :                                     else
    1389             :                                         ret =
    1390           0 :                                             "Reserved value for bit 14 and 15";
    1391           0 :                                     ret += ", ";
    1392           0 :                                     if ((v >> 13) & 1)
    1393             :                                         ret += "More than one HT set per "
    1394           0 :                                                "code-block";
    1395             :                                     else
    1396             :                                         ret +=
    1397           0 :                                             "Zero or one HT set per code-block";
    1398           0 :                                     ret += ", ";
    1399           0 :                                     if ((v >> 12) & 1)
    1400           0 :                                         ret += "ROI marker can be present";
    1401             :                                     else
    1402           0 :                                         ret += "No ROI marker";
    1403           0 :                                     ret += ", ";
    1404           0 :                                     if ((v >> 11) & 1)
    1405           0 :                                         ret += "Heterogeneous codestream";
    1406             :                                     else
    1407           0 :                                         ret += "Homogeneous codestream";
    1408           0 :                                     ret += ", ";
    1409           0 :                                     if ((v >> 5) & 1)
    1410             :                                         ret += "HT code-blocks can be used "
    1411           0 :                                                "with irreversible transforms";
    1412             :                                     else
    1413             :                                         ret += "HT code-blocks only used with "
    1414           0 :                                                "reversible transforms";
    1415           0 :                                     ret += ", ";
    1416           0 :                                     ret += "P=";
    1417           0 :                                     ret += CPLSPrintf("%d", v & 0x31);
    1418           0 :                                     return ret;
    1419             :                                 });
    1420             :                         }
    1421             :                         else
    1422             :                         {
    1423           0 :                             READ_MARKER_FIELD_UINT16(
    1424             :                                 CPLSPrintf("Scap_P%d", i + 1));
    1425             :                         }
    1426             :                     }
    1427             :                 }
    1428           0 :                 if (nRemainingMarkerSize > 0)
    1429           0 :                     AddElement(
    1430             :                         psMarker, psLastChild, psDumpContext,
    1431             :                         CPLCreateXMLElementAndValue(
    1432             :                             nullptr, "RemainingBytes",
    1433             :                             CPLSPrintf(
    1434             :                                 "%d", static_cast<int>(nRemainingMarkerSize))));
    1435             :             }
    1436             :         }
    1437         196 :         else if (abyMarker[1] == 0x51) /* SIZ */
    1438             :         {
    1439          45 :             if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1440           8 :                 strstr(psDumpContext->pszCodestreamMarkers, "SIZ"))
    1441             :             {
    1442          37 :                 psMarker = CreateCurrentMarker();
    1443          37 :                 if (!psMarker)
    1444           0 :                     break;
    1445          37 :                 READ_MARKER_FIELD_UINT16(
    1446             :                     "Rsiz",
    1447          37 :                     [](GUInt16 v)
    1448             :                     {
    1449             :                         return std::string((v == 0)   ? "Unrestricted profile"
    1450          62 :                                            : (v == 1) ? "Profile 0"
    1451          31 :                                            : (v == 2) ? "Profile 1"
    1452           0 :                                            : (v == 16384) ? "HTJ2K"
    1453          68 :                                                           : "");
    1454             :                     });
    1455          37 :                 READ_MARKER_FIELD_UINT32("Xsiz");
    1456          37 :                 READ_MARKER_FIELD_UINT32("Ysiz");
    1457          37 :                 READ_MARKER_FIELD_UINT32("XOsiz");
    1458          37 :                 READ_MARKER_FIELD_UINT32("YOsiz");
    1459          37 :                 READ_MARKER_FIELD_UINT32("XTsiz");
    1460          37 :                 READ_MARKER_FIELD_UINT32("YTsiz");
    1461          37 :                 READ_MARKER_FIELD_UINT32("XTOSiz");
    1462          37 :                 READ_MARKER_FIELD_UINT32("YTOSiz");
    1463          37 :                 Csiz = READ_MARKER_FIELD_UINT16("Csiz");
    1464          37 :                 bError = false;
    1465             :                 // cppcheck-suppress knownConditionTrueFalse
    1466          88 :                 for (int i = 0; i < Csiz && !bError; i++)
    1467             :                 {
    1468          51 :                     READ_MARKER_FIELD_UINT8(
    1469             :                         CPLSPrintf("Ssiz%d", i),
    1470          51 :                         [](GByte v)
    1471             :                         {
    1472          51 :                             const char *psz = GetInterpretationOfBPC(v);
    1473          51 :                             return std::string(psz ? psz : "");
    1474             :                         });
    1475          51 :                     READ_MARKER_FIELD_UINT8(CPLSPrintf("XRsiz%d", i));
    1476          51 :                     READ_MARKER_FIELD_UINT8(CPLSPrintf("YRsiz%d", i));
    1477             :                 }
    1478          37 :                 if (nRemainingMarkerSize > 0)
    1479           0 :                     AddElement(
    1480             :                         psMarker, psLastChild, psDumpContext,
    1481             :                         CPLCreateXMLElementAndValue(
    1482             :                             nullptr, "RemainingBytes",
    1483             :                             CPLSPrintf(
    1484             :                                 "%d", static_cast<int>(nRemainingMarkerSize))));
    1485             :             }
    1486             :         }
    1487         151 :         else if (abyMarker[1] == 0x52) /* COD */
    1488             :         {
    1489          45 :             if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1490           8 :                 strstr(psDumpContext->pszCodestreamMarkers, "COD"))
    1491             :             {
    1492          45 :                 psMarker = CreateCurrentMarker();
    1493          45 :                 if (!psMarker)
    1494           0 :                     break;
    1495          45 :                 bool bHasPrecincts = false;
    1496          45 :                 if (nRemainingMarkerSize >= 1)
    1497             :                 {
    1498          45 :                     auto nLastVal = *pabyMarkerDataIter;
    1499          45 :                     CPLString osInterp;
    1500          45 :                     if (nLastVal & 0x1)
    1501             :                     {
    1502          38 :                         bHasPrecincts = true;
    1503          38 :                         osInterp += "User defined precincts";
    1504             :                     }
    1505             :                     else
    1506           7 :                         osInterp += "Standard precincts";
    1507          45 :                     osInterp += ", ";
    1508          45 :                     if (nLastVal & 0x2)
    1509           0 :                         osInterp += "SOP marker segments may be used";
    1510             :                     else
    1511          45 :                         osInterp += "No SOP marker segments";
    1512          45 :                     osInterp += ", ";
    1513          45 :                     if (nLastVal & 0x4)
    1514           0 :                         osInterp += "EPH marker segments may be used";
    1515             :                     else
    1516          45 :                         osInterp += "No EPH marker segments";
    1517          45 :                     AddField(psMarker, psLastChild, psDumpContext, "Scod",
    1518             :                              nLastVal, osInterp.c_str());
    1519          45 :                     pabyMarkerDataIter += 1;
    1520          45 :                     nRemainingMarkerSize -= 1;
    1521             :                 }
    1522             :                 else
    1523             :                 {
    1524           0 :                     AddError(psMarker, psLastChild, psDumpContext,
    1525             :                              CPLSPrintf("Cannot read field %s", "Scod"));
    1526             :                 }
    1527          45 :                 READ_MARKER_FIELD_UINT8("SGcod_Progress", lambdaPOCType);
    1528          45 :                 READ_MARKER_FIELD_UINT16("SGcod_NumLayers");
    1529          45 :                 READ_MARKER_FIELD_UINT8("SGcod_MCT");
    1530          45 :                 READ_MARKER_FIELD_UINT8("SPcod_NumDecompositions");
    1531          45 :                 READ_MARKER_FIELD_UINT8(
    1532             :                     "SPcod_xcb_minus_2",
    1533          45 :                     [](GByte v)
    1534             :                     {
    1535             :                         return std::string(v <= 8
    1536          45 :                                                ? CPLSPrintf("%d", 1 << (2 + v))
    1537          90 :                                                : "invalid");
    1538             :                     });
    1539          45 :                 READ_MARKER_FIELD_UINT8(
    1540             :                     "SPcod_ycb_minus_2",
    1541          45 :                     [](GByte v)
    1542             :                     {
    1543             :                         return std::string(v <= 8
    1544          45 :                                                ? CPLSPrintf("%d", 1 << (2 + v))
    1545          90 :                                                : "invalid");
    1546             :                     });
    1547          45 :                 READ_MARKER_FIELD_UINT8("SPcod_cbstyle", cblkstyleLamba);
    1548          45 :                 READ_MARKER_FIELD_UINT8("SPcod_transformation",
    1549          45 :                                         [](GByte v)
    1550             :                                         {
    1551             :                                             return std::string(
    1552             :                                                 (v == 0)   ? "9-7 irreversible"
    1553          16 :                                                 : (v == 1) ? "5-3 reversible"
    1554          61 :                                                            : "");
    1555             :                                         });
    1556          45 :                 if (bHasPrecincts)
    1557             :                 {
    1558          38 :                     int i = 0;
    1559         116 :                     while (nRemainingMarkerSize >= 1)
    1560             :                     {
    1561          78 :                         auto nLastVal = *pabyMarkerDataIter;
    1562          78 :                         AddField(psMarker, psLastChild, psDumpContext,
    1563             :                                  CPLSPrintf("SPcod_Precincts%d", i),
    1564          78 :                                  *pabyMarkerDataIter,
    1565             :                                  CPLSPrintf("PPx=%d PPy=%d: %dx%d",
    1566          78 :                                             nLastVal & 0xf, nLastVal >> 4,
    1567          78 :                                             1 << (nLastVal & 0xf),
    1568          78 :                                             1 << (nLastVal >> 4)));
    1569          78 :                         pabyMarkerDataIter += 1;
    1570          78 :                         nRemainingMarkerSize -= 1;
    1571          78 :                         i++;
    1572             :                     }
    1573             :                 }
    1574          45 :                 if (nRemainingMarkerSize > 0)
    1575           0 :                     AddElement(
    1576             :                         psMarker, psLastChild, psDumpContext,
    1577             :                         CPLCreateXMLElementAndValue(
    1578             :                             nullptr, "RemainingBytes",
    1579             :                             CPLSPrintf(
    1580             :                                 "%d", static_cast<int>(nRemainingMarkerSize))));
    1581             :             }
    1582             :         }
    1583         106 :         else if (abyMarker[1] == 0x53) /* COC */
    1584             :         {
    1585           0 :             if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1586           0 :                 strstr(psDumpContext->pszCodestreamMarkers, "COC"))
    1587             :             {
    1588           0 :                 psMarker = CreateCurrentMarker();
    1589           0 :                 if (!psMarker)
    1590           0 :                     break;
    1591           0 :                 if (Csiz < 257)
    1592           0 :                     READ_MARKER_FIELD_UINT8("Ccoc");
    1593             :                 else
    1594           0 :                     READ_MARKER_FIELD_UINT16("Ccoc");
    1595             : 
    1596           0 :                 bool bHasPrecincts = false;
    1597           0 :                 if (nRemainingMarkerSize >= 1)
    1598             :                 {
    1599           0 :                     auto nLastVal = *pabyMarkerDataIter;
    1600           0 :                     CPLString osInterp;
    1601           0 :                     if (nLastVal & 0x1)
    1602             :                     {
    1603           0 :                         bHasPrecincts = true;
    1604           0 :                         osInterp += "User defined precincts";
    1605             :                     }
    1606             :                     else
    1607           0 :                         osInterp += "Standard precincts";
    1608           0 :                     AddField(psMarker, psLastChild, psDumpContext, "Scoc",
    1609             :                              nLastVal, osInterp.c_str());
    1610           0 :                     pabyMarkerDataIter += 1;
    1611           0 :                     nRemainingMarkerSize -= 1;
    1612             :                 }
    1613             :                 else
    1614             :                 {
    1615           0 :                     AddError(psMarker, psLastChild, psDumpContext,
    1616             :                              CPLSPrintf("Cannot read field %s", "Scoc"));
    1617             :                 }
    1618           0 :                 READ_MARKER_FIELD_UINT8("SPcoc_NumDecompositions");
    1619           0 :                 READ_MARKER_FIELD_UINT8(
    1620             :                     "SPcoc_xcb_minus_2",
    1621           0 :                     [](GByte v)
    1622             :                     {
    1623             :                         return std::string(v <= 8
    1624           0 :                                                ? CPLSPrintf("%d", 1 << (2 + v))
    1625           0 :                                                : "invalid");
    1626             :                     });
    1627           0 :                 READ_MARKER_FIELD_UINT8(
    1628             :                     "SPcoc_ycb_minus_2",
    1629           0 :                     [](GByte v)
    1630             :                     {
    1631             :                         return std::string(v <= 8
    1632           0 :                                                ? CPLSPrintf("%d", 1 << (2 + v))
    1633           0 :                                                : "invalid");
    1634             :                     });
    1635           0 :                 READ_MARKER_FIELD_UINT8("SPcoc_cbstyle", cblkstyleLamba);
    1636           0 :                 READ_MARKER_FIELD_UINT8("SPcoc_transformation",
    1637           0 :                                         [](GByte v)
    1638             :                                         {
    1639             :                                             return std::string(
    1640             :                                                 (v == 0)   ? "9-7 irreversible"
    1641           0 :                                                 : (v == 1) ? "5-3 reversible"
    1642           0 :                                                            : "");
    1643             :                                         });
    1644           0 :                 if (bHasPrecincts)
    1645             :                 {
    1646           0 :                     int i = 0;
    1647           0 :                     while (nRemainingMarkerSize >= 1)
    1648             :                     {
    1649           0 :                         auto nLastVal = *pabyMarkerDataIter;
    1650           0 :                         AddField(psMarker, psLastChild, psDumpContext,
    1651             :                                  CPLSPrintf("SPcoc_Precincts%d", i),
    1652           0 :                                  *pabyMarkerDataIter,
    1653             :                                  CPLSPrintf("PPx=%d PPy=%d: %dx%d",
    1654           0 :                                             nLastVal & 0xf, nLastVal >> 4,
    1655           0 :                                             1 << (nLastVal & 0xf),
    1656           0 :                                             1 << (nLastVal >> 4)));
    1657           0 :                         pabyMarkerDataIter += 1;
    1658           0 :                         nRemainingMarkerSize -= 1;
    1659           0 :                         i++;
    1660             :                     }
    1661             :                 }
    1662           0 :                 if (nRemainingMarkerSize > 0)
    1663           0 :                     AddElement(
    1664             :                         psMarker, psLastChild, psDumpContext,
    1665             :                         CPLCreateXMLElementAndValue(
    1666             :                             nullptr, "RemainingBytes",
    1667             :                             CPLSPrintf(
    1668             :                                 "%d", static_cast<int>(nRemainingMarkerSize))));
    1669             :             }
    1670             :         }
    1671         106 :         else if (abyMarker[1] == 0x55) /* TLM */
    1672             :         {
    1673           1 :             if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1674           0 :                 strstr(psDumpContext->pszCodestreamMarkers, "TLM"))
    1675             :             {
    1676           1 :                 psMarker = CreateCurrentMarker();
    1677           1 :                 if (!psMarker)
    1678           0 :                     break;
    1679           1 :                 READ_MARKER_FIELD_UINT8("Ztlm");
    1680           1 :                 auto Stlm = READ_MARKER_FIELD_UINT8(
    1681             :                     "Stlm",
    1682           1 :                     [](GByte v)
    1683             :                     {
    1684             :                         return std::string(CPLSPrintf(
    1685           1 :                             "ST=%d SP=%d", (v >> 4) & 3, (v >> 6) & 1));
    1686             :                     });
    1687           1 :                 int ST = (Stlm >> 4) & 3;
    1688           1 :                 int SP = (Stlm >> 6) & 1;
    1689           1 :                 int nTilePartDescLength = ST + ((SP == 0) ? 2 : 4);
    1690           1 :                 int i = 0;
    1691           5 :                 while (nRemainingMarkerSize >= nTilePartDescLength)
    1692             :                 {
    1693           4 :                     if (ST == 1)
    1694           0 :                         READ_MARKER_FIELD_UINT8(CPLSPrintf("Ttlm%d", i));
    1695           4 :                     else if (ST == 2)
    1696           4 :                         READ_MARKER_FIELD_UINT16(CPLSPrintf("Ttlm%d", i));
    1697           4 :                     if (SP == 0)
    1698           0 :                         READ_MARKER_FIELD_UINT16(CPLSPrintf("Ptlm%d", i));
    1699             :                     else
    1700           4 :                         READ_MARKER_FIELD_UINT32(CPLSPrintf("Ptlm%d", i));
    1701           4 :                     i++;
    1702             :                 }
    1703           1 :                 if (nRemainingMarkerSize > 0)
    1704           0 :                     AddElement(
    1705             :                         psMarker, psLastChild, psDumpContext,
    1706             :                         CPLCreateXMLElementAndValue(
    1707             :                             nullptr, "RemainingBytes",
    1708             :                             CPLSPrintf(
    1709             :                                 "%d", static_cast<int>(nRemainingMarkerSize))));
    1710             :             }
    1711             :         }
    1712         105 :         else if (abyMarker[1] == 0x57) /* PLM */
    1713             :         {
    1714           0 :             if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1715           0 :                 strstr(psDumpContext->pszCodestreamMarkers, "PLM"))
    1716             :             {
    1717           0 :                 psMarker = CreateCurrentMarker();
    1718           0 :                 if (!psMarker)
    1719           0 :                     break;
    1720             :             }
    1721             :         }
    1722         105 :         else if (abyMarker[1] == 0x58) /* PLT */
    1723             :         {
    1724          13 :             if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1725           3 :                 strstr(psDumpContext->pszCodestreamMarkers, "PLT"))
    1726             :             {
    1727          10 :                 psMarker = CreateCurrentMarker();
    1728          10 :                 if (!psMarker)
    1729           0 :                     break;
    1730          10 :                 READ_MARKER_FIELD_UINT8("Zplt");
    1731          10 :                 int i = 0;
    1732          10 :                 unsigned nPacketLength = 0;
    1733         196 :                 while (nRemainingMarkerSize >= 1)
    1734             :                 {
    1735         186 :                     auto nLastVal = *pabyMarkerDataIter;
    1736         186 :                     nPacketLength |= (nLastVal & 0x7f);
    1737         186 :                     if (nLastVal & 0x80)
    1738             :                     {
    1739          16 :                         nPacketLength <<= 7;
    1740             :                     }
    1741             :                     else
    1742             :                     {
    1743         170 :                         AddField(psMarker, psLastChild, psDumpContext,
    1744             :                                  CPLSPrintf("Iplt%d", i), nPacketLength);
    1745         170 :                         nPacketLength = 0;
    1746         170 :                         i++;
    1747             :                     }
    1748         186 :                     pabyMarkerDataIter += 1;
    1749         186 :                     nRemainingMarkerSize -= 1;
    1750             :                 }
    1751          10 :                 if (nPacketLength != 0)
    1752             :                 {
    1753           0 :                     AddError(psMarker, psLastChild, psDumpContext,
    1754             :                              "Incorrect PLT marker");
    1755             :                 }
    1756             :             }
    1757             :         }
    1758          92 :         else if (abyMarker[1] == 0x59) /* CPF (HTJ2K) */
    1759             :         {
    1760           0 :             if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1761           0 :                 strstr(psDumpContext->pszCodestreamMarkers, "CPF"))
    1762             :             {
    1763           0 :                 psMarker = CreateCurrentMarker();
    1764           0 :                 if (!psMarker)
    1765           0 :                     break;
    1766           0 :                 const GUInt16 Lcpf = nMarkerSize;
    1767           0 :                 if (Lcpf > 2 && (Lcpf % 2) == 0)
    1768             :                 {
    1769           0 :                     for (int i = 0; i < (Lcpf - 2) / 2; i++)
    1770             :                     {
    1771           0 :                         READ_MARKER_FIELD_UINT16(CPLSPrintf("Pcpf%d", i + 1));
    1772             :                     }
    1773             :                 }
    1774           0 :                 if (nRemainingMarkerSize > 0)
    1775           0 :                     AddElement(
    1776             :                         psMarker, psLastChild, psDumpContext,
    1777             :                         CPLCreateXMLElementAndValue(
    1778             :                             nullptr, "RemainingBytes",
    1779             :                             CPLSPrintf(
    1780             :                                 "%d", static_cast<int>(nRemainingMarkerSize))));
    1781             :             }
    1782             :         }
    1783          92 :         else if (abyMarker[1] == 0x5C) /* QCD */
    1784             :         {
    1785          45 :             if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1786           8 :                 strstr(psDumpContext->pszCodestreamMarkers, "QCD"))
    1787             :             {
    1788          37 :                 psMarker = CreateCurrentMarker();
    1789          37 :                 if (!psMarker)
    1790           0 :                     break;
    1791          37 :                 const int Sqcd = READ_MARKER_FIELD_UINT8(
    1792             :                     "Sqcd",
    1793          37 :                     [](GByte v)
    1794             :                     {
    1795          37 :                         std::string ret;
    1796          37 :                         if ((v & 31) == 0)
    1797          10 :                             ret = "No quantization";
    1798          27 :                         else if ((v & 31) == 1)
    1799           0 :                             ret = "Scalar derived";
    1800          27 :                         else if ((v & 31) == 2)
    1801          27 :                             ret = "Scalar expounded";
    1802          37 :                         ret += ", ";
    1803          37 :                         ret += CPLSPrintf("guard bits = %d", v >> 5);
    1804          37 :                         return ret;
    1805          37 :                     });
    1806          37 :                 if ((Sqcd & 31) == 0)
    1807             :                 {
    1808             :                     // Reversible
    1809          10 :                     int i = 0;
    1810          74 :                     while (nRemainingMarkerSize >= 1)
    1811             :                     {
    1812          64 :                         READ_MARKER_FIELD_UINT8(
    1813             :                             CPLSPrintf("SPqcd%d", i),
    1814          64 :                             [](GByte v)
    1815             :                             {
    1816             :                                 return std::string(
    1817          64 :                                     CPLSPrintf("epsilon_b = %d", v >> 3));
    1818             :                             });
    1819          64 :                         ++i;
    1820             :                     }
    1821             :                 }
    1822             :                 else
    1823             :                 {
    1824          27 :                     int i = 0;
    1825         123 :                     while (nRemainingMarkerSize >= 2)
    1826             :                     {
    1827          96 :                         READ_MARKER_FIELD_UINT16(
    1828             :                             CPLSPrintf("SPqcd%d", i),
    1829          96 :                             [](GUInt16 v)
    1830             :                             {
    1831             :                                 return std::string(CPLSPrintf(
    1832             :                                     "mantissa_b = %d, epsilon_b = %d",
    1833          96 :                                     v & ((1 << 11) - 1), v >> 11));
    1834             :                             });
    1835          96 :                         ++i;
    1836             :                     }
    1837             :                 }
    1838             :             }
    1839             :         }
    1840          47 :         else if (abyMarker[1] == 0x5D) /* QCC */
    1841             :         {
    1842           1 :             if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1843           0 :                 strstr(psDumpContext->pszCodestreamMarkers, "QCC"))
    1844             :             {
    1845           1 :                 psMarker = CreateCurrentMarker();
    1846           1 :                 if (!psMarker)
    1847           0 :                     break;
    1848           1 :                 if (Csiz < 257)
    1849           1 :                     READ_MARKER_FIELD_UINT8("Cqcc");
    1850             :                 else
    1851           0 :                     READ_MARKER_FIELD_UINT16("Cqcc");
    1852             : 
    1853           1 :                 const int Sqcc = READ_MARKER_FIELD_UINT8(
    1854             :                     "Sqcc",
    1855           1 :                     [](GByte v)
    1856             :                     {
    1857           1 :                         std::string ret;
    1858           1 :                         if ((v & 31) == 0)
    1859           0 :                             ret = "No quantization";
    1860           1 :                         else if ((v & 31) == 1)
    1861           0 :                             ret = "Scalar derived";
    1862           1 :                         else if ((v & 31) == 2)
    1863           1 :                             ret = "Scalar expounded";
    1864           1 :                         ret += ", ";
    1865           1 :                         ret += CPLSPrintf("guard bits = %d", v >> 5);
    1866           1 :                         return ret;
    1867           1 :                     });
    1868           1 :                 if ((Sqcc & 31) == 0)
    1869             :                 {
    1870             :                     // Reversible
    1871           0 :                     int i = 0;
    1872           0 :                     while (nRemainingMarkerSize >= 1)
    1873             :                     {
    1874           0 :                         READ_MARKER_FIELD_UINT8(
    1875             :                             CPLSPrintf("SPqcc%d", i),
    1876           0 :                             [](GByte v)
    1877             :                             {
    1878             :                                 return std::string(
    1879           0 :                                     CPLSPrintf("epsilon_b = %d", v >> 3));
    1880             :                             });
    1881           0 :                         ++i;
    1882             :                     }
    1883             :                 }
    1884             :                 else
    1885             :                 {
    1886           1 :                     int i = 0;
    1887           2 :                     while (nRemainingMarkerSize >= 2)
    1888             :                     {
    1889           1 :                         READ_MARKER_FIELD_UINT16(
    1890             :                             CPLSPrintf("SPqcc%d", i),
    1891           1 :                             [](GUInt16 v)
    1892             :                             {
    1893             :                                 return std::string(CPLSPrintf(
    1894             :                                     "mantissa_b = %d, epsilon_b = %d",
    1895           1 :                                     v & ((1 << 11) - 1), v >> 11));
    1896             :                             });
    1897           1 :                         ++i;
    1898             :                     }
    1899             :                 }
    1900             :             }
    1901             :         }
    1902          46 :         else if (abyMarker[1] == 0x5E) /* RGN */
    1903             :         {
    1904           1 :             if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1905           0 :                 strstr(psDumpContext->pszCodestreamMarkers, "RGN"))
    1906             :             {
    1907           1 :                 psMarker = CreateCurrentMarker();
    1908           1 :                 if (!psMarker)
    1909           0 :                     break;
    1910             :             }
    1911             :         }
    1912          45 :         else if (abyMarker[1] == 0x5F) /* POC */
    1913             :         {
    1914           1 :             if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1915           0 :                 strstr(psDumpContext->pszCodestreamMarkers, "POC"))
    1916             :             {
    1917           1 :                 psMarker = CreateCurrentMarker();
    1918           1 :                 if (!psMarker)
    1919           0 :                     break;
    1920           1 :                 const int nPOCEntrySize = Csiz < 257 ? 7 : 9;
    1921           1 :                 int i = 0;
    1922           2 :                 while (nRemainingMarkerSize >= nPOCEntrySize)
    1923             :                 {
    1924           1 :                     READ_MARKER_FIELD_UINT8(CPLSPrintf("RSpoc%d", i));
    1925           1 :                     if (nPOCEntrySize == 7)
    1926             :                     {
    1927           1 :                         READ_MARKER_FIELD_UINT8(CPLSPrintf("CSpoc%d", i));
    1928             :                     }
    1929             :                     else
    1930             :                     {
    1931           0 :                         READ_MARKER_FIELD_UINT16(CPLSPrintf("CSpoc%d", i));
    1932             :                     }
    1933           1 :                     READ_MARKER_FIELD_UINT16(CPLSPrintf("LYEpoc%d", i));
    1934           1 :                     READ_MARKER_FIELD_UINT8(CPLSPrintf("REpoc%d", i));
    1935           1 :                     if (nPOCEntrySize == 7)
    1936             :                     {
    1937           1 :                         READ_MARKER_FIELD_UINT8(CPLSPrintf("CEpoc%d", i));
    1938             :                     }
    1939             :                     else
    1940             :                     {
    1941           0 :                         READ_MARKER_FIELD_UINT16(CPLSPrintf("CEpoc%d", i));
    1942             :                     }
    1943           1 :                     READ_MARKER_FIELD_UINT8(CPLSPrintf("Ppoc%d", i),
    1944             :                                             lambdaPOCType);
    1945           1 :                     i++;
    1946             :                 }
    1947           1 :                 if (nRemainingMarkerSize > 0)
    1948             :                 {
    1949           0 :                     AddElement(
    1950             :                         psMarker, psLastChild, psDumpContext,
    1951             :                         CPLCreateXMLElementAndValue(
    1952             :                             nullptr, "RemainingBytes",
    1953             :                             CPLSPrintf(
    1954             :                                 "%d", static_cast<int>(nRemainingMarkerSize))));
    1955             :                 }
    1956             :             }
    1957             :         }
    1958          44 :         else if (abyMarker[1] == 0x60) /* PPM */
    1959             :         {
    1960           0 :             if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1961           0 :                 strstr(psDumpContext->pszCodestreamMarkers, "PPM"))
    1962             :             {
    1963           0 :                 psMarker = CreateCurrentMarker();
    1964           0 :                 if (!psMarker)
    1965           0 :                     break;
    1966             :             }
    1967             :         }
    1968          44 :         else if (abyMarker[1] == 0x61) /* PPT */
    1969             :         {
    1970           0 :             if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1971           0 :                 strstr(psDumpContext->pszCodestreamMarkers, "PPT"))
    1972             :             {
    1973           0 :                 psMarker = CreateCurrentMarker();
    1974           0 :                 if (!psMarker)
    1975           0 :                     break;
    1976             :             }
    1977             :         }
    1978          44 :         else if (abyMarker[1] == 0x63) /* CRG */
    1979             :         {
    1980           0 :             if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1981           0 :                 strstr(psDumpContext->pszCodestreamMarkers, "CRG"))
    1982             :             {
    1983           0 :                 psMarker = CreateCurrentMarker();
    1984           0 :                 if (!psMarker)
    1985           0 :                     break;
    1986             :             }
    1987             :         }
    1988          44 :         else if (abyMarker[1] == 0x64) /* COM */
    1989             :         {
    1990          44 :             if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1991          11 :                 strstr(psDumpContext->pszCodestreamMarkers, "COM"))
    1992             :             {
    1993          44 :                 psMarker = CreateCurrentMarker();
    1994          44 :                 if (!psMarker)
    1995           0 :                     break;
    1996          44 :                 auto RCom = READ_MARKER_FIELD_UINT16(
    1997             :                     "Rcom",
    1998          44 :                     [](GUInt16 v)
    1999             :                     {
    2000             :                         return std::string((v == 0)   ? "Binary"
    2001          44 :                                            : (v == 1) ? "LATIN1"
    2002          88 :                                                       : "");
    2003             :                     });
    2004          44 :                 if (RCom == 1)
    2005             :                 {
    2006          44 :                     GByte abyBackup = pabyMarkerDataIter[nRemainingMarkerSize];
    2007          44 :                     pabyMarkerDataIter[nRemainingMarkerSize] = 0;
    2008          44 :                     AddField(
    2009             :                         psMarker, psLastChild, psDumpContext, "COM",
    2010             :                         static_cast<int>(nRemainingMarkerSize),
    2011             :                         reinterpret_cast<const char *>(pabyMarkerDataIter));
    2012          44 :                     pabyMarkerDataIter[nRemainingMarkerSize] = abyBackup;
    2013             :                 }
    2014             :             }
    2015             :         }
    2016             : 
    2017         247 :         if (VSIFSeekL(fp, nOffset + 2 + nMarkerSize, SEEK_SET) != 0)
    2018             :         {
    2019           0 :             AddError(psCSBox, psLastChildCSBox, psDumpContext,
    2020           0 :                      "Cannot seek to next marker", nOffset + 2 + nMarkerSize);
    2021           0 :             break;
    2022             :         }
    2023             : 
    2024         247 :         CPL_IGNORE_RET_VAL(bError);
    2025             :     }
    2026          47 :     CPLFree(pabyMarkerData);
    2027          47 :     return psCSBox;
    2028             : }
    2029             : 
    2030             : /************************************************************************/
    2031             : /*                  GDALGetJPEG2000StructureInternal()                  */
    2032             : /************************************************************************/
    2033             : 
    2034         121 : static void GDALGetJPEG2000StructureInternal(CPLXMLNode *psParent, VSILFILE *fp,
    2035             :                                              GDALJP2Box *poParentBox,
    2036             :                                              int nRecLevel,
    2037             :                                              vsi_l_offset nFileOrParentBoxSize,
    2038             :                                              DumpContext *psDumpContext)
    2039             : {
    2040             :     // Limit recursion to a reasonable level. I believe that in practice 2
    2041             :     // should be sufficient, but just in case someone creates deeply
    2042             :     // nested "super-boxes", allow up to 5.
    2043         121 :     if (nRecLevel == 5)
    2044           0 :         return;
    2045             : 
    2046             :     static const char *const szHex = "0123456789ABCDEF";
    2047         242 :     GDALJP2Box oBox(fp);
    2048         121 :     oBox.SetAllowGetFileSize(psDumpContext->bAllowGetFileSize);
    2049         121 :     CPLXMLNode *psLastChild = nullptr;
    2050         121 :     if (oBox.ReadFirstChild(poParentBox))
    2051             :     {
    2052         806 :         while (strlen(oBox.GetType()) > 0 &&
    2053         403 :                psDumpContext->nCurLineCount <= psDumpContext->nMaxLineCount + 1)
    2054             :         {
    2055         401 :             GIntBig nBoxDataLength = oBox.GetDataLength();
    2056         401 :             const char *pszBoxType = oBox.GetType();
    2057         401 :             CPLXMLNode *psBox = nullptr;
    2058         758 :             const auto CreateBox = [&]()
    2059             :             {
    2060         758 :                 if (psBox != nullptr)
    2061         401 :                     return true;
    2062         357 :                 psBox = CPLCreateXMLNode(nullptr, CXT_Element, "JP2Box");
    2063         357 :                 psBox = AddElement(psParent, psLastChild, psDumpContext, psBox);
    2064         357 :                 if (!psBox)
    2065           0 :                     return false;
    2066         357 :                 CPLAddXMLAttributeAndValue(psBox, "name", pszBoxType);
    2067         357 :                 CPLAddXMLAttributeAndValue(
    2068             :                     psBox, "box_offset",
    2069         357 :                     CPLSPrintf(CPL_FRMT_GIB, oBox.GetBoxOffset()));
    2070         357 :                 const auto nBoxLength = oBox.GetBoxLength();
    2071         711 :                 CPLAddXMLAttributeAndValue(
    2072             :                     psBox, "box_length",
    2073         354 :                     nBoxLength > 0 ? CPLSPrintf(CPL_FRMT_GIB, nBoxLength)
    2074             :                                    : "unknown");
    2075         357 :                 CPLAddXMLAttributeAndValue(
    2076             :                     psBox, "data_offset",
    2077             :                     CPLSPrintf(CPL_FRMT_GIB, oBox.GetDataOffset()));
    2078         357 :                 CPLAddXMLAttributeAndValue(
    2079             :                     psBox, "data_length",
    2080         357 :                     nBoxDataLength > 0
    2081         352 :                         ? CPLSPrintf(CPL_FRMT_GIB, nBoxDataLength)
    2082             :                         : "unknown");
    2083             : 
    2084         357 :                 if (nBoxDataLength > GINTBIG_MAX - oBox.GetDataOffset())
    2085             :                 {
    2086           0 :                     CPLXMLNode *psLastChildBox = nullptr;
    2087           0 :                     AddError(psBox, psLastChildBox, psDumpContext,
    2088             :                              "Invalid box_length");
    2089           0 :                     return false;
    2090             :                 }
    2091         357 :                 return true;
    2092         401 :             };
    2093             : 
    2094             :             // Check large non-jp2c boxes against filesize
    2095         401 :             if (strcmp(pszBoxType, "jp2c") != 0 && nBoxDataLength > 100 * 1024)
    2096             :             {
    2097           0 :                 if (nFileOrParentBoxSize == 0)
    2098             :                 {
    2099           0 :                     CPL_IGNORE_RET_VAL(VSIFSeekL(fp, 0, SEEK_END));
    2100           0 :                     nFileOrParentBoxSize = VSIFTellL(fp);
    2101             :                 }
    2102             :             }
    2103         585 :             if (nFileOrParentBoxSize > 0 && nBoxDataLength > 0 &&
    2104         184 :                 (static_cast<vsi_l_offset>(oBox.GetDataOffset()) >
    2105         184 :                      nFileOrParentBoxSize ||
    2106         184 :                  static_cast<vsi_l_offset>(nBoxDataLength) >
    2107         184 :                      nFileOrParentBoxSize - oBox.GetDataOffset()))
    2108             :             {
    2109           0 :                 CPLXMLNode *psLastChildBox = nullptr;
    2110           0 :                 if (!CreateBox())
    2111           0 :                     break;
    2112           0 :                 AddError(psBox, psLastChildBox, psDumpContext,
    2113             :                          "Invalid box_length");
    2114           0 :                 break;
    2115             :             }
    2116             : 
    2117         401 :             if (oBox.IsSuperBox())
    2118             :             {
    2119          80 :                 if (!CreateBox())
    2120           0 :                     break;
    2121          80 :                 if (nBoxDataLength <= 0)
    2122           0 :                     break;
    2123         160 :                 GDALGetJPEG2000StructureInternal(
    2124             :                     psBox, fp, &oBox, nRecLevel + 1,
    2125          80 :                     oBox.GetDataOffset() +
    2126          80 :                         static_cast<vsi_l_offset>(nBoxDataLength),
    2127             :                     psDumpContext);
    2128             :             }
    2129             :             else
    2130             :             {
    2131         321 :                 if (strcmp(pszBoxType, "uuid") == 0 &&
    2132          23 :                     psDumpContext->bDumpJP2Boxes)
    2133             :                 {
    2134          17 :                     if (!CreateBox())
    2135           0 :                         break;
    2136             :                     char *pszBinaryContent =
    2137          17 :                         static_cast<char *>(VSIMalloc(2 * 16 + 1));
    2138          17 :                     const GByte *pabyUUID = oBox.GetUUID();
    2139         289 :                     for (int i = 0; i < 16; i++)
    2140             :                     {
    2141         272 :                         pszBinaryContent[2 * i] = szHex[pabyUUID[i] >> 4];
    2142         272 :                         pszBinaryContent[2 * i + 1] = szHex[pabyUUID[i] & 0xf];
    2143             :                     }
    2144          17 :                     pszBinaryContent[2 * 16] = '\0';
    2145             :                     CPLXMLNode *psUUIDNode =
    2146          17 :                         CPLCreateXMLNode(nullptr, CXT_Element, "UUID");
    2147          17 :                     if (GDALJP2Metadata::IsUUID_MSI(pabyUUID))
    2148          16 :                         CPLAddXMLAttributeAndValue(psUUIDNode, "description",
    2149             :                                                    "GeoTIFF");
    2150           1 :                     else if (GDALJP2Metadata::IsUUID_XMP(pabyUUID))
    2151           1 :                         CPLAddXMLAttributeAndValue(psUUIDNode, "description",
    2152             :                                                    "XMP");
    2153          17 :                     CPLCreateXMLNode(psUUIDNode, CXT_Text, pszBinaryContent);
    2154          17 :                     VSIFree(pszBinaryContent);
    2155             : 
    2156          17 :                     CPLXMLNode *psLastChildBox = nullptr;
    2157          17 :                     AddElement(psBox, psLastChildBox, psDumpContext,
    2158             :                                psUUIDNode);
    2159             :                 }
    2160             : 
    2161         321 :                 if (psDumpContext->bDumpBinaryContent &&
    2162         271 :                     strcmp(pszBoxType, "jp2c") != 0 &&
    2163         236 :                     nBoxDataLength < 100 * 1024)
    2164             :                 {
    2165         236 :                     if (!CreateBox())
    2166           0 :                         break;
    2167             :                     CPLXMLNode *psBinaryContent =
    2168         236 :                         CPLCreateXMLNode(nullptr, CXT_Element, "BinaryContent");
    2169         236 :                     GByte *pabyBoxData = oBox.ReadBoxData();
    2170             :                     const int nBoxLength = static_cast<int>(
    2171         236 :                         std::min<GIntBig>(nBoxDataLength, INT_MAX / 2 - 1));
    2172             :                     char *pszBinaryContent =
    2173         236 :                         static_cast<char *>(VSIMalloc(2 * nBoxLength + 1));
    2174         236 :                     if (pabyBoxData && pszBinaryContent)
    2175             :                     {
    2176       47647 :                         for (int i = 0; i < nBoxLength; i++)
    2177             :                         {
    2178       47411 :                             pszBinaryContent[2 * i] =
    2179       47411 :                                 szHex[pabyBoxData[i] >> 4];
    2180       47411 :                             pszBinaryContent[2 * i + 1] =
    2181       47411 :                                 szHex[pabyBoxData[i] & 0xf];
    2182             :                         }
    2183         236 :                         pszBinaryContent[2 * nBoxLength] = '\0';
    2184         236 :                         CPLCreateXMLNode(psBinaryContent, CXT_Text,
    2185             :                                          pszBinaryContent);
    2186             :                     }
    2187         236 :                     CPLFree(pabyBoxData);
    2188         236 :                     VSIFree(pszBinaryContent);
    2189             : 
    2190         236 :                     CPLXMLNode *psLastChildBox = nullptr;
    2191         236 :                     AddElement(psBox, psLastChildBox, psDumpContext,
    2192             :                                psBinaryContent);
    2193             :                 }
    2194             : 
    2195         321 :                 if (psDumpContext->bDumpTextContent &&
    2196         271 :                     strcmp(pszBoxType, "jp2c") != 0 &&
    2197         236 :                     nBoxDataLength < 100 * 1024)
    2198             :                 {
    2199         236 :                     if (!CreateBox())
    2200           0 :                         break;
    2201         236 :                     GByte *pabyBoxData = oBox.ReadBoxData();
    2202         236 :                     if (pabyBoxData)
    2203             :                     {
    2204         236 :                         const char *pszBoxData =
    2205             :                             reinterpret_cast<const char *>(pabyBoxData);
    2206         427 :                         if (CPLIsUTF8(pszBoxData, -1) &&
    2207         191 :                             static_cast<int>(strlen(pszBoxData)) + 2 >=
    2208             :                                 nBoxDataLength)
    2209             :                         {
    2210          58 :                             CPLXMLNode *psXMLContentBox = nullptr;
    2211          58 :                             if (pszBoxData[0] == '<')
    2212             :                             {
    2213          22 :                                 CPLPushErrorHandler(CPLQuietErrorHandler);
    2214          22 :                                 psXMLContentBox = CPLParseXMLString(pszBoxData);
    2215          22 :                                 CPLPopErrorHandler();
    2216             :                             }
    2217          58 :                             if (psXMLContentBox)
    2218             :                             {
    2219          22 :                                 CPLXMLNode *psXMLContentNode = CPLCreateXMLNode(
    2220             :                                     nullptr, CXT_Element, "XMLContent");
    2221          22 :                                 psXMLContentNode->psChild = psXMLContentBox;
    2222             : 
    2223          22 :                                 CPLXMLNode *psLastChildBox = nullptr;
    2224          22 :                                 AddElement(psBox, psLastChildBox, psDumpContext,
    2225             :                                            psXMLContentNode);
    2226             :                             }
    2227             :                             else
    2228             :                             {
    2229          36 :                                 auto psTextElement = CPLCreateXMLNode(
    2230             :                                     nullptr, CXT_Element, "TextContent");
    2231          36 :                                 CPLCreateXMLNode(psTextElement, CXT_Text,
    2232             :                                                  pszBoxData);
    2233             : 
    2234          36 :                                 CPLXMLNode *psLastChildBox = nullptr;
    2235          36 :                                 AddElement(psBox, psLastChildBox, psDumpContext,
    2236             :                                            psTextElement);
    2237             :                             }
    2238             :                         }
    2239             :                     }
    2240         236 :                     CPLFree(pabyBoxData);
    2241             :                 }
    2242             : 
    2243         321 :                 if (strcmp(pszBoxType, "jp2c") == 0)
    2244             :                 {
    2245          41 :                     if (psDumpContext->bDumpCodestream ||
    2246           6 :                         psDumpContext->pszCodestreamMarkers)
    2247             :                     {
    2248          41 :                         if (!CreateBox())
    2249           0 :                             break;
    2250          41 :                         DumpJPK2CodeStream(psBox, fp, oBox.GetDataOffset(),
    2251             :                                            nBoxDataLength, psDumpContext);
    2252          41 :                         if (psDumpContext->bStopAtSOD &&
    2253           6 :                             psDumpContext->bSODEncountered)
    2254             :                         {
    2255           6 :                             break;
    2256             :                         }
    2257             :                     }
    2258             :                 }
    2259         280 :                 else if (!psDumpContext->bDumpJP2Boxes)
    2260             :                 {
    2261             :                     // do nothing
    2262             :                 }
    2263         253 :                 else if (strcmp(pszBoxType, "uuid") == 0 &&
    2264          17 :                          GDALJP2Metadata::IsUUID_MSI(oBox.GetUUID()))
    2265             :                 {
    2266          16 :                     if (!CreateBox())
    2267           0 :                         break;
    2268          16 :                     DumpGeoTIFFBox(psBox, oBox, psDumpContext);
    2269             :                 }
    2270         220 :                 else if (strcmp(pszBoxType, "ftyp") == 0)
    2271             :                 {
    2272          35 :                     if (!CreateBox())
    2273           0 :                         break;
    2274          35 :                     DumpFTYPBox(psBox, oBox, psDumpContext);
    2275             :                 }
    2276         185 :                 else if (strcmp(pszBoxType, "ihdr") == 0)
    2277             :                 {
    2278          35 :                     if (!CreateBox())
    2279           0 :                         break;
    2280          35 :                     DumpIHDRBox(psBox, oBox, psDumpContext);
    2281             :                 }
    2282         150 :                 else if (strcmp(pszBoxType, "bpcc") == 0)
    2283             :                 {
    2284           3 :                     if (!CreateBox())
    2285           0 :                         break;
    2286           3 :                     DumpBPCCBox(psBox, oBox, psDumpContext);
    2287             :                 }
    2288         147 :                 else if (strcmp(pszBoxType, "colr") == 0)
    2289             :                 {
    2290          33 :                     if (!CreateBox())
    2291           0 :                         break;
    2292          33 :                     DumpCOLRBox(psBox, oBox, psDumpContext);
    2293             :                 }
    2294         114 :                 else if (strcmp(pszBoxType, "pclr") == 0)
    2295             :                 {
    2296           6 :                     if (!CreateBox())
    2297           0 :                         break;
    2298           6 :                     DumpPCLRBox(psBox, oBox, psDumpContext);
    2299             :                 }
    2300         108 :                 else if (strcmp(pszBoxType, "cmap") == 0)
    2301             :                 {
    2302           6 :                     if (!CreateBox())
    2303           0 :                         break;
    2304           6 :                     DumpCMAPBox(psBox, oBox, psDumpContext);
    2305             :                 }
    2306         102 :                 else if (strcmp(pszBoxType, "cdef") == 0)
    2307             :                 {
    2308           3 :                     if (!CreateBox())
    2309           0 :                         break;
    2310           3 :                     DumpCDEFBox(psBox, oBox, psDumpContext);
    2311             :                 }
    2312          99 :                 else if (strcmp(pszBoxType, "resc") == 0 ||
    2313          98 :                          strcmp(pszBoxType, "resd") == 0)
    2314             :                 {
    2315           1 :                     if (!CreateBox())
    2316           0 :                         break;
    2317           1 :                     DumpRESxBox(psBox, oBox, psDumpContext);
    2318             :                 }
    2319          98 :                 else if (strcmp(pszBoxType, "rreq") == 0)
    2320             :                 {
    2321          10 :                     if (!CreateBox())
    2322           0 :                         break;
    2323          10 :                     DumpRREQBox(psBox, oBox, psDumpContext);
    2324             :                 }
    2325             :             }
    2326             : 
    2327         395 :             if (!oBox.ReadNextChild(poParentBox))
    2328         113 :                 break;
    2329             :         }
    2330             :     }
    2331             : }
    2332             : 
    2333             : /************************************************************************/
    2334             : /*                      GDALGetJPEG2000Structure()                      */
    2335             : /************************************************************************/
    2336             : 
    2337             : constexpr unsigned char jpc_header[] = {0xff, 0x4f};
    2338             : constexpr unsigned char jp2_box_jp[] = {0x6a, 0x50, 0x20, 0x20}; /* 'jP  ' */
    2339             : 
    2340             : /** Dump the structure of a JPEG2000 file as a XML tree.
    2341             :  *
    2342             :  * @param pszFilename filename.
    2343             :  * @param papszOptions NULL terminated list of options, or NULL.
    2344             :  *                     Allowed options are BINARY_CONTENT=YES, TEXT_CONTENT=YES,
    2345             :  *                     CODESTREAM=YES, ALL=YES, JP2_BOXES=YES,
    2346             :  *                     CODESTREAM_MARKERS=list_of_marker_names_comma_separated,
    2347             :  *                     STOP_AT_SOD=YES, ALLOW_GET_FILE_SIZE=NO.
    2348             :  * @return XML tree (to be freed with CPLDestroyXMLNode()) or NULL in case
    2349             :  *         of error
    2350             :  */
    2351             : 
    2352          39 : CPLXMLNode *GDALGetJPEG2000Structure(const char *pszFilename,
    2353             :                                      CSLConstList papszOptions)
    2354             : {
    2355          39 :     VSILFILE *fp = VSIFOpenL(pszFilename, "rb");
    2356          39 :     if (fp == nullptr)
    2357             :     {
    2358           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s", pszFilename);
    2359           0 :         return nullptr;
    2360             :     }
    2361          39 :     auto psRet = GDALGetJPEG2000Structure(pszFilename, fp, papszOptions);
    2362          39 :     CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
    2363          39 :     return psRet;
    2364             : }
    2365             : 
    2366             : #ifndef DOXYGEN_SKIP
    2367             : 
    2368             : /************************************************************************/
    2369             : /*                      GDALGetJPEG2000Structure()                      */
    2370             : /************************************************************************/
    2371             : 
    2372          47 : CPLXMLNode *GDALGetJPEG2000Structure(const char *pszFilename, VSILFILE *fp,
    2373             :                                      CSLConstList papszOptions)
    2374             : {
    2375          47 :     if (fp == nullptr)
    2376           0 :         return GDALGetJPEG2000Structure(pszFilename, papszOptions);
    2377             : 
    2378             :     GByte abyHeader[16];
    2379          47 :     if (VSIFSeekL(fp, 0, SEEK_SET) != 0 ||
    2380          94 :         VSIFReadL(abyHeader, 16, 1, fp) != 1 ||
    2381          47 :         (memcmp(abyHeader, jpc_header, sizeof(jpc_header)) != 0 &&
    2382          41 :          memcmp(abyHeader + 4, jp2_box_jp, sizeof(jp2_box_jp)) != 0))
    2383             :     {
    2384           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s is not a JPEG2000 file",
    2385             :                  pszFilename);
    2386           0 :         return nullptr;
    2387             :     }
    2388             : 
    2389          47 :     CPLXMLNode *psParent = nullptr;
    2390          47 :     DumpContext dc;
    2391          47 :     dc.nCurLineCount = 0;
    2392          47 :     dc.nMaxLineCount = atoi(CSLFetchNameValueDef(
    2393             :         papszOptions, "MAX_LINES",
    2394             :         CPLGetConfigOption("GDAL_JPEG2000_STRUCTURE_MAX_LINES", "500000")));
    2395          47 :     if (dc.nMaxLineCount > INT_MAX - 1)
    2396           0 :         dc.nMaxLineCount = INT_MAX - 1;
    2397          47 :     dc.bDumpAll = CPLFetchBool(papszOptions, "ALL", false);
    2398          47 :     dc.bDumpCodestream =
    2399          47 :         dc.bDumpAll || CPLFetchBool(papszOptions, "CODESTREAM", false);
    2400          47 :     dc.bDumpBinaryContent =
    2401          47 :         dc.bDumpAll || CPLFetchBool(papszOptions, "BINARY_CONTENT", false);
    2402          47 :     dc.bDumpTextContent =
    2403          47 :         dc.bDumpAll || CPLFetchBool(papszOptions, "TEXT_CONTENT", false);
    2404          47 :     dc.pszCodestreamMarkers =
    2405          47 :         CSLFetchNameValue(papszOptions, "CODESTREAM_MARKERS");
    2406         102 :     dc.bDumpJP2Boxes = dc.bDumpAll ||
    2407          55 :                        CPLFetchBool(papszOptions, "JP2_BOXES", false) ||
    2408           8 :                        dc.pszCodestreamMarkers == nullptr;
    2409          47 :     dc.bStopAtSOD = CPLFetchBool(papszOptions, "STOP_AT_SOD", false);
    2410          47 :     dc.bAllowGetFileSize =
    2411          47 :         CPLFetchBool(papszOptions, "ALLOW_GET_FILE_SIZE", true);
    2412             : 
    2413          47 :     if (memcmp(abyHeader, jpc_header, sizeof(jpc_header)) == 0)
    2414             :     {
    2415           6 :         if (dc.bDumpCodestream || dc.pszCodestreamMarkers != nullptr)
    2416             :         {
    2417           6 :             GIntBig nBoxDataLength = -1;
    2418           6 :             if (dc.bAllowGetFileSize && VSIFSeekL(fp, 0, SEEK_END) == 0)
    2419             :             {
    2420           4 :                 nBoxDataLength = static_cast<GIntBig>(VSIFTellL(fp));
    2421             :             }
    2422           6 :             psParent = DumpJPK2CodeStream(nullptr, fp, 0, nBoxDataLength, &dc);
    2423           6 :             CPLAddXMLAttributeAndValue(psParent, "filename", pszFilename);
    2424             :         }
    2425             :     }
    2426             :     else
    2427             :     {
    2428          41 :         psParent = CPLCreateXMLNode(nullptr, CXT_Element, "JP2File");
    2429          41 :         CPLAddXMLAttributeAndValue(psParent, "filename", pszFilename);
    2430          41 :         vsi_l_offset nFileSize = 0;
    2431          41 :         GDALGetJPEG2000StructureInternal(psParent, fp, nullptr, 0, nFileSize,
    2432             :                                          &dc);
    2433             :     }
    2434             : 
    2435          47 :     if (dc.nCurLineCount > dc.nMaxLineCount)
    2436             :     {
    2437           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    2438             :                  "Maximum number of lines in JPEG2000 structure dump reached. "
    2439             :                  "Increase GDAL_JPEG2000_STRUCTURE_MAX_LINES beyond %d.",
    2440             :                  dc.nMaxLineCount);
    2441             :     }
    2442             : 
    2443          47 :     return psParent;
    2444             : }
    2445             : 
    2446             : /************************************************************************/
    2447             : /*                    GDALGetJPEG2000Reversibility()                    */
    2448             : /************************************************************************/
    2449             : 
    2450           8 : const char *GDALGetJPEG2000Reversibility(const char *pszFilename, VSILFILE *fp)
    2451             : {
    2452           8 :     const char *const apszOptions[] = {"ALLOW_GET_FILE_SIZE=NO",
    2453             :                                        "STOP_AT_SOD=YES",
    2454             :                                        "CODESTREAM_MARKERS=COD,COM", nullptr};
    2455           8 :     CPLXMLNode *psRes = GDALGetJPEG2000Structure(pszFilename, fp, apszOptions);
    2456           8 :     if (psRes == nullptr)
    2457           0 :         return nullptr;
    2458           8 :     const char *pszReversibility = nullptr;
    2459           8 :     const CPLXMLNode *psJP2C = CPLSearchXMLNode(psRes, "JP2KCodeStream");
    2460           8 :     if (psJP2C)
    2461             :     {
    2462           8 :         const char *pszTransformation = nullptr;
    2463           8 :         const char *pszCOM = nullptr;
    2464          29 :         for (const CPLXMLNode *psMarker = psJP2C->psChild; psMarker;
    2465          21 :              psMarker = psMarker->psNext)
    2466             :         {
    2467          61 :             if (psMarker->eType == CXT_Element &&
    2468          40 :                 strcmp(psMarker->pszValue, "Marker") == 0 &&
    2469          19 :                 strcmp(CPLGetXMLValue(psMarker, "name", ""), "COD") == 0)
    2470             :             {
    2471          96 :                 for (const CPLXMLNode *psField = psMarker->psChild; psField;
    2472          88 :                      psField = psField->psNext)
    2473             :                 {
    2474         264 :                     if (psField->eType == CXT_Element &&
    2475         168 :                         strcmp(psField->pszValue, "Field") == 0 &&
    2476          72 :                         strcmp(CPLGetXMLValue(psField, "name", ""),
    2477             :                                "SPcod_transformation") == 0)
    2478             :                     {
    2479             :                         pszTransformation =
    2480           8 :                             CPLGetXMLValue(psField, nullptr, nullptr);
    2481           8 :                         break;
    2482             :                     }
    2483             :                 }
    2484             :             }
    2485          37 :             else if (psMarker->eType == CXT_Element &&
    2486          24 :                      strcmp(psMarker->pszValue, "Marker") == 0 &&
    2487          11 :                      strcmp(CPLGetXMLValue(psMarker, "name", ""), "COM") == 0)
    2488             :             {
    2489          55 :                 for (const CPLXMLNode *psField = psMarker->psChild; psField;
    2490          44 :                      psField = psField->psNext)
    2491             :                 {
    2492         132 :                     if (psField->eType == CXT_Element &&
    2493          77 :                         strcmp(psField->pszValue, "Field") == 0 &&
    2494          22 :                         strcmp(CPLGetXMLValue(psField, "name", ""), "COM") == 0)
    2495             :                     {
    2496          11 :                         pszCOM = CPLGetXMLValue(psField, nullptr, nullptr);
    2497          11 :                         break;
    2498             :                     }
    2499             :                 }
    2500             :             }
    2501             :         }
    2502             : 
    2503           8 :         if (pszTransformation != nullptr &&
    2504           8 :             strcmp(pszTransformation, "0") ==
    2505             :                 0)  // 0 = 9x7 irreversible wavelet
    2506             :         {
    2507           2 :             pszReversibility = "LOSSY";
    2508             :         }
    2509           6 :         else if (pszTransformation != nullptr &&
    2510           6 :                  strcmp(pszTransformation, "1") ==
    2511             :                      0)  // 1 = 5x3 reversible wavelet
    2512             :         {
    2513             :             // 5x3 wavelet by itself doesn't guarantee full lossless mode
    2514             :             // if quality layers are discarded. hence the "possibly"
    2515           6 :             pszReversibility = "LOSSLESS (possibly)";
    2516             : 
    2517           6 :             if (pszCOM &&
    2518           6 :                 STARTS_WITH(
    2519             :                     pszCOM,
    2520             :                     "Kdu-Layer-Info: "
    2521             :                     "log_2{Delta-D(squared-error)/Delta-L(bytes)}, L(bytes)"))
    2522             :             {
    2523           0 :                 if (strstr(pszCOM, "-192.0,") != nullptr)
    2524             :                 {
    2525             :                     // Not really sure to understand this fully, but
    2526             :                     // experimentaly I've found that if the last row in the
    2527             :                     // Kdu-Layer-Info includes a line starting with "-192.0", it
    2528             :                     // means that the last layer includes everything to be
    2529             :                     // lossless.
    2530           0 :                     pszReversibility = "LOSSLESS";
    2531             :                 }
    2532             :                 else
    2533             :                 {
    2534           0 :                     pszReversibility = "LOSSY";
    2535             :                 }
    2536             :             }
    2537             :             // Kakadu < 6.4
    2538           6 :             else if (pszCOM &&
    2539           6 :                      STARTS_WITH(
    2540             :                          pszCOM,
    2541             :                          "Kdu-Layer-Info: "
    2542             :                          "log_2{Delta-D(MSE)/[2^16*Delta-L(bytes)]}, L(bytes)"))
    2543             :             {
    2544           2 :                 if (strstr(pszCOM, "-256.0,") != nullptr)
    2545             :                 {
    2546             :                     // Not really sure to understand this fully, but
    2547             :                     // experimentaly I've found that if the last row in the
    2548             :                     // Kdu-Layer-Info includes a line starting with "-256.0", it
    2549             :                     // means that the last layer includes everything to be
    2550             :                     // lossless.
    2551           2 :                     pszReversibility = "LOSSLESS";
    2552             :                 }
    2553             :                 else
    2554             :                 {
    2555           0 :                     pszReversibility = "LOSSY";
    2556             :                 }
    2557             :             }
    2558           4 :             else if (pszCOM && STARTS_WITH(pszCOM, "Created by OpenJPEG"))
    2559             :             {
    2560             :                 // Starting with GDAL 3.6, the JP2OpenJPEG driver will write
    2561             :                 // if the encoding parameters are lossless/lossy (for 5x3
    2562             :                 // wavelets)
    2563           4 :                 if (strstr(pszCOM, "LOSSLESS settings used"))
    2564             :                 {
    2565           3 :                     pszReversibility = "LOSSLESS";
    2566             :                 }
    2567           1 :                 else if (strstr(pszCOM, "LOSSY settings used"))
    2568             :                 {
    2569           1 :                     pszReversibility = "LOSSY";
    2570             :                 }
    2571             :             }
    2572             :         }
    2573             :     }
    2574           8 :     CPLDestroyXMLNode(psRes);
    2575           8 :     return pszReversibility;
    2576             : }
    2577             : 
    2578             : #endif /* #ifndef DOXYGEN_SKIP */

Generated by: LCOV version 1.14