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-01-11 15:50:51 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             :                         return std::string(v <= 8
    1535          45 :                                                ? CPLSPrintf("%d", 1 << (2 + v))
    1536          90 :                                                : "invalid");
    1537             :                     });
    1538          45 :                 READ_MARKER_FIELD_UINT8(
    1539             :                     "SPcod_ycb_minus_2",
    1540          45 :                     [](GByte v) {
    1541             :                         return std::string(v <= 8
    1542          45 :                                                ? CPLSPrintf("%d", 1 << (2 + v))
    1543          90 :                                                : "invalid");
    1544             :                     });
    1545          45 :                 READ_MARKER_FIELD_UINT8("SPcod_cbstyle", cblkstyleLamba);
    1546          45 :                 READ_MARKER_FIELD_UINT8("SPcod_transformation",
    1547          45 :                                         [](GByte v)
    1548             :                                         {
    1549             :                                             return std::string(
    1550             :                                                 (v == 0)   ? "9-7 irreversible"
    1551          16 :                                                 : (v == 1) ? "5-3 reversible"
    1552          61 :                                                            : "");
    1553             :                                         });
    1554          45 :                 if (bHasPrecincts)
    1555             :                 {
    1556          38 :                     int i = 0;
    1557         116 :                     while (nRemainingMarkerSize >= 1)
    1558             :                     {
    1559          78 :                         auto nLastVal = *pabyMarkerDataIter;
    1560          78 :                         AddField(psMarker, psLastChild, psDumpContext,
    1561             :                                  CPLSPrintf("SPcod_Precincts%d", i),
    1562          78 :                                  *pabyMarkerDataIter,
    1563             :                                  CPLSPrintf("PPx=%d PPy=%d: %dx%d",
    1564          78 :                                             nLastVal & 0xf, nLastVal >> 4,
    1565          78 :                                             1 << (nLastVal & 0xf),
    1566          78 :                                             1 << (nLastVal >> 4)));
    1567          78 :                         pabyMarkerDataIter += 1;
    1568          78 :                         nRemainingMarkerSize -= 1;
    1569          78 :                         i++;
    1570             :                     }
    1571             :                 }
    1572          45 :                 if (nRemainingMarkerSize > 0)
    1573           0 :                     AddElement(
    1574             :                         psMarker, psLastChild, psDumpContext,
    1575             :                         CPLCreateXMLElementAndValue(
    1576             :                             nullptr, "RemainingBytes",
    1577             :                             CPLSPrintf(
    1578             :                                 "%d", static_cast<int>(nRemainingMarkerSize))));
    1579             :             }
    1580             :         }
    1581         106 :         else if (abyMarker[1] == 0x53) /* COC */
    1582             :         {
    1583           0 :             if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1584           0 :                 strstr(psDumpContext->pszCodestreamMarkers, "COC"))
    1585             :             {
    1586           0 :                 psMarker = CreateCurrentMarker();
    1587           0 :                 if (!psMarker)
    1588           0 :                     break;
    1589           0 :                 if (Csiz < 257)
    1590           0 :                     READ_MARKER_FIELD_UINT8("Ccoc");
    1591             :                 else
    1592           0 :                     READ_MARKER_FIELD_UINT16("Ccoc");
    1593             : 
    1594           0 :                 bool bHasPrecincts = false;
    1595           0 :                 if (nRemainingMarkerSize >= 1)
    1596             :                 {
    1597           0 :                     auto nLastVal = *pabyMarkerDataIter;
    1598           0 :                     CPLString osInterp;
    1599           0 :                     if (nLastVal & 0x1)
    1600             :                     {
    1601           0 :                         bHasPrecincts = true;
    1602           0 :                         osInterp += "User defined precincts";
    1603             :                     }
    1604             :                     else
    1605           0 :                         osInterp += "Standard precincts";
    1606           0 :                     AddField(psMarker, psLastChild, psDumpContext, "Scoc",
    1607             :                              nLastVal, osInterp.c_str());
    1608           0 :                     pabyMarkerDataIter += 1;
    1609           0 :                     nRemainingMarkerSize -= 1;
    1610             :                 }
    1611             :                 else
    1612             :                 {
    1613           0 :                     AddError(psMarker, psLastChild, psDumpContext,
    1614             :                              CPLSPrintf("Cannot read field %s", "Scoc"));
    1615             :                 }
    1616           0 :                 READ_MARKER_FIELD_UINT8("SPcoc_NumDecompositions");
    1617           0 :                 READ_MARKER_FIELD_UINT8(
    1618             :                     "SPcoc_xcb_minus_2",
    1619           0 :                     [](GByte v) {
    1620             :                         return std::string(v <= 8
    1621           0 :                                                ? CPLSPrintf("%d", 1 << (2 + v))
    1622           0 :                                                : "invalid");
    1623             :                     });
    1624           0 :                 READ_MARKER_FIELD_UINT8(
    1625             :                     "SPcoc_ycb_minus_2",
    1626           0 :                     [](GByte v) {
    1627             :                         return std::string(v <= 8
    1628           0 :                                                ? CPLSPrintf("%d", 1 << (2 + v))
    1629           0 :                                                : "invalid");
    1630             :                     });
    1631           0 :                 READ_MARKER_FIELD_UINT8("SPcoc_cbstyle", cblkstyleLamba);
    1632           0 :                 READ_MARKER_FIELD_UINT8("SPcoc_transformation",
    1633           0 :                                         [](GByte v)
    1634             :                                         {
    1635             :                                             return std::string(
    1636             :                                                 (v == 0)   ? "9-7 irreversible"
    1637           0 :                                                 : (v == 1) ? "5-3 reversible"
    1638           0 :                                                            : "");
    1639             :                                         });
    1640           0 :                 if (bHasPrecincts)
    1641             :                 {
    1642           0 :                     int i = 0;
    1643           0 :                     while (nRemainingMarkerSize >= 1)
    1644             :                     {
    1645           0 :                         auto nLastVal = *pabyMarkerDataIter;
    1646           0 :                         AddField(psMarker, psLastChild, psDumpContext,
    1647             :                                  CPLSPrintf("SPcoc_Precincts%d", i),
    1648           0 :                                  *pabyMarkerDataIter,
    1649             :                                  CPLSPrintf("PPx=%d PPy=%d: %dx%d",
    1650           0 :                                             nLastVal & 0xf, nLastVal >> 4,
    1651           0 :                                             1 << (nLastVal & 0xf),
    1652           0 :                                             1 << (nLastVal >> 4)));
    1653           0 :                         pabyMarkerDataIter += 1;
    1654           0 :                         nRemainingMarkerSize -= 1;
    1655           0 :                         i++;
    1656             :                     }
    1657             :                 }
    1658           0 :                 if (nRemainingMarkerSize > 0)
    1659           0 :                     AddElement(
    1660             :                         psMarker, psLastChild, psDumpContext,
    1661             :                         CPLCreateXMLElementAndValue(
    1662             :                             nullptr, "RemainingBytes",
    1663             :                             CPLSPrintf(
    1664             :                                 "%d", static_cast<int>(nRemainingMarkerSize))));
    1665             :             }
    1666             :         }
    1667         106 :         else if (abyMarker[1] == 0x55) /* TLM */
    1668             :         {
    1669           1 :             if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1670           0 :                 strstr(psDumpContext->pszCodestreamMarkers, "TLM"))
    1671             :             {
    1672           1 :                 psMarker = CreateCurrentMarker();
    1673           1 :                 if (!psMarker)
    1674           0 :                     break;
    1675           1 :                 READ_MARKER_FIELD_UINT8("Ztlm");
    1676           1 :                 auto Stlm = READ_MARKER_FIELD_UINT8(
    1677             :                     "Stlm",
    1678           1 :                     [](GByte v) {
    1679             :                         return std::string(CPLSPrintf(
    1680           1 :                             "ST=%d SP=%d", (v >> 4) & 3, (v >> 6) & 1));
    1681             :                     });
    1682           1 :                 int ST = (Stlm >> 4) & 3;
    1683           1 :                 int SP = (Stlm >> 6) & 1;
    1684           1 :                 int nTilePartDescLength = ST + ((SP == 0) ? 2 : 4);
    1685           1 :                 int i = 0;
    1686           5 :                 while (nRemainingMarkerSize >= nTilePartDescLength)
    1687             :                 {
    1688           4 :                     if (ST == 1)
    1689           0 :                         READ_MARKER_FIELD_UINT8(CPLSPrintf("Ttlm%d", i));
    1690           4 :                     else if (ST == 2)
    1691           4 :                         READ_MARKER_FIELD_UINT16(CPLSPrintf("Ttlm%d", i));
    1692           4 :                     if (SP == 0)
    1693           0 :                         READ_MARKER_FIELD_UINT16(CPLSPrintf("Ptlm%d", i));
    1694             :                     else
    1695           4 :                         READ_MARKER_FIELD_UINT32(CPLSPrintf("Ptlm%d", i));
    1696           4 :                     i++;
    1697             :                 }
    1698           1 :                 if (nRemainingMarkerSize > 0)
    1699           0 :                     AddElement(
    1700             :                         psMarker, psLastChild, psDumpContext,
    1701             :                         CPLCreateXMLElementAndValue(
    1702             :                             nullptr, "RemainingBytes",
    1703             :                             CPLSPrintf(
    1704             :                                 "%d", static_cast<int>(nRemainingMarkerSize))));
    1705             :             }
    1706             :         }
    1707         105 :         else if (abyMarker[1] == 0x57) /* PLM */
    1708             :         {
    1709           0 :             if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1710           0 :                 strstr(psDumpContext->pszCodestreamMarkers, "PLM"))
    1711             :             {
    1712           0 :                 psMarker = CreateCurrentMarker();
    1713           0 :                 if (!psMarker)
    1714           0 :                     break;
    1715             :             }
    1716             :         }
    1717         105 :         else if (abyMarker[1] == 0x58) /* PLT */
    1718             :         {
    1719          13 :             if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1720           3 :                 strstr(psDumpContext->pszCodestreamMarkers, "PLT"))
    1721             :             {
    1722          10 :                 psMarker = CreateCurrentMarker();
    1723          10 :                 if (!psMarker)
    1724           0 :                     break;
    1725          10 :                 READ_MARKER_FIELD_UINT8("Zplt");
    1726          10 :                 int i = 0;
    1727          10 :                 unsigned nPacketLength = 0;
    1728         196 :                 while (nRemainingMarkerSize >= 1)
    1729             :                 {
    1730         186 :                     auto nLastVal = *pabyMarkerDataIter;
    1731         186 :                     nPacketLength |= (nLastVal & 0x7f);
    1732         186 :                     if (nLastVal & 0x80)
    1733             :                     {
    1734          16 :                         nPacketLength <<= 7;
    1735             :                     }
    1736             :                     else
    1737             :                     {
    1738         170 :                         AddField(psMarker, psLastChild, psDumpContext,
    1739             :                                  CPLSPrintf("Iplt%d", i), nPacketLength);
    1740         170 :                         nPacketLength = 0;
    1741         170 :                         i++;
    1742             :                     }
    1743         186 :                     pabyMarkerDataIter += 1;
    1744         186 :                     nRemainingMarkerSize -= 1;
    1745             :                 }
    1746          10 :                 if (nPacketLength != 0)
    1747             :                 {
    1748           0 :                     AddError(psMarker, psLastChild, psDumpContext,
    1749             :                              "Incorrect PLT marker");
    1750             :                 }
    1751             :             }
    1752             :         }
    1753          92 :         else if (abyMarker[1] == 0x59) /* CPF (HTJ2K) */
    1754             :         {
    1755           0 :             if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1756           0 :                 strstr(psDumpContext->pszCodestreamMarkers, "CPF"))
    1757             :             {
    1758           0 :                 psMarker = CreateCurrentMarker();
    1759           0 :                 if (!psMarker)
    1760           0 :                     break;
    1761           0 :                 const GUInt16 Lcpf = nMarkerSize;
    1762           0 :                 if (Lcpf > 2 && (Lcpf % 2) == 0)
    1763             :                 {
    1764           0 :                     for (int i = 0; i < (Lcpf - 2) / 2; i++)
    1765             :                     {
    1766           0 :                         READ_MARKER_FIELD_UINT16(CPLSPrintf("Pcpf%d", i + 1));
    1767             :                     }
    1768             :                 }
    1769           0 :                 if (nRemainingMarkerSize > 0)
    1770           0 :                     AddElement(
    1771             :                         psMarker, psLastChild, psDumpContext,
    1772             :                         CPLCreateXMLElementAndValue(
    1773             :                             nullptr, "RemainingBytes",
    1774             :                             CPLSPrintf(
    1775             :                                 "%d", static_cast<int>(nRemainingMarkerSize))));
    1776             :             }
    1777             :         }
    1778          92 :         else if (abyMarker[1] == 0x5C) /* QCD */
    1779             :         {
    1780          45 :             if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1781           8 :                 strstr(psDumpContext->pszCodestreamMarkers, "QCD"))
    1782             :             {
    1783          37 :                 psMarker = CreateCurrentMarker();
    1784          37 :                 if (!psMarker)
    1785           0 :                     break;
    1786          37 :                 const int Sqcd = READ_MARKER_FIELD_UINT8(
    1787             :                     "Sqcd",
    1788          37 :                     [](GByte v)
    1789             :                     {
    1790          37 :                         std::string ret;
    1791          37 :                         if ((v & 31) == 0)
    1792          10 :                             ret = "No quantization";
    1793          27 :                         else if ((v & 31) == 1)
    1794           0 :                             ret = "Scalar derived";
    1795          27 :                         else if ((v & 31) == 2)
    1796          27 :                             ret = "Scalar expounded";
    1797          37 :                         ret += ", ";
    1798          37 :                         ret += CPLSPrintf("guard bits = %d", v >> 5);
    1799          37 :                         return ret;
    1800          37 :                     });
    1801          37 :                 if ((Sqcd & 31) == 0)
    1802             :                 {
    1803             :                     // Reversible
    1804          10 :                     int i = 0;
    1805          74 :                     while (nRemainingMarkerSize >= 1)
    1806             :                     {
    1807          64 :                         READ_MARKER_FIELD_UINT8(
    1808             :                             CPLSPrintf("SPqcd%d", i),
    1809          64 :                             [](GByte v) {
    1810             :                                 return std::string(
    1811          64 :                                     CPLSPrintf("epsilon_b = %d", v >> 3));
    1812             :                             });
    1813          64 :                         ++i;
    1814             :                     }
    1815             :                 }
    1816             :                 else
    1817             :                 {
    1818          27 :                     int i = 0;
    1819         123 :                     while (nRemainingMarkerSize >= 2)
    1820             :                     {
    1821          96 :                         READ_MARKER_FIELD_UINT16(
    1822             :                             CPLSPrintf("SPqcd%d", i),
    1823          96 :                             [](GUInt16 v)
    1824             :                             {
    1825             :                                 return std::string(CPLSPrintf(
    1826             :                                     "mantissa_b = %d, epsilon_b = %d",
    1827          96 :                                     v & ((1 << 11) - 1), v >> 11));
    1828             :                             });
    1829          96 :                         ++i;
    1830             :                     }
    1831             :                 }
    1832             :             }
    1833             :         }
    1834          47 :         else if (abyMarker[1] == 0x5D) /* QCC */
    1835             :         {
    1836           1 :             if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1837           0 :                 strstr(psDumpContext->pszCodestreamMarkers, "QCC"))
    1838             :             {
    1839           1 :                 psMarker = CreateCurrentMarker();
    1840           1 :                 if (!psMarker)
    1841           0 :                     break;
    1842           1 :                 if (Csiz < 257)
    1843           1 :                     READ_MARKER_FIELD_UINT8("Cqcc");
    1844             :                 else
    1845           0 :                     READ_MARKER_FIELD_UINT16("Cqcc");
    1846             : 
    1847           1 :                 const int Sqcc = READ_MARKER_FIELD_UINT8(
    1848             :                     "Sqcc",
    1849           1 :                     [](GByte v)
    1850             :                     {
    1851           1 :                         std::string ret;
    1852           1 :                         if ((v & 31) == 0)
    1853           0 :                             ret = "No quantization";
    1854           1 :                         else if ((v & 31) == 1)
    1855           0 :                             ret = "Scalar derived";
    1856           1 :                         else if ((v & 31) == 2)
    1857           1 :                             ret = "Scalar expounded";
    1858           1 :                         ret += ", ";
    1859           1 :                         ret += CPLSPrintf("guard bits = %d", v >> 5);
    1860           1 :                         return ret;
    1861           1 :                     });
    1862           1 :                 if ((Sqcc & 31) == 0)
    1863             :                 {
    1864             :                     // Reversible
    1865           0 :                     int i = 0;
    1866           0 :                     while (nRemainingMarkerSize >= 1)
    1867             :                     {
    1868           0 :                         READ_MARKER_FIELD_UINT8(
    1869             :                             CPLSPrintf("SPqcc%d", i),
    1870           0 :                             [](GByte v) {
    1871             :                                 return std::string(
    1872           0 :                                     CPLSPrintf("epsilon_b = %d", v >> 3));
    1873             :                             });
    1874           0 :                         ++i;
    1875             :                     }
    1876             :                 }
    1877             :                 else
    1878             :                 {
    1879           1 :                     int i = 0;
    1880           2 :                     while (nRemainingMarkerSize >= 2)
    1881             :                     {
    1882           1 :                         READ_MARKER_FIELD_UINT16(
    1883             :                             CPLSPrintf("SPqcc%d", i),
    1884           1 :                             [](GUInt16 v)
    1885             :                             {
    1886             :                                 return std::string(CPLSPrintf(
    1887             :                                     "mantissa_b = %d, epsilon_b = %d",
    1888           1 :                                     v & ((1 << 11) - 1), v >> 11));
    1889             :                             });
    1890           1 :                         ++i;
    1891             :                     }
    1892             :                 }
    1893             :             }
    1894             :         }
    1895          46 :         else if (abyMarker[1] == 0x5E) /* RGN */
    1896             :         {
    1897           1 :             if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1898           0 :                 strstr(psDumpContext->pszCodestreamMarkers, "RGN"))
    1899             :             {
    1900           1 :                 psMarker = CreateCurrentMarker();
    1901           1 :                 if (!psMarker)
    1902           0 :                     break;
    1903             :             }
    1904             :         }
    1905          45 :         else if (abyMarker[1] == 0x5F) /* POC */
    1906             :         {
    1907           1 :             if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1908           0 :                 strstr(psDumpContext->pszCodestreamMarkers, "POC"))
    1909             :             {
    1910           1 :                 psMarker = CreateCurrentMarker();
    1911           1 :                 if (!psMarker)
    1912           0 :                     break;
    1913           1 :                 const int nPOCEntrySize = Csiz < 257 ? 7 : 9;
    1914           1 :                 int i = 0;
    1915           2 :                 while (nRemainingMarkerSize >= nPOCEntrySize)
    1916             :                 {
    1917           1 :                     READ_MARKER_FIELD_UINT8(CPLSPrintf("RSpoc%d", i));
    1918           1 :                     if (nPOCEntrySize == 7)
    1919             :                     {
    1920           1 :                         READ_MARKER_FIELD_UINT8(CPLSPrintf("CSpoc%d", i));
    1921             :                     }
    1922             :                     else
    1923             :                     {
    1924           0 :                         READ_MARKER_FIELD_UINT16(CPLSPrintf("CSpoc%d", i));
    1925             :                     }
    1926           1 :                     READ_MARKER_FIELD_UINT16(CPLSPrintf("LYEpoc%d", i));
    1927           1 :                     READ_MARKER_FIELD_UINT8(CPLSPrintf("REpoc%d", i));
    1928           1 :                     if (nPOCEntrySize == 7)
    1929             :                     {
    1930           1 :                         READ_MARKER_FIELD_UINT8(CPLSPrintf("CEpoc%d", i));
    1931             :                     }
    1932             :                     else
    1933             :                     {
    1934           0 :                         READ_MARKER_FIELD_UINT16(CPLSPrintf("CEpoc%d", i));
    1935             :                     }
    1936           1 :                     READ_MARKER_FIELD_UINT8(CPLSPrintf("Ppoc%d", i),
    1937             :                                             lambdaPOCType);
    1938           1 :                     i++;
    1939             :                 }
    1940           1 :                 if (nRemainingMarkerSize > 0)
    1941             :                 {
    1942           0 :                     AddElement(
    1943             :                         psMarker, psLastChild, psDumpContext,
    1944             :                         CPLCreateXMLElementAndValue(
    1945             :                             nullptr, "RemainingBytes",
    1946             :                             CPLSPrintf(
    1947             :                                 "%d", static_cast<int>(nRemainingMarkerSize))));
    1948             :                 }
    1949             :             }
    1950             :         }
    1951          44 :         else if (abyMarker[1] == 0x60) /* PPM */
    1952             :         {
    1953           0 :             if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1954           0 :                 strstr(psDumpContext->pszCodestreamMarkers, "PPM"))
    1955             :             {
    1956           0 :                 psMarker = CreateCurrentMarker();
    1957           0 :                 if (!psMarker)
    1958           0 :                     break;
    1959             :             }
    1960             :         }
    1961          44 :         else if (abyMarker[1] == 0x61) /* PPT */
    1962             :         {
    1963           0 :             if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1964           0 :                 strstr(psDumpContext->pszCodestreamMarkers, "PPT"))
    1965             :             {
    1966           0 :                 psMarker = CreateCurrentMarker();
    1967           0 :                 if (!psMarker)
    1968           0 :                     break;
    1969             :             }
    1970             :         }
    1971          44 :         else if (abyMarker[1] == 0x63) /* CRG */
    1972             :         {
    1973           0 :             if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1974           0 :                 strstr(psDumpContext->pszCodestreamMarkers, "CRG"))
    1975             :             {
    1976           0 :                 psMarker = CreateCurrentMarker();
    1977           0 :                 if (!psMarker)
    1978           0 :                     break;
    1979             :             }
    1980             :         }
    1981          44 :         else if (abyMarker[1] == 0x64) /* COM */
    1982             :         {
    1983          44 :             if (psDumpContext->pszCodestreamMarkers == nullptr ||
    1984          11 :                 strstr(psDumpContext->pszCodestreamMarkers, "COM"))
    1985             :             {
    1986          44 :                 psMarker = CreateCurrentMarker();
    1987          44 :                 if (!psMarker)
    1988           0 :                     break;
    1989          44 :                 auto RCom = READ_MARKER_FIELD_UINT16(
    1990             :                     "Rcom",
    1991          44 :                     [](GUInt16 v) {
    1992             :                         return std::string((v == 0)   ? "Binary"
    1993          44 :                                            : (v == 1) ? "LATIN1"
    1994          88 :                                                       : "");
    1995             :                     });
    1996          44 :                 if (RCom == 1)
    1997             :                 {
    1998          44 :                     GByte abyBackup = pabyMarkerDataIter[nRemainingMarkerSize];
    1999          44 :                     pabyMarkerDataIter[nRemainingMarkerSize] = 0;
    2000          44 :                     AddField(
    2001             :                         psMarker, psLastChild, psDumpContext, "COM",
    2002             :                         static_cast<int>(nRemainingMarkerSize),
    2003             :                         reinterpret_cast<const char *>(pabyMarkerDataIter));
    2004          44 :                     pabyMarkerDataIter[nRemainingMarkerSize] = abyBackup;
    2005             :                 }
    2006             :             }
    2007             :         }
    2008             : 
    2009         247 :         if (VSIFSeekL(fp, nOffset + 2 + nMarkerSize, SEEK_SET) != 0)
    2010             :         {
    2011           0 :             AddError(psCSBox, psLastChildCSBox, psDumpContext,
    2012           0 :                      "Cannot seek to next marker", nOffset + 2 + nMarkerSize);
    2013           0 :             break;
    2014             :         }
    2015             : 
    2016         247 :         CPL_IGNORE_RET_VAL(bError);
    2017             :     }
    2018          47 :     CPLFree(pabyMarkerData);
    2019          47 :     return psCSBox;
    2020             : }
    2021             : 
    2022             : /************************************************************************/
    2023             : /*                      GDALGetJPEG2000StructureInternal()              */
    2024             : /************************************************************************/
    2025             : 
    2026         121 : static void GDALGetJPEG2000StructureInternal(CPLXMLNode *psParent, VSILFILE *fp,
    2027             :                                              GDALJP2Box *poParentBox,
    2028             :                                              int nRecLevel,
    2029             :                                              vsi_l_offset nFileOrParentBoxSize,
    2030             :                                              DumpContext *psDumpContext)
    2031             : {
    2032             :     // Limit recursion to a reasonable level. I believe that in practice 2
    2033             :     // should be sufficient, but just in case someone creates deeply
    2034             :     // nested "super-boxes", allow up to 5.
    2035         121 :     if (nRecLevel == 5)
    2036           0 :         return;
    2037             : 
    2038             :     static const char *const szHex = "0123456789ABCDEF";
    2039         242 :     GDALJP2Box oBox(fp);
    2040         121 :     oBox.SetAllowGetFileSize(psDumpContext->bAllowGetFileSize);
    2041         121 :     CPLXMLNode *psLastChild = nullptr;
    2042         121 :     if (oBox.ReadFirstChild(poParentBox))
    2043             :     {
    2044         806 :         while (strlen(oBox.GetType()) > 0 &&
    2045         403 :                psDumpContext->nCurLineCount <= psDumpContext->nMaxLineCount + 1)
    2046             :         {
    2047         401 :             GIntBig nBoxDataLength = oBox.GetDataLength();
    2048         401 :             const char *pszBoxType = oBox.GetType();
    2049         401 :             CPLXMLNode *psBox = nullptr;
    2050         758 :             const auto CreateBox = [&]()
    2051             :             {
    2052         758 :                 if (psBox != nullptr)
    2053         401 :                     return true;
    2054         357 :                 psBox = CPLCreateXMLNode(nullptr, CXT_Element, "JP2Box");
    2055         357 :                 psBox = AddElement(psParent, psLastChild, psDumpContext, psBox);
    2056         357 :                 if (!psBox)
    2057           0 :                     return false;
    2058         357 :                 CPLAddXMLAttributeAndValue(psBox, "name", pszBoxType);
    2059         357 :                 CPLAddXMLAttributeAndValue(
    2060             :                     psBox, "box_offset",
    2061         357 :                     CPLSPrintf(CPL_FRMT_GIB, oBox.GetBoxOffset()));
    2062         357 :                 const auto nBoxLength = oBox.GetBoxLength();
    2063         711 :                 CPLAddXMLAttributeAndValue(
    2064             :                     psBox, "box_length",
    2065         354 :                     nBoxLength > 0 ? CPLSPrintf(CPL_FRMT_GIB, nBoxLength)
    2066             :                                    : "unknown");
    2067         357 :                 CPLAddXMLAttributeAndValue(
    2068             :                     psBox, "data_offset",
    2069             :                     CPLSPrintf(CPL_FRMT_GIB, oBox.GetDataOffset()));
    2070         357 :                 CPLAddXMLAttributeAndValue(
    2071             :                     psBox, "data_length",
    2072         357 :                     nBoxDataLength > 0
    2073         352 :                         ? CPLSPrintf(CPL_FRMT_GIB, nBoxDataLength)
    2074             :                         : "unknown");
    2075             : 
    2076         357 :                 if (nBoxDataLength > GINTBIG_MAX - oBox.GetDataOffset())
    2077             :                 {
    2078           0 :                     CPLXMLNode *psLastChildBox = nullptr;
    2079           0 :                     AddError(psBox, psLastChildBox, psDumpContext,
    2080             :                              "Invalid box_length");
    2081           0 :                     return false;
    2082             :                 }
    2083         357 :                 return true;
    2084         401 :             };
    2085             : 
    2086             :             // Check large non-jp2c boxes against filesize
    2087         401 :             if (strcmp(pszBoxType, "jp2c") != 0 && nBoxDataLength > 100 * 1024)
    2088             :             {
    2089           0 :                 if (nFileOrParentBoxSize == 0)
    2090             :                 {
    2091           0 :                     CPL_IGNORE_RET_VAL(VSIFSeekL(fp, 0, SEEK_END));
    2092           0 :                     nFileOrParentBoxSize = VSIFTellL(fp);
    2093             :                 }
    2094             :             }
    2095         585 :             if (nFileOrParentBoxSize > 0 && nBoxDataLength > 0 &&
    2096         184 :                 (static_cast<vsi_l_offset>(oBox.GetDataOffset()) >
    2097         184 :                      nFileOrParentBoxSize ||
    2098         184 :                  static_cast<vsi_l_offset>(nBoxDataLength) >
    2099         184 :                      nFileOrParentBoxSize - oBox.GetDataOffset()))
    2100             :             {
    2101           0 :                 CPLXMLNode *psLastChildBox = nullptr;
    2102           0 :                 if (!CreateBox())
    2103           0 :                     break;
    2104           0 :                 AddError(psBox, psLastChildBox, psDumpContext,
    2105             :                          "Invalid box_length");
    2106           0 :                 break;
    2107             :             }
    2108             : 
    2109         401 :             if (oBox.IsSuperBox())
    2110             :             {
    2111          80 :                 if (!CreateBox())
    2112           0 :                     break;
    2113          80 :                 if (nBoxDataLength <= 0)
    2114           0 :                     break;
    2115         160 :                 GDALGetJPEG2000StructureInternal(
    2116             :                     psBox, fp, &oBox, nRecLevel + 1,
    2117          80 :                     oBox.GetDataOffset() +
    2118          80 :                         static_cast<vsi_l_offset>(nBoxDataLength),
    2119             :                     psDumpContext);
    2120             :             }
    2121             :             else
    2122             :             {
    2123         321 :                 if (strcmp(pszBoxType, "uuid") == 0 &&
    2124          23 :                     psDumpContext->bDumpJP2Boxes)
    2125             :                 {
    2126          17 :                     if (!CreateBox())
    2127           0 :                         break;
    2128             :                     char *pszBinaryContent =
    2129          17 :                         static_cast<char *>(VSIMalloc(2 * 16 + 1));
    2130          17 :                     const GByte *pabyUUID = oBox.GetUUID();
    2131         289 :                     for (int i = 0; i < 16; i++)
    2132             :                     {
    2133         272 :                         pszBinaryContent[2 * i] = szHex[pabyUUID[i] >> 4];
    2134         272 :                         pszBinaryContent[2 * i + 1] = szHex[pabyUUID[i] & 0xf];
    2135             :                     }
    2136          17 :                     pszBinaryContent[2 * 16] = '\0';
    2137             :                     CPLXMLNode *psUUIDNode =
    2138          17 :                         CPLCreateXMLNode(nullptr, CXT_Element, "UUID");
    2139          17 :                     if (GDALJP2Metadata::IsUUID_MSI(pabyUUID))
    2140          16 :                         CPLAddXMLAttributeAndValue(psUUIDNode, "description",
    2141             :                                                    "GeoTIFF");
    2142           1 :                     else if (GDALJP2Metadata::IsUUID_XMP(pabyUUID))
    2143           1 :                         CPLAddXMLAttributeAndValue(psUUIDNode, "description",
    2144             :                                                    "XMP");
    2145          17 :                     CPLCreateXMLNode(psUUIDNode, CXT_Text, pszBinaryContent);
    2146          17 :                     VSIFree(pszBinaryContent);
    2147             : 
    2148          17 :                     CPLXMLNode *psLastChildBox = nullptr;
    2149          17 :                     AddElement(psBox, psLastChildBox, psDumpContext,
    2150             :                                psUUIDNode);
    2151             :                 }
    2152             : 
    2153         321 :                 if (psDumpContext->bDumpBinaryContent &&
    2154         271 :                     strcmp(pszBoxType, "jp2c") != 0 &&
    2155         236 :                     nBoxDataLength < 100 * 1024)
    2156             :                 {
    2157         236 :                     if (!CreateBox())
    2158           0 :                         break;
    2159             :                     CPLXMLNode *psBinaryContent =
    2160         236 :                         CPLCreateXMLNode(nullptr, CXT_Element, "BinaryContent");
    2161         236 :                     GByte *pabyBoxData = oBox.ReadBoxData();
    2162             :                     const int nBoxLength = static_cast<int>(
    2163         236 :                         std::min<GIntBig>(nBoxDataLength, INT_MAX / 2 - 1));
    2164             :                     char *pszBinaryContent =
    2165         236 :                         static_cast<char *>(VSIMalloc(2 * nBoxLength + 1));
    2166         236 :                     if (pabyBoxData && pszBinaryContent)
    2167             :                     {
    2168       47647 :                         for (int i = 0; i < nBoxLength; i++)
    2169             :                         {
    2170       47411 :                             pszBinaryContent[2 * i] =
    2171       47411 :                                 szHex[pabyBoxData[i] >> 4];
    2172       47411 :                             pszBinaryContent[2 * i + 1] =
    2173       47411 :                                 szHex[pabyBoxData[i] & 0xf];
    2174             :                         }
    2175         236 :                         pszBinaryContent[2 * nBoxLength] = '\0';
    2176         236 :                         CPLCreateXMLNode(psBinaryContent, CXT_Text,
    2177             :                                          pszBinaryContent);
    2178             :                     }
    2179         236 :                     CPLFree(pabyBoxData);
    2180         236 :                     VSIFree(pszBinaryContent);
    2181             : 
    2182         236 :                     CPLXMLNode *psLastChildBox = nullptr;
    2183         236 :                     AddElement(psBox, psLastChildBox, psDumpContext,
    2184             :                                psBinaryContent);
    2185             :                 }
    2186             : 
    2187         321 :                 if (psDumpContext->bDumpTextContent &&
    2188         271 :                     strcmp(pszBoxType, "jp2c") != 0 &&
    2189         236 :                     nBoxDataLength < 100 * 1024)
    2190             :                 {
    2191         236 :                     if (!CreateBox())
    2192           0 :                         break;
    2193         236 :                     GByte *pabyBoxData = oBox.ReadBoxData();
    2194         236 :                     if (pabyBoxData)
    2195             :                     {
    2196         236 :                         const char *pszBoxData =
    2197             :                             reinterpret_cast<const char *>(pabyBoxData);
    2198         427 :                         if (CPLIsUTF8(pszBoxData, -1) &&
    2199         191 :                             static_cast<int>(strlen(pszBoxData)) + 2 >=
    2200             :                                 nBoxDataLength)
    2201             :                         {
    2202          58 :                             CPLXMLNode *psXMLContentBox = nullptr;
    2203          58 :                             if (pszBoxData[0] == '<')
    2204             :                             {
    2205          22 :                                 CPLPushErrorHandler(CPLQuietErrorHandler);
    2206          22 :                                 psXMLContentBox = CPLParseXMLString(pszBoxData);
    2207          22 :                                 CPLPopErrorHandler();
    2208             :                             }
    2209          58 :                             if (psXMLContentBox)
    2210             :                             {
    2211          22 :                                 CPLXMLNode *psXMLContentNode = CPLCreateXMLNode(
    2212             :                                     nullptr, CXT_Element, "XMLContent");
    2213          22 :                                 psXMLContentNode->psChild = psXMLContentBox;
    2214             : 
    2215          22 :                                 CPLXMLNode *psLastChildBox = nullptr;
    2216          22 :                                 AddElement(psBox, psLastChildBox, psDumpContext,
    2217             :                                            psXMLContentNode);
    2218             :                             }
    2219             :                             else
    2220             :                             {
    2221          36 :                                 auto psTextElement = CPLCreateXMLNode(
    2222             :                                     nullptr, CXT_Element, "TextContent");
    2223          36 :                                 CPLCreateXMLNode(psTextElement, CXT_Text,
    2224             :                                                  pszBoxData);
    2225             : 
    2226          36 :                                 CPLXMLNode *psLastChildBox = nullptr;
    2227          36 :                                 AddElement(psBox, psLastChildBox, psDumpContext,
    2228             :                                            psTextElement);
    2229             :                             }
    2230             :                         }
    2231             :                     }
    2232         236 :                     CPLFree(pabyBoxData);
    2233             :                 }
    2234             : 
    2235         321 :                 if (strcmp(pszBoxType, "jp2c") == 0)
    2236             :                 {
    2237          41 :                     if (psDumpContext->bDumpCodestream ||
    2238           6 :                         psDumpContext->pszCodestreamMarkers)
    2239             :                     {
    2240          41 :                         if (!CreateBox())
    2241           0 :                             break;
    2242          41 :                         DumpJPK2CodeStream(psBox, fp, oBox.GetDataOffset(),
    2243             :                                            nBoxDataLength, psDumpContext);
    2244          41 :                         if (psDumpContext->bStopAtSOD &&
    2245           6 :                             psDumpContext->bSODEncountered)
    2246             :                         {
    2247           6 :                             break;
    2248             :                         }
    2249             :                     }
    2250             :                 }
    2251         280 :                 else if (!psDumpContext->bDumpJP2Boxes)
    2252             :                 {
    2253             :                     // do nothing
    2254             :                 }
    2255         253 :                 else if (strcmp(pszBoxType, "uuid") == 0 &&
    2256          17 :                          GDALJP2Metadata::IsUUID_MSI(oBox.GetUUID()))
    2257             :                 {
    2258          16 :                     if (!CreateBox())
    2259           0 :                         break;
    2260          16 :                     DumpGeoTIFFBox(psBox, oBox, psDumpContext);
    2261             :                 }
    2262         220 :                 else if (strcmp(pszBoxType, "ftyp") == 0)
    2263             :                 {
    2264          35 :                     if (!CreateBox())
    2265           0 :                         break;
    2266          35 :                     DumpFTYPBox(psBox, oBox, psDumpContext);
    2267             :                 }
    2268         185 :                 else if (strcmp(pszBoxType, "ihdr") == 0)
    2269             :                 {
    2270          35 :                     if (!CreateBox())
    2271           0 :                         break;
    2272          35 :                     DumpIHDRBox(psBox, oBox, psDumpContext);
    2273             :                 }
    2274         150 :                 else if (strcmp(pszBoxType, "bpcc") == 0)
    2275             :                 {
    2276           3 :                     if (!CreateBox())
    2277           0 :                         break;
    2278           3 :                     DumpBPCCBox(psBox, oBox, psDumpContext);
    2279             :                 }
    2280         147 :                 else if (strcmp(pszBoxType, "colr") == 0)
    2281             :                 {
    2282          33 :                     if (!CreateBox())
    2283           0 :                         break;
    2284          33 :                     DumpCOLRBox(psBox, oBox, psDumpContext);
    2285             :                 }
    2286         114 :                 else if (strcmp(pszBoxType, "pclr") == 0)
    2287             :                 {
    2288           6 :                     if (!CreateBox())
    2289           0 :                         break;
    2290           6 :                     DumpPCLRBox(psBox, oBox, psDumpContext);
    2291             :                 }
    2292         108 :                 else if (strcmp(pszBoxType, "cmap") == 0)
    2293             :                 {
    2294           6 :                     if (!CreateBox())
    2295           0 :                         break;
    2296           6 :                     DumpCMAPBox(psBox, oBox, psDumpContext);
    2297             :                 }
    2298         102 :                 else if (strcmp(pszBoxType, "cdef") == 0)
    2299             :                 {
    2300           3 :                     if (!CreateBox())
    2301           0 :                         break;
    2302           3 :                     DumpCDEFBox(psBox, oBox, psDumpContext);
    2303             :                 }
    2304          99 :                 else if (strcmp(pszBoxType, "resc") == 0 ||
    2305          98 :                          strcmp(pszBoxType, "resd") == 0)
    2306             :                 {
    2307           1 :                     if (!CreateBox())
    2308           0 :                         break;
    2309           1 :                     DumpRESxBox(psBox, oBox, psDumpContext);
    2310             :                 }
    2311          98 :                 else if (strcmp(pszBoxType, "rreq") == 0)
    2312             :                 {
    2313          10 :                     if (!CreateBox())
    2314           0 :                         break;
    2315          10 :                     DumpRREQBox(psBox, oBox, psDumpContext);
    2316             :                 }
    2317             :             }
    2318             : 
    2319         395 :             if (!oBox.ReadNextChild(poParentBox))
    2320         113 :                 break;
    2321             :         }
    2322             :     }
    2323             : }
    2324             : 
    2325             : /************************************************************************/
    2326             : /*                        GDALGetJPEG2000Structure()                    */
    2327             : /************************************************************************/
    2328             : 
    2329             : constexpr unsigned char jpc_header[] = {0xff, 0x4f};
    2330             : constexpr unsigned char jp2_box_jp[] = {0x6a, 0x50, 0x20, 0x20}; /* 'jP  ' */
    2331             : 
    2332             : /** Dump the structure of a JPEG2000 file as a XML tree.
    2333             :  *
    2334             :  * @param pszFilename filename.
    2335             :  * @param papszOptions NULL terminated list of options, or NULL.
    2336             :  *                     Allowed options are BINARY_CONTENT=YES, TEXT_CONTENT=YES,
    2337             :  *                     CODESTREAM=YES, ALL=YES, JP2_BOXES=YES,
    2338             :  *                     CODESTREAM_MARKERS=list_of_marker_names_comma_separated,
    2339             :  *                     STOP_AT_SOD=YES, ALLOW_GET_FILE_SIZE=NO.
    2340             :  * @return XML tree (to be freed with CPLDestroyXMLNode()) or NULL in case
    2341             :  *         of error
    2342             :  */
    2343             : 
    2344          39 : CPLXMLNode *GDALGetJPEG2000Structure(const char *pszFilename,
    2345             :                                      CSLConstList papszOptions)
    2346             : {
    2347          39 :     VSILFILE *fp = VSIFOpenL(pszFilename, "rb");
    2348          39 :     if (fp == nullptr)
    2349             :     {
    2350           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s", pszFilename);
    2351           0 :         return nullptr;
    2352             :     }
    2353          39 :     auto psRet = GDALGetJPEG2000Structure(pszFilename, fp, papszOptions);
    2354          39 :     CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
    2355          39 :     return psRet;
    2356             : }
    2357             : 
    2358             : #ifndef DOXYGEN_SKIP
    2359             : 
    2360             : /************************************************************************/
    2361             : /*                        GDALGetJPEG2000Structure()                    */
    2362             : /************************************************************************/
    2363             : 
    2364          47 : CPLXMLNode *GDALGetJPEG2000Structure(const char *pszFilename, VSILFILE *fp,
    2365             :                                      CSLConstList papszOptions)
    2366             : {
    2367          47 :     if (fp == nullptr)
    2368           0 :         return GDALGetJPEG2000Structure(pszFilename, papszOptions);
    2369             : 
    2370             :     GByte abyHeader[16];
    2371          47 :     if (VSIFSeekL(fp, 0, SEEK_SET) != 0 ||
    2372          94 :         VSIFReadL(abyHeader, 16, 1, fp) != 1 ||
    2373          47 :         (memcmp(abyHeader, jpc_header, sizeof(jpc_header)) != 0 &&
    2374          41 :          memcmp(abyHeader + 4, jp2_box_jp, sizeof(jp2_box_jp)) != 0))
    2375             :     {
    2376           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s is not a JPEG2000 file",
    2377             :                  pszFilename);
    2378           0 :         return nullptr;
    2379             :     }
    2380             : 
    2381          47 :     CPLXMLNode *psParent = nullptr;
    2382          47 :     DumpContext dc;
    2383          47 :     dc.nCurLineCount = 0;
    2384          47 :     dc.nMaxLineCount = atoi(CSLFetchNameValueDef(
    2385             :         papszOptions, "MAX_LINES",
    2386             :         CPLGetConfigOption("GDAL_JPEG2000_STRUCTURE_MAX_LINES", "500000")));
    2387          47 :     if (dc.nMaxLineCount > INT_MAX - 1)
    2388           0 :         dc.nMaxLineCount = INT_MAX - 1;
    2389          47 :     dc.bDumpAll = CPLFetchBool(papszOptions, "ALL", false);
    2390          47 :     dc.bDumpCodestream =
    2391          47 :         dc.bDumpAll || CPLFetchBool(papszOptions, "CODESTREAM", false);
    2392          47 :     dc.bDumpBinaryContent =
    2393          47 :         dc.bDumpAll || CPLFetchBool(papszOptions, "BINARY_CONTENT", false);
    2394          47 :     dc.bDumpTextContent =
    2395          47 :         dc.bDumpAll || CPLFetchBool(papszOptions, "TEXT_CONTENT", false);
    2396          47 :     dc.pszCodestreamMarkers =
    2397          47 :         CSLFetchNameValue(papszOptions, "CODESTREAM_MARKERS");
    2398         102 :     dc.bDumpJP2Boxes = dc.bDumpAll ||
    2399          55 :                        CPLFetchBool(papszOptions, "JP2_BOXES", false) ||
    2400           8 :                        dc.pszCodestreamMarkers == nullptr;
    2401          47 :     dc.bStopAtSOD = CPLFetchBool(papszOptions, "STOP_AT_SOD", false);
    2402          47 :     dc.bAllowGetFileSize =
    2403          47 :         CPLFetchBool(papszOptions, "ALLOW_GET_FILE_SIZE", true);
    2404             : 
    2405          47 :     if (memcmp(abyHeader, jpc_header, sizeof(jpc_header)) == 0)
    2406             :     {
    2407           6 :         if (dc.bDumpCodestream || dc.pszCodestreamMarkers != nullptr)
    2408             :         {
    2409           6 :             GIntBig nBoxDataLength = -1;
    2410           6 :             if (dc.bAllowGetFileSize && VSIFSeekL(fp, 0, SEEK_END) == 0)
    2411             :             {
    2412           4 :                 nBoxDataLength = static_cast<GIntBig>(VSIFTellL(fp));
    2413             :             }
    2414           6 :             psParent = DumpJPK2CodeStream(nullptr, fp, 0, nBoxDataLength, &dc);
    2415           6 :             CPLAddXMLAttributeAndValue(psParent, "filename", pszFilename);
    2416             :         }
    2417             :     }
    2418             :     else
    2419             :     {
    2420          41 :         psParent = CPLCreateXMLNode(nullptr, CXT_Element, "JP2File");
    2421          41 :         CPLAddXMLAttributeAndValue(psParent, "filename", pszFilename);
    2422          41 :         vsi_l_offset nFileSize = 0;
    2423          41 :         GDALGetJPEG2000StructureInternal(psParent, fp, nullptr, 0, nFileSize,
    2424             :                                          &dc);
    2425             :     }
    2426             : 
    2427          47 :     if (dc.nCurLineCount > dc.nMaxLineCount)
    2428             :     {
    2429           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    2430             :                  "Maximum number of lines in JPEG2000 structure dump reached. "
    2431             :                  "Increase GDAL_JPEG2000_STRUCTURE_MAX_LINES beyond %d.",
    2432             :                  dc.nMaxLineCount);
    2433             :     }
    2434             : 
    2435          47 :     return psParent;
    2436             : }
    2437             : 
    2438             : /************************************************************************/
    2439             : /*                     GDALGetJPEG2000Reversibility()                   */
    2440             : /************************************************************************/
    2441             : 
    2442           8 : const char *GDALGetJPEG2000Reversibility(const char *pszFilename, VSILFILE *fp)
    2443             : {
    2444           8 :     const char *const apszOptions[] = {"ALLOW_GET_FILE_SIZE=NO",
    2445             :                                        "STOP_AT_SOD=YES",
    2446             :                                        "CODESTREAM_MARKERS=COD,COM", nullptr};
    2447           8 :     CPLXMLNode *psRes = GDALGetJPEG2000Structure(pszFilename, fp, apszOptions);
    2448           8 :     if (psRes == nullptr)
    2449           0 :         return nullptr;
    2450           8 :     const char *pszReversibility = nullptr;
    2451           8 :     const CPLXMLNode *psJP2C = CPLSearchXMLNode(psRes, "JP2KCodeStream");
    2452           8 :     if (psJP2C)
    2453             :     {
    2454           8 :         const char *pszTransformation = nullptr;
    2455           8 :         const char *pszCOM = nullptr;
    2456          29 :         for (const CPLXMLNode *psMarker = psJP2C->psChild; psMarker;
    2457          21 :              psMarker = psMarker->psNext)
    2458             :         {
    2459          61 :             if (psMarker->eType == CXT_Element &&
    2460          40 :                 strcmp(psMarker->pszValue, "Marker") == 0 &&
    2461          19 :                 strcmp(CPLGetXMLValue(psMarker, "name", ""), "COD") == 0)
    2462             :             {
    2463          96 :                 for (const CPLXMLNode *psField = psMarker->psChild; psField;
    2464          88 :                      psField = psField->psNext)
    2465             :                 {
    2466         264 :                     if (psField->eType == CXT_Element &&
    2467         168 :                         strcmp(psField->pszValue, "Field") == 0 &&
    2468          72 :                         strcmp(CPLGetXMLValue(psField, "name", ""),
    2469             :                                "SPcod_transformation") == 0)
    2470             :                     {
    2471             :                         pszTransformation =
    2472           8 :                             CPLGetXMLValue(psField, nullptr, nullptr);
    2473           8 :                         break;
    2474             :                     }
    2475             :                 }
    2476             :             }
    2477          37 :             else if (psMarker->eType == CXT_Element &&
    2478          24 :                      strcmp(psMarker->pszValue, "Marker") == 0 &&
    2479          11 :                      strcmp(CPLGetXMLValue(psMarker, "name", ""), "COM") == 0)
    2480             :             {
    2481          55 :                 for (const CPLXMLNode *psField = psMarker->psChild; psField;
    2482          44 :                      psField = psField->psNext)
    2483             :                 {
    2484         132 :                     if (psField->eType == CXT_Element &&
    2485          77 :                         strcmp(psField->pszValue, "Field") == 0 &&
    2486          22 :                         strcmp(CPLGetXMLValue(psField, "name", ""), "COM") == 0)
    2487             :                     {
    2488          11 :                         pszCOM = CPLGetXMLValue(psField, nullptr, nullptr);
    2489          11 :                         break;
    2490             :                     }
    2491             :                 }
    2492             :             }
    2493             :         }
    2494             : 
    2495           8 :         if (pszTransformation != nullptr &&
    2496           8 :             strcmp(pszTransformation, "0") ==
    2497             :                 0)  // 0 = 9x7 irreversible wavelet
    2498             :         {
    2499           2 :             pszReversibility = "LOSSY";
    2500             :         }
    2501           6 :         else if (pszTransformation != nullptr &&
    2502           6 :                  strcmp(pszTransformation, "1") ==
    2503             :                      0)  // 1 = 5x3 reversible wavelet
    2504             :         {
    2505             :             // 5x3 wavelet by itself doesn't guarantee full lossless mode
    2506             :             // if quality layers are discarded. hence the "possibly"
    2507           6 :             pszReversibility = "LOSSLESS (possibly)";
    2508             : 
    2509           6 :             if (pszCOM &&
    2510           6 :                 STARTS_WITH(
    2511             :                     pszCOM,
    2512             :                     "Kdu-Layer-Info: "
    2513             :                     "log_2{Delta-D(squared-error)/Delta-L(bytes)}, L(bytes)"))
    2514             :             {
    2515           0 :                 if (strstr(pszCOM, "-192.0,") != nullptr)
    2516             :                 {
    2517             :                     // Not really sure to understand this fully, but
    2518             :                     // experimentaly I've found that if the last row in the
    2519             :                     // Kdu-Layer-Info includes a line starting with "-192.0", it
    2520             :                     // means that the last layer includes everything to be
    2521             :                     // lossless.
    2522           0 :                     pszReversibility = "LOSSLESS";
    2523             :                 }
    2524             :                 else
    2525             :                 {
    2526           0 :                     pszReversibility = "LOSSY";
    2527             :                 }
    2528             :             }
    2529             :             // Kakadu < 6.4
    2530           6 :             else if (pszCOM &&
    2531           6 :                      STARTS_WITH(
    2532             :                          pszCOM,
    2533             :                          "Kdu-Layer-Info: "
    2534             :                          "log_2{Delta-D(MSE)/[2^16*Delta-L(bytes)]}, L(bytes)"))
    2535             :             {
    2536           2 :                 if (strstr(pszCOM, "-256.0,") != nullptr)
    2537             :                 {
    2538             :                     // Not really sure to understand this fully, but
    2539             :                     // experimentaly I've found that if the last row in the
    2540             :                     // Kdu-Layer-Info includes a line starting with "-256.0", it
    2541             :                     // means that the last layer includes everything to be
    2542             :                     // lossless.
    2543           2 :                     pszReversibility = "LOSSLESS";
    2544             :                 }
    2545             :                 else
    2546             :                 {
    2547           0 :                     pszReversibility = "LOSSY";
    2548             :                 }
    2549             :             }
    2550           4 :             else if (pszCOM && STARTS_WITH(pszCOM, "Created by OpenJPEG"))
    2551             :             {
    2552             :                 // Starting with GDAL 3.6, the JP2OpenJPEG driver will write
    2553             :                 // if the encoding parameters are lossless/lossy (for 5x3
    2554             :                 // wavelets)
    2555           4 :                 if (strstr(pszCOM, "LOSSLESS settings used"))
    2556             :                 {
    2557           3 :                     pszReversibility = "LOSSLESS";
    2558             :                 }
    2559           1 :                 else if (strstr(pszCOM, "LOSSY settings used"))
    2560             :                 {
    2561           1 :                     pszReversibility = "LOSSY";
    2562             :                 }
    2563             :             }
    2564             :         }
    2565             :     }
    2566           8 :     CPLDestroyXMLNode(psRes);
    2567           8 :     return pszReversibility;
    2568             : }
    2569             : 
    2570             : #endif /* #ifndef DOXYGEN_SKIP */

Generated by: LCOV version 1.14