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

Generated by: LCOV version 1.14