/****************************************************************************** * * Project: GDAL * Purpose: GDALJP2Stucture - Dump structure of a JP2/J2K file * Author: Even Rouault, * ****************************************************************************** * Copyright (c) 2015, European Union (European Environment Agency) * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. ****************************************************************************/ #include "cpl_port.h" #include "gdaljp2metadata.h" #include #include #if HAVE_FCNTL_H # include #endif #include #include "cpl_conv.h" #include "cpl_error.h" #include "cpl_minixml.h" #include "cpl_string.h" #include "cpl_vsi.h" #include "gdal.h" #include "gdal_priv.h" constexpr int knbMaxJPEG2000Components = 16384; // per the JPEG2000 standard static CPLXMLNode* GetLastChild(CPLXMLNode* psParent) { CPLXMLNode* psChild = psParent->psChild; while( psChild && psChild->psNext ) psChild = psChild->psNext; return psChild; } static void AddElement(CPLXMLNode* psParent, CPLXMLNode*& psLastChild, CPLXMLNode* psNewElt) { if( psLastChild == nullptr ) psLastChild = GetLastChild(psParent); if( psLastChild == nullptr ) psParent->psChild = psNewElt; else psLastChild->psNext = psNewElt; psLastChild = psNewElt; } static void AddField(CPLXMLNode* psParent, CPLXMLNode*& psLastChild, const char* pszFieldName, int nFieldSize, const char* pszValue, const char* pszDescription = nullptr) { CPLXMLNode* psField = CPLCreateXMLElementAndValue( nullptr, "Field", pszValue ); CPLAddXMLAttributeAndValue(psField, "name", pszFieldName ); CPLAddXMLAttributeAndValue(psField, "type", "string" ); CPLAddXMLAttributeAndValue(psField, "size", CPLSPrintf("%d", nFieldSize ) ); if( pszDescription ) CPLAddXMLAttributeAndValue(psField, "description", pszDescription ); AddElement(psParent, psLastChild, psField); } static void AddHexField(CPLXMLNode* psParent, CPLXMLNode*& psLastChild, const char* pszFieldName, int nFieldSize, const char* pszValue, const char* pszDescription = nullptr) { CPLXMLNode* psField = CPLCreateXMLElementAndValue( nullptr, "Field", pszValue ); CPLAddXMLAttributeAndValue(psField, "name", pszFieldName ); CPLAddXMLAttributeAndValue(psField, "type", "hexint" ); CPLAddXMLAttributeAndValue(psField, "size", CPLSPrintf("%d", nFieldSize ) ); if( pszDescription ) CPLAddXMLAttributeAndValue(psField, "description", pszDescription ); AddElement(psParent, psLastChild, psField); } static void AddField(CPLXMLNode* psParent, CPLXMLNode*& psLastChild, const char* pszFieldName, GByte nVal, const char* pszDescription = nullptr) { CPLXMLNode* psField = CPLCreateXMLElementAndValue( nullptr, "Field", CPLSPrintf("%d", nVal) ); CPLAddXMLAttributeAndValue(psField, "name", pszFieldName ); CPLAddXMLAttributeAndValue(psField, "type", "uint8" ); if( pszDescription ) CPLAddXMLAttributeAndValue(psField, "description", pszDescription ); AddElement(psParent, psLastChild, psField); } static void AddField(CPLXMLNode* psParent, CPLXMLNode*& psLastChild, const char* pszFieldName, GUInt16 nVal, const char* pszDescription = nullptr) { CPLXMLNode* psField = CPLCreateXMLElementAndValue( nullptr, "Field", CPLSPrintf("%d", nVal) ); CPLAddXMLAttributeAndValue(psField, "name", pszFieldName ); CPLAddXMLAttributeAndValue(psField, "type", "uint16" ); if( pszDescription ) CPLAddXMLAttributeAndValue(psField, "description", pszDescription ); AddElement(psParent, psLastChild, psField); } static void AddField(CPLXMLNode* psParent, CPLXMLNode*& psLastChild, const char* pszFieldName, GUInt32 nVal, const char* pszDescription = nullptr) { CPLXMLNode* psField = CPLCreateXMLElementAndValue( nullptr, "Field", CPLSPrintf("%u", nVal) ); CPLAddXMLAttributeAndValue(psField, "name", pszFieldName ); CPLAddXMLAttributeAndValue(psField, "type", "uint32" ); if( pszDescription ) CPLAddXMLAttributeAndValue(psField, "description", pszDescription ); AddElement(psParent, psLastChild, psField); } static const char* GetInterpretationOfBPC(GByte bpc) { if( bpc == 255 ) return nullptr; if( (bpc & 0x80) ) return CPLSPrintf("Signed %d bits", 1 + (bpc & 0x7F)); else return CPLSPrintf("Unsigned %d bits", 1 + bpc); } static const char* GetStandardFieldString(GUInt16 nVal) { switch(nVal) { case 1: return "Codestream contains no extensions"; case 2: return "Contains multiple composition layers"; case 3: return "Codestream is compressed using JPEG 2000 and requires at least a Profile 0 decoder"; case 4: return "Codestream is compressed using JPEG 2000 and requires at least a Profile 1 decoder"; case 5: return "Codestream is compressed using JPEG 2000 unrestricted"; case 35: return "Contains IPR metadata"; case 67: return "Contains GMLJP2 metadata"; default: return nullptr; } } static void DumpGeoTIFFBox(CPLXMLNode* psBox, GDALJP2Box& oBox) { GIntBig nBoxDataLength = oBox.GetDataLength(); GByte* pabyBoxData = oBox.ReadBoxData(); GDALDriver* poVRTDriver = static_cast(GDALGetDriverByName("VRT")); if( pabyBoxData && poVRTDriver) { CPLString osTmpFilename(CPLSPrintf("/vsimem/tmp_%p.tif", oBox.GetFILE())); CPL_IGNORE_RET_VAL(VSIFCloseL(VSIFileFromMemBuffer( osTmpFilename, pabyBoxData, nBoxDataLength, FALSE) )); CPLPushErrorHandler(CPLQuietErrorHandler); GDALDataset* poDS = GDALDataset::FromHandle(GDALOpen(osTmpFilename, GA_ReadOnly)); CPLPopErrorHandler(); // Reject GeoJP2 boxes with a TIFF with band_count > 1. if( poDS && poDS->GetRasterCount() > 1 ) { GDALClose(poDS); poDS = nullptr; } if( poDS ) { CPLString osTmpVRTFilename(CPLSPrintf("/vsimem/tmp_%p.vrt", oBox.GetFILE())); GDALDataset* poVRTDS = poVRTDriver->CreateCopy(osTmpVRTFilename, poDS, FALSE, nullptr, nullptr, nullptr); GDALClose(poVRTDS); GByte* pabyXML = VSIGetMemFileBuffer( osTmpVRTFilename, nullptr, FALSE ); CPLXMLNode* psXMLVRT = CPLParseXMLString(reinterpret_cast(pabyXML)); if( psXMLVRT ) { CPLXMLNode* psXMLContentNode = CPLCreateXMLNode( psBox, CXT_Element, "DecodedGeoTIFF" ); psXMLContentNode->psChild = psXMLVRT; CPLXMLNode* psPrev = nullptr; for(CPLXMLNode* psIter = psXMLVRT->psChild; psIter; psIter = psIter->psNext) { if( psIter->eType == CXT_Element && strcmp(psIter->pszValue, "VRTRasterBand") == 0 ) { CPLXMLNode* psNext = psIter->psNext; psIter->psNext = nullptr; CPLDestroyXMLNode(psIter); if( psPrev ) psPrev->psNext = psNext; else break; psIter = psPrev; } psPrev = psIter; } CPLCreateXMLNode(psXMLVRT, CXT_Element, "VRTRasterBand"); } VSIUnlink(osTmpVRTFilename); GDALClose(poDS); } VSIUnlink(osTmpFilename); } CPLFree(pabyBoxData); } static void DumpFTYPBox(CPLXMLNode* psBox, GDALJP2Box& oBox) { GIntBig nBoxDataLength = oBox.GetDataLength(); GByte* pabyBoxData = oBox.ReadBoxData(); if( pabyBoxData ) { CPLXMLNode* psDecodedContent = CPLCreateXMLNode( psBox, CXT_Element, "DecodedContent" ); GIntBig nRemainingLength = nBoxDataLength; GByte* pabyIter = pabyBoxData; CPLXMLNode* psLastChild = nullptr; if( nRemainingLength >= 4 ) { char szBranding[5]; memcpy(szBranding, pabyIter, 4); szBranding[4] = 0; AddField(psDecodedContent, psLastChild, "BR", 4, szBranding); pabyIter += 4; nRemainingLength -= 4; } if( nRemainingLength >= 4 ) { GUInt32 nVal; memcpy(&nVal, pabyIter, 4); CPL_MSBPTR32(&nVal); AddField(psDecodedContent, psLastChild, "MinV", nVal); pabyIter += 4; nRemainingLength -= 4; } int nCLIndex = 0; while( nRemainingLength >= 4 ) { char szBranding[5]; memcpy(szBranding, pabyIter, 4); szBranding[4] = 0; AddField(psDecodedContent, psLastChild, CPLSPrintf("CL%d", nCLIndex), 4, szBranding); pabyIter += 4; nRemainingLength -= 4; nCLIndex ++; } if( nRemainingLength > 0 ) AddElement( psDecodedContent, psLastChild, CPLCreateXMLElementAndValue( nullptr, "RemainingBytes", CPLSPrintf("%d", static_cast(nRemainingLength) ))); } CPLFree(pabyBoxData); } static void DumpIHDRBox(CPLXMLNode* psBox, GDALJP2Box& oBox) { GIntBig nBoxDataLength = oBox.GetDataLength(); GByte* pabyBoxData = oBox.ReadBoxData(); if( pabyBoxData ) { CPLXMLNode* psDecodedContent = CPLCreateXMLNode( psBox, CXT_Element, "DecodedContent" ); GIntBig nRemainingLength = nBoxDataLength; GByte* pabyIter = pabyBoxData; CPLXMLNode* psLastChild = nullptr; if( nRemainingLength >= 4 ) { GUInt32 nVal; memcpy(&nVal, pabyIter, 4); CPL_MSBPTR32(&nVal); AddField(psDecodedContent, psLastChild, "HEIGHT", nVal); pabyIter += 4; nRemainingLength -= 4; } if( nRemainingLength >= 4 ) { GUInt32 nVal; memcpy(&nVal, pabyIter, 4); CPL_MSBPTR32(&nVal); AddField(psDecodedContent, psLastChild, "WIDTH", nVal); pabyIter += 4; nRemainingLength -= 4; } if( nRemainingLength >= 2 ) { GUInt16 nVal; memcpy(&nVal, pabyIter, 2); CPL_MSBPTR16(&nVal); AddField(psDecodedContent, psLastChild, "NC", nVal); pabyIter += 2; nRemainingLength -= 2; } if( nRemainingLength >= 1 ) { AddField(psDecodedContent, psLastChild, "BPC", *pabyIter, GetInterpretationOfBPC(*pabyIter)); pabyIter += 1; nRemainingLength -= 1; } if( nRemainingLength >= 1 ) { AddField(psDecodedContent, psLastChild, "C", *pabyIter); pabyIter += 1; nRemainingLength -= 1; } if( nRemainingLength >= 1 ) { AddField(psDecodedContent, psLastChild, "UnkC", *pabyIter); pabyIter += 1; nRemainingLength -= 1; } if( nRemainingLength >= 1 ) { AddField(psDecodedContent, psLastChild, "IPR", *pabyIter); /*pabyIter += 1;*/ nRemainingLength -= 1; } if( nRemainingLength > 0 ) AddElement( psDecodedContent, psLastChild, CPLCreateXMLElementAndValue( nullptr, "RemainingBytes", CPLSPrintf("%d", static_cast(nRemainingLength) ))); } CPLFree(pabyBoxData); } static void DumpBPCCBox(CPLXMLNode* psBox, GDALJP2Box& oBox) { GIntBig nBoxDataLength = oBox.GetDataLength(); GByte* pabyBoxData = oBox.ReadBoxData(); if( pabyBoxData ) { CPLXMLNode* psDecodedContent = CPLCreateXMLNode( psBox, CXT_Element, "DecodedContent" ); GIntBig nRemainingLength = nBoxDataLength; GByte* pabyIter = pabyBoxData; int nBPCIndex = 0; CPLXMLNode* psLastChild = nullptr; while( nRemainingLength >= 1 && nBPCIndex < knbMaxJPEG2000Components ) { AddField(psDecodedContent, psLastChild, CPLSPrintf("BPC%d", nBPCIndex), *pabyIter, GetInterpretationOfBPC(*pabyIter)); nBPCIndex ++; pabyIter += 1; nRemainingLength -= 1; } if( nRemainingLength > 0 ) AddElement( psDecodedContent, psLastChild, CPLCreateXMLElementAndValue( nullptr, "RemainingBytes", CPLSPrintf("%d", static_cast(nRemainingLength) ))); } CPLFree(pabyBoxData); } static void DumpCOLRBox(CPLXMLNode* psBox, GDALJP2Box& oBox) { GIntBig nBoxDataLength = oBox.GetDataLength(); GByte* pabyBoxData = oBox.ReadBoxData(); if( pabyBoxData ) { CPLXMLNode* psDecodedContent = CPLCreateXMLNode( psBox, CXT_Element, "DecodedContent" ); GIntBig nRemainingLength = nBoxDataLength; GByte* pabyIter = pabyBoxData; GByte nMeth; CPLXMLNode* psLastChild = nullptr; if( nRemainingLength >= 1 ) { nMeth = *pabyIter; AddField(psDecodedContent, psLastChild, "METH", nMeth, (nMeth == 0) ? "Enumerated Colourspace": (nMeth == 1) ? "Restricted ICC profile": nullptr); pabyIter += 1; nRemainingLength -= 1; } if( nRemainingLength >= 1 ) { AddField(psDecodedContent, psLastChild, "PREC", *pabyIter); pabyIter += 1; nRemainingLength -= 1; } if( nRemainingLength >= 1 ) { AddField(psDecodedContent, psLastChild, "APPROX", *pabyIter); pabyIter += 1; nRemainingLength -= 1; } if( nRemainingLength >= 4 ) { GUInt32 nVal; memcpy(&nVal, pabyIter, 4); CPL_MSBPTR32(&nVal); AddField(psDecodedContent, psLastChild, "EnumCS", nVal, (nVal == 16) ? "sRGB" : (nVal == 17) ? "greyscale": (nVal == 18) ? "sYCC" : nullptr); /*pabyIter += 4;*/ nRemainingLength -= 4; } if( nRemainingLength > 0 ) AddElement(psDecodedContent, psLastChild, CPLCreateXMLElementAndValue( nullptr, "RemainingBytes", CPLSPrintf("%d", static_cast(nRemainingLength) ))); } CPLFree(pabyBoxData); } static void DumpPCLRBox(CPLXMLNode* psBox, GDALJP2Box& oBox) { GIntBig nBoxDataLength = oBox.GetDataLength(); GByte* pabyBoxData = oBox.ReadBoxData(); if( pabyBoxData ) { CPLXMLNode* psDecodedContent = CPLCreateXMLNode( psBox, CXT_Element, "DecodedContent" ); GIntBig nRemainingLength = nBoxDataLength; GByte* pabyIter = pabyBoxData; GUInt16 NE = 0; CPLXMLNode* psLastChild = nullptr; if( nRemainingLength >= 2 ) { GUInt16 nVal; memcpy(&nVal, pabyIter, 2); CPL_MSBPTR16(&nVal); NE = nVal; AddField(psDecodedContent, psLastChild, "NE", nVal); pabyIter += 2; nRemainingLength -= 2; } GByte NPC = 0; if( nRemainingLength >= 1 ) { NPC = *pabyIter; AddField(psDecodedContent, psLastChild, "NPC", NPC); pabyIter += 1; nRemainingLength -= 1; } int b8BitOnly = TRUE; for(int i=0;i= 1 ) { b8BitOnly &= (*pabyIter <= 7); AddField(psDecodedContent, psLastChild, CPLSPrintf("B%d", i), *pabyIter, GetInterpretationOfBPC(*pabyIter)); pabyIter += 1; nRemainingLength -= 1; } } if( b8BitOnly ) { for(int j=0;j= 1 ) { AddField(psDecodedContent, psLastChild, CPLSPrintf("C_%d_%d", j, i), *pabyIter); pabyIter += 1; nRemainingLength -= 1; } } } } if( nRemainingLength > 0 ) AddElement( psDecodedContent, psLastChild, CPLCreateXMLElementAndValue( nullptr, "RemainingBytes", CPLSPrintf("%d", static_cast(nRemainingLength) ))); } CPLFree(pabyBoxData); } static void DumpCMAPBox(CPLXMLNode* psBox, GDALJP2Box& oBox) { GIntBig nBoxDataLength = oBox.GetDataLength(); GByte* pabyBoxData = oBox.ReadBoxData(); if( pabyBoxData ) { CPLXMLNode* psDecodedContent = CPLCreateXMLNode( psBox, CXT_Element, "DecodedContent" ); GIntBig nRemainingLength = nBoxDataLength; GByte* pabyIter = pabyBoxData; int nIndex = 0; CPLXMLNode* psLastChild = nullptr; while( nRemainingLength >= 2 + 1 + 1 && nIndex < knbMaxJPEG2000Components ) { GUInt16 nVal; memcpy(&nVal, pabyIter, 2); CPL_MSBPTR16(&nVal); AddField(psDecodedContent, psLastChild, CPLSPrintf("CMP%d", nIndex), nVal); pabyIter += 2; nRemainingLength -= 2; AddField(psDecodedContent, psLastChild, CPLSPrintf("MTYP%d", nIndex), *pabyIter, (*pabyIter == 0) ? "Direct use": (*pabyIter == 1) ? "Palette mapping": nullptr); pabyIter += 1; nRemainingLength -= 1; AddField(psDecodedContent, psLastChild, CPLSPrintf("PCOL%d", nIndex), *pabyIter); pabyIter += 1; nRemainingLength -= 1; nIndex ++; } if( nRemainingLength > 0 ) AddElement( psDecodedContent, psLastChild, CPLCreateXMLElementAndValue( nullptr, "RemainingBytes", CPLSPrintf("%d", static_cast(nRemainingLength) ))); } CPLFree(pabyBoxData); } static void DumpCDEFBox(CPLXMLNode* psBox, GDALJP2Box& oBox) { GIntBig nBoxDataLength = oBox.GetDataLength(); GByte* pabyBoxData = oBox.ReadBoxData(); if( pabyBoxData ) { CPLXMLNode* psDecodedContent = CPLCreateXMLNode( psBox, CXT_Element, "DecodedContent" ); GIntBig nRemainingLength = nBoxDataLength; GByte* pabyIter = pabyBoxData; GUInt16 nChannels = 0; CPLXMLNode* psLastChild = nullptr; if( nRemainingLength >= 2 ) { GUInt16 nVal; memcpy(&nVal, pabyIter, 2); nChannels = nVal; CPL_MSBPTR16(&nVal); AddField(psDecodedContent, psLastChild, "N", nVal); pabyIter += 2; nRemainingLength -= 2; } for( int i=0; i < nChannels; i++ ) { if( nRemainingLength >= 2 ) { GUInt16 nVal; memcpy(&nVal, pabyIter, 2); CPL_MSBPTR16(&nVal); AddField(psDecodedContent, psLastChild, CPLSPrintf("Cn%d", i), nVal); pabyIter += 2; nRemainingLength -= 2; } if( nRemainingLength >= 2 ) { GUInt16 nVal; memcpy(&nVal, pabyIter, 2); CPL_MSBPTR16(&nVal); AddField(psDecodedContent, psLastChild, CPLSPrintf("Typ%d", i), nVal, (nVal == 0) ? "Colour channel": (nVal == 1) ? "Opacity channel": (nVal == 2) ? "Premultiplied opacity": (nVal == 65535) ? "Not specified" : nullptr); pabyIter += 2; nRemainingLength -= 2; } if( nRemainingLength >= 2 ) { GUInt16 nVal; memcpy(&nVal, pabyIter, 2); CPL_MSBPTR16(&nVal); AddField(psDecodedContent, psLastChild, CPLSPrintf("Asoc%d", i), nVal, (nVal == 0) ? "Associated to the whole image": (nVal == 65535) ? "Not associated with a particular colour": "Associated with a particular colour"); pabyIter += 2; nRemainingLength -= 2; } } if( nRemainingLength > 0 ) AddElement( psDecodedContent, psLastChild, CPLCreateXMLElementAndValue( nullptr, "RemainingBytes", CPLSPrintf("%d", static_cast(nRemainingLength) ))); } CPLFree(pabyBoxData); } static void DumpRESxBox(CPLXMLNode* psBox, GDALJP2Box& oBox) { GIntBig nBoxDataLength = oBox.GetDataLength(); GByte* pabyBoxData = oBox.ReadBoxData(); char chC = oBox.GetType()[3]; if( pabyBoxData ) { CPLXMLNode* psDecodedContent = CPLCreateXMLNode( psBox, CXT_Element, "DecodedContent" ); GIntBig nRemainingLength = nBoxDataLength; GByte* pabyIter = pabyBoxData; GUInt16 nNumV = 0; GUInt16 nNumH = 0; GUInt16 nDenomV = 1; GUInt16 nDenomH = 1; GUInt16 nExpV = 0; GUInt16 nExpH = 0; CPLXMLNode* psLastChild = nullptr; if( nRemainingLength >= 2 ) { GUInt16 nVal; memcpy(&nVal, pabyIter, 2); CPL_MSBPTR16(&nVal); nNumV = nVal; AddField(psDecodedContent, psLastChild, CPLSPrintf("VR%cN", chC), nVal); pabyIter += 2; nRemainingLength -= 2; } if( nRemainingLength >= 2 ) { GUInt16 nVal; memcpy(&nVal, pabyIter, 2); CPL_MSBPTR16(&nVal); nDenomV = nVal; AddField(psDecodedContent, psLastChild, CPLSPrintf("VR%cD", chC), nVal); pabyIter += 2; nRemainingLength -= 2; } if( nRemainingLength >= 2 ) { GUInt16 nVal; memcpy(&nVal, pabyIter, 2); CPL_MSBPTR16(&nVal); nNumH = nVal; AddField(psDecodedContent, psLastChild, CPLSPrintf("HR%cN", chC), nVal); pabyIter += 2; nRemainingLength -= 2; } if( nRemainingLength >= 2 ) { GUInt16 nVal; memcpy(&nVal, pabyIter, 2); CPL_MSBPTR16(&nVal); nDenomH = nVal; AddField(psDecodedContent, psLastChild, CPLSPrintf("HR%cD", chC), nVal); pabyIter += 2; nRemainingLength -= 2; } if( nRemainingLength >= 1 ) { AddField(psDecodedContent, psLastChild, CPLSPrintf("VR%cE", chC), *pabyIter); nExpV = *pabyIter; pabyIter += 1; nRemainingLength -= 1; } if( nRemainingLength >= 1 ) { AddField(psDecodedContent, psLastChild, CPLSPrintf("HR%cE", chC), *pabyIter); nExpH = *pabyIter; /*pabyIter += 1;*/ nRemainingLength -= 1; } if( nRemainingLength == 0 ) { const char* pszVRes = (nDenomV == 0) ? "invalid" : CPLSPrintf("%.03f", 1.0 * nNumV / nDenomV * pow(10.0, nExpV)); AddElement(psDecodedContent, psLastChild, CPLCreateXMLElementAndValue( nullptr, "VRes", pszVRes )); const char* pszHRes = (nDenomH == 0) ? "invalid" : CPLSPrintf("%.03f", 1.0 * nNumH / nDenomH * pow(10.0, nExpH)); AddElement(psDecodedContent, psLastChild, CPLCreateXMLElementAndValue( nullptr, "HRes", pszHRes )); } else if( nRemainingLength > 0 ) AddElement(psDecodedContent, psLastChild, CPLCreateXMLElementAndValue( nullptr, "RemainingBytes", CPLSPrintf("%d", static_cast(nRemainingLength) ))); } CPLFree(pabyBoxData); } static void DumpRREQBox(CPLXMLNode* psBox, GDALJP2Box& oBox) { GIntBig nBoxDataLength = oBox.GetDataLength(); GByte* pabyBoxData = oBox.ReadBoxData(); if( pabyBoxData ) { CPLXMLNode* psDecodedContent = CPLCreateXMLNode( psBox, CXT_Element, "DecodedContent" ); GIntBig nRemainingLength = nBoxDataLength; GByte* pabyIter = pabyBoxData; GByte ML = 0; CPLXMLNode* psLastChild = nullptr; if( nRemainingLength >= 1 ) { ML = *pabyIter; AddField(psDecodedContent, psLastChild, "ML", *pabyIter); pabyIter += 1; nRemainingLength -= 1; } if( nRemainingLength >= ML ) { CPLString osHex("0x"); for(int i=0;i(ML), osHex.c_str()); } if( nRemainingLength >= ML ) { CPLString osHex("0x"); for(int i=0;i(ML), osHex.c_str()); } GUInt16 NSF = 0; if( nRemainingLength >= 2 ) { GUInt16 nVal; memcpy(&nVal, pabyIter, 2); CPL_MSBPTR16(&nVal); NSF = nVal; AddField(psDecodedContent, psLastChild, "NSF", nVal); pabyIter += 2; nRemainingLength -= 2; } for(int iNSF=0;iNSF= 2 ) { GUInt16 nVal; memcpy(&nVal, pabyIter, 2); CPL_MSBPTR16(&nVal); AddField(psDecodedContent, psLastChild, CPLSPrintf("SF%d", iNSF), nVal, GetStandardFieldString(nVal)); pabyIter += 2; nRemainingLength -= 2; } else break; if( nRemainingLength >= ML ) { CPLString osHex("0x"); for(int i=0;i(ML), osHex.c_str()); } else break; } GUInt16 NVF = 0; if( nRemainingLength >= 2 ) { GUInt16 nVal; memcpy(&nVal, pabyIter, 2); CPL_MSBPTR16(&nVal); NVF = nVal; AddField(psDecodedContent, psLastChild, "NVF", nVal); pabyIter += 2; nRemainingLength -= 2; } for(int iNVF=0;iNVF= 16 ) { CPLString osHex("0x"); for(int i=0;i<16;i++) { osHex += CPLSPrintf("%02X", *pabyIter); pabyIter += 1; nRemainingLength -= 1; } AddHexField(psDecodedContent, psLastChild, CPLSPrintf("VF%d", iNVF), static_cast(ML), osHex.c_str()); } else break; if( nRemainingLength >= ML ) { CPLString osHex("0x"); for(int i=0;i(ML), osHex.c_str()); } else break; } if( nRemainingLength > 0 ) AddElement( psDecodedContent, psLastChild, CPLCreateXMLElementAndValue( nullptr, "RemainingBytes", CPLSPrintf("%d", static_cast(nRemainingLength) ))); } CPLFree(pabyBoxData); } static CPLXMLNode* CreateMarker(CPLXMLNode* psCSBox, CPLXMLNode*& psLastChildCSBox, const char* pszName, GIntBig nOffset, GIntBig nLength) { CPLXMLNode* psMarker = CPLCreateXMLNode( nullptr, CXT_Element, "Marker" ); CPLAddXMLAttributeAndValue(psMarker, "name", pszName ); CPLAddXMLAttributeAndValue(psMarker, "offset", CPLSPrintf(CPL_FRMT_GIB, nOffset ) ); CPLAddXMLAttributeAndValue(psMarker, "length", CPLSPrintf(CPL_FRMT_GIB, 2 + nLength ) ); AddElement( psCSBox, psLastChildCSBox, psMarker ); return psMarker; } static CPLXMLNode* _AddError(CPLXMLNode* psParent, const char* pszErrorMsg, GIntBig nOffset = 0) { CPLXMLNode* psError = CPLCreateXMLNode( psParent, CXT_Element, "Error" ); CPLAddXMLAttributeAndValue(psError, "message", pszErrorMsg ); if( nOffset ) { CPLAddXMLAttributeAndValue(psError, "offset", CPLSPrintf(CPL_FRMT_GIB, nOffset ) ); } return psError; } static void AddError(CPLXMLNode* psParent, CPLXMLNode*& psLastChild, const char* pszErrorMsg, GIntBig nOffset = 0) { AddElement( psParent, psLastChild, _AddError(nullptr, pszErrorMsg, nOffset) ); } static const char* GetMarkerName(GByte byVal) { switch(byVal) { case 0x90: return "SOT"; case 0x50: return "CAP"; case 0x51: return "SIZ"; case 0x52: return "COD"; case 0x53: return "COC"; case 0x55: return "TLM"; case 0x57: return "PLM"; case 0x58: return "PLT"; case 0x5C: return "QCD"; case 0x5D: return "QCC"; case 0x5E: return "RGN"; case 0x5F: return "POC"; case 0x60: return "PPM"; case 0x61: return "PPT"; case 0x63: return "CRG"; case 0x64: return "COM"; default: return CPLSPrintf("Unknown 0xFF%02X", byVal); } } /************************************************************************/ /* DumpJPK2CodeStream() */ /************************************************************************/ static CPLXMLNode* DumpJPK2CodeStream(CPLXMLNode* psBox, VSILFILE* fp, GIntBig nBoxDataOffset, GIntBig nBoxDataLength) { GByte abyMarker[2]; CPLXMLNode* psCSBox = CPLCreateXMLNode( psBox, CXT_Element, "JP2KCodeStream" ); CPLXMLNode* psLastChildCSBox = nullptr; if( VSIFSeekL(fp, nBoxDataOffset, SEEK_SET) != 0 ) { AddError(psCSBox, psLastChildCSBox, "Cannot read codestream", 0); return psCSBox; } GByte* pabyMarkerData = static_cast(CPLMalloc(65535+1)); GIntBig nNextTileOffset = 0; int Csiz = -1; const auto lambdaPOCType = [](GByte v) { return (v == 0) ? "LRCP" : (v == 1) ? "RLCP" : (v == 2) ? "RPCL" : (v == 3) ? "PCRL" : (v == 4) ? "CPRL" : nullptr; }; while( true ) { GIntBig nOffset = static_cast(VSIFTellL(fp)); if( nOffset == nBoxDataOffset + nBoxDataLength ) break; if( VSIFReadL(abyMarker, 2, 1, fp) != 1 ) { AddError(psCSBox, psLastChildCSBox, "Cannot read marker", nOffset); break; } if( abyMarker[0] != 0xFF ) { AddError(psCSBox, psLastChildCSBox, "Not a marker", nOffset); break; } if( abyMarker[1] == 0x4F ) { CreateMarker( psCSBox, psLastChildCSBox, "SOC", nOffset, 0 ); continue; } if( abyMarker[1] == 0x93 ) { GIntBig nMarkerSize = 0; bool bBreak = false; if( nNextTileOffset == 0 ) { nMarkerSize = (nBoxDataOffset + nBoxDataLength - 2) - nOffset - 2; if( VSIFSeekL(fp, nBoxDataOffset + nBoxDataLength - 2, SEEK_SET) != 0 || VSIFReadL(abyMarker, 2, 1, fp) != 1 || abyMarker[0] != 0xFF || abyMarker[1] != 0xD9 ) { /* autotest/gdrivers/data/rgb16_ecwsdk.jp2 does not end */ /* with a EOC... */ nMarkerSize += 2; bBreak = true; } } else if( nNextTileOffset >= nOffset + 2 ) nMarkerSize = nNextTileOffset - nOffset - 2; CreateMarker( psCSBox, psLastChildCSBox, "SOD", nOffset, nMarkerSize ); if( bBreak ) break; if( nNextTileOffset && nNextTileOffset == nOffset ) { /* Found with Pleiades images. openjpeg doesn't like it either */ nNextTileOffset = 0; } else if( nNextTileOffset && nNextTileOffset >= nOffset + 2 ) { if( VSIFSeekL(fp, nNextTileOffset, SEEK_SET) != 0 ) AddError(psCSBox, psLastChildCSBox, "Cannot seek to", nNextTileOffset); nNextTileOffset = 0; } else { /* We have seek and check before we hit a EOC */ nOffset = nBoxDataOffset + nBoxDataLength - 2; CreateMarker( psCSBox, psLastChildCSBox, "EOC", nOffset, 0 ); } continue; } if( abyMarker[1] == 0xD9 ) { CreateMarker( psCSBox, psLastChildCSBox, "EOC", nOffset, 0 ); continue; } /* Reserved markers */ if( abyMarker[1] >= 0x30 && abyMarker[1] <= 0x3F ) { CreateMarker( psCSBox, psLastChildCSBox, CPLSPrintf("Unknown 0xFF%02X", abyMarker[1]), nOffset, 0 ); continue; } GUInt16 nMarkerSize; if( VSIFReadL(&nMarkerSize, 2, 1, fp) != 1 ) { AddError(psCSBox, psLastChildCSBox, CPLSPrintf("Cannot read marker size of %s", GetMarkerName(abyMarker[1])), nOffset); break; } CPL_MSBPTR16(&nMarkerSize); if( nMarkerSize < 2 ) { AddError(psCSBox, psLastChildCSBox, CPLSPrintf("Invalid marker size of %s", GetMarkerName(abyMarker[1])), nOffset); break; } CPLXMLNode* psMarker = CreateMarker( psCSBox, psLastChildCSBox, GetMarkerName(abyMarker[1]), nOffset, nMarkerSize ); CPLXMLNode* psLastChild = nullptr; if( VSIFReadL(pabyMarkerData, nMarkerSize - 2, 1, fp) != 1 ) { AddError(psMarker, psLastChild, "Cannot read marker data", nOffset); break; } GByte* pabyMarkerDataIter = pabyMarkerData; GUInt16 nRemainingMarkerSize = nMarkerSize - 2; bool bError = false; auto READ_MARKER_FIELD_UINT8 = [&](const char* name, const char* (*commentFunc)(GByte) = nullptr) { GByte v; if( nRemainingMarkerSize >= 1 ) { v = *pabyMarkerDataIter; AddField(psMarker, psLastChild, name, *pabyMarkerDataIter, commentFunc ? commentFunc(v) : nullptr); pabyMarkerDataIter += 1; nRemainingMarkerSize -= 1; } else { AddError(psMarker, psLastChild, CPLSPrintf("Cannot read field %s", name)); v = 0; bError = true; } return v; }; auto READ_MARKER_FIELD_UINT16 = [&](const char* name, const char* (*commentFunc)(GUInt16) = nullptr) { GUInt16 v; if( nRemainingMarkerSize >= 2 ) { memcpy(&v, pabyMarkerDataIter, 2); CPL_MSBPTR16(&v); AddField(psMarker, psLastChild, name, v, commentFunc ? commentFunc(v) : nullptr); pabyMarkerDataIter += 2; nRemainingMarkerSize -= 2; } else { AddError(psMarker, psLastChild, CPLSPrintf("Cannot read field %s", name)); v = 0; bError = true; } return v; }; auto READ_MARKER_FIELD_UINT32 = [&](const char* name, const char* (*commentFunc)(GUInt32) = nullptr) { GUInt32 v; if( nRemainingMarkerSize >= 4 ) { memcpy(&v, pabyMarkerDataIter, 4); CPL_MSBPTR32(&v); AddField(psMarker, psLastChild, name, v, commentFunc ? commentFunc(v) : nullptr); pabyMarkerDataIter += 4; nRemainingMarkerSize -= 4; } else { AddError(psMarker, psLastChild, CPLSPrintf("Cannot read field %s", name)); v = 0; bError = true; } return v; }; if( abyMarker[1] == 0x90 ) /* SOT */ { READ_MARKER_FIELD_UINT16("Isot"); GUInt32 PSOT = READ_MARKER_FIELD_UINT32("Psot"); READ_MARKER_FIELD_UINT8("TPsot"); READ_MARKER_FIELD_UINT8("TNsot"); if( nRemainingMarkerSize > 0 ) AddElement( psMarker, psLastChild, CPLCreateXMLElementAndValue( nullptr, "RemainingBytes", CPLSPrintf("%d", static_cast(nRemainingMarkerSize) ))); if( PSOT ) nNextTileOffset = nOffset + PSOT; } else if( abyMarker[1] == 0x50 ) /* CAP (HTJ2K) */ { const GUInt32 Pcap = READ_MARKER_FIELD_UINT32("Pcap"); for( int i = 0; i < 32; i++ ) { if( (Pcap >> (31 - i)) & 1 ) { READ_MARKER_FIELD_UINT16(CPLSPrintf("Scap_P%d", i+1)); } } if( nRemainingMarkerSize > 0 ) AddElement( psMarker, psLastChild, CPLCreateXMLElementAndValue( nullptr, "RemainingBytes", CPLSPrintf("%d", static_cast(nRemainingMarkerSize) ))); } else if( abyMarker[1] == 0x51 ) /* SIZ */ { READ_MARKER_FIELD_UINT16("Rsiz", [](GUInt16 v) { return (v == 0) ? "Unrestricted profile": (v == 1) ? "Profile 0": (v == 2) ? "Profile 1": (v == 16384) ? "HTJ2K": nullptr; }); READ_MARKER_FIELD_UINT32("Xsiz"); READ_MARKER_FIELD_UINT32("Ysiz"); READ_MARKER_FIELD_UINT32("XOsiz"); READ_MARKER_FIELD_UINT32("YOsiz"); READ_MARKER_FIELD_UINT32("XTsiz"); READ_MARKER_FIELD_UINT32("YTsiz"); READ_MARKER_FIELD_UINT32("XTOSiz"); READ_MARKER_FIELD_UINT32("YTOSiz"); Csiz = READ_MARKER_FIELD_UINT16("Csiz"); bError = false; // cppcheck-suppress knownConditionTrueFalse for(int i=0;i 0 ) AddElement( psMarker, psLastChild, CPLCreateXMLElementAndValue( nullptr, "RemainingBytes", CPLSPrintf("%d", static_cast(nRemainingMarkerSize) ))); } else if( abyMarker[1] == 0x52 ) /* COD */ { bool bHasPrecincts = false; if( nRemainingMarkerSize >= 1 ) { auto nLastVal = *pabyMarkerDataIter; CPLString osInterp; if( nLastVal & 0x1 ) { bHasPrecincts = true; osInterp += "User defined precincts"; } else osInterp += "Standard precincts"; osInterp += ", "; if( nLastVal & 0x2 ) osInterp += "SOP marker segments may be used"; else osInterp += "No SOP marker segments"; osInterp += ", "; if( nLastVal & 0x4 ) osInterp += "EPH marker segments may be used"; else osInterp += "No EPH marker segments"; AddField(psMarker, psLastChild, "Scod", nLastVal, osInterp.c_str()); pabyMarkerDataIter += 1; nRemainingMarkerSize -= 1; } else { AddError(psMarker, psLastChild, CPLSPrintf("Cannot read field %s", "Scod")); } READ_MARKER_FIELD_UINT8("SGcod_Progress", lambdaPOCType); READ_MARKER_FIELD_UINT16("SGcod_NumLayers"); READ_MARKER_FIELD_UINT8("SGcod_MCT"); READ_MARKER_FIELD_UINT8("SPcod_NumDecompositions"); READ_MARKER_FIELD_UINT8("SPcod_xcb_minus_2", [](GByte v) { return v <= 8 ? CPLSPrintf("%d", 1 << (2+v)) : "invalid"; }); READ_MARKER_FIELD_UINT8("SPcod_ycb_minus_2", [](GByte v) { return v <= 8 ? CPLSPrintf("%d", 1 << (2+v)) : "invalid"; }); if( nRemainingMarkerSize >= 1 ) { auto nLastVal = *pabyMarkerDataIter; CPLString osInterp; if( nLastVal & 0x1 ) osInterp += "Selective arithmetic coding bypass"; else osInterp += "No selective arithmetic coding bypass"; osInterp += ", "; if( nLastVal & 0x2 ) osInterp += "Reset context probabilities on coding pass boundaries"; else osInterp += "No reset of context probabilities on coding pass boundaries"; osInterp += ", "; if( nLastVal & 0x4 ) osInterp += "Termination on each coding pass"; else osInterp += "No termination on each coding pass"; osInterp += ", "; if( nLastVal & 0x8 ) osInterp += "Vertically causal context"; else osInterp += "No vertically causal context"; osInterp += ", "; if( nLastVal & 0x10 ) osInterp += "Predictable termination"; else osInterp += "No predictable termination"; osInterp += ", "; if( nLastVal & 0x20 ) osInterp += "Segmentation symbols are used"; else osInterp += "No segmentation symbols are used"; if( nLastVal & 0x40 ) osInterp += ", High Throughput algorithm"; AddField(psMarker, psLastChild, "SPcod_cbstyle", static_cast(nLastVal), osInterp.c_str()); pabyMarkerDataIter += 1; nRemainingMarkerSize -= 1; } else { AddError(psMarker, psLastChild, CPLSPrintf("Cannot read field %s", "SPcod_cbstyle")); } READ_MARKER_FIELD_UINT8("SPcod_transformation", [](GByte v) { return (v == 0) ? "9-7 irreversible": (v == 1) ? "5-3 reversible": nullptr; }); if( bHasPrecincts ) { int i = 0; while( nRemainingMarkerSize >= 1 ) { auto nLastVal = *pabyMarkerDataIter; AddField(psMarker, psLastChild, CPLSPrintf("SPcod_Precincts%d", i), *pabyMarkerDataIter, CPLSPrintf("PPx=%d PPy=%d: %dx%d", nLastVal & 0xf, nLastVal >> 4, 1 << (nLastVal & 0xf), 1 << (nLastVal >> 4))); pabyMarkerDataIter += 1; nRemainingMarkerSize -= 1; i ++; } } if( nRemainingMarkerSize > 0 ) AddElement( psMarker, psLastChild, CPLCreateXMLElementAndValue( nullptr, "RemainingBytes", CPLSPrintf("%d", static_cast(nRemainingMarkerSize) ))); } else if( abyMarker[1] == 0x53 ) /* COC */ { } else if( abyMarker[1] == 0x55 ) /* TLM */ { READ_MARKER_FIELD_UINT8("Ztlm"); auto Stlm = READ_MARKER_FIELD_UINT8("Stlm", [](GByte v) { return CPLSPrintf("ST=%d SP=%d", (v >> 4) & 3, (v >> 6) & 1); }); int ST = (Stlm >> 4) & 3; int SP = (Stlm >> 6) & 1; int nTilePartDescLength = ST + ((SP == 0) ? 2 : 4); int i = 0; while( nRemainingMarkerSize >= nTilePartDescLength ) { if( ST == 1 ) READ_MARKER_FIELD_UINT8(CPLSPrintf("Ttlm%d", i)); else if( ST == 2 ) READ_MARKER_FIELD_UINT16(CPLSPrintf("Ttlm%d", i)); if( SP == 0 ) READ_MARKER_FIELD_UINT16(CPLSPrintf("Ptlm%d", i)); else READ_MARKER_FIELD_UINT32(CPLSPrintf("Ptlm%d", i)); i ++; } if( nRemainingMarkerSize > 0 ) AddElement( psMarker, psLastChild, CPLCreateXMLElementAndValue( nullptr, "RemainingBytes", CPLSPrintf("%d", static_cast(nRemainingMarkerSize) ))); } else if( abyMarker[1] == 0x57 ) /* PLM */ { } else if( abyMarker[1] == 0x58 ) /* PLT */ { READ_MARKER_FIELD_UINT8("Zplt"); int i = 0; unsigned nPacketLength = 0; while( nRemainingMarkerSize >= 1 ) { auto nLastVal = *pabyMarkerDataIter; nPacketLength |= (nLastVal & 0x7f); if (nLastVal & 0x80) { nPacketLength <<= 7; } else { AddField(psMarker, psLastChild, CPLSPrintf("Iplt%d", i), nPacketLength); nPacketLength = 0; i ++; } pabyMarkerDataIter += 1; nRemainingMarkerSize -= 1; } if( nPacketLength != 0 ) { AddError(psMarker, psLastChild, "Incorrect PLT marker"); } } else if( abyMarker[1] == 0x5C ) /* QCD */ { } else if( abyMarker[1] == 0x5D ) /* QCC */ { } else if( abyMarker[1] == 0x5E ) /* RGN */ { } else if( abyMarker[1] == 0x5F ) /* POC */ { const int nPOCEntrySize = Csiz < 257 ? 7 : 9; int i = 0; while( nRemainingMarkerSize >= nPOCEntrySize ) { READ_MARKER_FIELD_UINT8(CPLSPrintf("RSpoc%d", i)); if( nPOCEntrySize == 7 ) { READ_MARKER_FIELD_UINT8(CPLSPrintf("CSpoc%d", i)); } else { READ_MARKER_FIELD_UINT16(CPLSPrintf("CSpoc%d", i)); } READ_MARKER_FIELD_UINT16(CPLSPrintf("LYEpoc%d", i)); READ_MARKER_FIELD_UINT8(CPLSPrintf("REpoc%d", i)); if( nPOCEntrySize == 7 ) { READ_MARKER_FIELD_UINT8(CPLSPrintf("CEpoc%d", i)); } else { READ_MARKER_FIELD_UINT16(CPLSPrintf("CEpoc%d", i)); } READ_MARKER_FIELD_UINT8(CPLSPrintf("Ppoc%d", i), lambdaPOCType); i ++; } if( nRemainingMarkerSize > 0 ) { AddElement( psMarker, psLastChild, CPLCreateXMLElementAndValue( nullptr, "RemainingBytes", CPLSPrintf("%d", static_cast(nRemainingMarkerSize) ))); } } else if( abyMarker[1] == 0x60 ) /* PPM */ { } else if( abyMarker[1] == 0x61 ) /* PPT */ { } else if( abyMarker[1] == 0x63 ) /* CRG */ { } else if( abyMarker[1] == 0x64 ) /* COM */ { auto RCom = READ_MARKER_FIELD_UINT16("Rcom", [](GUInt16 v) { return (v == 0 ) ? "Binary" : (v == 1) ? "LATIN1" : nullptr; }); if( RCom == 1 ) { GByte abyBackup = pabyMarkerDataIter[nRemainingMarkerSize]; pabyMarkerDataIter[nRemainingMarkerSize] = 0; AddField(psMarker, psLastChild, "COM", static_cast(nRemainingMarkerSize), reinterpret_cast(pabyMarkerDataIter)); pabyMarkerDataIter[nRemainingMarkerSize] = abyBackup; } } if( VSIFSeekL(fp, nOffset + 2 + nMarkerSize, SEEK_SET) != 0 ) { AddError(psCSBox, psLastChildCSBox, "Cannot seek to next marker", nOffset + 2 + nMarkerSize); break; } CPL_IGNORE_RET_VAL(bError); } CPLFree(pabyMarkerData); return psCSBox; } /************************************************************************/ /* GDALGetJPEG2000StructureInternal() */ /************************************************************************/ static void GDALGetJPEG2000StructureInternal(CPLXMLNode* psParent, VSILFILE* fp, GDALJP2Box* poParentBox, CSLConstList papszOptions, int nRecLevel, vsi_l_offset nFileOrParentBoxSize) { // Limit recursion to a reasonable level. I believe that in practice 2 // should be sufficient, but just in case someone creates deeply // nested "super-boxes", allow up to 5. if( nRecLevel == 5 ) return; static const char* const szHex = "0123456789ABCDEF"; GDALJP2Box oBox( fp ); CPLXMLNode* psLastChild = nullptr; if( oBox.ReadFirstChild(poParentBox) ) { while( strlen(oBox.GetType()) > 0 ) { GIntBig nBoxDataLength = oBox.GetDataLength(); const char* pszBoxType = oBox.GetType(); CPLXMLNode* psBox = CPLCreateXMLNode( nullptr, CXT_Element, "JP2Box" ); AddElement( psParent, psLastChild, psBox ); CPLAddXMLAttributeAndValue(psBox, "name", pszBoxType ); CPLAddXMLAttributeAndValue(psBox, "box_offset", CPLSPrintf(CPL_FRMT_GIB, oBox.GetBoxOffset() ) ); CPLAddXMLAttributeAndValue(psBox, "box_length", CPLSPrintf(CPL_FRMT_GIB, oBox.GetBoxLength() ) ); CPLAddXMLAttributeAndValue(psBox, "data_offset", CPLSPrintf(CPL_FRMT_GIB, oBox.GetDataOffset() ) ); CPLAddXMLAttributeAndValue(psBox, "data_length", CPLSPrintf(CPL_FRMT_GIB, nBoxDataLength ) ); if( nBoxDataLength > GINTBIG_MAX - oBox.GetDataOffset() ) { CPLXMLNode* psLastChildBox = nullptr; AddError(psBox, psLastChildBox, "Invalid box_length"); break; } // Check large non-jp2c boxes against filesize if( strcmp(pszBoxType, "jp2c") != 0 && nBoxDataLength > 100 * 1024 ) { if( nFileOrParentBoxSize == 0 ) { CPL_IGNORE_RET_VAL(VSIFSeekL(fp, 0, SEEK_END)); nFileOrParentBoxSize = VSIFTellL(fp); } } if( nFileOrParentBoxSize > 0 && (static_cast(oBox.GetDataOffset()) > nFileOrParentBoxSize || static_cast(nBoxDataLength) > nFileOrParentBoxSize - oBox.GetDataOffset()) ) { CPLXMLNode* psLastChildBox = nullptr; AddError(psBox, psLastChildBox, "Invalid box_length"); break; } if( oBox.IsSuperBox() ) { GDALGetJPEG2000StructureInternal(psBox, fp, &oBox, papszOptions, nRecLevel + 1, oBox.GetDataOffset() + static_cast(nBoxDataLength)); } else { if( strcmp(pszBoxType, "uuid") == 0 ) { char* pszBinaryContent = static_cast(VSIMalloc( 2 * 16 + 1 )); const GByte* pabyUUID = oBox.GetUUID(); for(int i=0;i<16;i++) { pszBinaryContent[2*i] = szHex[pabyUUID[i] >> 4]; pszBinaryContent[2*i+1] = szHex[pabyUUID[i] & 0xf]; } pszBinaryContent[2*16] = '\0'; CPLXMLNode* psUUIDNode = CPLCreateXMLNode( psBox, CXT_Element, "UUID" ); if( GDALJP2Metadata::IsUUID_MSI(pabyUUID) ) CPLAddXMLAttributeAndValue(psUUIDNode, "description", "GeoTIFF" ); else if( GDALJP2Metadata::IsUUID_XMP(pabyUUID) ) CPLAddXMLAttributeAndValue(psUUIDNode, "description", "XMP" ); CPLCreateXMLNode( psUUIDNode, CXT_Text, pszBinaryContent); VSIFree(pszBinaryContent); } if( (CPLFetchBool(papszOptions, "BINARY_CONTENT", false) || CPLFetchBool(papszOptions, "ALL", false) ) && strcmp(pszBoxType, "jp2c") != 0 && nBoxDataLength < 100 * 1024 ) { CPLXMLNode* psBinaryContent = CPLCreateXMLNode( psBox, CXT_Element, "BinaryContent" ); GByte* pabyBoxData = oBox.ReadBoxData(); int nBoxLength = static_cast(nBoxDataLength); char* pszBinaryContent = static_cast(VSIMalloc( 2 * nBoxLength + 1 )); if( pabyBoxData && pszBinaryContent ) { for(int i=0;i> 4]; pszBinaryContent[2*i+1] = szHex[pabyBoxData[i] & 0xf]; } pszBinaryContent[2*nBoxLength] = '\0'; CPLCreateXMLNode( psBinaryContent, CXT_Text, pszBinaryContent ); } CPLFree(pabyBoxData); VSIFree(pszBinaryContent); } if( (CPLFetchBool(papszOptions, "TEXT_CONTENT", false) || CPLFetchBool(papszOptions, "ALL", false) ) && strcmp(pszBoxType, "jp2c") != 0 && nBoxDataLength < 100 * 1024 ) { GByte* pabyBoxData = oBox.ReadBoxData(); if( pabyBoxData ) { const char* pszBoxData = reinterpret_cast(pabyBoxData); if( CPLIsUTF8(pszBoxData, -1) && static_cast(strlen(pszBoxData)) + 2 >= nBoxDataLength ) { CPLXMLNode* psXMLContentBox = nullptr; if( pszBoxData[0] == '<' ) { CPLPushErrorHandler(CPLQuietErrorHandler); psXMLContentBox = CPLParseXMLString(pszBoxData); CPLPopErrorHandler(); } if( psXMLContentBox ) { CPLXMLNode* psXMLContentNode = CPLCreateXMLNode( psBox, CXT_Element, "XMLContent" ); psXMLContentNode->psChild = psXMLContentBox; } else { CPLCreateXMLNode( CPLCreateXMLNode( psBox, CXT_Element, "TextContent" ), CXT_Text, pszBoxData); } } } CPLFree(pabyBoxData); } if( strcmp(pszBoxType, "jp2c") == 0 ) { if( CPLFetchBool(papszOptions, "CODESTREAM", false) || CPLFetchBool(papszOptions, "ALL", false) ) { DumpJPK2CodeStream(psBox, fp, oBox.GetDataOffset(), nBoxDataLength); } } else if( strcmp(pszBoxType, "uuid") == 0 && GDALJP2Metadata::IsUUID_MSI(oBox.GetUUID()) ) { DumpGeoTIFFBox(psBox, oBox); } else if( strcmp(pszBoxType, "ftyp") == 0 ) { DumpFTYPBox(psBox, oBox); } else if( strcmp(pszBoxType, "ihdr") == 0 ) { DumpIHDRBox(psBox, oBox); } else if( strcmp(pszBoxType, "bpcc") == 0 ) { DumpBPCCBox(psBox, oBox); } else if( strcmp(pszBoxType, "colr") == 0 ) { DumpCOLRBox(psBox, oBox); } else if( strcmp(pszBoxType, "pclr") == 0 ) { DumpPCLRBox(psBox, oBox); } else if( strcmp(pszBoxType, "cmap") == 0 ) { DumpCMAPBox(psBox, oBox); } else if( strcmp(pszBoxType, "cdef") == 0 ) { DumpCDEFBox(psBox, oBox); } else if( strcmp(pszBoxType, "resc") == 0 || strcmp(pszBoxType, "resd") == 0) { DumpRESxBox(psBox, oBox); } else if( strcmp(pszBoxType, "rreq") == 0 ) { DumpRREQBox(psBox, oBox); } } if (!oBox.ReadNextChild(poParentBox)) break; } } } /************************************************************************/ /* GDALGetJPEG2000Structure() */ /************************************************************************/ constexpr unsigned char jpc_header[] = {0xff,0x4f}; constexpr unsigned char jp2_box_jp[] = {0x6a,0x50,0x20,0x20}; /* 'jP ' */ /** Dump the structure of a JPEG2000 file as a XML tree. * * @param pszFilename filename. * @param papszOptions NULL terminated list of options, or NULL. * Allowed options are BINARY_CONTENT=YES, TEXT_CONTENT=YES, * CODESTREAM=YES, ALL=YES. * @return XML tree (to be freed with CPLDestroyXMLNode()) or NULL in case * of error * @since GDAL 2.0 */ CPLXMLNode* GDALGetJPEG2000Structure(const char* pszFilename, CSLConstList papszOptions) { VSILFILE* fp = VSIFOpenL(pszFilename, "rb"); if( fp == nullptr ) { CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s", pszFilename); return nullptr; } GByte abyHeader[16]; if( VSIFReadL(abyHeader, 16, 1, fp) != 1 || (memcmp(abyHeader, jpc_header, sizeof(jpc_header)) != 0 && memcmp(abyHeader + 4, jp2_box_jp, sizeof(jp2_box_jp)) != 0) ) { CPLError(CE_Failure, CPLE_AppDefined, "%s is not a JPEG2000 file", pszFilename); CPL_IGNORE_RET_VAL(VSIFCloseL(fp)); return nullptr; } CPLXMLNode* psParent = nullptr; if( memcmp(abyHeader, jpc_header, sizeof(jpc_header)) == 0 ) { if( CPLFetchBool(papszOptions, "CODESTREAM", false) || CPLFetchBool(papszOptions, "ALL", false) ) { if( VSIFSeekL(fp, 0, SEEK_END) == 0 ) { GIntBig nBoxDataLength = static_cast(VSIFTellL(fp)); psParent = DumpJPK2CodeStream(nullptr, fp, 0, nBoxDataLength); CPLAddXMLAttributeAndValue(psParent, "filename", pszFilename ); } } } else { psParent = CPLCreateXMLNode( nullptr, CXT_Element, "JP2File" ); CPLAddXMLAttributeAndValue(psParent, "filename", pszFilename ); vsi_l_offset nFileSize = 0; GDALGetJPEG2000StructureInternal(psParent, fp, nullptr, papszOptions, 0, nFileSize); } CPL_IGNORE_RET_VAL(VSIFCloseL(fp)); return psParent; }