LCOV - code coverage report
Current view: top level - frmts/nitf - nitfdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 2536 3096 81.9 %
Date: 2026-06-18 03:37:25 Functions: 58 60 96.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  NITF Read/Write Translator
       4             :  * Purpose:  NITFDataset and driver related implementations.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2002, Frank Warmerdam
       9             :  * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * Portions Copyright (c) Her majesty the Queen in right of Canada as
      12             :  * represented by the Minister of National Defence, 2006, 2020
      13             :  *
      14             :  * SPDX-License-Identifier: MIT
      15             :  ****************************************************************************/
      16             : 
      17             : #include "cpl_port.h"
      18             : #include "nitfdataset.h"
      19             : #include "nitfdrivercore.h"
      20             : 
      21             : #include "gdal_mdreader.h"
      22             : 
      23             : #include <algorithm>
      24             : #include <cmath>
      25             : #include <cstdio>
      26             : #include <cstdlib>
      27             : #include <cstring>
      28             : #include <memory>
      29             : #include <mutex>
      30             : #include <string>
      31             : #include <vector>
      32             : 
      33             : #include "cpl_conv.h"
      34             : #include "cpl_csv.h"
      35             : #include "cpl_error.h"
      36             : #include "cpl_minixml.h"
      37             : #include "cpl_progress.h"
      38             : #include "cpl_string.h"
      39             : #include "cpl_vsi.h"
      40             : #include "gdal.h"
      41             : #include "gdal_frmts.h"
      42             : #include "gdal_priv.h"
      43             : #include "ogr_api.h"
      44             : #include "ogr_core.h"
      45             : #include "ogr_srs_api.h"
      46             : 
      47             : #ifdef EMBED_RESOURCE_FILES
      48             : #include "embedded_resources.h"
      49             : #endif
      50             : 
      51             : #include "offsetpatcher.h"
      52             : #include "rpfframewriter.h"
      53             : 
      54             : static bool NITFPatchImageLength(const char *pszFilename, VSILFILE *fp,
      55             :                                  int nIMIndex, GUIntBig nImageOffset,
      56             :                                  GIntBig nPixelCount, const char *pszIC,
      57             :                                  vsi_l_offset nICOffset,
      58             :                                  CSLConstList papszCreationOptions);
      59             : static bool
      60             : NITFWriteExtraSegments(const char *pszFilename, VSILFILE *fpIn,
      61             :                        CSLConstList papszCgmMD, CSLConstList papszTextMD,
      62             :                        GDALOffsetPatcher::OffsetPatcher *offsetPatcher,
      63             :                        const CPLStringList &aosOptions, int nReciprocalScale);
      64             : 
      65             : #ifdef JPEG_SUPPORTED
      66             : static bool NITFWriteJPEGImage(GDALDataset *, VSILFILE *, vsi_l_offset,
      67             :                                CSLConstList, GDALProgressFunc pfnProgress,
      68             :                                void *pProgressData);
      69             : #endif
      70             : 
      71             : static void SetBandMetadata(NITFImage *psImage, GDALRasterBand *poBand,
      72             :                             int nBand, bool bReportISUBCAT);
      73             : 
      74             : static bool NITFWriteCGMSegments(const char *pszFilename, VSILFILE *&fpVSIL,
      75             :                                  CSLConstList papszList);
      76             : static bool NITFWriteTextSegments(const char *pszFilename, VSILFILE *&fpVSIL,
      77             :                                   CSLConstList papszList);
      78             : static bool UpdateFileLength(VSILFILE *fp);
      79             : 
      80             : /************************************************************************/
      81             : /* ==================================================================== */
      82             : /*                             NITFDataset                              */
      83             : /* ==================================================================== */
      84             : /************************************************************************/
      85             : 
      86             : /************************************************************************/
      87             : /*                            NITFDataset()                             */
      88             : /************************************************************************/
      89             : 
      90         765 : NITFDataset::NITFDataset()
      91             : {
      92         765 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
      93         765 :     m_oGCPSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
      94             : 
      95         765 :     poDriver = GDALDriver::FromHandle(GDALGetDriverByName("NITF"));
      96         765 : }
      97             : 
      98             : /************************************************************************/
      99             : /*                            ~NITFDataset()                            */
     100             : /************************************************************************/
     101             : 
     102        1530 : NITFDataset::~NITFDataset()
     103             : 
     104             : {
     105         765 :     NITFDataset::Close();
     106             : 
     107             :     /* -------------------------------------------------------------------- */
     108             :     /*      Free datastructures.                                            */
     109             :     /* -------------------------------------------------------------------- */
     110             : 
     111         765 :     GDALDeinitGCPs(nGCPCount, pasGCPList);
     112         765 :     CPLFree(pasGCPList);
     113             : 
     114         765 :     CPLFree(panJPEGBlockOffset);
     115         765 :     CPLFree(pabyJPEGBlock);
     116        1530 : }
     117             : 
     118             : /************************************************************************/
     119             : /*                               Close()                                */
     120             : /************************************************************************/
     121             : 
     122        1391 : CPLErr NITFDataset::Close(GDALProgressFunc, void *)
     123             : {
     124        1391 :     int bHasDroppedRef = FALSE;
     125        2782 :     return NITFDataset::Close(bHasDroppedRef);
     126             : }
     127             : 
     128        1391 : CPLErr NITFDataset::Close(int &bHasDroppedRef)
     129             : {
     130        1391 :     CPLErr eErr = CE_None;
     131        1391 :     bHasDroppedRef = FALSE;
     132        1391 :     if (nOpenFlags != OPEN_FLAGS_CLOSED)
     133             :     {
     134         765 :         eErr = NITFDataset::FlushCache(true);
     135             : 
     136         765 :         bHasDroppedRef = GDALPamDataset::CloseDependentDatasets();
     137             : 
     138             :         /* -------------------------------------------------------------------- */
     139             :         /*      If we have been writing to a JPEG2000 file, check if the        */
     140             :         /*      color interpretations were set.  If so, apply the settings      */
     141             :         /*      to the NITF file.                                               */
     142             :         /* -------------------------------------------------------------------- */
     143         765 :         if (poJ2KDataset != nullptr && bJP2Writing)
     144             :         {
     145           4 :             for (int i = 0; i < nBands && papoBands != nullptr; i++)
     146             :             {
     147           3 :                 if (papoBands[i]->GetColorInterpretation() != GCI_Undefined)
     148           3 :                     NITFSetColorInterpretation(
     149           3 :                         psImage, i + 1, papoBands[i]->GetColorInterpretation());
     150             :             }
     151             :         }
     152             : 
     153             :         /* -------------------------------------------------------------------- */
     154             :         /*      Close the underlying NITF file.                                 */
     155             :         /* -------------------------------------------------------------------- */
     156         765 :         if (psFile != nullptr)
     157             :         {
     158         760 :             eErr = GDAL::Combine(eErr, NITFClose(psFile));
     159         760 :             psFile = nullptr;
     160             :         }
     161             : 
     162             :         /* -------------------------------------------------------------------- */
     163             :         /*      If we have a jpeg2000 output file, make sure it gets closed     */
     164             :         /*      and flushed out.                                                */
     165             :         /* -------------------------------------------------------------------- */
     166         765 :         if (poJ2KDataset != nullptr)
     167             :         {
     168          37 :             eErr = GDAL::Combine(eErr, poJ2KDataset->Close());
     169          37 :             poJ2KDataset.reset();
     170          37 :             bHasDroppedRef = TRUE;
     171             :         }
     172             : 
     173             :         /* -------------------------------------------------------------------- */
     174             :         /*      Update file length, and COMRAT for JPEG2000 files we are        */
     175             :         /*      writing to.                                                     */
     176             :         /* -------------------------------------------------------------------- */
     177         765 :         if (bJP2Writing)
     178             :         {
     179           1 :             const GIntBig nPixelCount =
     180           1 :                 static_cast<GIntBig>(nRasterXSize) * nRasterYSize * nBands;
     181             : 
     182           1 :             eErr = GDAL::Combine(
     183           1 :                 eErr, NITFPatchImageLength(
     184           1 :                           GetDescription(), nullptr, m_nIMIndex, m_nImageOffset,
     185             :                           nPixelCount, "C8", m_nICOffset, nullptr));
     186             :         }
     187             : 
     188         765 :         bJP2Writing = FALSE;
     189             : 
     190             :         /* -------------------------------------------------------------------- */
     191             :         /*      If we have a jpeg output file, make sure it gets closed         */
     192             :         /*      and flushed out.                                                */
     193             :         /* -------------------------------------------------------------------- */
     194         765 :         if (poJPEGDataset != nullptr)
     195             :         {
     196          18 :             eErr = GDAL::Combine(eErr, poJPEGDataset->Close());
     197          18 :             poJPEGDataset.reset();
     198          18 :             bHasDroppedRef = TRUE;
     199             :         }
     200             : 
     201             :         /* -------------------------------------------------------------------- */
     202             :         /*      If the dataset was opened by Create(), we may need to write     */
     203             :         /*      the CGM and TEXT segments                                       */
     204             :         /* -------------------------------------------------------------------- */
     205         765 :         if (m_nIMIndex + 1 == m_nImageCount)
     206             :         {
     207         153 :             eErr = GDAL::Combine(
     208         153 :                 eErr, NITFWriteExtraSegments(
     209         153 :                           GetDescription(), nullptr, papszCgmMDToWrite,
     210         153 :                           papszTextMDToWrite, nullptr, aosCreationOptions, 0));
     211             :         }
     212             : 
     213         765 :         CSLDestroy(papszTextMDToWrite);
     214         765 :         papszTextMDToWrite = nullptr;
     215         765 :         CSLDestroy(papszCgmMDToWrite);
     216         765 :         papszCgmMDToWrite = nullptr;
     217             : 
     218         765 :         eErr = GDAL::Combine(eErr, GDALPamDataset::Close());
     219             : 
     220             :         /* -------------------------------------------------------------------- */
     221             :         /*      Destroy the raster bands if they exist.                         */
     222             :         /* We must do it now since the rasterbands can be NITFWrapperRasterBand */
     223             :         /* that derive from the GDALProxyRasterBand object, which keeps         */
     224             :         /* a reference on the JPEG/JP2K dataset, so any later call to           */
     225             :         /* FlushCache() would result in FlushCache() being called on a          */
     226             :         /* already destroyed object                                             */
     227             :         /* -------------------------------------------------------------------- */
     228      141812 :         for (int iBand = 0; iBand < nBands; iBand++)
     229             :         {
     230      141047 :             delete papoBands[iBand];
     231             :         }
     232         765 :         nBands = 0;
     233             :     }
     234        1391 :     return eErr;
     235             : }
     236             : 
     237             : /************************************************************************/
     238             : /*                       CloseDependentDatasets()                       */
     239             : /************************************************************************/
     240             : 
     241           0 : int NITFDataset::CloseDependentDatasets()
     242             : {
     243           0 :     int bHasDroppedRef = FALSE;
     244           0 :     Close(bHasDroppedRef);
     245           0 :     return bHasDroppedRef;
     246             : }
     247             : 
     248             : /************************************************************************/
     249             : /*                             FlushCache()                             */
     250             : /************************************************************************/
     251             : 
     252         780 : CPLErr NITFDataset::FlushCache(bool bAtClosing)
     253             : 
     254             : {
     255             :     // If the JPEG/JP2K dataset has dirty pam info, then we should consider
     256             :     // ourselves to as well.
     257         780 :     if (poJPEGDataset != nullptr &&
     258         798 :         (poJPEGDataset->GetMOFlags() & GMO_PAM_CLASS) &&
     259          18 :         (cpl::down_cast<GDALPamDataset *>(poJPEGDataset.get())->GetPamFlags() &
     260             :          GPF_DIRTY))
     261           3 :         MarkPamDirty();
     262             : 
     263         780 :     if (poJ2KDataset != nullptr &&
     264         816 :         (poJ2KDataset->GetMOFlags() & GMO_PAM_CLASS) &&
     265          36 :         (cpl::down_cast<GDALPamDataset *>(poJ2KDataset.get())->GetPamFlags() &
     266             :          GPF_DIRTY))
     267           2 :         MarkPamDirty();
     268             : 
     269         780 :     CPLErr eErr = CE_None;
     270         780 :     if (poJ2KDataset != nullptr && bJP2Writing)
     271           2 :         eErr = poJ2KDataset->FlushCache(bAtClosing);
     272             : 
     273         780 :     if (GDALPamDataset::FlushCache(bAtClosing) != CE_None)
     274           0 :         eErr = CE_Failure;
     275         780 :     return eErr;
     276             : }
     277             : 
     278             : #ifdef ESRI_BUILD
     279             : 
     280             : /************************************************************************/
     281             : /*                           ExtractEsriMD()                            */
     282             : /*                                                                      */
     283             : /*      Extracts ESRI-specific required meta data from metadata         */
     284             : /*      string list papszStrList.                                       */
     285             : /************************************************************************/
     286             : 
     287             : static char **ExtractEsriMD(char **papszMD)
     288             : {
     289             :     char **papszEsriMD = NULL;
     290             : 
     291             :     if (papszMD)
     292             :     {
     293             :         // These are the current generic ESRI metadata.
     294             :         const char *const pEsriMDAcquisitionDate = "ESRI_MD_ACQUISITION_DATE";
     295             :         const char *const pEsriMDAngleToNorth = "ESRI_MD_ANGLE_TO_NORTH";
     296             :         const char *const pEsriMDCircularError = "ESRI_MD_CE";
     297             :         const char *const pEsriMDDataType = "ESRI_MD_DATA_TYPE";
     298             :         const char *const pEsriMDIsCloudCover = "ESRI_MD_ISCLOUDCOVER";
     299             :         const char *const pEsriMDLinearError = "ESRI_MD_LE";
     300             :         const char *const pEsriMDOffNaDir = "ESRI_MD_OFF_NADIR";
     301             :         const char *const pEsriMDPercentCloudCover =
     302             :             "ESRI_MD_PERCENT_CLOUD_COVER";
     303             :         const char *const pEsriMDProductName = "ESRI_MD_PRODUCT_NAME";
     304             :         const char *const pEsriMDSensorAzimuth = "ESRI_MD_SENSOR_AZIMUTH";
     305             :         const char *const pEsriMDSensorElevation = "ESRI_MD_SENSOR_ELEVATION";
     306             :         const char *const pEsriMDSensorName = "ESRI_MD_SENSOR_NAME";
     307             :         const char *const pEsriMDSunAzimuth = "ESRI_MD_SUN_AZIMUTH";
     308             :         const char *const pEsriMDSunElevation = "ESRI_MD_SUN_ELEVATION";
     309             : 
     310             :         const char *pCCImageSegment = CSLFetchNameValue(papszMD, "NITF_IID1");
     311             :         std::string ccSegment("false");
     312             : 
     313             :         if ((pCCImageSegment != NULL) && (strlen(pCCImageSegment) <= 10))
     314             :         {
     315             :             char szField[11] = {0};
     316             :             strncpy(szField, pCCImageSegment, strlen(pCCImageSegment));
     317             :             szField[strlen(pCCImageSegment)] = '\0';
     318             : 
     319             :             // Trim white off tag.
     320             :             while ((strlen(szField) > 0) &&
     321             :                    (szField[strlen(szField) - 1] == ' '))
     322             :                 szField[strlen(szField) - 1] = '\0';
     323             : 
     324             :             if ((strlen(szField) == 2) && (STARTS_WITH_CI(szField, "CC")))
     325             :                 ccSegment.assign("true");
     326             :         }
     327             : 
     328             :         const char *pAcquisitionDate = CSLFetchNameValue(papszMD, "NITF_FDT");
     329             :         const char *pAngleToNorth =
     330             :             CSLFetchNameValue(papszMD, "NITF_CSEXRA_ANGLE_TO_NORTH");
     331             :         const char *pCircularError = CSLFetchNameValue(
     332             :             papszMD, "NITF_CSEXRA_CIRCL_ERR");  // Unit in feet.
     333             :         const char *pLinearError = CSLFetchNameValue(
     334             :             papszMD, "NITF_CSEXRA_LINEAR_ERR");  // Unit in feet.
     335             :         const char *pPercentCloudCover =
     336             :             CSLFetchNameValue(papszMD, "NITF_PIAIMC_CLOUDCVR");
     337             :         const char *pProductName =
     338             :             CSLFetchNameValue(papszMD, "NITF_CSDIDA_PRODUCT_ID");
     339             :         const char *pSensorName =
     340             :             CSLFetchNameValue(papszMD, "NITF_PIAIMC_SENSNAME");
     341             :         const char *pSunAzimuth =
     342             :             CSLFetchNameValue(papszMD, "NITF_CSEXRA_SUN_AZIMUTH");
     343             :         const char *pSunElevation =
     344             :             CSLFetchNameValue(papszMD, "NITF_CSEXRA_SUN_ELEVATION");
     345             : 
     346             :         // Get ESRI_MD_DATA_TYPE.
     347             :         const char *pImgSegFieldICAT = CSLFetchNameValue(papszMD, "NITF_ICAT");
     348             : 
     349             :         const char *pDataType = NULL;
     350             :         if ((pImgSegFieldICAT != NULL) &&
     351             :             (STARTS_WITH_CI(pImgSegFieldICAT, "DTEM")))
     352             :             pDataType = "Elevation";
     353             :         else
     354             :             pDataType = "Generic";
     355             : 
     356             :         if (pAngleToNorth == NULL)
     357             :             pAngleToNorth =
     358             :                 CSLFetchNameValue(papszMD, "NITF_USE00A_ANGLE_TO_NORTH");
     359             : 
     360             :         // Percent cloud cover == 999 means that the information is not
     361             :         // available.
     362             :         if ((pPercentCloudCover != NULL) &&
     363             :             (STARTS_WITH_CI(pPercentCloudCover, "999")))
     364             :             pPercentCloudCover = NULL;
     365             : 
     366             :         pAngleToNorth =
     367             :             CSLFetchNameValue(papszMD, "NITF_USE00A_ANGLE_TO_NORTH");
     368             : 
     369             :         if (pSunAzimuth == NULL)
     370             :             pSunAzimuth = CSLFetchNameValue(papszMD, "NITF_USE00A_SUN_AZ");
     371             : 
     372             :         if (pSunElevation == NULL)
     373             :             pSunElevation = CSLFetchNameValue(papszMD, "NITF_USE00A_SUN_EL");
     374             : 
     375             :         // CSLAddNameValue will not add the key/value pair if the value is NULL.
     376             :         papszEsriMD = CSLAddNameValue(papszEsriMD, pEsriMDAcquisitionDate,
     377             :                                       pAcquisitionDate);
     378             :         papszEsriMD =
     379             :             CSLAddNameValue(papszEsriMD, pEsriMDAngleToNorth, pAngleToNorth);
     380             :         papszEsriMD =
     381             :             CSLAddNameValue(papszEsriMD, pEsriMDCircularError, pCircularError);
     382             :         papszEsriMD = CSLAddNameValue(papszEsriMD, pEsriMDDataType, pDataType);
     383             :         papszEsriMD = CSLAddNameValue(papszEsriMD, pEsriMDIsCloudCover,
     384             :                                       ccSegment.c_str());
     385             :         papszEsriMD =
     386             :             CSLAddNameValue(papszEsriMD, pEsriMDLinearError, pLinearError);
     387             :         papszEsriMD =
     388             :             CSLAddNameValue(papszEsriMD, pEsriMDProductName, pProductName);
     389             :         papszEsriMD = CSLAddNameValue(papszEsriMD, pEsriMDPercentCloudCover,
     390             :                                       pPercentCloudCover);
     391             :         papszEsriMD =
     392             :             CSLAddNameValue(papszEsriMD, pEsriMDSensorName, pSensorName);
     393             :         papszEsriMD =
     394             :             CSLAddNameValue(papszEsriMD, pEsriMDSunAzimuth, pSunAzimuth);
     395             :         papszEsriMD =
     396             :             CSLAddNameValue(papszEsriMD, pEsriMDSunElevation, pSunElevation);
     397             :     }
     398             : 
     399             :     return papszEsriMD;
     400             : }
     401             : 
     402             : #endif /* def ESRI_BUILD */
     403             : 
     404             : /************************************************************************/
     405             : /*                          SetBandMetadata()                           */
     406             : /************************************************************************/
     407             : 
     408      141037 : static void SetBandMetadata(NITFImage *psImage, GDALRasterBand *poBand,
     409             :                             int nBand, bool bReportISUBCAT)
     410             : {
     411      141037 :     const NITFBandInfo *psBandInfo = psImage->pasBandInfo + nBand - 1;
     412             : 
     413             :     /* The ISUBCAT is particularly valuable for interpreting SAR bands */
     414      141037 :     if (bReportISUBCAT && strlen(psBandInfo->szISUBCAT) > 0)
     415             :     {
     416           4 :         poBand->SetMetadataItem("NITF_ISUBCAT", psBandInfo->szISUBCAT);
     417             :     }
     418      141037 : }
     419             : 
     420             : /************************************************************************/
     421             : /*                                Open()                                */
     422             : /************************************************************************/
     423             : 
     424         449 : GDALDataset *NITFDataset::Open(GDALOpenInfo *poOpenInfo)
     425             : {
     426         449 :     return OpenInternal(poOpenInfo, nullptr, false, -1);
     427             : }
     428             : 
     429         762 : NITFDataset *NITFDataset::OpenInternal(GDALOpenInfo *poOpenInfo,
     430             :                                        GDALDataset *poWritableJ2KDataset,
     431             :                                        bool bOpenForCreate, int nIMIndex)
     432             : 
     433             : {
     434         762 :     if (!NITFDriverIdentify(poOpenInfo))
     435           0 :         return nullptr;
     436             : 
     437         762 :     const char *pszFilename = poOpenInfo->pszFilename;
     438             : 
     439             :     /* -------------------------------------------------------------------- */
     440             :     /*      Select a specific subdataset.                                   */
     441             :     /* -------------------------------------------------------------------- */
     442         762 :     if (STARTS_WITH_CI(pszFilename, "NITF_IM:"))
     443             :     {
     444          20 :         pszFilename += 8;
     445          20 :         nIMIndex = atoi(pszFilename);
     446             : 
     447          54 :         while (*pszFilename != '\0' && *pszFilename != ':')
     448          34 :             pszFilename++;
     449             : 
     450          20 :         if (*pszFilename == ':')
     451          20 :             pszFilename++;
     452             :     }
     453             : 
     454             :     /* -------------------------------------------------------------------- */
     455             :     /*      Open the file with library.                                     */
     456             :     /* -------------------------------------------------------------------- */
     457         762 :     NITFFile *psFile = nullptr;
     458             : 
     459         762 :     if (poOpenInfo->fpL)
     460             :     {
     461         742 :         VSILFILE *fpL = poOpenInfo->fpL;
     462         742 :         poOpenInfo->fpL = nullptr;
     463         742 :         psFile = NITFOpenEx(fpL, pszFilename);
     464             :     }
     465             :     else
     466          20 :         psFile = NITFOpen(pszFilename, poOpenInfo->eAccess == GA_Update);
     467         762 :     if (psFile == nullptr)
     468             :     {
     469           2 :         return nullptr;
     470             :     }
     471             : 
     472         760 :     if (!bOpenForCreate)
     473             :     {
     474         447 :         NITFCollectAttachments(psFile);
     475         447 :         NITFReconcileAttachments(psFile);
     476             :     }
     477             : 
     478             :     /* -------------------------------------------------------------------- */
     479             :     /*      Is there an image to operate on?                                */
     480             :     /* -------------------------------------------------------------------- */
     481         760 :     int nThisIM = 0;
     482         760 :     NITFImage *psImage = nullptr;
     483             : 
     484         760 :     int iSegment = 0;  // Used after for loop.
     485        7766 :     for (; iSegment < psFile->nSegmentCount; iSegment++)
     486             :     {
     487       15514 :         if (EQUAL(psFile->pasSegmentInfo[iSegment].szSegmentType, "IM") &&
     488        7755 :             (nThisIM++ == nIMIndex || nIMIndex == -1))
     489             :         {
     490         753 :             psImage = NITFImageAccess(psFile, iSegment);
     491         753 :             if (psImage == nullptr)
     492             :             {
     493           0 :                 NITFClose(psFile);
     494           0 :                 return nullptr;
     495             :             }
     496         753 :             break;
     497             :         }
     498             :     }
     499             : 
     500             :     /* -------------------------------------------------------------------- */
     501             :     /*      If no image segments found report this to the user.             */
     502             :     /* -------------------------------------------------------------------- */
     503         760 :     if (psImage == nullptr)
     504             :     {
     505           7 :         CPLError(CE_Warning, CPLE_AppDefined,
     506             :                  "The file %s appears to be an NITF file, but no image "
     507             :                  "blocks were found on it.",
     508             :                  poOpenInfo->pszFilename);
     509             :     }
     510         753 :     else if (psImage->nBitsPerSample > 16 &&
     511          44 :              (EQUAL(psImage->szIC, "C3") || EQUAL(psImage->szIC, "M3")))
     512             :     {
     513             :         // Early rejection of JPEG compressed images with invalid bit depth
     514             :         // Otherwise this will cause potentially heap buffer overflows
     515             :         // as ReadJPEGBlock() assumes that the data type size is no larger
     516             :         // than 2 bytes.
     517           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     518           0 :                  "IC=%s and ABPP=%d are not supported", psImage->szIC,
     519             :                  psImage->nBitsPerSample);
     520           0 :         NITFClose(psFile);
     521           0 :         return nullptr;
     522             :     }
     523             : 
     524             :     /* -------------------------------------------------------------------- */
     525             :     /*      Create a corresponding GDALDataset.                             */
     526             :     /* -------------------------------------------------------------------- */
     527         760 :     NITFDataset *poDS = new NITFDataset();
     528             : 
     529         760 :     poDS->psFile = psFile;
     530         760 :     poDS->psImage = psImage;
     531         760 :     poDS->eAccess = poOpenInfo->eAccess;
     532         760 :     poDS->osNITFFilename = pszFilename;
     533         760 :     poDS->nIMIndex = nIMIndex;
     534             : 
     535         760 :     if (psImage)
     536             :     {
     537         753 :         if (psImage->nCols <= 0 || psImage->nRows <= 0 ||
     538         753 :             psImage->nBlockWidth <= 0 || psImage->nBlockHeight <= 0)
     539             :         {
     540           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     541             :                      "Bad values in NITF image : nCols=%d, nRows=%d, "
     542             :                      "nBlockWidth=%d, nBlockHeight=%d",
     543             :                      psImage->nCols, psImage->nRows, psImage->nBlockWidth,
     544             :                      psImage->nBlockHeight);
     545           0 :             delete poDS;
     546           0 :             return nullptr;
     547             :         }
     548             : 
     549         753 :         poDS->nRasterXSize = psImage->nCols;
     550         753 :         poDS->nRasterYSize = psImage->nRows;
     551             :     }
     552             :     else
     553             :     {
     554           7 :         poDS->nRasterXSize = 1;
     555           7 :         poDS->nRasterYSize = 1;
     556             :     }
     557             : 
     558             :     /* Can be set to NO to avoid opening the underlying JPEG2000/JPEG */
     559             :     /* stream. Might speed up operations when just metadata is needed */
     560             :     bool bOpenUnderlyingDS =
     561         760 :         CPLTestBool(CPLGetConfigOption("NITF_OPEN_UNDERLYING_DS", "YES"));
     562             : 
     563             :     /* -------------------------------------------------------------------- */
     564             :     /*      If the image is JPEG2000 (C8) compressed, we will need to       */
     565             :     /*      open the image data as a JPEG2000 dataset.                      */
     566             :     /* -------------------------------------------------------------------- */
     567         760 :     int nUsableBands = 0;
     568         760 :     bool bSetColorInterpretation = true;
     569         760 :     bool bSetColorTable = false;
     570             : 
     571         760 :     if (psImage)
     572         753 :         nUsableBands = psImage->nBands;
     573             : 
     574         760 :     if (bOpenUnderlyingDS && psImage != nullptr && EQUAL(psImage->szIC, "C8"))
     575             :     {
     576          37 :         CPLString osDSName;
     577             : 
     578             :         osDSName.Printf("/vsisubfile/" CPL_FRMT_GUIB "_" CPL_FRMT_GUIB ",%s",
     579          37 :                         psFile->pasSegmentInfo[iSegment].nSegmentStart,
     580          37 :                         psFile->pasSegmentInfo[iSegment].nSegmentSize,
     581          37 :                         pszFilename);
     582             : 
     583          37 :         if (poWritableJ2KDataset != nullptr)
     584             :         {
     585           1 :             poDS->poJ2KDataset.reset(poWritableJ2KDataset);
     586           1 :             poDS->bJP2Writing = TRUE;
     587           1 :             poWritableJ2KDataset = nullptr;
     588             :         }
     589             :         else
     590             :         {
     591             :             // We explicitly list the allowed drivers to avoid hostile content
     592             :             // to be opened by a random driver.
     593             :             static const char *const apszDrivers[] = {
     594             :                 "JP2KAK", "JP2ECW", "JP2MRSID", "JP2OPENJPEG", nullptr};
     595          36 :             poDS->poJ2KDataset.reset(GDALDataset::Open(
     596             :                 osDSName, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR, apszDrivers,
     597             :                 nullptr, nullptr));
     598             : 
     599          36 :             if (poDS->poJ2KDataset == nullptr)
     600             :             {
     601           0 :                 bool bFoundJPEG2000Driver = false;
     602           0 :                 for (int iDriver = 0; apszDrivers[iDriver] != nullptr;
     603             :                      iDriver++)
     604             :                 {
     605           0 :                     if (GDALGetDriverByName(apszDrivers[iDriver]) != nullptr)
     606           0 :                         bFoundJPEG2000Driver = true;
     607             :                 }
     608             : 
     609           0 :                 CPLError(
     610             :                     CE_Failure, CPLE_AppDefined,
     611             :                     "Unable to open JPEG2000 image within NITF file.\n%s\n%s",
     612             :                     !bFoundJPEG2000Driver
     613             :                         ? "No JPEG2000 capable driver (JP2KAK, JP2ECW, "
     614             :                           "JP2MRSID, "
     615             :                           "JP2OPENJPEG, etc...) is available."
     616             :                         : "One or several JPEG2000 capable drivers are "
     617             :                           "available but "
     618             :                           "the datastream could not be opened successfully.",
     619             :                     "You can define the NITF_OPEN_UNDERLYING_DS configuration "
     620             :                     "option to NO, in order to just get the metadata.");
     621           0 :                 delete poDS;
     622           0 :                 return nullptr;
     623             :             }
     624             : 
     625          36 :             if (poDS->poJ2KDataset->GetMOFlags() & GMO_PAM_CLASS)
     626             :             {
     627             :                 cpl::down_cast<GDALPamDataset *>(poDS->poJ2KDataset.get())
     628          72 :                     ->SetPamFlags(reinterpret_cast<GDALPamDataset *>(
     629          36 :                                       poDS->poJ2KDataset.get())
     630          36 :                                       ->GetPamFlags() |
     631             :                                   GPF_NOSAVE);
     632             :             }
     633             :         }
     634             : 
     635          74 :         if (poDS->GetRasterXSize() != poDS->poJ2KDataset->GetRasterXSize() ||
     636          37 :             poDS->GetRasterYSize() != poDS->poJ2KDataset->GetRasterYSize())
     637             :         {
     638           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     639             :                      "JPEG2000 data stream has not the same dimensions as "
     640             :                      "the NITF file.");
     641           0 :             delete poDS;
     642           0 :             return nullptr;
     643             :         }
     644             : 
     645          37 :         if (nUsableBands == 1)
     646             :         {
     647             :             const char *pszIREP =
     648          22 :                 CSLFetchNameValue(psImage->papszMetadata, "NITF_IREP");
     649          22 :             if (pszIREP != nullptr && EQUAL(pszIREP, "RGB/LUT"))
     650             :             {
     651           0 :                 if (poDS->poJ2KDataset->GetRasterCount() == 3)
     652             :                 {
     653             :                     // Test case:
     654             :                     // http://www.gwg.nga.mil/ntb/baseline/software/testfile/Jpeg2000/jp2_09/file9_jp2_2places.ntf
     655             :                     /* 256-entry palette/LUT in both JP2 Header and image
     656             :                      * Subheader */
     657             :                     /* In this case, the JPEG2000 driver will probably do the
     658             :                      * RGB expansion. */
     659           0 :                     nUsableBands = 3;
     660           0 :                     bSetColorInterpretation = false;
     661             :                 }
     662           0 :                 else if (poDS->poJ2KDataset->GetRasterCount() == 1 &&
     663           0 :                          psImage->pasBandInfo[0].nSignificantLUTEntries > 0)
     664             :                 {
     665             :                     // Test case:
     666             :                     // http://www.gwg.nga.mil/ntb/baseline/software/testfile/Jpeg2000/jp2_09/file9_j2c.ntf
     667             : 
     668             :                     // 256-entry/LUT in Image Subheader, JP2 header completely
     669             :                     // removed. The JPEG2000 driver will decode it as a grey
     670             :                     // band So we must set the color table on the wrapper band
     671             :                     // or for file9_jp2_2places.ntf as well if the J2K driver
     672             :                     // does do RGB expansion
     673           0 :                     bSetColorTable = true;
     674             :                 }
     675             :             }
     676             :         }
     677             : 
     678          37 :         if (poDS->poJ2KDataset->GetRasterCount() < nUsableBands)
     679             :         {
     680           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     681             :                      "JPEG2000 data stream has less useful bands than "
     682             :                      "expected, likely because some channels have "
     683             :                      "differing resolutions.");
     684             : 
     685           0 :             nUsableBands = poDS->poJ2KDataset->GetRasterCount();
     686          37 :         }
     687             :     }
     688             : 
     689             :     /* -------------------------------------------------------------------- */
     690             :     /*      If the image is JPEG (C3) compressed, we will need to open      */
     691             :     /*      the image data as a JPEG dataset.                               */
     692             :     /* -------------------------------------------------------------------- */
     693         723 :     else if (bOpenUnderlyingDS && psImage != nullptr &&
     694         716 :              EQUAL(psImage->szIC, "C3") && psImage->nBlocksPerRow == 1 &&
     695          18 :              psImage->nBlocksPerColumn == 1)
     696             :     {
     697          18 :         GUIntBig nJPEGStart = psFile->pasSegmentInfo[iSegment].nSegmentStart;
     698             : 
     699          18 :         bool bError = false;
     700          18 :         poDS->nQLevel = poDS->ScanJPEGQLevel(&nJPEGStart, &bError);
     701             : 
     702          18 :         CPLString osDSName;
     703             : 
     704          18 :         if (psFile->pasSegmentInfo[iSegment].nSegmentSize <
     705          18 :             nJPEGStart - psFile->pasSegmentInfo[iSegment].nSegmentStart)
     706             :         {
     707           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Corrupted segment size");
     708           0 :             delete poDS;
     709           0 :             return nullptr;
     710             :         }
     711             : 
     712          18 :         osDSName.Printf(
     713             :             "JPEG_SUBFILE:Q%d," CPL_FRMT_GUIB "," CPL_FRMT_GUIB ",%s",
     714             :             poDS->nQLevel, nJPEGStart,
     715          18 :             psFile->pasSegmentInfo[iSegment].nSegmentSize -
     716          18 :                 (nJPEGStart - psFile->pasSegmentInfo[iSegment].nSegmentStart),
     717          18 :             pszFilename);
     718             : 
     719          18 :         CPLDebug("GDAL", "NITFDataset::Open() as IC=C3 (JPEG compressed)\n");
     720             : 
     721          18 :         poDS->poJPEGDataset.reset(GDALDataset::Open(
     722             :             osDSName, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
     723          18 :         if (poDS->poJPEGDataset == nullptr)
     724             :         {
     725             :             const bool bFoundJPEGDriver =
     726           0 :                 GDALGetDriverByName("JPEG") != nullptr;
     727           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     728             :                      "Unable to open JPEG image within NITF file.\n%s\n%s",
     729             :                      (!bFoundJPEGDriver)
     730             :                          ? "The JPEG driver is not available."
     731             :                          : "The JPEG driver is available but the datastream "
     732             :                            "could not be opened successfully.",
     733             :                      "You can define the NITF_OPEN_UNDERLYING_DS configuration "
     734             :                      "option to NO, in order to just get the metadata.");
     735           0 :             delete poDS;
     736           0 :             return nullptr;
     737             :         }
     738             : 
     739             :         /* In some circumstances, the JPEG image can be larger than the NITF */
     740             :         /* (NCOLS, NROWS) dimensions (#5001), so accept it as a valid case */
     741             :         /* But reject when it is smaller than the NITF dimensions. */
     742          36 :         if (poDS->GetRasterXSize() > poDS->poJPEGDataset->GetRasterXSize() ||
     743          18 :             poDS->GetRasterYSize() > poDS->poJPEGDataset->GetRasterYSize())
     744             :         {
     745           0 :             CPLError(
     746             :                 CE_Failure, CPLE_AppDefined,
     747             :                 "JPEG data stream has smaller dimensions than the NITF file.");
     748           0 :             delete poDS;
     749           0 :             return nullptr;
     750             :         }
     751             : 
     752          18 :         if (poDS->poJPEGDataset->GetMOFlags() & GMO_PAM_CLASS)
     753             :         {
     754             :             (cpl::down_cast<GDALPamDataset *>(poDS->poJPEGDataset.get()))
     755          36 :                 ->SetPamFlags((reinterpret_cast<GDALPamDataset *>(
     756          18 :                                    poDS->poJPEGDataset.get()))
     757          18 :                                   ->GetPamFlags() |
     758             :                               GPF_NOSAVE);
     759             :         }
     760             : 
     761          18 :         if (poDS->poJPEGDataset->GetRasterCount() < nUsableBands)
     762             :         {
     763           0 :             CPLError(
     764             :                 CE_Warning, CPLE_AppDefined,
     765             :                 "JPEG data stream has less useful bands than expected, likely\n"
     766             :                 "because some channels have differing resolutions.");
     767             : 
     768           0 :             nUsableBands = poDS->poJPEGDataset->GetRasterCount();
     769             :         }
     770             :     }
     771             : 
     772             :     /* -------------------------------------------------------------------- */
     773             :     /*      Create band information objects.                                */
     774             :     /* -------------------------------------------------------------------- */
     775             : 
     776             :     /* Keep temporary non-based dataset bands */
     777         760 :     bool bIsTempBandUsed = false;
     778         760 :     GDALDataType dtFirstBand = GDT_Unknown;
     779         760 :     GDALDataType dtSecondBand = GDT_Unknown;
     780        1520 :     std::vector<GDALRasterBand *> apoNewBands(nUsableBands);
     781             : 
     782         760 :     GDALDataset *poBaseDS = nullptr;
     783         760 :     if (poDS->poJ2KDataset != nullptr)
     784          37 :         poBaseDS = poDS->poJ2KDataset.get();
     785         723 :     else if (poDS->poJPEGDataset != nullptr)
     786          18 :         poBaseDS = poDS->poJPEGDataset.get();
     787             : 
     788      141802 :     for (int iBand = 0; iBand < nUsableBands; iBand++)
     789             :     {
     790      141042 :         if (poBaseDS != nullptr)
     791             :         {
     792          99 :             GDALRasterBand *poBaseBand = poBaseDS->GetRasterBand(iBand + 1);
     793             : 
     794          99 :             SetBandMetadata(psImage, poBaseBand, iBand + 1, true);
     795             : 
     796             :             NITFWrapperRasterBand *poBand =
     797          99 :                 new NITFWrapperRasterBand(poDS, poBaseBand, iBand + 1);
     798             : 
     799          99 :             NITFBandInfo *psBandInfo = psImage->pasBandInfo + iBand;
     800          99 :             if (bSetColorInterpretation)
     801             :             {
     802             :                 /* FIXME? Does it make sense if the JPEG/JPEG2000 driver decodes
     803             :                  */
     804             :                 /* YCbCr data as RGB. We probably don't want to set */
     805             :                 /* the color interpretation as Y, Cb, Cr */
     806          99 :                 if (EQUAL(psBandInfo->szIREPBAND, "R"))
     807          12 :                     poBand->SetColorInterpretation(GCI_RedBand);
     808          99 :                 if (EQUAL(psBandInfo->szIREPBAND, "G"))
     809          12 :                     poBand->SetColorInterpretation(GCI_GreenBand);
     810          99 :                 if (EQUAL(psBandInfo->szIREPBAND, "B"))
     811          12 :                     poBand->SetColorInterpretation(GCI_BlueBand);
     812          99 :                 if (EQUAL(psBandInfo->szIREPBAND, "M"))
     813          37 :                     poBand->SetColorInterpretation(GCI_GrayIndex);
     814          99 :                 if (EQUAL(psBandInfo->szIREPBAND, "Y"))
     815           8 :                     poBand->SetColorInterpretation(GCI_YCbCr_YBand);
     816          99 :                 if (EQUAL(psBandInfo->szIREPBAND, "Cb"))
     817           8 :                     poBand->SetColorInterpretation(GCI_YCbCr_CbBand);
     818          99 :                 if (EQUAL(psBandInfo->szIREPBAND, "Cr"))
     819           8 :                     poBand->SetColorInterpretation(GCI_YCbCr_CrBand);
     820             :             }
     821          99 :             if (bSetColorTable)
     822             :             {
     823           0 :                 poBand->SetColorTableFromNITFBandInfo();
     824           0 :                 poBand->SetColorInterpretation(GCI_PaletteIndex);
     825             :             }
     826             : 
     827          99 :             poDS->SetBand(iBand + 1, poBand);
     828             : 
     829          99 :             if (iBand == 0)
     830          55 :                 dtFirstBand = poBand->GetRasterDataType();
     831          44 :             else if (iBand == 1)
     832          23 :                 dtSecondBand = poBand->GetRasterDataType();
     833             :         }
     834             :         else
     835             :         {
     836      140943 :             bIsTempBandUsed = true;
     837             : 
     838      140943 :             NITFRasterBand *poBand = new NITFRasterBand(poDS, iBand + 1);
     839      140943 :             if (poBand->GetRasterDataType() == GDT_Unknown)
     840             :             {
     841           0 :                 for (auto *poOtherBand : apoNewBands)
     842           0 :                     delete poOtherBand;
     843           0 :                 delete poBand;
     844           0 :                 delete poDS;
     845           0 :                 return nullptr;
     846             :             }
     847             : 
     848      140943 :             apoNewBands[iBand] = poBand;
     849             : 
     850      140943 :             if (iBand == 0)
     851         698 :                 dtFirstBand = poBand->GetRasterDataType();
     852      140943 :             if (iBand == 1)
     853         123 :                 dtSecondBand = poBand->GetRasterDataType();
     854             :         }
     855             :     }
     856             : 
     857             :     /* -------------------------------------------------------------------- */
     858             :     /*      SAR images may store complex data in 2 bands (I and Q)          */
     859             :     /*      Map onto a GDAL complex raster band                             */
     860             :     /* -------------------------------------------------------------------- */
     861         760 :     bool bIsTempBandSet = false;
     862         447 :     if (!bOpenForCreate && psImage &&
     863         441 :         EQUAL(psImage->szICAT, "SAR")  //SAR image...
     864           6 :         && bIsTempBandUsed &&
     865           6 :         nUsableBands == psImage->nBands
     866             :         //...with 2 bands ... (modified to allow an even number - spec seems to indicate only 2 bands allowed?)
     867           6 :         && (nUsableBands % 2) == 0 &&
     868             :         dtFirstBand == dtSecondBand  //...that have the same datatype...
     869           6 :         && !GDALDataTypeIsComplex(dtFirstBand)  //...and are not complex...
     870             :         //..and can be mapped directly to a complex type
     871           6 :         && (dtFirstBand == GDT_Int16 || dtFirstBand == GDT_Int32 ||
     872        1207 :             dtFirstBand == GDT_Float32 || dtFirstBand == GDT_Float64) &&
     873           6 :         CPLTestBool(CPLGetConfigOption("NITF_SAR_AS_COMPLEX_TYPE", "YES")))
     874             :     {
     875           5 :         bool allBandsIQ = true;
     876          10 :         for (int i = 0; i < nUsableBands; i += 2)
     877             :         {
     878           5 :             const NITFBandInfo *psBandInfo1 = psImage->pasBandInfo + i;
     879           5 :             const NITFBandInfo *psBandInfo2 = psImage->pasBandInfo + i + 1;
     880             : 
     881             :             //check that the ISUBCAT is labelled "I" and "Q" on the 2 bands
     882           5 :             if (!EQUAL(psBandInfo1->szISUBCAT, "I") ||
     883           5 :                 !EQUAL(psBandInfo2->szISUBCAT, "Q"))
     884             :             {
     885           0 :                 allBandsIQ = false;
     886           0 :                 break;
     887             :             }
     888             :         }
     889             : 
     890           5 :         if (allBandsIQ)
     891             :         {
     892           5 :             poDS->m_bHasComplexRasterBand = true;
     893          10 :             for (int i = 0; i < (nUsableBands / 2); i++)
     894             :             {
     895             :                 //wrap the I and Q bands into a single complex band
     896           5 :                 const int iBandIndex = 2 * i;
     897           5 :                 const int qBandIndex = 2 * i + 1;
     898             :                 NITFComplexRasterBand *poBand = new NITFComplexRasterBand(
     899           5 :                     poDS, apoNewBands[iBandIndex], apoNewBands[qBandIndex],
     900           5 :                     iBandIndex + 1, qBandIndex + 1);
     901           5 :                 SetBandMetadata(psImage, poBand, i + 1, false);
     902           5 :                 poDS->SetBand(i + 1, poBand);
     903           5 :                 bIsTempBandSet = true;
     904             :             }
     905             :         }
     906             :     }
     907             : 
     908         760 :     if (bIsTempBandUsed && !bIsTempBandSet)
     909             :     {
     910             :         // Reset properly bands that are not complex
     911      141626 :         for (int iBand = 0; iBand < nUsableBands; iBand++)
     912             :         {
     913      140933 :             GDALRasterBand *poBand = apoNewBands[iBand];
     914      140933 :             SetBandMetadata(psImage, poBand, iBand + 1, true);
     915      140933 :             poDS->SetBand(iBand + 1, poBand);
     916             :         }
     917             :     }
     918             : 
     919             :     /* -------------------------------------------------------------------- */
     920             :     /*      Report problems with odd bit sizes.                             */
     921             :     /* -------------------------------------------------------------------- */
     922         288 :     if (poOpenInfo->eAccess == GA_Update && psImage != nullptr &&
     923        1048 :         (psImage->nBitsPerSample % 8 != 0) && poDS->poJPEGDataset == nullptr &&
     924           0 :         poDS->poJ2KDataset == nullptr)
     925             :     {
     926           0 :         CPLError(
     927             :             CE_Warning, CPLE_AppDefined,
     928             :             "Image with %d bits per sample cannot be opened in update mode.",
     929             :             psImage->nBitsPerSample);
     930           0 :         delete poDS;
     931           0 :         return nullptr;
     932             :     }
     933             : 
     934             :     /* -------------------------------------------------------------------- */
     935             :     /*      Process the projection from the ICORDS.                         */
     936             :     /* -------------------------------------------------------------------- */
     937         760 :     if (psImage == nullptr)
     938             :     {
     939             :         /* nothing */
     940             :     }
     941         753 :     else if (psImage->chICORDS == 'G' || psImage->chICORDS == 'D')
     942             :     {
     943         323 :         poDS->m_oSRS.SetWellKnownGeogCS("WGS84");
     944             :     }
     945         430 :     else if (psImage->chICORDS == 'C')
     946             :     {
     947           0 :         poDS->m_oSRS.SetWellKnownGeogCS("WGS84");
     948             : 
     949             :         /* convert latitudes from geocentric to geodetic form. */
     950             : 
     951           0 :         psImage->dfULY =
     952           0 :             NITF_WGS84_Geocentric_Latitude_To_Geodetic_Latitude(psImage->dfULY);
     953           0 :         psImage->dfLLY =
     954           0 :             NITF_WGS84_Geocentric_Latitude_To_Geodetic_Latitude(psImage->dfLLY);
     955           0 :         psImage->dfURY =
     956           0 :             NITF_WGS84_Geocentric_Latitude_To_Geodetic_Latitude(psImage->dfURY);
     957           0 :         psImage->dfLRY =
     958           0 :             NITF_WGS84_Geocentric_Latitude_To_Geodetic_Latitude(psImage->dfLRY);
     959             :     }
     960         430 :     else if (psImage->chICORDS == 'S' || psImage->chICORDS == 'N')
     961             :     {
     962             :         // in open-for-create mode, we don't have a valid UTM zone, which
     963             :         // would make PROJ unhappy
     964          59 :         if (!bOpenForCreate)
     965             :         {
     966          37 :             poDS->m_oSRS.SetUTM(psImage->nZone, psImage->chICORDS == 'N');
     967          37 :             poDS->m_oSRS.SetWellKnownGeogCS("WGS84");
     968             :         }
     969             :     }
     970         371 :     else if (psImage->chICORDS == 'U' && psImage->nZone != 0)
     971             :     {
     972           1 :         poDS->m_oSRS.SetUTM(std::abs(psImage->nZone), psImage->nZone > 0);
     973           1 :         poDS->m_oSRS.SetWellKnownGeogCS("WGS84");
     974             :     }
     975             : 
     976             :     /* -------------------------------------------------------------------- */
     977             :     /*      Try looking for a .nfw file.                                    */
     978             :     /* -------------------------------------------------------------------- */
     979        1513 :     if (psImage && GDALReadWorldFile2(pszFilename, "nfw", poDS->m_gt.data(),
     980         753 :                                       poOpenInfo->GetSiblingFiles(), nullptr))
     981             :     {
     982             :         int isNorth;
     983             :         int zone;
     984             : 
     985           2 :         poDS->bGotGeoTransform = TRUE;
     986             : 
     987             :         /* If nfw found, try looking for a header with projection info */
     988             :         /* in space imaging style format                               */
     989           4 :         std::string osHDR = CPLResetExtensionSafe(pszFilename, "hdr");
     990             : 
     991           2 :         VSILFILE *fpHDR = VSIFOpenL(osHDR.c_str(), "rt");
     992             : 
     993           2 :         if (fpHDR == nullptr && VSIIsCaseSensitiveFS(osHDR.c_str()))
     994             :         {
     995           0 :             osHDR = CPLResetExtensionSafe(pszFilename, "HDR");
     996           0 :             fpHDR = VSIFOpenL(osHDR.c_str(), "rt");
     997             :         }
     998             : 
     999           2 :         if (fpHDR != nullptr)
    1000             :         {
    1001           2 :             CPL_IGNORE_RET_VAL(VSIFCloseL(fpHDR));
    1002           2 :             char **papszLines = CSLLoad2(osHDR.c_str(), 16, 200, nullptr);
    1003           2 :             if (CSLCount(papszLines) == 16)
    1004             :             {
    1005             : 
    1006           2 :                 if (psImage->chICORDS == 'N')
    1007           2 :                     isNorth = 1;
    1008           0 :                 else if (psImage->chICORDS == 'S')
    1009           0 :                     isNorth = 0;
    1010           0 :                 else if (psImage->chICORDS == 'G' || psImage->chICORDS == 'D' ||
    1011           0 :                          psImage->chICORDS == 'C')
    1012             :                 {
    1013           0 :                     if (psImage->dfLLY + psImage->dfLRY + psImage->dfULY +
    1014           0 :                             psImage->dfURY <
    1015             :                         0)
    1016           0 :                         isNorth = 0;
    1017             :                     else
    1018           0 :                         isNorth = 1;
    1019             :                 }
    1020           0 :                 else if (psImage->chICORDS == 'U')
    1021             :                 {
    1022           0 :                     isNorth = psImage->nZone >= 0;
    1023             :                 }
    1024             :                 else
    1025             :                 {
    1026             :                     // Arbitrarily suppose we are in northern hemisphere.
    1027           0 :                     isNorth = 1;
    1028             : 
    1029             :                     /* unless we have other information to determine the
    1030             :                      * hemisphere */
    1031           0 :                     char **papszUSE00A_MD = NITFReadSTDIDC(psImage);
    1032           0 :                     if (papszUSE00A_MD != nullptr)
    1033             :                     {
    1034           0 :                         const char *pszLocation = CSLFetchNameValue(
    1035             :                             papszUSE00A_MD, "NITF_STDIDC_LOCATION");
    1036           0 :                         if (pszLocation && strlen(pszLocation) == 11)
    1037             :                         {
    1038           0 :                             isNorth = (pszLocation[4] == 'N');
    1039             :                         }
    1040           0 :                         CSLDestroy(papszUSE00A_MD);
    1041             :                     }
    1042             :                     else
    1043             :                     {
    1044             :                         NITFRPC00BInfo sRPCInfo;
    1045           0 :                         if (NITFReadRPC00B(psImage, &sRPCInfo) &&
    1046           0 :                             sRPCInfo.SUCCESS)
    1047             :                         {
    1048           0 :                             isNorth = (sRPCInfo.LAT_OFF >= 0);
    1049             :                         }
    1050             :                     }
    1051             :                 }
    1052             : 
    1053           2 :                 if ((STARTS_WITH_CI(papszLines[7],
    1054             :                                     "Selected Projection: Universal Transverse "
    1055           2 :                                     "Mercator")) &&
    1056           2 :                     (STARTS_WITH_CI(papszLines[8], "Zone: ")) &&
    1057           2 :                     (strlen(papszLines[8]) >= 7))
    1058             :                 {
    1059           2 :                     zone = atoi(&(papszLines[8][6]));
    1060           2 :                     poDS->m_oSRS.Clear();
    1061           2 :                     poDS->m_oSRS.SetUTM(zone, isNorth);
    1062           2 :                     poDS->m_oSRS.SetWellKnownGeogCS("WGS84");
    1063             :                 }
    1064             :                 else
    1065             :                 {
    1066             :                     /* Couldn't find associated projection info.
    1067             :                        Go back to original file for geotransform.
    1068             :                     */
    1069           0 :                     poDS->bGotGeoTransform = FALSE;
    1070             :                 }
    1071             :             }
    1072             :             else
    1073           0 :                 poDS->bGotGeoTransform = FALSE;
    1074           2 :             CSLDestroy(papszLines);
    1075             :         }
    1076             :         else
    1077           0 :             poDS->bGotGeoTransform = FALSE;
    1078             :     }
    1079             : 
    1080             :     /* -------------------------------------------------------------------- */
    1081             :     /*      Does this look like a CADRG polar tile ? (#2940)                */
    1082             :     /* -------------------------------------------------------------------- */
    1083             :     const char *pszIID1 =
    1084         760 :         (psImage) ? CSLFetchNameValue(psImage->papszMetadata, "NITF_IID1")
    1085         760 :                   : nullptr;
    1086             :     const char *pszITITLE =
    1087         760 :         (psImage) ? CSLFetchNameValue(psImage->papszMetadata, "NITF_ITITLE")
    1088         760 :                   : nullptr;
    1089         760 :     if (psImage != nullptr && !poDS->bGotGeoTransform &&
    1090         751 :         (psImage->chICORDS == 'G' || psImage->chICORDS == 'D') &&
    1091         323 :         pszIID1 != nullptr && EQUAL(pszIID1, "CADRG") && pszITITLE != nullptr &&
    1092         155 :         strlen(pszITITLE) >= 12 &&
    1093         155 :         (pszITITLE[strlen(pszITITLE) - 1] == '9' ||
    1094         141 :          pszITITLE[strlen(pszITITLE) - 1] == 'J'))
    1095             :     {
    1096          36 :         OGRSpatialReference oSRS_AEQD, oSRS_WGS84;
    1097             : 
    1098          18 :         const char *pszPolarProjection = (psImage->dfULY > 0)
    1099             :                                              ? pszNorthPolarProjection
    1100             :                                              : pszSouthPolarProjection;
    1101             : 
    1102          18 :         oSRS_AEQD.importFromWkt(pszPolarProjection);
    1103             : 
    1104          18 :         oSRS_WGS84.SetWellKnownGeogCS("WGS84");
    1105          18 :         oSRS_WGS84.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1106             : 
    1107          18 :         CPLPushErrorHandler(CPLQuietErrorHandler);
    1108             :         auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
    1109          36 :             OGRCreateCoordinateTransformation(&oSRS_WGS84, &oSRS_AEQD));
    1110          18 :         CPLPopErrorHandler();
    1111          18 :         if (poCT)
    1112             :         {
    1113          18 :             double dfULX_AEQD = psImage->dfULX;
    1114          18 :             double dfULY_AEQD = psImage->dfULY;
    1115          18 :             double dfURX_AEQD = psImage->dfURX;
    1116          18 :             double dfURY_AEQD = psImage->dfURY;
    1117          18 :             double dfLLX_AEQD = psImage->dfLLX;
    1118          18 :             double dfLLY_AEQD = psImage->dfLLY;
    1119          18 :             double dfLRX_AEQD = psImage->dfLRX;
    1120          18 :             double dfLRY_AEQD = psImage->dfLRY;
    1121          18 :             double z = 0;
    1122          18 :             int bSuccess = TRUE;
    1123          18 :             bSuccess &= poCT->Transform(1, &dfULX_AEQD, &dfULY_AEQD, &z);
    1124          18 :             bSuccess &= poCT->Transform(1, &dfURX_AEQD, &dfURY_AEQD, &z);
    1125          18 :             bSuccess &= poCT->Transform(1, &dfLLX_AEQD, &dfLLY_AEQD, &z);
    1126          18 :             bSuccess &= poCT->Transform(1, &dfLRX_AEQD, &dfLRY_AEQD, &z);
    1127          18 :             if (bSuccess)
    1128             :             {
    1129             :                 /* Check that the coordinates of the 4 corners in Azimuthal
    1130             :                  * Equidistant projection */
    1131             :                 /* are a rectangle */
    1132          18 :                 if (fabs(dfULX_AEQD - dfLLX_AEQD) < 1e-6 * fabs(dfLLX_AEQD) &&
    1133          18 :                     fabs(dfURX_AEQD - dfLRX_AEQD) < 1e-6 * fabs(dfLRX_AEQD) &&
    1134          18 :                     fabs(dfULY_AEQD - dfURY_AEQD) < 1e-6 * fabs(dfURY_AEQD) &&
    1135          18 :                     fabs(dfLLY_AEQD - dfLRY_AEQD) < 1e-6 * fabs(dfLRY_AEQD))
    1136             :                 {
    1137          18 :                     poDS->m_oSRS = std::move(oSRS_AEQD);
    1138             : 
    1139          18 :                     poDS->bGotGeoTransform = TRUE;
    1140          18 :                     poDS->m_gt.xorig = dfULX_AEQD;
    1141          18 :                     poDS->m_gt.xscale =
    1142          18 :                         (dfURX_AEQD - dfULX_AEQD) / poDS->nRasterXSize;
    1143          18 :                     poDS->m_gt.xrot = 0;
    1144          18 :                     poDS->m_gt.yorig = dfULY_AEQD;
    1145          18 :                     poDS->m_gt.yrot = 0;
    1146          18 :                     poDS->m_gt.yscale =
    1147          18 :                         (dfLLY_AEQD - dfULY_AEQD) / poDS->nRasterYSize;
    1148             :                 }
    1149             :             }
    1150             :         }
    1151             :         else
    1152             :         {
    1153             :             // if we cannot instantiate the transformer, then we
    1154             :             // will at least attempt to record what we believe the
    1155             :             // natural coordinate system of the image is.  This is
    1156             :             // primarily used by ArcGIS (#3337)
    1157             : 
    1158           0 :             CPLErrorReset();
    1159             : 
    1160           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    1161             :                      "Failed to instantiate coordinate system transformer, "
    1162             :                      "likely PROJ.DLL/libproj.so is not available.  Returning "
    1163             :                      "image corners as lat/long GCPs as a fallback.");
    1164             : 
    1165           0 :             char *pszAEQD = nullptr;
    1166           0 :             oSRS_AEQD.exportToWkt(&(pszAEQD));
    1167           0 :             poDS->SetMetadataItem("GCPPROJECTIONX", pszAEQD,
    1168           0 :                                   GDAL_MDD_IMAGE_STRUCTURE);
    1169           0 :             CPLFree(pszAEQD);
    1170             :         }
    1171             :     }
    1172             : 
    1173             :     /* -------------------------------------------------------------------- */
    1174             :     /*      Do we have RPCs?                                                */
    1175             :     /* -------------------------------------------------------------------- */
    1176         760 :     bool bHasRPC00 = false;
    1177             :     NITFRPC00BInfo sRPCInfo;
    1178         760 :     memset(&sRPCInfo, 0,
    1179             :            sizeof(sRPCInfo)); /* To avoid warnings from not clever compilers */
    1180             : 
    1181         760 :     if (psImage && NITFReadRPC00B(psImage, &sRPCInfo) && sRPCInfo.SUCCESS)
    1182          54 :         bHasRPC00 = true;
    1183             : 
    1184             :     /* -------------------------------------------------------------------- */
    1185             :     /*      Do we have IGEOLO data that can be treated as a                 */
    1186             :     /*      geotransform?  Our approach should support images in an         */
    1187             :     /*      affine rotated frame of reference.                              */
    1188             :     /* -------------------------------------------------------------------- */
    1189         760 :     int nGCPCount = 0;
    1190         760 :     GDAL_GCP *psGCPs = nullptr;
    1191             : 
    1192         760 :     if (psImage && !poDS->bGotGeoTransform && psImage->chICORDS != ' ')
    1193             :     {
    1194         363 :         nGCPCount = 4;
    1195             : 
    1196             :         psGCPs = reinterpret_cast<GDAL_GCP *>(
    1197         363 :             CPLMalloc(sizeof(GDAL_GCP) * nGCPCount));
    1198         363 :         GDALInitGCPs(nGCPCount, psGCPs);
    1199             : 
    1200         363 :         if (psImage->bIsBoxCenterOfPixel)
    1201             :         {
    1202         206 :             psGCPs[0].dfGCPPixel = 0.5;
    1203         206 :             psGCPs[0].dfGCPLine = 0.5;
    1204         206 :             psGCPs[1].dfGCPPixel = poDS->nRasterXSize - 0.5;
    1205         206 :             psGCPs[1].dfGCPLine = 0.5;
    1206         206 :             psGCPs[2].dfGCPPixel = poDS->nRasterXSize - 0.5;
    1207         206 :             psGCPs[2].dfGCPLine = poDS->nRasterYSize - 0.5;
    1208         206 :             psGCPs[3].dfGCPPixel = 0.5;
    1209         206 :             psGCPs[3].dfGCPLine = poDS->nRasterYSize - 0.5;
    1210             :         }
    1211             :         else
    1212             :         {
    1213         157 :             psGCPs[0].dfGCPPixel = 0.0;
    1214         157 :             psGCPs[0].dfGCPLine = 0.0;
    1215         157 :             psGCPs[1].dfGCPPixel = poDS->nRasterXSize;
    1216         157 :             psGCPs[1].dfGCPLine = 0.0;
    1217         157 :             psGCPs[2].dfGCPPixel = poDS->nRasterXSize;
    1218         157 :             psGCPs[2].dfGCPLine = poDS->nRasterYSize;
    1219         157 :             psGCPs[3].dfGCPPixel = 0.0;
    1220         157 :             psGCPs[3].dfGCPLine = poDS->nRasterYSize;
    1221             :         }
    1222             : 
    1223         363 :         psGCPs[0].dfGCPX = psImage->dfULX;
    1224         363 :         psGCPs[0].dfGCPY = psImage->dfULY;
    1225             : 
    1226         363 :         psGCPs[1].dfGCPX = psImage->dfURX;
    1227         363 :         psGCPs[1].dfGCPY = psImage->dfURY;
    1228             : 
    1229         363 :         psGCPs[2].dfGCPX = psImage->dfLRX;
    1230         363 :         psGCPs[2].dfGCPY = psImage->dfLRY;
    1231             : 
    1232         363 :         psGCPs[3].dfGCPX = psImage->dfLLX;
    1233         363 :         psGCPs[3].dfGCPY = psImage->dfLLY;
    1234             : 
    1235             : /* -------------------------------------------------------------------- */
    1236             : /*      ESRI desires to use the RPCs to produce a denser and more       */
    1237             : /*      accurate set of GCPs in this case.  Details are unclear at      */
    1238             : /*      this time.                                                      */
    1239             : /* -------------------------------------------------------------------- */
    1240             : #ifdef ESRI_BUILD
    1241             :         if (bHasRPC00 &&
    1242             :             ((psImage->chICORDS == 'G') || (psImage->chICORDS == 'C')))
    1243             :         {
    1244             :             if (nGCPCount == 4)
    1245             :                 NITFDensifyGCPs(&psGCPs, &nGCPCount);
    1246             : 
    1247             :             NITFUpdateGCPsWithRPC(&sRPCInfo, psGCPs, &nGCPCount);
    1248             :         }
    1249             : #endif /* def ESRI_BUILD */
    1250             :     }
    1251             : 
    1252             :     /* -------------------------------------------------------------------- */
    1253             :     /*      Convert the GCPs into a geotransform definition, if possible.   */
    1254             :     /* -------------------------------------------------------------------- */
    1255         760 :     if (!psImage)
    1256             :     {
    1257             :         /* nothing */
    1258             :     }
    1259        1116 :     else if (poDS->bGotGeoTransform == FALSE && nGCPCount > 0 &&
    1260         363 :              GDALGCPsToGeoTransform(nGCPCount, psGCPs, poDS->m_gt.data(),
    1261             :                                     FALSE))
    1262             :     {
    1263         283 :         poDS->bGotGeoTransform = TRUE;
    1264             :     }
    1265             : 
    1266             :     /* -------------------------------------------------------------------- */
    1267             :     /*      If we have IGEOLO that isn't north up, return it as GCPs.       */
    1268             :     /* -------------------------------------------------------------------- */
    1269         470 :     else if ((psImage->dfULX != 0 || psImage->dfURX != 0 ||
    1270         448 :               psImage->dfLRX != 0 || psImage->dfLLX != 0) &&
    1271          22 :              psImage->chICORDS != ' ' && (poDS->bGotGeoTransform == FALSE) &&
    1272             :              nGCPCount >= 4)
    1273             :     {
    1274           4 :         CPLDebug("GDAL",
    1275             :                  "NITFDataset::Open() was not able to derive a first order\n"
    1276             :                  "geotransform.  It will be returned as GCPs.");
    1277             : 
    1278           4 :         poDS->nGCPCount = nGCPCount;
    1279           4 :         poDS->pasGCPList = psGCPs;
    1280             : 
    1281           4 :         psGCPs = nullptr;
    1282           4 :         nGCPCount = 0;
    1283             : 
    1284           4 :         CPLFree(poDS->pasGCPList[0].pszId);
    1285           4 :         poDS->pasGCPList[0].pszId = CPLStrdup("UpperLeft");
    1286             : 
    1287           4 :         CPLFree(poDS->pasGCPList[1].pszId);
    1288           4 :         poDS->pasGCPList[1].pszId = CPLStrdup("UpperRight");
    1289             : 
    1290           4 :         CPLFree(poDS->pasGCPList[2].pszId);
    1291           4 :         poDS->pasGCPList[2].pszId = CPLStrdup("LowerRight");
    1292             : 
    1293           4 :         CPLFree(poDS->pasGCPList[3].pszId);
    1294           4 :         poDS->pasGCPList[3].pszId = CPLStrdup("LowerLeft");
    1295             : 
    1296           4 :         poDS->m_oGCPSRS = poDS->m_oSRS;
    1297             :     }
    1298             : 
    1299             :     // This cleans up the original copy of the GCPs used to test if
    1300             :     // this IGEOLO could be used for a geotransform if we did not
    1301             :     // steal the to use as primary gcps.
    1302         760 :     if (nGCPCount > 0)
    1303             :     {
    1304         359 :         GDALDeinitGCPs(nGCPCount, psGCPs);
    1305         359 :         CPLFree(psGCPs);
    1306             :     }
    1307             : 
    1308             :     /* -------------------------------------------------------------------- */
    1309             :     /*      Do we have PRJPSB and MAPLOB TREs to get better                 */
    1310             :     /*      georeferencing from?                                            */
    1311             :     /* -------------------------------------------------------------------- */
    1312         760 :     if (psImage)
    1313         753 :         poDS->CheckGeoSDEInfo();
    1314             : 
    1315             :     /* -------------------------------------------------------------------- */
    1316             :     /*      Do we have metadata.                                            */
    1317             :     /* -------------------------------------------------------------------- */
    1318             : 
    1319             :     // File and Image level metadata.
    1320         760 :     char **papszMergedMD = CSLDuplicate(poDS->psFile->papszMetadata);
    1321             : 
    1322         760 :     if (psImage)
    1323             :     {
    1324         753 :         papszMergedMD = CSLInsertStrings(papszMergedMD, CSLCount(papszMergedMD),
    1325         753 :                                          psImage->papszMetadata);
    1326             : 
    1327             :         // Comments.
    1328         753 :         if (psImage->pszComments != nullptr &&
    1329         753 :             strlen(psImage->pszComments) != 0)
    1330           6 :             papszMergedMD = CSLSetNameValue(
    1331           6 :                 papszMergedMD, "NITF_IMAGE_COMMENTS", psImage->pszComments);
    1332             : 
    1333             :         // Compression code.
    1334             :         papszMergedMD =
    1335         753 :             CSLSetNameValue(papszMergedMD, "NITF_IC", psImage->szIC);
    1336             : 
    1337             :         // IMODE
    1338             :         char szIMODE[2];
    1339         753 :         szIMODE[0] = psImage->chIMODE;
    1340         753 :         szIMODE[1] = '\0';
    1341         753 :         papszMergedMD = CSLSetNameValue(papszMergedMD, "NITF_IMODE", szIMODE);
    1342             : 
    1343             :         // ILOC/Attachment info
    1344         753 :         if (psImage->nIDLVL != 0)
    1345             :         {
    1346         753 :             NITFSegmentInfo *psSegInfo =
    1347         753 :                 psFile->pasSegmentInfo + psImage->iSegment;
    1348             : 
    1349             :             papszMergedMD =
    1350         753 :                 CSLSetNameValue(papszMergedMD, "NITF_IDLVL",
    1351        1506 :                                 CPLString().Printf("%d", psImage->nIDLVL));
    1352             :             papszMergedMD =
    1353         753 :                 CSLSetNameValue(papszMergedMD, "NITF_IALVL",
    1354        1506 :                                 CPLString().Printf("%d", psImage->nIALVL));
    1355             :             papszMergedMD =
    1356         753 :                 CSLSetNameValue(papszMergedMD, "NITF_ILOC_ROW",
    1357        1506 :                                 CPLString().Printf("%d", psImage->nILOCRow));
    1358             :             papszMergedMD =
    1359         753 :                 CSLSetNameValue(papszMergedMD, "NITF_ILOC_COLUMN",
    1360        1506 :                                 CPLString().Printf("%d", psImage->nILOCColumn));
    1361             :             papszMergedMD =
    1362         753 :                 CSLSetNameValue(papszMergedMD, "NITF_CCS_ROW",
    1363        1506 :                                 CPLString().Printf("%d", psSegInfo->nCCS_R));
    1364             :             papszMergedMD =
    1365         753 :                 CSLSetNameValue(papszMergedMD, "NITF_CCS_COLUMN",
    1366        1506 :                                 CPLString().Printf("%d", psSegInfo->nCCS_C));
    1367             :             papszMergedMD =
    1368         753 :                 CSLSetNameValue(papszMergedMD, "NITF_IMAG", psImage->szIMAG);
    1369             :         }
    1370             : 
    1371             :         papszMergedMD =
    1372         753 :             NITFGenericMetadataRead(papszMergedMD, psFile, psImage, nullptr);
    1373             : 
    1374             :         // BLOCKA
    1375         753 :         char **papszTRE_MD = NITFReadBLOCKA(psImage);
    1376         753 :         if (papszTRE_MD != nullptr)
    1377             :         {
    1378          22 :             papszMergedMD = CSLInsertStrings(
    1379             :                 papszMergedMD, CSLCount(papszTRE_MD), papszTRE_MD);
    1380          22 :             CSLDestroy(papszTRE_MD);
    1381             :         }
    1382             :     }
    1383             : 
    1384             : #ifdef ESRI_BUILD
    1385             :     // Extract ESRI generic metadata.
    1386             :     char **papszESRI_MD = ExtractEsriMD(papszMergedMD);
    1387             :     if (papszESRI_MD != NULL)
    1388             :     {
    1389             :         papszMergedMD = CSLInsertStrings(papszMergedMD, CSLCount(papszESRI_MD),
    1390             :                                          papszESRI_MD);
    1391             :         CSLDestroy(papszESRI_MD);
    1392             :     }
    1393             : #endif
    1394             : 
    1395         760 :     poDS->SetMetadata(papszMergedMD);
    1396         760 :     CSLDestroy(papszMergedMD);
    1397             : 
    1398             :     /* -------------------------------------------------------------------- */
    1399             :     /*      Image structure metadata.                                       */
    1400             :     /* -------------------------------------------------------------------- */
    1401         760 :     if (psImage == nullptr)
    1402             :         /* do nothing */;
    1403         753 :     else if (psImage->szIC[1] == '1')
    1404           2 :         poDS->SetMetadataItem(GDALMD_COMPRESSION, "BILEVEL",
    1405           2 :                               GDAL_MDD_IMAGE_STRUCTURE);
    1406         751 :     else if (psImage->szIC[1] == '2')
    1407           0 :         poDS->SetMetadataItem(GDALMD_COMPRESSION, "ARIDPCM",
    1408           0 :                               GDAL_MDD_IMAGE_STRUCTURE);
    1409         751 :     else if (psImage->szIC[1] == '3')
    1410          25 :         poDS->SetMetadataItem(GDALMD_COMPRESSION, "JPEG",
    1411          25 :                               GDAL_MDD_IMAGE_STRUCTURE);
    1412         726 :     else if (psImage->szIC[1] == '4')
    1413         155 :         poDS->SetMetadataItem(GDALMD_COMPRESSION, "VECTOR QUANTIZATION",
    1414         155 :                               GDAL_MDD_IMAGE_STRUCTURE);
    1415         571 :     else if (psImage->szIC[1] == '5')
    1416           0 :         poDS->SetMetadataItem(GDALMD_COMPRESSION, "LOSSLESS JPEG",
    1417           0 :                               GDAL_MDD_IMAGE_STRUCTURE);
    1418         571 :     else if (psImage->szIC[1] == '8')
    1419          37 :         poDS->SetMetadataItem(GDALMD_COMPRESSION, "JPEG2000",
    1420          37 :                               GDAL_MDD_IMAGE_STRUCTURE);
    1421             : 
    1422             :     /* -------------------------------------------------------------------- */
    1423             :     /*      Do we have RPC info.                                            */
    1424             :     /* -------------------------------------------------------------------- */
    1425             : 
    1426             :     // get _rpc.txt file
    1427        1520 :     const std::string osDirName = CPLGetDirnameSafe(pszFilename);
    1428        1520 :     const std::string osBaseName = CPLGetBasenameSafe(pszFilename);
    1429             :     std::string osRPCTXTFilename = CPLFormFilenameSafe(
    1430         760 :         osDirName.c_str(), std::string(osBaseName).append("_rpc").c_str(),
    1431         760 :         "txt");
    1432         760 :     if (CPLCheckForFile(osRPCTXTFilename.data(), poOpenInfo->GetSiblingFiles()))
    1433             :     {
    1434           4 :         poDS->m_osRPCTXTFilename = osRPCTXTFilename;
    1435             :     }
    1436             :     else
    1437             :     {
    1438        1512 :         osRPCTXTFilename = CPLFormFilenameSafe(
    1439        1512 :             osDirName.c_str(), std::string(osBaseName).append("_RPC").c_str(),
    1440         756 :             "TXT");
    1441         756 :         CPL_IGNORE_RET_VAL(osBaseName);
    1442         756 :         if (CPLCheckForFile(osRPCTXTFilename.data(),
    1443        1512 :                             poOpenInfo->GetSiblingFiles()))
    1444             :         {
    1445           0 :             poDS->m_osRPCTXTFilename = osRPCTXTFilename;
    1446             :         }
    1447             :     }
    1448         760 :     bool bHasLoadedRPCTXT = false;
    1449         760 :     if (!poDS->m_osRPCTXTFilename.empty())
    1450             :     {
    1451           4 :         char **papszMD = GDALLoadRPCFile(poDS->m_osRPCTXTFilename);
    1452           4 :         if (papszMD != nullptr)
    1453             :         {
    1454           4 :             bHasLoadedRPCTXT = true;
    1455           4 :             poDS->SetMetadata(papszMD, GDAL_MDD_RPC);
    1456           4 :             CSLDestroy(papszMD);
    1457             :         }
    1458             :         else
    1459             :         {
    1460           0 :             poDS->m_osRPCTXTFilename.clear();
    1461             :         }
    1462             :     }
    1463             : 
    1464         760 :     if (psImage && bHasRPC00 && !bHasLoadedRPCTXT)
    1465             :     {
    1466             :         char szValue[1280];
    1467             : 
    1468          50 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g", sRPCInfo.ERR_BIAS);
    1469          50 :         poDS->SetMetadataItem("ERR_BIAS", szValue, GDAL_MDD_RPC);
    1470             : 
    1471          50 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g", sRPCInfo.ERR_RAND);
    1472          50 :         poDS->SetMetadataItem("ERR_RAND", szValue, GDAL_MDD_RPC);
    1473             : 
    1474          50 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g", sRPCInfo.LINE_OFF);
    1475          50 :         poDS->SetMetadataItem("LINE_OFF", szValue, GDAL_MDD_RPC);
    1476             : 
    1477          50 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g", sRPCInfo.LINE_SCALE);
    1478          50 :         poDS->SetMetadataItem("LINE_SCALE", szValue, GDAL_MDD_RPC);
    1479             : 
    1480          50 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g", sRPCInfo.SAMP_OFF);
    1481          50 :         poDS->SetMetadataItem("SAMP_OFF", szValue, GDAL_MDD_RPC);
    1482             : 
    1483          50 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g", sRPCInfo.SAMP_SCALE);
    1484          50 :         poDS->SetMetadataItem("SAMP_SCALE", szValue, GDAL_MDD_RPC);
    1485             : 
    1486          50 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g", sRPCInfo.LONG_OFF);
    1487          50 :         poDS->SetMetadataItem("LONG_OFF", szValue, GDAL_MDD_RPC);
    1488             : 
    1489          50 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g", sRPCInfo.LONG_SCALE);
    1490          50 :         poDS->SetMetadataItem("LONG_SCALE", szValue, GDAL_MDD_RPC);
    1491             : 
    1492          50 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g", sRPCInfo.LAT_OFF);
    1493          50 :         poDS->SetMetadataItem("LAT_OFF", szValue, GDAL_MDD_RPC);
    1494             : 
    1495          50 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g", sRPCInfo.LAT_SCALE);
    1496          50 :         poDS->SetMetadataItem("LAT_SCALE", szValue, GDAL_MDD_RPC);
    1497             : 
    1498          50 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g", sRPCInfo.HEIGHT_OFF);
    1499          50 :         poDS->SetMetadataItem("HEIGHT_OFF", szValue, GDAL_MDD_RPC);
    1500             : 
    1501          50 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g", sRPCInfo.HEIGHT_SCALE);
    1502          50 :         poDS->SetMetadataItem("HEIGHT_SCALE", szValue, GDAL_MDD_RPC);
    1503             : 
    1504          50 :         szValue[0] = '\0';
    1505        1050 :         for (int i = 0; i < 20; i++)
    1506        1000 :             CPLsnprintf(szValue + strlen(szValue),
    1507        1000 :                         sizeof(szValue) - strlen(szValue), "%.16g ",
    1508             :                         sRPCInfo.LINE_NUM_COEFF[i]);
    1509          50 :         poDS->SetMetadataItem("LINE_NUM_COEFF", szValue, GDAL_MDD_RPC);
    1510             : 
    1511          50 :         szValue[0] = '\0';
    1512        1050 :         for (int i = 0; i < 20; i++)
    1513        1000 :             CPLsnprintf(szValue + strlen(szValue),
    1514        1000 :                         sizeof(szValue) - strlen(szValue), "%.16g ",
    1515             :                         sRPCInfo.LINE_DEN_COEFF[i]);
    1516          50 :         poDS->SetMetadataItem("LINE_DEN_COEFF", szValue, GDAL_MDD_RPC);
    1517             : 
    1518          50 :         szValue[0] = '\0';
    1519        1050 :         for (int i = 0; i < 20; i++)
    1520        1000 :             CPLsnprintf(szValue + strlen(szValue),
    1521        1000 :                         sizeof(szValue) - strlen(szValue), "%.16g ",
    1522             :                         sRPCInfo.SAMP_NUM_COEFF[i]);
    1523          50 :         poDS->SetMetadataItem("SAMP_NUM_COEFF", szValue, GDAL_MDD_RPC);
    1524             : 
    1525          50 :         szValue[0] = '\0';
    1526        1050 :         for (int i = 0; i < 20; i++)
    1527        1000 :             CPLsnprintf(szValue + strlen(szValue),
    1528        1000 :                         sizeof(szValue) - strlen(szValue), "%.16g ",
    1529             :                         sRPCInfo.SAMP_DEN_COEFF[i]);
    1530          50 :         poDS->SetMetadataItem("SAMP_DEN_COEFF", szValue, GDAL_MDD_RPC);
    1531             : 
    1532          50 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g",
    1533          50 :                     sRPCInfo.LONG_OFF - sRPCInfo.LONG_SCALE);
    1534          50 :         poDS->SetMetadataItem("MIN_LONG", szValue, GDAL_MDD_RPC);
    1535             : 
    1536          50 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g",
    1537          50 :                     sRPCInfo.LONG_OFF + sRPCInfo.LONG_SCALE);
    1538          50 :         poDS->SetMetadataItem("MAX_LONG", szValue, GDAL_MDD_RPC);
    1539             : 
    1540          50 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g",
    1541          50 :                     sRPCInfo.LAT_OFF - sRPCInfo.LAT_SCALE);
    1542          50 :         poDS->SetMetadataItem("MIN_LAT", szValue, GDAL_MDD_RPC);
    1543             : 
    1544          50 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g",
    1545          50 :                     sRPCInfo.LAT_OFF + sRPCInfo.LAT_SCALE);
    1546          50 :         poDS->SetMetadataItem("MAX_LAT", szValue, GDAL_MDD_RPC);
    1547             :     }
    1548             : 
    1549             :     /* -------------------------------------------------------------------- */
    1550             :     /*      Do we have Chip info?                                            */
    1551             :     /* -------------------------------------------------------------------- */
    1552             :     NITFICHIPBInfo sChipInfo;
    1553             : 
    1554         762 :     if (psImage && NITFReadICHIPB(psImage, &sChipInfo) &&
    1555           2 :         sChipInfo.XFRM_FLAG == 0)
    1556             :     {
    1557             :         char szValue[1280];
    1558             : 
    1559           2 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.SCALE_FACTOR);
    1560           2 :         poDS->SetMetadataItem("ICHIP_SCALE_FACTOR", szValue);
    1561             : 
    1562             :         // TODO: Why do these two not use CPLsnprintf?
    1563           2 :         snprintf(szValue, sizeof(szValue), "%d", sChipInfo.ANAMORPH_CORR);
    1564           2 :         poDS->SetMetadataItem("ICHIP_ANAMORPH_CORR", szValue);
    1565             : 
    1566           2 :         snprintf(szValue, sizeof(szValue), "%d", sChipInfo.SCANBLK_NUM);
    1567           2 :         poDS->SetMetadataItem("ICHIP_SCANBLK_NUM", szValue);
    1568             : 
    1569           2 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.OP_ROW_11);
    1570           2 :         poDS->SetMetadataItem("ICHIP_OP_ROW_11", szValue);
    1571             : 
    1572           2 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.OP_COL_11);
    1573           2 :         poDS->SetMetadataItem("ICHIP_OP_COL_11", szValue);
    1574             : 
    1575           2 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.OP_ROW_12);
    1576           2 :         poDS->SetMetadataItem("ICHIP_OP_ROW_12", szValue);
    1577             : 
    1578           2 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.OP_COL_12);
    1579           2 :         poDS->SetMetadataItem("ICHIP_OP_COL_12", szValue);
    1580             : 
    1581           2 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.OP_ROW_21);
    1582           2 :         poDS->SetMetadataItem("ICHIP_OP_ROW_21", szValue);
    1583             : 
    1584           2 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.OP_COL_21);
    1585           2 :         poDS->SetMetadataItem("ICHIP_OP_COL_21", szValue);
    1586             : 
    1587           2 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.OP_ROW_22);
    1588           2 :         poDS->SetMetadataItem("ICHIP_OP_ROW_22", szValue);
    1589             : 
    1590           2 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.OP_COL_22);
    1591           2 :         poDS->SetMetadataItem("ICHIP_OP_COL_22", szValue);
    1592             : 
    1593           2 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.FI_ROW_11);
    1594           2 :         poDS->SetMetadataItem("ICHIP_FI_ROW_11", szValue);
    1595             : 
    1596           2 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.FI_COL_11);
    1597           2 :         poDS->SetMetadataItem("ICHIP_FI_COL_11", szValue);
    1598             : 
    1599           2 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.FI_ROW_12);
    1600           2 :         poDS->SetMetadataItem("ICHIP_FI_ROW_12", szValue);
    1601             : 
    1602           2 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.FI_COL_12);
    1603           2 :         poDS->SetMetadataItem("ICHIP_FI_COL_12", szValue);
    1604             : 
    1605           2 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.FI_ROW_21);
    1606           2 :         poDS->SetMetadataItem("ICHIP_FI_ROW_21", szValue);
    1607             : 
    1608           2 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.FI_COL_21);
    1609           2 :         poDS->SetMetadataItem("ICHIP_FI_COL_21", szValue);
    1610             : 
    1611           2 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.FI_ROW_22);
    1612           2 :         poDS->SetMetadataItem("ICHIP_FI_ROW_22", szValue);
    1613             : 
    1614           2 :         CPLsnprintf(szValue, sizeof(szValue), "%.16g", sChipInfo.FI_COL_22);
    1615           2 :         poDS->SetMetadataItem("ICHIP_FI_COL_22", szValue);
    1616             : 
    1617             :         // Why not CPLsnprintf?
    1618           2 :         snprintf(szValue, sizeof(szValue), "%d", sChipInfo.FI_ROW);
    1619           2 :         poDS->SetMetadataItem("ICHIP_FI_ROW", szValue);
    1620             : 
    1621           2 :         snprintf(szValue, sizeof(szValue), "%d", sChipInfo.FI_COL);
    1622           2 :         poDS->SetMetadataItem("ICHIP_FI_COL", szValue);
    1623             :     }
    1624             : 
    1625         760 :     const NITFSeries *series = NITFGetSeriesInfo(pszFilename);
    1626         760 :     if (series)
    1627             :     {
    1628         161 :         poDS->SetMetadataItem("NITF_SERIES_ABBREVIATION",
    1629         161 :                               (series->abbreviation) ? series->abbreviation
    1630         161 :                                                      : "Unknown");
    1631         161 :         poDS->SetMetadataItem("NITF_SERIES_NAME",
    1632         161 :                               (series->name) ? series->name : "Unknown");
    1633             :     }
    1634             : 
    1635             :     /* -------------------------------------------------------------------- */
    1636             :     /*      If there are multiple image segments, and no specific one is    */
    1637             :     /*      asker for, then setup the subdataset metadata.                  */
    1638             :     /* -------------------------------------------------------------------- */
    1639         760 :     int nSubDSCount = 0;
    1640             : 
    1641             :     {
    1642         760 :         char **papszSubdatasets = nullptr;
    1643             : 
    1644       10701 :         for (iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++)
    1645             :         {
    1646        9941 :             if (EQUAL(psFile->pasSegmentInfo[iSegment].szSegmentType, "IM"))
    1647             :             {
    1648       19528 :                 CPLString oName;
    1649        9764 :                 CPLString oValue;
    1650             : 
    1651        9764 :                 if (nIMIndex == -1)
    1652             :                 {
    1653         575 :                     oName.Printf("SUBDATASET_%d_NAME", nSubDSCount + 1);
    1654         575 :                     oValue.Printf("NITF_IM:%d:%s", nSubDSCount, pszFilename);
    1655             :                     papszSubdatasets =
    1656         575 :                         CSLSetNameValue(papszSubdatasets, oName, oValue);
    1657             : 
    1658         575 :                     oName.Printf("SUBDATASET_%d_DESC", nSubDSCount + 1);
    1659             :                     oValue.Printf("Image %d of %s", nSubDSCount + 1,
    1660         575 :                                   pszFilename);
    1661             :                     papszSubdatasets =
    1662         575 :                         CSLSetNameValue(papszSubdatasets, oName, oValue);
    1663             :                 }
    1664             : 
    1665        9764 :                 nSubDSCount++;
    1666             :             }
    1667             :         }
    1668             : 
    1669         760 :         if (nIMIndex == -1 && nSubDSCount > 1)
    1670             :         {
    1671           4 :             poDS->GDALMajorObject::SetMetadata(papszSubdatasets,
    1672             :                                                GDAL_MDD_SUBDATASETS);
    1673             :         }
    1674             : 
    1675         760 :         CSLDestroy(papszSubdatasets);
    1676             :     }
    1677             : 
    1678             :     /* -------------------------------------------------------------------- */
    1679             :     /*      Initialize any PAM information.                                 */
    1680             :     /* -------------------------------------------------------------------- */
    1681         760 :     poDS->SetDescription(poOpenInfo->pszFilename);
    1682         760 :     poDS->SetPhysicalFilename(pszFilename);
    1683             : 
    1684         760 :     if (nSubDSCount > 1 || nIMIndex != -1)
    1685             :     {
    1686         187 :         if (nIMIndex == -1)
    1687             :         {
    1688           4 :             nIMIndex = 0;
    1689             :         }
    1690         183 :         else if (nIMIndex == 0 && nSubDSCount == 1)
    1691             :         {
    1692             :             // If subdataset 0 is explicitly specified, and there's a single
    1693             :             // subdataset, and that PAM .aux.xml doesn't have a Subdataset node,
    1694             :             // then don't set the subdataset name to get metadata from the
    1695             :             // top PAM node.
    1696         155 :             const char *pszPAMFilename = poDS->BuildPamFilename();
    1697             :             VSIStatBufL sStatBuf;
    1698         310 :             if (pszPAMFilename != nullptr &&
    1699         155 :                 VSIStatExL(pszPAMFilename, &sStatBuf,
    1700         310 :                            VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG) == 0 &&
    1701           2 :                 VSI_ISREG(sStatBuf.st_mode))
    1702             :             {
    1703           4 :                 CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
    1704           2 :                 CPLXMLNode *psTree = CPLParseXMLFile(pszPAMFilename);
    1705           2 :                 if (psTree)
    1706             :                 {
    1707           2 :                     if (CPLGetXMLNode(psTree, "=PAMDataset.Subdataset") ==
    1708             :                         nullptr)
    1709             :                     {
    1710           1 :                         nIMIndex = -1;
    1711             :                     }
    1712             :                 }
    1713           2 :                 CPLDestroyXMLNode(psTree);
    1714             :             }
    1715             :         }
    1716             : 
    1717         187 :         if (nIMIndex >= 0)
    1718             :         {
    1719         186 :             poDS->SetSubdatasetName(CPLString().Printf("%d", nIMIndex));
    1720         187 :         }
    1721             :     }
    1722         573 :     else if (/* nIMIndex == -1 && */ nSubDSCount == 1)
    1723             :     {
    1724             :         // GDAL 3.4.0 to 3.5.0 used to save the PAM metadata if a Subdataset
    1725             :         // node, even if there was one single subdataset.
    1726             :         // Detect that situation to automatically read it even if not explicitly
    1727             :         // specifying that single subdataset.
    1728         566 :         const char *pszPAMFilename = poDS->BuildPamFilename();
    1729             :         VSIStatBufL sStatBuf;
    1730        1132 :         if (pszPAMFilename != nullptr &&
    1731         566 :             VSIStatExL(pszPAMFilename, &sStatBuf,
    1732        1132 :                        VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG) == 0 &&
    1733          72 :             VSI_ISREG(sStatBuf.st_mode))
    1734             :         {
    1735         144 :             CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
    1736          72 :             CPLXMLNode *psTree = CPLParseXMLFile(pszPAMFilename);
    1737          72 :             if (psTree)
    1738             :             {
    1739             :                 const auto psSubdatasetNode =
    1740          72 :                     CPLGetXMLNode(psTree, "=PAMDataset.Subdataset");
    1741          93 :                 if (psSubdatasetNode != nullptr &&
    1742          21 :                     strcmp(CPLGetXMLValue(psSubdatasetNode, "name", ""), "0") ==
    1743             :                         0)
    1744             :                 {
    1745          21 :                     poDS->SetSubdatasetName("0");
    1746          21 :                     poDS->SetPhysicalFilename(pszFilename);
    1747             :                 }
    1748          72 :                 CPLDestroyXMLNode(psTree);
    1749             :             }
    1750             :         }
    1751             :     }
    1752             : 
    1753         760 :     poDS->bInLoadXML = TRUE;
    1754         760 :     poDS->TryLoadXML(poOpenInfo->GetSiblingFiles());
    1755         760 :     poDS->bInLoadXML = FALSE;
    1756             : 
    1757             :     /* -------------------------------------------------------------------- */
    1758             :     /*      Do we have a special overview file?  If not, do we have         */
    1759             :     /*      RSets that should be treated as an overview file?               */
    1760             :     /* -------------------------------------------------------------------- */
    1761             :     const char *pszOverviewFile =
    1762         760 :         poDS->GetMetadataItem("OVERVIEW_FILE", "OVERVIEWS");
    1763             : 
    1764         760 :     if (pszOverviewFile == nullptr)
    1765             :     {
    1766         752 :         if (poDS->CheckForRSets(pszFilename, poOpenInfo->GetSiblingFiles()))
    1767           3 :             pszOverviewFile = poDS->osRSetVRT;
    1768             :     }
    1769             : 
    1770             :     /* -------------------------------------------------------------------- */
    1771             :     /*      If we have jpeg or jpeg2000 bands we may need to set the        */
    1772             :     /*      overview file on their dataset. (#3276)                         */
    1773             :     /* -------------------------------------------------------------------- */
    1774         760 :     GDALDataset *poSubDS = poDS->poJ2KDataset.get();
    1775         760 :     if (poDS->poJPEGDataset)
    1776          18 :         poSubDS = poDS->poJPEGDataset.get();
    1777             : 
    1778         760 :     if (poSubDS && pszOverviewFile != nullptr)
    1779             :     {
    1780           2 :         poSubDS->SetMetadataItem("OVERVIEW_FILE", pszOverviewFile, "OVERVIEWS");
    1781             :     }
    1782             : 
    1783             :     /* -------------------------------------------------------------------- */
    1784             :     /*      If we have jpeg, or jpeg2000 bands we may need to clear         */
    1785             :     /*      their PAM dirty flag too.                                       */
    1786             :     /* -------------------------------------------------------------------- */
    1787         797 :     if (poDS->poJ2KDataset != nullptr &&
    1788          37 :         (poDS->poJ2KDataset->GetMOFlags() & GMO_PAM_CLASS))
    1789             :         (cpl::down_cast<GDALPamDataset *>(poDS->poJ2KDataset.get()))
    1790          72 :             ->SetPamFlags(
    1791             :                 (cpl::down_cast<GDALPamDataset *>(poDS->poJ2KDataset.get()))
    1792          36 :                     ->GetPamFlags() &
    1793             :                 ~GPF_DIRTY);
    1794         778 :     if (poDS->poJPEGDataset != nullptr &&
    1795          18 :         (poDS->poJPEGDataset->GetMOFlags() & GMO_PAM_CLASS))
    1796             :         (cpl::down_cast<GDALPamDataset *>(poDS->poJPEGDataset.get()))
    1797          36 :             ->SetPamFlags(
    1798             :                 (cpl::down_cast<GDALPamDataset *>(poDS->poJPEGDataset.get()))
    1799          18 :                     ->GetPamFlags() &
    1800             :                 ~GPF_DIRTY);
    1801             : 
    1802             :     /* -------------------------------------------------------------------- */
    1803             :     /*      Check for overviews.                                            */
    1804             :     /* -------------------------------------------------------------------- */
    1805         760 :     if (!EQUAL(poOpenInfo->pszFilename, pszFilename))
    1806          20 :         poDS->oOvManager.Initialize(poDS, ":::VIRTUAL:::");
    1807             :     else
    1808        1480 :         poDS->oOvManager.Initialize(poDS, pszFilename,
    1809         740 :                                     poOpenInfo->GetSiblingFiles());
    1810             : 
    1811             :     /* If there are PAM overviews, don't expose the underlying JPEG dataset */
    1812             :     /* overviews (in case of monoblock C3) */
    1813         760 :     if (poDS->GetRasterCount() > 0 && poDS->GetRasterBand(1) != nullptr)
    1814         753 :         poDS->bExposeUnderlyingJPEGDatasetOverviews =
    1815         753 :             (reinterpret_cast<GDALPamRasterBand *>(poDS->GetRasterBand(1)))
    1816         753 :                 ->GDALPamRasterBand::GetOverviewCount() == 0;
    1817             : 
    1818         760 :     if (CPLFetchBool(poOpenInfo->papszOpenOptions, "VALIDATE", false))
    1819             :     {
    1820           8 :         if (!poDS->Validate() &&
    1821           4 :             CPLFetchBool(poOpenInfo->papszOpenOptions,
    1822             :                          "FAIL_IF_VALIDATION_ERROR", false))
    1823             :         {
    1824           2 :             delete poDS;
    1825           2 :             poDS = nullptr;
    1826             :         }
    1827             :     }
    1828             : 
    1829         760 :     return poDS;
    1830             : }
    1831             : 
    1832             : /************************************************************************/
    1833             : /*                              Validate()                              */
    1834             : /************************************************************************/
    1835             : 
    1836           4 : bool NITFDataset::Validate()
    1837             : {
    1838           4 :     bool bSuccess = InitializeTREMetadata(true);
    1839           4 :     if (!InitializeNITFDESs(true))
    1840           2 :         bSuccess = false;
    1841           4 :     return bSuccess;
    1842             : }
    1843             : 
    1844             : /************************************************************************/
    1845             : /*                            LoadDODDatum()                            */
    1846             : /*                                                                      */
    1847             : /*      Try to turn a US military datum name into a datum definition.   */
    1848             : /************************************************************************/
    1849             : 
    1850           2 : static OGRErr LoadDODDatum(OGRSpatialReference *poSRS, const char *pszDatumName)
    1851             : 
    1852             : {
    1853             :     /* -------------------------------------------------------------------- */
    1854             :     /*      The most common case...                                         */
    1855             :     /* -------------------------------------------------------------------- */
    1856           2 :     if (STARTS_WITH_CI(pszDatumName, "WGE "))
    1857             :     {
    1858           0 :         poSRS->SetWellKnownGeogCS("WGS84");
    1859           0 :         return OGRERR_NONE;
    1860             :     }
    1861             : 
    1862             : #if defined(USE_ONLY_EMBEDDED_RESOURCE_FILES) && !defined(EMBED_RESOURCE_FILES)
    1863             :     return OGRERR_FAILURE;
    1864             : #else
    1865             : 
    1866             :     /* -------------------------------------------------------------------- */
    1867             :     /*      All the rest we will try and load from gt_datum.csv             */
    1868             :     /*      (Geotrans datum file).                                          */
    1869             :     /* -------------------------------------------------------------------- */
    1870             :     char szExpanded[6];
    1871           2 :     const char *pszGTDatum = nullptr;
    1872           2 :     CPL_IGNORE_RET_VAL(pszGTDatum);
    1873             : #ifndef USE_ONLY_EMBEDDED_RESOURCE_FILES
    1874           2 :     pszGTDatum = CSVFilename("gt_datum.csv");
    1875             : #endif
    1876             : #ifdef EMBED_RESOURCE_FILES
    1877             :     std::string osTmpFilename;
    1878             :     // CSVFilename() returns the same content as pszFilename if it does not
    1879             :     // find the file.
    1880             :     if (!pszGTDatum || strcmp(pszGTDatum, "gt_datum.csv") == 0)
    1881             :     {
    1882             :         osTmpFilename = VSIMemGenerateHiddenFilename("gt_datum.csv");
    1883             :         const char *pszFileContent = NITFGetGTDatum();
    1884             :         VSIFCloseL(VSIFileFromMemBuffer(
    1885             :             osTmpFilename.c_str(),
    1886             :             const_cast<GByte *>(
    1887             :                 reinterpret_cast<const GByte *>(pszFileContent)),
    1888             :             static_cast<int>(strlen(pszFileContent)),
    1889             :             /* bTakeOwnership = */ false));
    1890             :         pszGTDatum = osTmpFilename.c_str();
    1891             :     }
    1892             : #endif
    1893             : 
    1894           2 :     strncpy(szExpanded, pszDatumName, 3);
    1895           2 :     szExpanded[3] = '\0';
    1896           2 :     if (pszDatumName[3] != ' ')
    1897             :     {
    1898             :         size_t nLen;
    1899           2 :         strcat(szExpanded, "-");
    1900           2 :         nLen = strlen(szExpanded);
    1901           2 :         szExpanded[nLen] = pszDatumName[3];
    1902           2 :         szExpanded[nLen + 1] = '\0';
    1903             :     }
    1904             : 
    1905             :     CPLString osDName =
    1906           4 :         CSVGetField(pszGTDatum, "CODE", szExpanded, CC_ApproxString, "NAME");
    1907           2 :     if (osDName.empty())
    1908             :     {
    1909           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1910             :                  "Failed to find datum %s/%s in gt_datum.csv.", pszDatumName,
    1911             :                  szExpanded);
    1912             : 
    1913             : #ifdef EMBED_RESOURCE_FILES
    1914             :         if (!osTmpFilename.empty())
    1915             :         {
    1916             :             CSVDeaccess(osTmpFilename.c_str());
    1917             :             VSIUnlink(osTmpFilename.c_str());
    1918             :         }
    1919             : #endif
    1920           0 :         return OGRERR_FAILURE;
    1921             :     }
    1922             : 
    1923             :     CPLString osEllipseCode = CSVGetField(pszGTDatum, "CODE", szExpanded,
    1924           4 :                                           CC_ApproxString, "ELLIPSOID");
    1925           2 :     double dfDeltaX = CPLAtof(
    1926             :         CSVGetField(pszGTDatum, "CODE", szExpanded, CC_ApproxString, "DELTAX"));
    1927           2 :     double dfDeltaY = CPLAtof(
    1928             :         CSVGetField(pszGTDatum, "CODE", szExpanded, CC_ApproxString, "DELTAY"));
    1929           2 :     double dfDeltaZ = CPLAtof(
    1930             :         CSVGetField(pszGTDatum, "CODE", szExpanded, CC_ApproxString, "DELTAZ"));
    1931             : 
    1932             : #ifdef EMBED_RESOURCE_FILES
    1933             :     if (!osTmpFilename.empty())
    1934             :     {
    1935             :         CSVDeaccess(osTmpFilename.c_str());
    1936             :         VSIUnlink(osTmpFilename.c_str());
    1937             :         osTmpFilename.clear();
    1938             :     }
    1939             : #endif
    1940             : 
    1941             :     /* -------------------------------------------------------------------- */
    1942             :     /*      Lookup the ellipse code.                                        */
    1943             :     /* -------------------------------------------------------------------- */
    1944           2 :     const char *pszGTEllipse = nullptr;
    1945           2 :     CPL_IGNORE_RET_VAL(pszGTEllipse);
    1946             : #ifndef USE_ONLY_EMBEDDED_RESOURCE_FILES
    1947           2 :     pszGTEllipse = CSVFilename("gt_ellips.csv");
    1948             : #endif
    1949             : 
    1950             : #ifdef EMBED_RESOURCE_FILES
    1951             :     // CSVFilename() returns the same content as pszFilename if it does not
    1952             :     // find the file.
    1953             :     if (!pszGTEllipse || strcmp(pszGTEllipse, "gt_ellips.csv") == 0)
    1954             :     {
    1955             :         osTmpFilename = VSIMemGenerateHiddenFilename("gt_ellips");
    1956             :         const char *pszFileContent = NITFGetGTEllips();
    1957             :         VSIFCloseL(VSIFileFromMemBuffer(
    1958             :             osTmpFilename.c_str(),
    1959             :             const_cast<GByte *>(
    1960             :                 reinterpret_cast<const GByte *>(pszFileContent)),
    1961             :             static_cast<int>(strlen(pszFileContent)),
    1962             :             /* bTakeOwnership = */ false));
    1963             :         pszGTEllipse = osTmpFilename.c_str();
    1964             :     }
    1965             : #endif
    1966             : 
    1967             :     CPLString osEName = CSVGetField(pszGTEllipse, "CODE", osEllipseCode,
    1968           4 :                                     CC_ApproxString, "NAME");
    1969           2 :     osEName = osEName.Trim();
    1970           2 :     if (osEName.empty())
    1971             :     {
    1972           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1973             :                  "Failed to find datum %s in gt_ellips.csv.",
    1974             :                  osEllipseCode.c_str());
    1975             : 
    1976             : #ifdef EMBED_RESOURCE_FILES
    1977             :         if (!osTmpFilename.empty())
    1978             :         {
    1979             :             CSVDeaccess(osTmpFilename.c_str());
    1980             :             VSIUnlink(osTmpFilename.c_str());
    1981             :         }
    1982             : #endif
    1983           0 :         return OGRERR_FAILURE;
    1984             :     }
    1985             : 
    1986           2 :     double dfA = CPLAtof(
    1987             :         CSVGetField(pszGTEllipse, "CODE", osEllipseCode, CC_ApproxString, "A"));
    1988           2 :     double dfInvF = CPLAtof(CSVGetField(pszGTEllipse, "CODE", osEllipseCode,
    1989             :                                         CC_ApproxString, "RF"));
    1990             : 
    1991             :     /* -------------------------------------------------------------------- */
    1992             :     /*      Create geographic coordinate system.                            */
    1993             :     /* -------------------------------------------------------------------- */
    1994           2 :     poSRS->SetGeogCS(osDName, osDName, osEName, dfA, dfInvF);
    1995             : 
    1996           2 :     poSRS->SetTOWGS84(dfDeltaX, dfDeltaY, dfDeltaZ);
    1997             : 
    1998             : #ifdef EMBED_RESOURCE_FILES
    1999             :     if (!osTmpFilename.empty())
    2000             :     {
    2001             :         CSVDeaccess(osTmpFilename.c_str());
    2002             :         VSIUnlink(osTmpFilename.c_str());
    2003             :         osTmpFilename.clear();
    2004             :     }
    2005             : #endif
    2006             : 
    2007           2 :     return OGRERR_NONE;
    2008             : #endif
    2009             : }
    2010             : 
    2011             : /************************************************************************/
    2012             : /*                          CheckGeoSDEInfo()                           */
    2013             : /*                                                                      */
    2014             : /*      Check for GeoSDE TREs (GEOPSB/PRJPSB and MAPLOB).  If we        */
    2015             : /*      have them, use them to override our coordinate system and       */
    2016             : /*      geotransform info.                                              */
    2017             : /************************************************************************/
    2018             : 
    2019         753 : void NITFDataset::CheckGeoSDEInfo()
    2020             : 
    2021             : {
    2022         753 :     if (!psImage)
    2023         751 :         return;
    2024             : 
    2025             :     /* -------------------------------------------------------------------- */
    2026             :     /*      Do we have the required TREs?                                   */
    2027             :     /* -------------------------------------------------------------------- */
    2028             :     int nGEOPSBSize, nPRJPSBSize, nMAPLOBSize;
    2029             : 
    2030             :     const char *pszGEOPSB =
    2031         753 :         NITFFindTRE(psFile->pachTRE, psFile->nTREBytes, "GEOPSB", &nGEOPSBSize);
    2032             :     const char *pszPRJPSB =
    2033         753 :         NITFFindTRE(psFile->pachTRE, psFile->nTREBytes, "PRJPSB", &nPRJPSBSize);
    2034         753 :     const char *pszMAPLOB = NITFFindTRE(psImage->pachTRE, psImage->nTREBytes,
    2035             :                                         "MAPLOB", &nMAPLOBSize);
    2036             : 
    2037         753 :     if (pszGEOPSB == nullptr || pszPRJPSB == nullptr || pszMAPLOB == nullptr)
    2038         751 :         return;
    2039             : 
    2040             :     /* -------------------------------------------------------------------- */
    2041             :     /*      Collect projection parameters.                                  */
    2042             :     /* -------------------------------------------------------------------- */
    2043             : 
    2044             :     char szParam[16];
    2045           2 :     if (nPRJPSBSize < 82 + 1)
    2046             :     {
    2047           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2048             :                  "Cannot read PRJPSB TRE. Not enough bytes");
    2049           0 :         return;
    2050             :     }
    2051           2 :     const int nParamCount = atoi(NITFGetField(szParam, pszPRJPSB, 82, 1));
    2052           2 :     if (nPRJPSBSize < 83 + 15 * nParamCount + 15 + 15)
    2053             :     {
    2054           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2055             :                  "Cannot read PRJPSB TRE. Not enough bytes");
    2056           0 :         return;
    2057             :     }
    2058             : 
    2059           2 :     double adfParam[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
    2060           2 :     for (int i = 0; i < nParamCount; i++)
    2061           0 :         adfParam[i] =
    2062           0 :             CPLAtof(NITFGetField(szParam, pszPRJPSB, 83 + 15 * i, 15));
    2063             : 
    2064             :     const double dfFE =
    2065           2 :         CPLAtof(NITFGetField(szParam, pszPRJPSB, 83 + 15 * nParamCount, 15));
    2066           4 :     const double dfFN = CPLAtof(
    2067           2 :         NITFGetField(szParam, pszPRJPSB, 83 + 15 * nParamCount + 15, 15));
    2068             : 
    2069             :     /* -------------------------------------------------------------------- */
    2070             :     /*      Try to handle the projection.                                   */
    2071             :     /* -------------------------------------------------------------------- */
    2072           2 :     OGRSpatialReference oSRS;
    2073           2 :     oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    2074             : 
    2075           2 :     if (STARTS_WITH_CI(pszPRJPSB + 80, "AC"))
    2076           2 :         oSRS.SetACEA(adfParam[1], adfParam[2], adfParam[3], adfParam[0], dfFE,
    2077             :                      dfFN);
    2078             : 
    2079           0 :     else if (STARTS_WITH_CI(pszPRJPSB + 80, "AK"))
    2080           0 :         oSRS.SetLAEA(adfParam[1], adfParam[0], dfFE, dfFN);
    2081             : 
    2082           0 :     else if (STARTS_WITH_CI(pszPRJPSB + 80, "AL"))
    2083           0 :         oSRS.SetAE(adfParam[1], adfParam[0], dfFE, dfFN);
    2084             : 
    2085           0 :     else if (STARTS_WITH_CI(pszPRJPSB + 80, "BF"))
    2086           0 :         oSRS.SetBonne(adfParam[1], adfParam[0], dfFE, dfFN);
    2087             : 
    2088           0 :     else if (STARTS_WITH_CI(pszPRJPSB + 80, "CP"))
    2089           0 :         oSRS.SetEquirectangular(adfParam[1], adfParam[0], dfFE, dfFN);
    2090             : 
    2091           0 :     else if (STARTS_WITH_CI(pszPRJPSB + 80, "CS"))
    2092           0 :         oSRS.SetCS(adfParam[1], adfParam[0], dfFE, dfFN);
    2093             : 
    2094           0 :     else if (STARTS_WITH_CI(pszPRJPSB + 80, "EF"))
    2095           0 :         oSRS.SetEckertIV(adfParam[0], dfFE, dfFN);
    2096             : 
    2097           0 :     else if (STARTS_WITH_CI(pszPRJPSB + 80, "ED"))
    2098           0 :         oSRS.SetEckertVI(adfParam[0], dfFE, dfFN);
    2099             : 
    2100           0 :     else if (STARTS_WITH_CI(pszPRJPSB + 80, "GN"))
    2101           0 :         oSRS.SetGnomonic(adfParam[1], adfParam[0], dfFE, dfFN);
    2102             : 
    2103           0 :     else if (STARTS_WITH_CI(pszPRJPSB + 80, "HX"))
    2104           0 :         oSRS.SetHOM2PNO(adfParam[1], adfParam[3], adfParam[2], adfParam[5],
    2105             :                         adfParam[4], adfParam[0], dfFE, dfFN);
    2106             : 
    2107           0 :     else if (STARTS_WITH_CI(pszPRJPSB + 80, "KA"))
    2108           0 :         oSRS.SetEC(adfParam[1], adfParam[2], adfParam[3], adfParam[0], dfFE,
    2109             :                    dfFN);
    2110             : 
    2111           0 :     else if (STARTS_WITH_CI(pszPRJPSB + 80, "LE"))
    2112           0 :         oSRS.SetLCC(adfParam[1], adfParam[2], adfParam[3], adfParam[0], dfFE,
    2113             :                     dfFN);
    2114             : 
    2115           0 :     else if (STARTS_WITH_CI(pszPRJPSB + 80, "LI"))
    2116           0 :         oSRS.SetCEA(adfParam[1], adfParam[0], dfFE, dfFN);
    2117             : 
    2118           0 :     else if (STARTS_WITH_CI(pszPRJPSB + 80, "MC"))
    2119           0 :         oSRS.SetMercator(adfParam[2], adfParam[1], 1.0, dfFE, dfFN);
    2120             : 
    2121           0 :     else if (STARTS_WITH_CI(pszPRJPSB + 80, "MH"))
    2122           0 :         oSRS.SetMC(0.0, adfParam[1], dfFE, dfFN);
    2123             : 
    2124           0 :     else if (STARTS_WITH_CI(pszPRJPSB + 80, "MP"))
    2125           0 :         oSRS.SetMollweide(adfParam[0], dfFE, dfFN);
    2126             : 
    2127           0 :     else if (STARTS_WITH_CI(pszPRJPSB + 80, "NT"))
    2128           0 :         oSRS.SetNZMG(adfParam[1], adfParam[0], dfFE, dfFN);
    2129             : 
    2130           0 :     else if (STARTS_WITH_CI(pszPRJPSB + 80, "OD"))
    2131           0 :         oSRS.SetOrthographic(adfParam[1], adfParam[0], dfFE, dfFN);
    2132             : 
    2133           0 :     else if (STARTS_WITH_CI(pszPRJPSB + 80, "PC"))
    2134           0 :         oSRS.SetPolyconic(adfParam[1], adfParam[0], dfFE, dfFN);
    2135             : 
    2136           0 :     else if (STARTS_WITH_CI(pszPRJPSB + 80, "PG"))
    2137           0 :         oSRS.SetPS(adfParam[1], adfParam[0], 1.0, dfFE, dfFN);
    2138             : 
    2139           0 :     else if (STARTS_WITH_CI(pszPRJPSB + 80, "RX"))
    2140           0 :         oSRS.SetRobinson(adfParam[0], dfFE, dfFN);
    2141             : 
    2142           0 :     else if (STARTS_WITH_CI(pszPRJPSB + 80, "SA"))
    2143           0 :         oSRS.SetSinusoidal(adfParam[0], dfFE, dfFN);
    2144             : 
    2145           0 :     else if (STARTS_WITH_CI(pszPRJPSB + 80, "TC"))
    2146           0 :         oSRS.SetTM(adfParam[2], adfParam[0], adfParam[1], dfFE, dfFN);
    2147             : 
    2148           0 :     else if (STARTS_WITH_CI(pszPRJPSB + 80, "VA"))
    2149           0 :         oSRS.SetVDG(adfParam[0], dfFE, dfFN);
    2150             : 
    2151             :     else
    2152             :     {
    2153             :         char szName[81];
    2154           0 :         oSRS.SetLocalCS(NITFGetField(szName, pszPRJPSB, 0, 80));
    2155             :     }
    2156             : 
    2157             :     /* -------------------------------------------------------------------- */
    2158             :     /*      Try to apply the datum.                                         */
    2159             :     /* -------------------------------------------------------------------- */
    2160           2 :     if (nGEOPSBSize < 86 + 4)
    2161             :     {
    2162           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2163             :                  "Cannot read GEOPSB TRE. Not enough bytes");
    2164           0 :         return;
    2165             :     }
    2166           2 :     LoadDODDatum(&oSRS, NITFGetField(szParam, pszGEOPSB, 86, 4));
    2167             : 
    2168             :     /* -------------------------------------------------------------------- */
    2169             :     /*      Get the geotransform                                            */
    2170             :     /* -------------------------------------------------------------------- */
    2171           2 :     if (nMAPLOBSize < 28 + 15)
    2172             :     {
    2173           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2174             :                  "Cannot read MAPLOB TRE. Not enough bytes");
    2175           0 :         return;
    2176             :     }
    2177             : 
    2178           2 :     double dfMeterPerUnit = 1.0;
    2179           2 :     if (STARTS_WITH_CI(pszMAPLOB + 0, "DM "))
    2180           0 :         dfMeterPerUnit = 0.1;
    2181           2 :     else if (STARTS_WITH_CI(pszMAPLOB + 0, "CM "))
    2182           0 :         dfMeterPerUnit = 0.01;
    2183           2 :     else if (STARTS_WITH_CI(pszMAPLOB + 0, "MM "))
    2184           0 :         dfMeterPerUnit = 0.001;
    2185           2 :     else if (STARTS_WITH_CI(pszMAPLOB + 0, "UM "))
    2186           0 :         dfMeterPerUnit = 0.000001;
    2187           2 :     else if (STARTS_WITH_CI(pszMAPLOB + 0, "KM "))
    2188           0 :         dfMeterPerUnit = 1000.0;
    2189           2 :     else if (STARTS_WITH_CI(pszMAPLOB + 0, "M  "))
    2190           2 :         dfMeterPerUnit = 1.0;
    2191             :     else
    2192             :     {
    2193           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    2194             :                  "MAPLOB Unit=%3.3s not recognized, geolocation may be wrong.",
    2195             :                  pszMAPLOB + 0);
    2196             :     }
    2197             : 
    2198           2 :     m_gt.xorig = CPLAtof(NITFGetField(szParam, pszMAPLOB, 13, 15));
    2199           2 :     m_gt.xscale =
    2200           2 :         CPLAtof(NITFGetField(szParam, pszMAPLOB, 3, 5)) * dfMeterPerUnit;
    2201           2 :     m_gt.xrot = 0.0;
    2202           2 :     m_gt.yorig = CPLAtof(NITFGetField(szParam, pszMAPLOB, 28, 15));
    2203           2 :     m_gt.yrot = 0.0;
    2204           2 :     m_gt.yscale =
    2205           2 :         -CPLAtof(NITFGetField(szParam, pszMAPLOB, 8, 5)) * dfMeterPerUnit;
    2206             : 
    2207           2 :     m_oSRS = std::move(oSRS);
    2208             : 
    2209           2 :     bGotGeoTransform = TRUE;
    2210             : }
    2211             : 
    2212             : /************************************************************************/
    2213             : /*                             AdviseRead()                             */
    2214             : /************************************************************************/
    2215             : 
    2216          19 : CPLErr NITFDataset::AdviseRead(int nXOff, int nYOff, int nXSize, int nYSize,
    2217             :                                int nBufXSize, int nBufYSize, GDALDataType eDT,
    2218             :                                int nBandCount, int *panBandList,
    2219             :                                CSLConstList papszOptions)
    2220             : 
    2221             : {
    2222             :     //go through GDALDataset::AdviseRead for the complex SAR
    2223          19 :     if (poJ2KDataset == nullptr || m_bHasComplexRasterBand)
    2224          17 :         return GDALDataset::AdviseRead(nXOff, nYOff, nXSize, nYSize, nBufXSize,
    2225             :                                        nBufYSize, eDT, nBandCount, panBandList,
    2226          17 :                                        papszOptions);
    2227           2 :     else if (poJPEGDataset != nullptr)
    2228           0 :         return poJPEGDataset->AdviseRead(nXOff, nYOff, nXSize, nYSize,
    2229             :                                          nBufXSize, nBufYSize, eDT, nBandCount,
    2230           0 :                                          panBandList, papszOptions);
    2231             :     else
    2232           4 :         return poJ2KDataset->AdviseRead(nXOff, nYOff, nXSize, nYSize, nBufXSize,
    2233             :                                         nBufYSize, eDT, nBandCount, panBandList,
    2234           2 :                                         papszOptions);
    2235             : }
    2236             : 
    2237             : /************************************************************************/
    2238             : /*                             IRasterIO()                              */
    2239             : /************************************************************************/
    2240             : 
    2241        1206 : CPLErr NITFDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
    2242             :                               int nXSize, int nYSize, void *pData,
    2243             :                               int nBufXSize, int nBufYSize,
    2244             :                               GDALDataType eBufType, int nBandCount,
    2245             :                               BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
    2246             :                               GSpacing nLineSpace, GSpacing nBandSpace,
    2247             :                               GDALRasterIOExtraArg *psExtraArg)
    2248             : 
    2249             : {
    2250             :     //go through GDALDataset::IRasterIO for the complex SAR
    2251        1206 :     if (poJ2KDataset != nullptr && !m_bHasComplexRasterBand)
    2252         101 :         return poJ2KDataset->RasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    2253             :                                       pData, nBufXSize, nBufYSize, eBufType,
    2254             :                                       nBandCount, panBandMap, nPixelSpace,
    2255         101 :                                       nLineSpace, nBandSpace, psExtraArg);
    2256        1105 :     else if (poJPEGDataset != nullptr && !m_bHasComplexRasterBand)
    2257          64 :         return poJPEGDataset->RasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    2258             :                                        pData, nBufXSize, nBufYSize, eBufType,
    2259             :                                        nBandCount, panBandMap, nPixelSpace,
    2260          64 :                                        nLineSpace, nBandSpace, psExtraArg);
    2261             :     else
    2262        1041 :         return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    2263             :                                       pData, nBufXSize, nBufYSize, eBufType,
    2264             :                                       nBandCount, panBandMap, nPixelSpace,
    2265        1041 :                                       nLineSpace, nBandSpace, psExtraArg);
    2266             : }
    2267             : 
    2268             : /************************************************************************/
    2269             : /*                          GetGeoTransform()                           */
    2270             : /************************************************************************/
    2271             : 
    2272         381 : CPLErr NITFDataset::GetGeoTransform(GDALGeoTransform &gt) const
    2273             : 
    2274             : {
    2275         381 :     gt = m_gt;
    2276             : 
    2277         381 :     if (bGotGeoTransform)
    2278         363 :         return CE_None;
    2279             : 
    2280          18 :     return GDALPamDataset::GetGeoTransform(gt);
    2281             : }
    2282             : 
    2283             : /************************************************************************/
    2284             : /*                          SetGeoTransform()                           */
    2285             : /************************************************************************/
    2286             : 
    2287         137 : CPLErr NITFDataset::SetGeoTransform(const GDALGeoTransform &gt)
    2288             : 
    2289             : {
    2290         137 :     bGotGeoTransform = TRUE;
    2291         137 :     m_gt = gt;
    2292             : 
    2293         137 :     double dfIGEOLOULX = m_gt.xorig + 0.5 * m_gt.xscale + 0.5 * m_gt.xrot;
    2294         137 :     double dfIGEOLOULY = m_gt.yorig + 0.5 * m_gt.yrot + 0.5 * m_gt.yscale;
    2295         137 :     double dfIGEOLOURX = dfIGEOLOULX + m_gt.xscale * (nRasterXSize - 1);
    2296         137 :     double dfIGEOLOURY = dfIGEOLOULY + m_gt.yrot * (nRasterXSize - 1);
    2297         137 :     double dfIGEOLOLRX = dfIGEOLOULX + m_gt.xscale * (nRasterXSize - 1) +
    2298         137 :                          m_gt.xrot * (nRasterYSize - 1);
    2299         137 :     double dfIGEOLOLRY = dfIGEOLOULY + m_gt.yrot * (nRasterXSize - 1) +
    2300         137 :                          m_gt.yscale * (nRasterYSize - 1);
    2301         137 :     double dfIGEOLOLLX = dfIGEOLOULX + m_gt.xrot * (nRasterYSize - 1);
    2302         137 :     double dfIGEOLOLLY = dfIGEOLOULY + m_gt.yscale * (nRasterYSize - 1);
    2303             : 
    2304         273 :     if (psImage != nullptr &&
    2305         136 :         NITFWriteIGEOLO(psImage, psImage->chICORDS, psImage->nZone, dfIGEOLOULX,
    2306             :                         dfIGEOLOULY, dfIGEOLOURX, dfIGEOLOURY, dfIGEOLOLRX,
    2307             :                         dfIGEOLOLRY, dfIGEOLOLLX, dfIGEOLOLLY))
    2308         117 :         return CE_None;
    2309             : 
    2310          20 :     return GDALPamDataset::SetGeoTransform(gt);
    2311             : }
    2312             : 
    2313             : /************************************************************************/
    2314             : /*                              SetGCPs()                               */
    2315             : /************************************************************************/
    2316             : 
    2317           3 : CPLErr NITFDataset::SetGCPs(int nGCPCountIn, const GDAL_GCP *pasGCPListIn,
    2318             :                             const OGRSpatialReference *poGCPSRSIn)
    2319             : {
    2320           3 :     if (nGCPCountIn != 4)
    2321             :     {
    2322           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    2323             :                  "NITF only supports writing 4 GCPs.");
    2324           0 :         return CE_Failure;
    2325             :     }
    2326             : 
    2327             :     /* Free previous GCPs */
    2328           3 :     GDALDeinitGCPs(nGCPCount, pasGCPList);
    2329           3 :     CPLFree(pasGCPList);
    2330             : 
    2331             :     /* Duplicate in GCPs */
    2332           3 :     nGCPCount = nGCPCountIn;
    2333           3 :     pasGCPList = GDALDuplicateGCPs(nGCPCount, pasGCPListIn);
    2334             : 
    2335           3 :     m_oGCPSRS.Clear();
    2336           3 :     if (poGCPSRSIn)
    2337           3 :         m_oGCPSRS = *poGCPSRSIn;
    2338             : 
    2339           3 :     int iUL = -1;
    2340           3 :     int iUR = -1;
    2341           3 :     int iLR = -1;
    2342           3 :     int iLL = -1;
    2343             : 
    2344             : #define EPS_GCP 1e-5
    2345          15 :     for (int i = 0; i < 4; i++)
    2346             :     {
    2347          12 :         if (fabs(pasGCPList[i].dfGCPPixel - 0.5) < EPS_GCP &&
    2348           6 :             fabs(pasGCPList[i].dfGCPLine - 0.5) < EPS_GCP)
    2349           3 :             iUL = i;
    2350             : 
    2351           9 :         else if (fabs(pasGCPList[i].dfGCPPixel - (nRasterXSize - 0.5)) <
    2352           6 :                      EPS_GCP &&
    2353           6 :                  fabs(pasGCPList[i].dfGCPLine - 0.5) < EPS_GCP)
    2354           3 :             iUR = i;
    2355             : 
    2356           6 :         else if (fabs(pasGCPList[i].dfGCPPixel - (nRasterXSize - 0.5)) <
    2357           3 :                      EPS_GCP &&
    2358           3 :                  fabs(pasGCPList[i].dfGCPLine - (nRasterYSize - 0.5)) < EPS_GCP)
    2359           3 :             iLR = i;
    2360             : 
    2361           3 :         else if (fabs(pasGCPList[i].dfGCPPixel - 0.5) < EPS_GCP &&
    2362           3 :                  fabs(pasGCPList[i].dfGCPLine - (nRasterYSize - 0.5)) < EPS_GCP)
    2363           3 :             iLL = i;
    2364             :     }
    2365             : 
    2366           3 :     if (iUL < 0 || iUR < 0 || iLR < 0 || iLL < 0)
    2367             :     {
    2368           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    2369             :                  "The 4 GCPs image coordinates must be exactly "
    2370             :                  "at the *center* of the 4 corners of the image "
    2371             :                  "( (%.1f, %.1f), (%.1f %.1f), (%.1f %.1f), (%.1f %.1f) ).",
    2372           0 :                  0.5, 0.5, nRasterYSize - 0.5, 0.5, nRasterXSize - 0.5,
    2373           0 :                  nRasterYSize - 0.5, nRasterXSize - 0.5, 0.5);
    2374           0 :         return CE_Failure;
    2375             :     }
    2376             : 
    2377           3 :     double dfIGEOLOULX = pasGCPList[iUL].dfGCPX;
    2378           3 :     double dfIGEOLOULY = pasGCPList[iUL].dfGCPY;
    2379           3 :     double dfIGEOLOURX = pasGCPList[iUR].dfGCPX;
    2380           3 :     double dfIGEOLOURY = pasGCPList[iUR].dfGCPY;
    2381           3 :     double dfIGEOLOLRX = pasGCPList[iLR].dfGCPX;
    2382           3 :     double dfIGEOLOLRY = pasGCPList[iLR].dfGCPY;
    2383           3 :     double dfIGEOLOLLX = pasGCPList[iLL].dfGCPX;
    2384           3 :     double dfIGEOLOLLY = pasGCPList[iLL].dfGCPY;
    2385             : 
    2386             :     /* To recompute the zone */
    2387           6 :     OGRSpatialReference oSRSBackup = m_oSRS;
    2388           3 :     CPLErr eErr = SetSpatialRef(&m_oGCPSRS);
    2389           3 :     m_oSRS = std::move(oSRSBackup);
    2390             : 
    2391           3 :     if (eErr != CE_None)
    2392           0 :         return eErr;
    2393             : 
    2394           3 :     if (NITFWriteIGEOLO(psImage, psImage->chICORDS, psImage->nZone, dfIGEOLOULX,
    2395             :                         dfIGEOLOULY, dfIGEOLOURX, dfIGEOLOURY, dfIGEOLOLRX,
    2396           3 :                         dfIGEOLOLRY, dfIGEOLOLLX, dfIGEOLOLLY))
    2397           3 :         return CE_None;
    2398             : 
    2399           0 :     return CE_Failure;
    2400             : }
    2401             : 
    2402             : /************************************************************************/
    2403             : /*                           GetSpatialRef()                            */
    2404             : /************************************************************************/
    2405             : 
    2406         299 : const OGRSpatialReference *NITFDataset::GetSpatialRef() const
    2407             : 
    2408             : {
    2409         299 :     if (bGotGeoTransform)
    2410         285 :         return &m_oSRS;
    2411             : 
    2412          14 :     return GDALPamDataset::GetSpatialRef();
    2413             : }
    2414             : 
    2415             : /************************************************************************/
    2416             : /*                           SetSpatialRef()                            */
    2417             : /************************************************************************/
    2418             : 
    2419          32 : CPLErr NITFDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
    2420             : 
    2421             : {
    2422             :     int bNorth;
    2423          64 :     OGRSpatialReference oSRS, oSRS_WGS84;
    2424             : 
    2425          32 :     if (poSRS == nullptr)
    2426           0 :         return CE_Failure;
    2427             : 
    2428          32 :     oSRS_WGS84.SetWellKnownGeogCS("WGS84");
    2429          32 :     if (poSRS->IsSameGeogCS(&oSRS_WGS84) == FALSE)
    2430             :     {
    2431           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    2432             :                  "NITF only supports WGS84 geographic and UTM projections.");
    2433           0 :         return CE_Failure;
    2434             :     }
    2435             : 
    2436          32 :     if (poSRS->IsGeographic() && poSRS->GetPrimeMeridian() == 0.0)
    2437             :     {
    2438          30 :         if (psImage->chICORDS != 'G' && psImage->chICORDS != 'D')
    2439             :         {
    2440          19 :             CPLError(CE_Failure, CPLE_NotSupported,
    2441             :                      "NITF file should have been created with creation option "
    2442             :                      "'ICORDS=G' (or 'ICORDS=D').");
    2443          19 :             return CE_Failure;
    2444             :         }
    2445             :     }
    2446           2 :     else if (poSRS->GetUTMZone(&bNorth) > 0)
    2447             :     {
    2448           1 :         if (bNorth && psImage->chICORDS != 'N')
    2449             :         {
    2450           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    2451             :                      "NITF file should have been created with creation option "
    2452             :                      "'ICORDS=N'.");
    2453           0 :             return CE_Failure;
    2454             :         }
    2455           1 :         else if (!bNorth && psImage->chICORDS != 'S')
    2456             :         {
    2457           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    2458             :                      "NITF file should have been created with creation option "
    2459             :                      "'ICORDS=S'.");
    2460           0 :             return CE_Failure;
    2461             :         }
    2462             : 
    2463           1 :         psImage->nZone = poSRS->GetUTMZone(nullptr);
    2464             :     }
    2465             :     else
    2466             :     {
    2467           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    2468             :                  "NITF only supports WGS84 geographic and UTM projections.");
    2469           1 :         return CE_Failure;
    2470             :     }
    2471             : 
    2472          12 :     m_oSRS = *poSRS;
    2473             : 
    2474          12 :     if (bGotGeoTransform)
    2475           9 :         SetGeoTransform(m_gt);
    2476             : 
    2477          12 :     return CE_None;
    2478             : }
    2479             : 
    2480             : #ifdef ESRI_BUILD
    2481             : /************************************************************************/
    2482             : /*                     InitializeNITFDESMetadata()                      */
    2483             : /************************************************************************/
    2484             : 
    2485             : void NITFDataset::InitializeNITFDESMetadata()
    2486             : {
    2487             :     static const char *const pszDESMetadataDomain = "NITF_DES_METADATA";
    2488             :     static const char *const pszDESsDomain = "xml:DES";
    2489             :     static const char *const pszMDXmlDataContentDESDATA =
    2490             :         "NITF_DES_XML_DATA_CONTENT_DESDATA";
    2491             :     static const char *const pszXmlDataContent = "XML_DATA_CONTENT";
    2492             :     constexpr int idxXmlDataContentDESDATA = 973;
    2493             :     static const int sizeXmlDataContent =
    2494             :         static_cast<int>(strlen(pszXmlDataContent));
    2495             : 
    2496             :     char **ppszDESMetadataList = oSpecialMD.GetMetadata(pszDESMetadataDomain);
    2497             : 
    2498             :     if (ppszDESMetadataList != NULL)
    2499             :         return;
    2500             : 
    2501             :     char **ppszDESsList = this->GetMetadata(pszDESsDomain);
    2502             : 
    2503             :     if (ppszDESsList == NULL)
    2504             :         return;
    2505             : 
    2506             :     bool foundXmlDataContent = false;
    2507             :     char *pachNITFDES = NULL;
    2508             : 
    2509             :     // Set metadata "NITF_DES_XML_DATA_CONTENT_DESDATA".
    2510             :     // NOTE: There should only be one instance of XML_DATA_CONTENT DES.
    2511             : 
    2512             :     while (((pachNITFDES = *ppszDESsList) != NULL) && (!foundXmlDataContent))
    2513             :     {
    2514             :         // The data stream has been Base64 encoded, need to decode it.
    2515             :         // NOTE: The actual length of the DES data stream is appended at the
    2516             :         // beginning of the encoded
    2517             :         //       data and is separated by a space.
    2518             : 
    2519             :         const char *pszSpace = strchr(pachNITFDES, ' ');
    2520             : 
    2521             :         char *pszData = NULL;
    2522             :         int nDataLen = 0;
    2523             :         if (pszSpace)
    2524             :         {
    2525             :             pszData = CPLStrdup(pszSpace + 1);
    2526             :             nDataLen =
    2527             :                 CPLBase64DecodeInPlace(reinterpret_cast<GByte *>(pszData));
    2528             :             pszData[nDataLen] = 0;
    2529             :         }
    2530             : 
    2531             :         if (nDataLen > 2 + sizeXmlDataContent && STARTS_WITH_CI(pszData, "DE"))
    2532             :         {
    2533             :             // Check to see if this is a XML_DATA_CONTENT DES.
    2534             :             if (EQUALN(pszData + 2, pszXmlDataContent, sizeXmlDataContent) &&
    2535             :                 nDataLen > idxXmlDataContentDESDATA)
    2536             :             {
    2537             :                 foundXmlDataContent = true;
    2538             : 
    2539             :                 // Get the value of the DESDATA field and set metadata
    2540             :                 // "NITF_DES_XML_DATA_CONTENT_DESDATA".
    2541             :                 const char *pszXML = pszData + idxXmlDataContentDESDATA;
    2542             : 
    2543             :                 // Set the metadata.
    2544             :                 oSpecialMD.SetMetadataItem(pszMDXmlDataContentDESDATA, pszXML,
    2545             :                                            pszDESMetadataDomain);
    2546             :             }
    2547             :         }
    2548             : 
    2549             :         CPLFree(pszData);
    2550             : 
    2551             :         pachNITFDES = NULL;
    2552             :         ppszDESsList += 1;
    2553             :     }
    2554             : }
    2555             : 
    2556             : /************************************************************************/
    2557             : /*                         InitializeNITFTREs()                         */
    2558             : /************************************************************************/
    2559             : 
    2560             : void NITFDataset::InitializeNITFTREs()
    2561             : {
    2562             :     static const char *const pszFileHeaderTREsDomain = "NITF_FILE_HEADER_TRES";
    2563             :     static const char *const pszImageSegmentTREsDomain =
    2564             :         "NITF_IMAGE_SEGMENT_TRES";
    2565             : 
    2566             :     char **ppszFileHeaderTREsList =
    2567             :         oSpecialMD.GetMetadata(pszFileHeaderTREsDomain);
    2568             :     char **ppszImageSegmentTREsList =
    2569             :         oSpecialMD.GetMetadata(pszImageSegmentTREsDomain);
    2570             : 
    2571             :     if ((ppszFileHeaderTREsList != NULL) && (ppszImageSegmentTREsList != NULL))
    2572             :         return;
    2573             : 
    2574             :     /* -------------------------------------------------------------------- */
    2575             :     /*      Loop over TRE sources (file and image).                         */
    2576             :     /* -------------------------------------------------------------------- */
    2577             : 
    2578             :     for (int nTRESrc = 0; nTRESrc < 2; nTRESrc++)
    2579             :     {
    2580             :         int nTREBytes = 0;
    2581             :         char *pszTREData = NULL;
    2582             :         const char *pszTREsDomain = NULL;
    2583             :         CPLStringList aosList;
    2584             : 
    2585             :         /* --------------------------------------------------------------------
    2586             :          */
    2587             :         /*      Extract file header or image segment TREs. */
    2588             :         /* --------------------------------------------------------------------
    2589             :          */
    2590             : 
    2591             :         if (nTRESrc == 0)
    2592             :         {
    2593             :             if (ppszFileHeaderTREsList != NULL)
    2594             :                 continue;
    2595             : 
    2596             :             nTREBytes = psFile->nTREBytes;
    2597             :             pszTREData = psFile->pachTRE;
    2598             :             pszTREsDomain = pszFileHeaderTREsDomain;
    2599             :         }
    2600             :         else
    2601             :         {
    2602             :             if (ppszImageSegmentTREsList != NULL)
    2603             :                 continue;
    2604             : 
    2605             :             if (psImage)
    2606             :             {
    2607             :                 nTREBytes = psImage->nTREBytes;
    2608             :                 pszTREData = psImage->pachTRE;
    2609             :                 pszTREsDomain = pszImageSegmentTREsDomain;
    2610             :             }
    2611             :             else
    2612             :             {
    2613             :                 nTREBytes = 0;
    2614             :                 pszTREData = NULL;
    2615             :             }
    2616             :         }
    2617             : 
    2618             :         /* --------------------------------------------------------------------
    2619             :          */
    2620             :         /*      Loop over TREs. */
    2621             :         /* --------------------------------------------------------------------
    2622             :          */
    2623             : 
    2624             :         while (nTREBytes >= 11)
    2625             :         {
    2626             :             char szTemp[100];
    2627             :             char szTag[7];
    2628             :             char *pszEscapedData = NULL;
    2629             :             int nThisTRESize = atoi(NITFGetField(szTemp, pszTREData, 6, 5));
    2630             : 
    2631             :             if (nThisTRESize < 0)
    2632             :             {
    2633             :                 NITFGetField(szTemp, pszTREData, 0, 6);
    2634             :                 CPLError(CE_Failure, CPLE_AppDefined,
    2635             :                          "Invalid size (%d) for TRE %s", nThisTRESize, szTemp);
    2636             :                 return;
    2637             :             }
    2638             : 
    2639             :             if (nThisTRESize > nTREBytes - 11)
    2640             :             {
    2641             :                 CPLError(CE_Failure, CPLE_AppDefined,
    2642             :                          "Not enough bytes in TRE");
    2643             :                 return;
    2644             :             }
    2645             : 
    2646             :             strncpy(szTag, pszTREData, 6);
    2647             :             szTag[6] = '\0';
    2648             : 
    2649             :             // trim white off tag.
    2650             :             while (strlen(szTag) > 0 && szTag[strlen(szTag) - 1] == ' ')
    2651             :                 szTag[strlen(szTag) - 1] = '\0';
    2652             : 
    2653             :             // escape data.
    2654             :             pszEscapedData = CPLEscapeString(pszTREData + 6, nThisTRESize + 5,
    2655             :                                              CPLES_BackslashQuotable);
    2656             : 
    2657             :             const size_t nLineLen = strlen(szTag) + strlen(pszEscapedData) + 2;
    2658             :             char *pszLine = static_cast<char *>(CPLMalloc(nLineLen));
    2659             :             snprintf(pszLine, nLineLen, "%s=%s", szTag, pszEscapedData);
    2660             :             aosList.AddString(pszLine);
    2661             :             CPLFree(pszLine);
    2662             :             pszLine = NULL;
    2663             : 
    2664             :             CPLFree(pszEscapedData);
    2665             :             pszEscapedData = NULL;
    2666             : 
    2667             :             nTREBytes -= (nThisTRESize + 11);
    2668             :             pszTREData += (nThisTRESize + 11);
    2669             :         }
    2670             : 
    2671             :         if (!aosList.empty())
    2672             :             oSpecialMD.SetMetadata(aosList.List(), pszTREsDomain);
    2673             :     }
    2674             : }
    2675             : #endif
    2676             : 
    2677             : /************************************************************************/
    2678             : /*                         InitializeNITFDESs()                         */
    2679             : /************************************************************************/
    2680             : 
    2681          15 : bool NITFDataset::InitializeNITFDESs(bool bValidate)
    2682             : {
    2683          15 :     char **papszDESsList = oSpecialMD.GetMetadata("xml:DES");
    2684             : 
    2685          15 :     if (papszDESsList != nullptr)
    2686             :     {
    2687           1 :         return true;
    2688             :     }
    2689             : 
    2690          14 :     bool bSuccess = true;
    2691             :     CPLXMLNode *psDesListNode =
    2692          14 :         CPLCreateXMLNode(nullptr, CXT_Element, "des_list");
    2693             : 
    2694          44 :     for (int iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++)
    2695             :     {
    2696          30 :         NITFSegmentInfo *psSegInfo = psFile->pasSegmentInfo + iSegment;
    2697             : 
    2698          30 :         if (EQUAL(psSegInfo->szSegmentType, "DE"))
    2699             :         {
    2700          12 :             bool bGotError = false;
    2701             :             CPLXMLNode *psDesNode =
    2702          12 :                 NITFDESGetXml(psFile, iSegment, bValidate, &bGotError);
    2703          12 :             if (bGotError)
    2704           2 :                 bSuccess = false;
    2705             : 
    2706          12 :             if (psDesNode != nullptr)
    2707             :             {
    2708          12 :                 CPLAddXMLChild(psDesListNode, psDesNode);
    2709             :             }
    2710             :         }
    2711             :     }
    2712             : 
    2713          14 :     if (psDesListNode->psChild != nullptr)
    2714             :     {
    2715          11 :         char *pszXML = CPLSerializeXMLTree(psDesListNode);
    2716          11 :         char *apszMD[2] = {pszXML, nullptr};
    2717          11 :         oSpecialMD.SetMetadata(apszMD, "xml:DES");
    2718          11 :         CPLFree(pszXML);
    2719             :     }
    2720          14 :     CPLDestroyXMLNode(psDesListNode);
    2721          14 :     return bSuccess;
    2722             : }
    2723             : 
    2724             : /************************************************************************/
    2725             : /*                       InitializeNITFMetadata()                       */
    2726             : /************************************************************************/
    2727             : 
    2728           5 : void NITFDataset::InitializeNITFMetadata()
    2729             : 
    2730             : {
    2731             :     static const char *const pszDomainName = "NITF_METADATA";
    2732             :     static const char *const pszTagNITFFileHeader = "NITFFileHeader";
    2733             :     static const char *const pszTagNITFImageSubheader = "NITFImageSubheader";
    2734             : 
    2735           5 :     if (oSpecialMD.GetMetadata(pszDomainName) != nullptr)
    2736           1 :         return;
    2737             : 
    2738             :     // nHeaderLenOffset is the number of bytes to skip from the beginning of the
    2739             :     // NITF file header in order to get to the field HL (NITF file header
    2740             :     // length).
    2741             : 
    2742           4 :     int nHeaderLen = 0;
    2743           4 :     int nHeaderLenOffset = 0;
    2744             : 
    2745             :     // Get the NITF file header length.
    2746             : 
    2747           4 :     if (psFile->pachHeader != nullptr)
    2748             :     {
    2749           4 :         if ((STARTS_WITH(psFile->pachHeader, "NITF02.10")) ||
    2750           0 :             (STARTS_WITH(psFile->pachHeader, "NSIF01.00")))
    2751           4 :             nHeaderLenOffset = 354;
    2752           0 :         else if ((STARTS_WITH(psFile->pachHeader, "NITF01.10")) ||
    2753           0 :                  (STARTS_WITH(psFile->pachHeader, "NITF02.00")))
    2754           0 :             nHeaderLenOffset =
    2755           0 :                 (STARTS_WITH((psFile->pachHeader + 280), "999998")) ? 394 : 354;
    2756             :     }
    2757             : 
    2758             :     char fieldHL[7];
    2759             : 
    2760           4 :     if (nHeaderLenOffset > 0)
    2761             :     {
    2762           4 :         char *pszFieldHL = psFile->pachHeader + nHeaderLenOffset;
    2763             : 
    2764           4 :         memcpy(fieldHL, pszFieldHL, 6);
    2765           4 :         fieldHL[6] = '\0';
    2766           4 :         nHeaderLen = atoi(fieldHL);
    2767             :     }
    2768             : 
    2769           4 :     if (nHeaderLen <= 0)
    2770             :     {
    2771           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Zero length NITF file header!");
    2772           0 :         return;
    2773             :     }
    2774             : 
    2775           8 :     char *encodedHeader = CPLBase64Encode(
    2776           4 :         nHeaderLen, reinterpret_cast<GByte *>(psFile->pachHeader));
    2777             : 
    2778           4 :     if (encodedHeader == nullptr || strlen(encodedHeader) == 0)
    2779             :     {
    2780           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2781             :                  "Failed to encode NITF file header!");
    2782           0 :         CPLFree(encodedHeader);
    2783           0 :         return;
    2784             :     }
    2785             : 
    2786             :     // The length of the NITF file header plus a space is append to the
    2787             :     // beginning of the encoded string so that we can recover the length of the
    2788             :     // NITF file header when we decode it without having to pull it out the HL
    2789             :     // field again.
    2790             : 
    2791           4 :     std::string nitfFileheaderStr(fieldHL);
    2792           4 :     nitfFileheaderStr.append(" ");
    2793           4 :     nitfFileheaderStr.append(encodedHeader);
    2794             : 
    2795           4 :     CPLFree(encodedHeader);
    2796             : 
    2797           4 :     oSpecialMD.SetMetadataItem(pszTagNITFFileHeader, nitfFileheaderStr.c_str(),
    2798             :                                pszDomainName);
    2799             : 
    2800             :     // Get the image subheader length.
    2801             : 
    2802           4 :     int nImageSubheaderLen = 0;
    2803             : 
    2804           4 :     if (psImage != nullptr &&
    2805           3 :         STARTS_WITH(psFile->pasSegmentInfo[psImage->iSegment].szSegmentType,
    2806             :                     "IM"))
    2807             :     {
    2808           3 :         nImageSubheaderLen =
    2809           3 :             psFile->pasSegmentInfo[psImage->iSegment].nSegmentHeaderSize;
    2810             :     }
    2811             : 
    2812           4 :     if (nImageSubheaderLen < 0)
    2813             :     {
    2814           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2815             :                  "Invalid length NITF image subheader!");
    2816           0 :         return;
    2817             :     }
    2818             : 
    2819           4 :     if (nImageSubheaderLen > 0)
    2820             :     {
    2821           6 :         char *encodedImageSubheader = CPLBase64Encode(
    2822           3 :             nImageSubheaderLen, reinterpret_cast<GByte *>(psImage->pachHeader));
    2823             : 
    2824           3 :         if (encodedImageSubheader == nullptr ||
    2825           3 :             strlen(encodedImageSubheader) == 0)
    2826             :         {
    2827           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2828             :                      "Failed to encode image subheader!");
    2829           0 :             CPLFree(encodedImageSubheader);
    2830           0 :             return;
    2831             :         }
    2832             : 
    2833             :         // The length of the image subheader plus a space is append to the
    2834             :         // beginning of the encoded string so that we can recover the actual
    2835             :         // length of the image subheader when we decode it.
    2836             : 
    2837             :         char buffer[20];
    2838             : 
    2839           3 :         snprintf(buffer, sizeof(buffer), "%d", nImageSubheaderLen);
    2840             : 
    2841           6 :         std::string imageSubheaderStr(buffer);
    2842           3 :         imageSubheaderStr.append(" ");
    2843           3 :         imageSubheaderStr.append(encodedImageSubheader);
    2844             : 
    2845           3 :         CPLFree(encodedImageSubheader);
    2846             : 
    2847           3 :         oSpecialMD.SetMetadataItem(pszTagNITFImageSubheader,
    2848             :                                    imageSubheaderStr.c_str(), pszDomainName);
    2849             :     }
    2850             : }
    2851             : 
    2852             : /************************************************************************/
    2853             : /*                       InitializeCGMMetadata()                        */
    2854             : /************************************************************************/
    2855             : 
    2856          18 : void NITFDataset::InitializeCGMMetadata()
    2857             : 
    2858             : {
    2859          18 :     if (oSpecialMD.GetMetadataItem("SEGMENT_COUNT", "CGM") != nullptr)
    2860           5 :         return;
    2861             : 
    2862          13 :     int iCGM = 0;
    2863          13 :     char **papszCGMMetadata = CSLSetNameValue(nullptr, "SEGMENT_COUNT", "0");
    2864             : 
    2865             :     /* ==================================================================== */
    2866             :     /*      Process all graphics segments.                                  */
    2867             :     /* ==================================================================== */
    2868          35 :     for (int iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++)
    2869             :     {
    2870          22 :         NITFSegmentInfo *psSegment = psFile->pasSegmentInfo + iSegment;
    2871             : 
    2872          22 :         if (!EQUAL(psSegment->szSegmentType, "GR") &&
    2873          18 :             !EQUAL(psSegment->szSegmentType, "SY"))
    2874          18 :             continue;
    2875             : 
    2876           4 :         papszCGMMetadata = CSLSetNameValue(
    2877           8 :             papszCGMMetadata, CPLString().Printf("SEGMENT_%d_SLOC_ROW", iCGM),
    2878           8 :             CPLString().Printf("%d", psSegment->nLOC_R));
    2879           4 :         papszCGMMetadata = CSLSetNameValue(
    2880           8 :             papszCGMMetadata, CPLString().Printf("SEGMENT_%d_SLOC_COL", iCGM),
    2881           8 :             CPLString().Printf("%d", psSegment->nLOC_C));
    2882             : 
    2883           4 :         papszCGMMetadata = CSLSetNameValue(
    2884           8 :             papszCGMMetadata, CPLString().Printf("SEGMENT_%d_CCS_ROW", iCGM),
    2885           8 :             CPLString().Printf("%d", psSegment->nCCS_R));
    2886           4 :         papszCGMMetadata = CSLSetNameValue(
    2887           8 :             papszCGMMetadata, CPLString().Printf("SEGMENT_%d_CCS_COL", iCGM),
    2888           8 :             CPLString().Printf("%d", psSegment->nCCS_C));
    2889             : 
    2890           4 :         papszCGMMetadata = CSLSetNameValue(
    2891           8 :             papszCGMMetadata, CPLString().Printf("SEGMENT_%d_SDLVL", iCGM),
    2892           8 :             CPLString().Printf("%d", psSegment->nDLVL));
    2893           4 :         papszCGMMetadata = CSLSetNameValue(
    2894           8 :             papszCGMMetadata, CPLString().Printf("SEGMENT_%d_SALVL", iCGM),
    2895           8 :             CPLString().Printf("%d", psSegment->nALVL));
    2896             : 
    2897             :         /* --------------------------------------------------------------------
    2898             :          */
    2899             :         /*      Load the raw CGM data itself. */
    2900             :         /* --------------------------------------------------------------------
    2901             :          */
    2902             : 
    2903           4 :         char *pabyCGMData = static_cast<char *>(VSI_CALLOC_VERBOSE(
    2904             :             1, static_cast<size_t>(psSegment->nSegmentSize)));
    2905           4 :         if (pabyCGMData == nullptr)
    2906             :         {
    2907           0 :             CSLDestroy(papszCGMMetadata);
    2908           0 :             return;
    2909             :         }
    2910           8 :         if (VSIFSeekL(psFile->fp, psSegment->nSegmentStart, SEEK_SET) != 0 ||
    2911           8 :             VSIFReadL(pabyCGMData, 1,
    2912           4 :                       static_cast<size_t>(psSegment->nSegmentSize),
    2913           4 :                       psFile->fp) != psSegment->nSegmentSize)
    2914             :         {
    2915           0 :             CPLError(CE_Warning, CPLE_FileIO,
    2916             :                      "Failed to read " CPL_FRMT_GUIB
    2917             :                      " bytes of graphic data at " CPL_FRMT_GUIB ".",
    2918             :                      psSegment->nSegmentSize, psSegment->nSegmentStart);
    2919           0 :             CPLFree(pabyCGMData);
    2920           0 :             CSLDestroy(papszCGMMetadata);
    2921           0 :             return;
    2922             :         }
    2923             : 
    2924           8 :         char *pszEscapedCGMData = CPLEscapeString(
    2925           4 :             pabyCGMData, static_cast<int>(psSegment->nSegmentSize),
    2926             :             CPLES_BackslashQuotable);
    2927             : 
    2928           4 :         if (pszEscapedCGMData == nullptr)
    2929             :         {
    2930           0 :             CPLFree(pabyCGMData);
    2931           0 :             CSLDestroy(papszCGMMetadata);
    2932           0 :             return;
    2933             :         }
    2934             : 
    2935           4 :         papszCGMMetadata = CSLSetNameValue(
    2936           8 :             papszCGMMetadata, CPLString().Printf("SEGMENT_%d_DATA", iCGM),
    2937             :             pszEscapedCGMData);
    2938           4 :         CPLFree(pszEscapedCGMData);
    2939           4 :         CPLFree(pabyCGMData);
    2940             : 
    2941           4 :         iCGM++;
    2942             :     }
    2943             : 
    2944             :     /* -------------------------------------------------------------------- */
    2945             :     /*      Record the CGM segment count.                                   */
    2946             :     /* -------------------------------------------------------------------- */
    2947          13 :     papszCGMMetadata = CSLSetNameValue(papszCGMMetadata, "SEGMENT_COUNT",
    2948          26 :                                        CPLString().Printf("%d", iCGM));
    2949             : 
    2950          13 :     oSpecialMD.SetMetadata(papszCGMMetadata, "CGM");
    2951             : 
    2952          13 :     CSLDestroy(papszCGMMetadata);
    2953             : }
    2954             : 
    2955             : /************************************************************************/
    2956             : /*                       InitializeTextMetadata()                       */
    2957             : /************************************************************************/
    2958             : 
    2959          19 : void NITFDataset::InitializeTextMetadata()
    2960             : 
    2961             : {
    2962          19 :     if (oSpecialMD.GetMetadata("TEXT") != nullptr)
    2963           2 :         return;
    2964             : 
    2965          17 :     int iText = 0;
    2966             : 
    2967             :     /* ==================================================================== */
    2968             :     /*      Process all text segments.                                  */
    2969             :     /* ==================================================================== */
    2970          45 :     for (int iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++)
    2971             :     {
    2972          28 :         NITFSegmentInfo *psSegment = psFile->pasSegmentInfo + iSegment;
    2973             : 
    2974          28 :         if (!EQUAL(psSegment->szSegmentType, "TX"))
    2975          21 :             continue;
    2976             : 
    2977             :         /* --------------------------------------------------------------------
    2978             :          */
    2979             :         /*      Load the text header */
    2980             :         /* --------------------------------------------------------------------
    2981             :          */
    2982             : 
    2983             :         /* Allocate one extra byte for the NULL terminating character */
    2984          14 :         char *pabyHeaderData = static_cast<char *>(CPLCalloc(
    2985           7 :             1, static_cast<size_t>(psSegment->nSegmentHeaderSize + 1)));
    2986           7 :         if (VSIFSeekL(psFile->fp, psSegment->nSegmentHeaderStart, SEEK_SET) !=
    2987          14 :                 0 ||
    2988          14 :             VSIFReadL(pabyHeaderData, 1,
    2989           7 :                       static_cast<size_t>(psSegment->nSegmentHeaderSize),
    2990           7 :                       psFile->fp) != psSegment->nSegmentHeaderSize)
    2991             :         {
    2992           0 :             CPLError(
    2993             :                 CE_Warning, CPLE_FileIO,
    2994             :                 "Failed to read %d bytes of text header data at " CPL_FRMT_GUIB
    2995             :                 ".",
    2996             :                 psSegment->nSegmentHeaderSize, psSegment->nSegmentHeaderStart);
    2997           0 :             CPLFree(pabyHeaderData);
    2998           0 :             return;
    2999             :         }
    3000             : 
    3001           7 :         oSpecialMD.SetMetadataItem(CPLString().Printf("HEADER_%d", iText),
    3002             :                                    pabyHeaderData, "TEXT");
    3003           7 :         CPLFree(pabyHeaderData);
    3004             : 
    3005             :         /* --------------------------------------------------------------------
    3006             :          */
    3007             :         /*      Load the raw TEXT data itself. */
    3008             :         /* --------------------------------------------------------------------
    3009             :          */
    3010             :         /* Allocate one extra byte for the NULL terminating character */
    3011           7 :         char *pabyTextData = static_cast<char *>(VSI_CALLOC_VERBOSE(
    3012             :             1, static_cast<size_t>(psSegment->nSegmentSize) + 1));
    3013           7 :         if (pabyTextData == nullptr)
    3014             :         {
    3015           0 :             return;
    3016             :         }
    3017          14 :         if (VSIFSeekL(psFile->fp, psSegment->nSegmentStart, SEEK_SET) != 0 ||
    3018          14 :             VSIFReadL(pabyTextData, 1,
    3019           7 :                       static_cast<size_t>(psSegment->nSegmentSize),
    3020           7 :                       psFile->fp) != psSegment->nSegmentSize)
    3021             :         {
    3022           0 :             CPLError(CE_Warning, CPLE_FileIO,
    3023             :                      "Failed to read " CPL_FRMT_GUIB
    3024             :                      " bytes of text data at " CPL_FRMT_GUIB ".",
    3025             :                      psSegment->nSegmentSize, psSegment->nSegmentStart);
    3026           0 :             CPLFree(pabyTextData);
    3027           0 :             return;
    3028             :         }
    3029             : 
    3030           7 :         oSpecialMD.SetMetadataItem(CPLString().Printf("DATA_%d", iText),
    3031             :                                    pabyTextData, "TEXT");
    3032           7 :         CPLFree(pabyTextData);
    3033             : 
    3034           7 :         iText++;
    3035             :     }
    3036             : }
    3037             : 
    3038             : /************************************************************************/
    3039             : /*                       InitializeTREMetadata()                        */
    3040             : /************************************************************************/
    3041             : 
    3042          78 : bool NITFDataset::InitializeTREMetadata(bool bValidate)
    3043             : 
    3044             : {
    3045         149 :     if (oSpecialMD.GetMetadata("TRE") != nullptr ||
    3046          71 :         oSpecialMD.GetMetadata("xml:TRE") != nullptr)
    3047           7 :         return true;
    3048             : 
    3049          71 :     bool bGotError = false;
    3050          71 :     CPLXMLNode *psTresNode = CPLCreateXMLNode(nullptr, CXT_Element, "tres");
    3051          71 :     bool bFoundRPFIMG = false;
    3052             : 
    3053             :     /* -------------------------------------------------------------------- */
    3054             :     /*      Loop over TRE sources (file and image).                         */
    3055             :     /* -------------------------------------------------------------------- */
    3056         213 :     for (int nTRESrc = 0; nTRESrc < 2; nTRESrc++)
    3057             :     {
    3058         142 :         int nTREBytes = 0;
    3059         142 :         char *pszTREData = nullptr;
    3060             : 
    3061         142 :         if (nTRESrc == 0)
    3062             :         {
    3063          71 :             nTREBytes = psFile->nTREBytes;
    3064          71 :             pszTREData = psFile->pachTRE;
    3065             :         }
    3066             :         else
    3067             :         {
    3068          71 :             if (psImage)
    3069             :             {
    3070          67 :                 nTREBytes = psImage->nTREBytes;
    3071          67 :                 pszTREData = psImage->pachTRE;
    3072             :             }
    3073             :             else
    3074             :             {
    3075           4 :                 nTREBytes = 0;
    3076           4 :                 pszTREData = nullptr;
    3077             :             }
    3078             :         }
    3079             : 
    3080             :         /* --------------------------------------------------------------------
    3081             :          */
    3082             :         /*      Loop over TREs. */
    3083             :         /* --------------------------------------------------------------------
    3084             :          */
    3085             : 
    3086         209 :         while (nTREBytes >= 11)
    3087             :         {
    3088             :             char szTemp[100];
    3089             :             char szTag[7];
    3090             :             const int nThisTRESize =
    3091          67 :                 atoi(NITFGetField(szTemp, pszTREData, 6, 5));
    3092             : 
    3093          67 :             if (nThisTRESize < 0)
    3094             :             {
    3095           0 :                 NITFGetField(szTemp, pszTREData, 0, 6);
    3096           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3097             :                          "Invalid size (%d) for TRE %s", nThisTRESize, szTemp);
    3098           0 :                 CPLDestroyXMLNode(psTresNode);
    3099           0 :                 bGotError = true;
    3100           0 :                 return bGotError;
    3101             :             }
    3102          67 :             if (nThisTRESize > nTREBytes - 11)
    3103             :             {
    3104           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3105             :                          "Not enough bytes in TRE");
    3106           0 :                 CPLDestroyXMLNode(psTresNode);
    3107           0 :                 bGotError = true;
    3108           0 :                 return bGotError;
    3109             :             }
    3110             : 
    3111          67 :             strncpy(szTag, pszTREData, 6);
    3112          67 :             szTag[6] = '\0';
    3113             : 
    3114             :             // trim white off tag.
    3115          67 :             while (strlen(szTag) > 0 && szTag[strlen(szTag) - 1] == ' ')
    3116           0 :                 szTag[strlen(szTag) - 1] = '\0';
    3117             : 
    3118          67 :             if (strcmp(szTag, "RPFIMG") == 0)
    3119           4 :                 bFoundRPFIMG = true;
    3120             : 
    3121             :             CPLXMLNode *psTreNode =
    3122          67 :                 NITFCreateXMLTre(psFile, szTag, pszTREData + 11, nThisTRESize,
    3123             :                                  bValidate, &bGotError);
    3124          67 :             if (psTreNode)
    3125             :             {
    3126          62 :                 CPLCreateXMLNode(
    3127             :                     CPLCreateXMLNode(psTreNode, CXT_Attribute, "location"),
    3128             :                     CXT_Text, nTRESrc == 0 ? "file" : "image");
    3129          62 :                 CPLAddXMLChild(psTresNode, psTreNode);
    3130             :             }
    3131             : 
    3132             :             // escape data.
    3133         134 :             char *pszEscapedData = CPLEscapeString(
    3134          67 :                 pszTREData + 11, nThisTRESize, CPLES_BackslashQuotable);
    3135          67 :             if (pszEscapedData == nullptr)
    3136             :             {
    3137           0 :                 bGotError = true;
    3138             :             }
    3139             :             else
    3140             :             {
    3141             :                 char szUniqueTag[32];
    3142          67 :                 strcpy(szUniqueTag, szTag);
    3143          67 :                 int nCountUnique = 2;
    3144          67 :                 while (oSpecialMD.GetMetadataItem(szUniqueTag, "TRE") !=
    3145             :                        nullptr)
    3146             :                 {
    3147           0 :                     snprintf(szUniqueTag, sizeof(szUniqueTag), "%s_%d", szTag,
    3148             :                              nCountUnique);
    3149           0 :                     nCountUnique++;
    3150             :                 }
    3151          67 :                 oSpecialMD.SetMetadataItem(szUniqueTag, pszEscapedData, "TRE");
    3152          67 :                 CPLFree(pszEscapedData);
    3153             :             }
    3154             : 
    3155          67 :             nTREBytes -= (nThisTRESize + 11);
    3156          67 :             pszTREData += (nThisTRESize + 11);
    3157             :         }
    3158             :     }
    3159             : 
    3160             :     /* -------------------------------------------------------------------- */
    3161             :     /*      Loop over TRE in DES                                            */
    3162             :     /* -------------------------------------------------------------------- */
    3163         149 :     for (int iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++)
    3164             :     {
    3165          78 :         NITFSegmentInfo *psSegInfo = psFile->pasSegmentInfo + iSegment;
    3166          78 :         if (!EQUAL(psSegInfo->szSegmentType, "DE"))
    3167          69 :             continue;
    3168             : 
    3169           9 :         NITFDES *psDES = NITFDESAccess(psFile, iSegment);
    3170           9 :         if (psDES == nullptr)
    3171           0 :             continue;
    3172             : 
    3173           9 :         char *pabyTREData = nullptr;
    3174           9 :         int nOffset = 0;
    3175             :         char szTREName[7];
    3176             :         int nThisTRESize;
    3177             : 
    3178          19 :         while (NITFDESGetTRE(psDES, nOffset, szTREName, &pabyTREData,
    3179          19 :                              &nThisTRESize))
    3180             :         {
    3181          10 :             if (nThisTRESize == 0)
    3182             :             {
    3183           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    3184             :                          "Invalid size=0 for TRE %s", szTREName);
    3185           0 :                 break;
    3186             :             }
    3187             : 
    3188          10 :             if (!(bFoundRPFIMG && strcmp(szTREName, "RPFDES") == 0))
    3189             :             {
    3190           7 :                 char *pszEscapedData = CPLEscapeString(
    3191             :                     pabyTREData, nThisTRESize, CPLES_BackslashQuotable);
    3192           7 :                 if (pszEscapedData == nullptr)
    3193             :                 {
    3194           0 :                     NITFDESFreeTREData(pabyTREData);
    3195           0 :                     bGotError = true;
    3196           0 :                     break;
    3197             :                 }
    3198             : 
    3199             :                 // trim white off tag.
    3200           7 :                 while (strlen(szTREName) > 0 &&
    3201           7 :                        szTREName[strlen(szTREName) - 1] == ' ')
    3202           0 :                     szTREName[strlen(szTREName) - 1] = '\0';
    3203             : 
    3204             :                 CPLXMLNode *psTreNode =
    3205           7 :                     NITFCreateXMLTre(psFile, szTREName, pabyTREData,
    3206             :                                      nThisTRESize, bValidate, &bGotError);
    3207           7 :                 if (psTreNode)
    3208             :                 {
    3209             :                     const char *pszDESID =
    3210           7 :                         CSLFetchNameValue(psDES->papszMetadata, "DESID");
    3211          14 :                     CPLCreateXMLNode(
    3212             :                         CPLCreateXMLNode(psTreNode, CXT_Attribute, "location"),
    3213             :                         CXT_Text,
    3214           7 :                         pszDESID ? CPLSPrintf("des %s", pszDESID) : "des");
    3215           7 :                     CPLAddXMLChild(psTresNode, psTreNode);
    3216             :                 }
    3217             : 
    3218             :                 char szUniqueTag[32];
    3219           7 :                 strcpy(szUniqueTag, szTREName);
    3220           7 :                 int nCountUnique = 2;
    3221           7 :                 while (oSpecialMD.GetMetadataItem(szUniqueTag, "TRE") !=
    3222             :                        nullptr)
    3223             :                 {
    3224           0 :                     snprintf(szUniqueTag, sizeof(szUniqueTag), "%s_%d",
    3225             :                              szTREName, nCountUnique);
    3226           0 :                     nCountUnique++;
    3227             :                 }
    3228           7 :                 oSpecialMD.SetMetadataItem(szUniqueTag, pszEscapedData, "TRE");
    3229             : 
    3230           7 :                 CPLFree(pszEscapedData);
    3231             :             }
    3232             : 
    3233          10 :             nOffset += 11 + nThisTRESize;
    3234             : 
    3235          10 :             NITFDESFreeTREData(pabyTREData);
    3236             :         }
    3237             : 
    3238           9 :         NITFDESDeaccess(psDES);
    3239             :     }
    3240             : 
    3241          71 :     if (psTresNode->psChild != nullptr)
    3242             :     {
    3243          60 :         char *pszXML = CPLSerializeXMLTree(psTresNode);
    3244          60 :         char *apszMD[2] = {pszXML, nullptr};
    3245          60 :         oSpecialMD.SetMetadata(apszMD, "xml:TRE");
    3246          60 :         CPLFree(pszXML);
    3247             :     }
    3248          71 :     CPLDestroyXMLNode(psTresNode);
    3249             : 
    3250          71 :     return !bGotError;
    3251             : }
    3252             : 
    3253             : /************************************************************************/
    3254             : /*                       GetMetadataDomainList()                        */
    3255             : /************************************************************************/
    3256             : 
    3257           1 : char **NITFDataset::GetMetadataDomainList()
    3258             : {
    3259           1 :     return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
    3260             :                                    TRUE, "NITF_METADATA", "xml:DES",
    3261             :                                    "NITF_DES_METADATA", "NITF_FILE_HEADER_TRES",
    3262             :                                    "NITF_IMAGE_SEGMENT_TRES", "CGM", "TEXT",
    3263           1 :                                    "TRE", "xml:TRE", "OVERVIEWS", nullptr);
    3264             : }
    3265             : 
    3266             : /************************************************************************/
    3267             : /*                  InitializeImageStructureMetadata()                  */
    3268             : /************************************************************************/
    3269             : 
    3270           2 : void NITFDataset::InitializeImageStructureMetadata()
    3271             : {
    3272           2 :     if (oSpecialMD.GetMetadata(GDAL_MDD_IMAGE_STRUCTURE) != nullptr)
    3273           0 :         return;
    3274             : 
    3275           2 :     oSpecialMD.SetMetadata(
    3276             :         GDALPamDataset::GetMetadata(GDAL_MDD_IMAGE_STRUCTURE),
    3277             :         GDAL_MDD_IMAGE_STRUCTURE);
    3278           2 :     if (poJ2KDataset)
    3279             :     {
    3280           2 :         const char *pszReversibility = poJ2KDataset->GetMetadataItem(
    3281           2 :             "COMPRESSION_REVERSIBILITY", GDAL_MDD_IMAGE_STRUCTURE);
    3282           2 :         if (pszReversibility)
    3283             :         {
    3284           2 :             oSpecialMD.SetMetadataItem("COMPRESSION_REVERSIBILITY",
    3285             :                                        pszReversibility,
    3286             :                                        GDAL_MDD_IMAGE_STRUCTURE);
    3287             :         }
    3288             :     }
    3289             : }
    3290             : 
    3291             : /************************************************************************/
    3292             : /*                            GetMetadata()                             */
    3293             : /************************************************************************/
    3294             : 
    3295         386 : CSLConstList NITFDataset::GetMetadata(const char *pszDomain)
    3296             : 
    3297             : {
    3298         386 :     if (pszDomain != nullptr && EQUAL(pszDomain, "NITF_METADATA"))
    3299             :     {
    3300             :         // InitializeNITFMetadata retrieves the NITF file header and all image
    3301             :         // segment file headers. (NOTE: The returned strings are
    3302             :         // base64-encoded).
    3303             : 
    3304           4 :         InitializeNITFMetadata();
    3305           4 :         return oSpecialMD.GetMetadata(pszDomain);
    3306             :     }
    3307             : 
    3308         382 :     if (pszDomain != nullptr && EQUAL(pszDomain, "xml:DES"))
    3309             :     {
    3310             :         // InitializeNITFDESs retrieves all the DES file headers (NOTE: The
    3311             :         // returned strings are base64-encoded).
    3312             : 
    3313          11 :         InitializeNITFDESs(false);
    3314          11 :         return oSpecialMD.GetMetadata(pszDomain);
    3315             :     }
    3316             : 
    3317             : #ifdef ESRI_BUILD
    3318             :     if (pszDomain != NULL && EQUAL(pszDomain, "NITF_DES_METADATA"))
    3319             :     {
    3320             :         // InitializeNITFDESs retrieves all the DES file headers (NOTE: The
    3321             :         // returned strings are base64-encoded).
    3322             : 
    3323             :         InitializeNITFDESMetadata(false);
    3324             :         return oSpecialMD.GetMetadata(pszDomain);
    3325             :     }
    3326             : 
    3327             :     if (pszDomain != NULL && EQUAL(pszDomain, "NITF_FILE_HEADER_TRES"))
    3328             :     {
    3329             :         // InitializeNITFTREs retrieves all the TREs that are resides in the
    3330             :         // NITF file header and all the TREs that are resides in the current
    3331             :         // image segment. NOTE: the returned strings are backslash-escaped
    3332             : 
    3333             :         InitializeNITFTREs();
    3334             :         return oSpecialMD.GetMetadata(pszDomain);
    3335             :     }
    3336             : 
    3337             :     if (pszDomain != NULL && EQUAL(pszDomain, "NITF_IMAGE_SEGMENT_TRES"))
    3338             :     {
    3339             :         // InitializeNITFTREs retrieves all the TREs that are resides in the
    3340             :         // NITF file header and all the TREs that are resides in the current
    3341             :         // image segment. NOTE: the returned strings are backslash-escaped
    3342             : 
    3343             :         InitializeNITFTREs();
    3344             :         return oSpecialMD.GetMetadata(pszDomain);
    3345             :     }
    3346             : #endif
    3347             : 
    3348         371 :     if (pszDomain != nullptr && EQUAL(pszDomain, "CGM"))
    3349             :     {
    3350          17 :         InitializeCGMMetadata();
    3351          17 :         return oSpecialMD.GetMetadata(pszDomain);
    3352             :     }
    3353             : 
    3354         354 :     if (pszDomain != nullptr && EQUAL(pszDomain, "TEXT"))
    3355             :     {
    3356          18 :         InitializeTextMetadata();
    3357          18 :         return oSpecialMD.GetMetadata(pszDomain);
    3358             :     }
    3359             : 
    3360         336 :     if (pszDomain != nullptr && EQUAL(pszDomain, "TRE"))
    3361             :     {
    3362          21 :         InitializeTREMetadata(false);
    3363          21 :         return oSpecialMD.GetMetadata(pszDomain);
    3364             :     }
    3365             : 
    3366         315 :     if (pszDomain != nullptr && EQUAL(pszDomain, "xml:TRE"))
    3367             :     {
    3368          31 :         InitializeTREMetadata(false);
    3369          31 :         return oSpecialMD.GetMetadata(pszDomain);
    3370             :     }
    3371             : 
    3372         296 :     if (pszDomain != nullptr && EQUAL(pszDomain, GDAL_MDD_IMAGE_STRUCTURE) &&
    3373          12 :         poJ2KDataset)
    3374             :     {
    3375           1 :         InitializeImageStructureMetadata();
    3376           1 :         return oSpecialMD.GetMetadata(pszDomain);
    3377             :     }
    3378             : 
    3379         283 :     return GDALPamDataset::GetMetadata(pszDomain);
    3380             : }
    3381             : 
    3382             : /************************************************************************/
    3383             : /*                          GetMetadataItem()                           */
    3384             : /************************************************************************/
    3385             : 
    3386        2644 : const char *NITFDataset::GetMetadataItem(const char *pszName,
    3387             :                                          const char *pszDomain)
    3388             : 
    3389             : {
    3390        2644 :     if (pszDomain != nullptr && EQUAL(pszDomain, "NITF_METADATA"))
    3391             :     {
    3392             :         // InitializeNITFMetadata retrieves the NITF file header and all image
    3393             :         // segment file headers. (NOTE: The returned strings are
    3394             :         // base64-encoded).
    3395             : 
    3396           1 :         InitializeNITFMetadata();
    3397           1 :         return oSpecialMD.GetMetadataItem(pszName, pszDomain);
    3398             :     }
    3399             : 
    3400             : #ifdef ESRI_BUILD
    3401             :     if (pszDomain != NULL && EQUAL(pszDomain, "NITF_DES_METADATA"))
    3402             :     {
    3403             :         // InitializeNITFDESs retrieves all the DES file headers (NOTE: The
    3404             :         // returned strings are base64-encoded).
    3405             : 
    3406             :         InitializeNITFDESMetadata(false);
    3407             :         return oSpecialMD.GetMetadataItem(pszName, pszDomain);
    3408             :     }
    3409             : 
    3410             :     if (pszDomain != NULL && EQUAL(pszDomain, "NITF_FILE_HEADER_TRES"))
    3411             :     {
    3412             :         // InitializeNITFTREs retrieves all the TREs that are resides in the
    3413             :         // NITF file header and all the TREs that are resides in the current
    3414             :         // image segment. NOTE: the returned strings are backslash-escaped
    3415             : 
    3416             :         InitializeNITFTREs();
    3417             :         return oSpecialMD.GetMetadataItem(pszName, pszDomain);
    3418             :     }
    3419             : 
    3420             :     if (pszDomain != NULL && EQUAL(pszDomain, "NITF_IMAGE_SEGMENT_TRES"))
    3421             :     {
    3422             :         // InitializeNITFTREs retrieves all the TREs that are resides in the
    3423             :         // NITF file header and all the TREs that are resides in the current
    3424             :         // image segment. NOTE: the returned strings are backslash-escaped
    3425             : 
    3426             :         InitializeNITFTREs();
    3427             :         return oSpecialMD.GetMetadataItem(pszName, pszDomain);
    3428             :     }
    3429             : #endif
    3430             : 
    3431        2643 :     if (pszDomain != nullptr && EQUAL(pszDomain, "CGM"))
    3432             :     {
    3433           1 :         InitializeCGMMetadata();
    3434           1 :         return oSpecialMD.GetMetadataItem(pszName, pszDomain);
    3435             :     }
    3436             : 
    3437        2642 :     if (pszDomain != nullptr && EQUAL(pszDomain, "TEXT"))
    3438             :     {
    3439           1 :         InitializeTextMetadata();
    3440           1 :         return oSpecialMD.GetMetadataItem(pszName, pszDomain);
    3441             :     }
    3442             : 
    3443        2641 :     if (pszDomain != nullptr && EQUAL(pszDomain, "TRE"))
    3444             :     {
    3445          22 :         InitializeTREMetadata(false);
    3446          22 :         return oSpecialMD.GetMetadataItem(pszName, pszDomain);
    3447             :     }
    3448             : 
    3449        4137 :     if (pszDomain != nullptr && EQUAL(pszDomain, "OVERVIEWS") &&
    3450        1518 :         !osRSetVRT.empty())
    3451           2 :         return osRSetVRT;
    3452             : 
    3453        3573 :     if (pszDomain != nullptr && EQUAL(pszDomain, GDAL_MDD_IMAGE_STRUCTURE) &&
    3454        6190 :         poJ2KDataset && EQUAL(pszName, "COMPRESSION_REVERSIBILITY"))
    3455             :     {
    3456           1 :         InitializeImageStructureMetadata();
    3457           1 :         return oSpecialMD.GetMetadataItem(pszName, pszDomain);
    3458             :     }
    3459             : 
    3460             :     // For unit test purposes
    3461        2604 :     if (pszDomain != nullptr && EQUAL(pszDomain, "DEBUG") &&
    3462        5220 :         EQUAL(pszName, "JPEG2000_DATASET_NAME") && poJ2KDataset)
    3463           3 :         return poJ2KDataset->GetDescription();
    3464             : 
    3465             :     // For unit test purposes
    3466        2613 :     if (pszDomain != nullptr && EQUAL(pszDomain, "DEBUG") &&
    3467           2 :         EQUAL(pszName, "COMRAT") && psImage)
    3468           2 :         return psImage->szCOMRAT;
    3469             : 
    3470        2611 :     return GDALPamDataset::GetMetadataItem(pszName, pszDomain);
    3471             : }
    3472             : 
    3473             : /************************************************************************/
    3474             : /*                            GetGCPCount()                             */
    3475             : /************************************************************************/
    3476             : 
    3477          33 : int NITFDataset::GetGCPCount()
    3478             : 
    3479             : {
    3480          33 :     return nGCPCount;
    3481             : }
    3482             : 
    3483             : /************************************************************************/
    3484             : /*                          GetGCPSpatialRef()                          */
    3485             : /************************************************************************/
    3486             : 
    3487           8 : const OGRSpatialReference *NITFDataset::GetGCPSpatialRef() const
    3488             : 
    3489             : {
    3490           8 :     if (nGCPCount > 0 && !m_oGCPSRS.IsEmpty())
    3491           2 :         return &m_oGCPSRS;
    3492             : 
    3493           6 :     return nullptr;
    3494             : }
    3495             : 
    3496             : /************************************************************************/
    3497             : /*                               GetGCP()                               */
    3498             : /************************************************************************/
    3499             : 
    3500           5 : const GDAL_GCP *NITFDataset::GetGCPs()
    3501             : 
    3502             : {
    3503           5 :     return pasGCPList;
    3504             : }
    3505             : 
    3506             : /************************************************************************/
    3507             : /*                           CheckForRSets()                            */
    3508             : /*                                                                      */
    3509             : /*      Check for reduced resolution images in .r<n> files and if       */
    3510             : /*      found return filename for a virtual file wrapping them as an    */
    3511             : /*      overview file. (#3457)                                          */
    3512             : /************************************************************************/
    3513             : 
    3514         752 : int NITFDataset::CheckForRSets(const char *pszNITFFilename,
    3515             :                                char **papszSiblingFiles)
    3516             : 
    3517             : {
    3518         752 :     bool isR0File = EQUAL(CPLGetExtensionSafe(pszNITFFilename).c_str(), "r0");
    3519             : 
    3520             :     /* -------------------------------------------------------------------- */
    3521             :     /*      Check to see if we have RSets.                                  */
    3522             :     /* -------------------------------------------------------------------- */
    3523        1504 :     std::vector<CPLString> aosRSetFilenames;
    3524             : 
    3525         758 :     for (int i = 1; i <= 5; i++)
    3526             :     {
    3527         758 :         CPLString osTarget;
    3528             :         VSIStatBufL sStat;
    3529             : 
    3530         758 :         if (isR0File)
    3531             :         {
    3532           9 :             osTarget = pszNITFFilename;
    3533           9 :             osTarget.back() = static_cast<char>('0' + i);
    3534             :         }
    3535             :         else
    3536         749 :             osTarget.Printf("%s.r%d", pszNITFFilename, i);
    3537             : 
    3538         758 :         if (papszSiblingFiles == nullptr)
    3539             :         {
    3540          12 :             if (VSIStatL(osTarget, &sStat) != 0)
    3541          12 :                 break;
    3542             :         }
    3543             :         else
    3544             :         {
    3545         746 :             if (CSLFindStringCaseSensitive(papszSiblingFiles,
    3546         746 :                                            CPLGetFilename(osTarget)) < 0)
    3547         740 :                 break;
    3548             :         }
    3549             : 
    3550           6 :         aosRSetFilenames.push_back(std::move(osTarget));
    3551             :     }
    3552             : 
    3553         752 :     if (aosRSetFilenames.empty())
    3554             :     {
    3555             :         //try for remoteview RRDS (with .rv%d extension)
    3556         749 :         for (int i = 1; i <= 7; i++)
    3557             :         {
    3558         749 :             CPLString osTarget;
    3559             :             VSIStatBufL sStat;
    3560             : 
    3561         749 :             osTarget.Printf("%s.rv%d", pszNITFFilename, i);
    3562             : 
    3563         749 :             if (VSIStatL(osTarget, &sStat) != 0)
    3564         749 :                 break;
    3565             : 
    3566           0 :             aosRSetFilenames.push_back(std::move(osTarget));
    3567             :         }
    3568             : 
    3569         749 :         if (aosRSetFilenames.empty())
    3570         749 :             return FALSE;
    3571             :     }
    3572             : 
    3573             :     /* -------------------------------------------------------------------- */
    3574             :     /*      We do, so try to create a wrapping VRT file.                    */
    3575             :     /* -------------------------------------------------------------------- */
    3576           3 :     CPLString osFragment;
    3577             : 
    3578             :     osRSetVRT.Printf("<VRTDataset rasterXSize=\"%d\" rasterYSize=\"%d\">\n",
    3579           3 :                      GetRasterXSize() / 2, GetRasterYSize() / 2);
    3580             : 
    3581          12 :     for (int iBand = 0; iBand < GetRasterCount(); iBand++)
    3582             :     {
    3583           9 :         GDALRasterBand *poBand = GetRasterBand(iBand + 1);
    3584             : 
    3585             :         osRSetVRT += osFragment.Printf(
    3586             :             "  <VRTRasterBand dataType=\"%s\" band=\"%d\">\n",
    3587           9 :             GDALGetDataTypeName(poBand->GetRasterDataType()), iBand + 1);
    3588             : 
    3589          27 :         for (int i = 0; i < static_cast<int>(aosRSetFilenames.size()); i++)
    3590             :         {
    3591             :             char *pszEscaped =
    3592          18 :                 CPLEscapeString(aosRSetFilenames[i].c_str(), -1, CPLES_XML);
    3593          18 :             if (i == 0)
    3594             :                 osRSetVRT +=
    3595             :                     osFragment.Printf("    "
    3596             :                                       "<SimpleSource><SourceFilename>%s</"
    3597             :                                       "SourceFilename><SourceBand>%d</"
    3598             :                                       "SourceBand></SimpleSource>\n",
    3599           9 :                                       pszEscaped, iBand + 1);
    3600             :             else
    3601             :                 osRSetVRT += osFragment.Printf(
    3602             :                     "    "
    3603             :                     "<Overview><SourceFilename>%s</"
    3604             :                     "SourceFilename><SourceBand>%d</SourceBand></Overview>\n",
    3605           9 :                     pszEscaped, iBand + 1);
    3606          18 :             CPLFree(pszEscaped);
    3607             :         }
    3608           9 :         osRSetVRT += osFragment.Printf("  </VRTRasterBand>\n");
    3609             :     }
    3610             : 
    3611           3 :     osRSetVRT += "</VRTDataset>\n";
    3612             : 
    3613           3 :     return TRUE;
    3614             : }
    3615             : 
    3616             : /************************************************************************/
    3617             : /*                          IBuildOverviews()                           */
    3618             : /************************************************************************/
    3619             : 
    3620           5 : CPLErr NITFDataset::IBuildOverviews(const char *pszResampling, int nOverviews,
    3621             :                                     const int *panOverviewList, int nListBands,
    3622             :                                     const int *panBandList,
    3623             :                                     GDALProgressFunc pfnProgress,
    3624             :                                     void *pProgressData,
    3625             :                                     CSLConstList papszOptions)
    3626             : 
    3627             : {
    3628             :     /* -------------------------------------------------------------------- */
    3629             :     /*      If we have been using RSets we will need to clear them first.   */
    3630             :     /* -------------------------------------------------------------------- */
    3631           5 :     if (!osRSetVRT.empty())
    3632             :     {
    3633           1 :         oOvManager.CleanOverviews();
    3634           1 :         osRSetVRT = "";
    3635             :     }
    3636             : 
    3637           5 :     bExposeUnderlyingJPEGDatasetOverviews = FALSE;
    3638             : 
    3639             :     /* -------------------------------------------------------------------- */
    3640             :     /*      If we have an underlying JPEG2000 dataset (hopefully via        */
    3641             :     /*      JP2KAK) we will try and build zero overviews as a way of        */
    3642             :     /*      tricking it into clearing existing overviews-from-jpeg2000.     */
    3643             :     /* -------------------------------------------------------------------- */
    3644           7 :     if (poJ2KDataset != nullptr &&
    3645           2 :         !poJ2KDataset->GetMetadataItem("OVERVIEW_FILE", "OVERVIEWS"))
    3646           2 :         poJ2KDataset->BuildOverviews(pszResampling, 0, nullptr, nListBands,
    3647             :                                      panBandList, GDALDummyProgress, nullptr,
    3648             :                                      /* papszOptions = */ nullptr);
    3649             : 
    3650             :     /* -------------------------------------------------------------------- */
    3651             :     /*      Use the overview manager to build requested overviews.          */
    3652             :     /* -------------------------------------------------------------------- */
    3653           5 :     CPLErr eErr = GDALPamDataset::IBuildOverviews(
    3654             :         pszResampling, nOverviews, panOverviewList, nListBands, panBandList,
    3655             :         pfnProgress, pProgressData, papszOptions);
    3656             : 
    3657             :     /* -------------------------------------------------------------------- */
    3658             :     /*      If we are working with jpeg or jpeg2000, let the underlying     */
    3659             :     /*      dataset know about the overview file.                           */
    3660             :     /* -------------------------------------------------------------------- */
    3661           5 :     GDALDataset *poSubDS = poJ2KDataset.get();
    3662           5 :     if (poJPEGDataset)
    3663           1 :         poSubDS = poJPEGDataset.get();
    3664             : 
    3665           5 :     const char *pszOverviewFile = GetMetadataItem("OVERVIEW_FILE", "OVERVIEWS");
    3666             : 
    3667           8 :     if (poSubDS && pszOverviewFile != nullptr && eErr == CE_None &&
    3668           3 :         poSubDS->GetMetadataItem("OVERVIEW_FILE", "OVERVIEWS") == nullptr)
    3669             :     {
    3670           3 :         poSubDS->SetMetadataItem("OVERVIEW_FILE", pszOverviewFile, "OVERVIEWS");
    3671             :     }
    3672             : 
    3673           5 :     return eErr;
    3674             : }
    3675             : 
    3676             : /************************************************************************/
    3677             : /*                           ScanJPEGQLevel()                           */
    3678             : /*                                                                      */
    3679             : /*      Search the NITF APP header in the jpeg data stream to find      */
    3680             : /*      out what predefined Q level tables should be used (or -1 if     */
    3681             : /*      they are inline).                                               */
    3682             : /************************************************************************/
    3683             : 
    3684          24 : int NITFDataset::ScanJPEGQLevel(GUIntBig *pnDataStart, bool *pbError)
    3685             : 
    3686             : {
    3687          24 :     if (VSIFSeekL(psFile->fp, *pnDataStart, SEEK_SET) != 0)
    3688             :     {
    3689           0 :         CPLError(CE_Failure, CPLE_FileIO, "Seek error to jpeg data stream.");
    3690           0 :         *pbError = true;
    3691           0 :         return 0;
    3692             :     }
    3693             : 
    3694             :     GByte abyHeader[100];
    3695          24 :     if (VSIFReadL(abyHeader, 1, sizeof(abyHeader), psFile->fp) <
    3696             :         sizeof(abyHeader))
    3697             :     {
    3698           0 :         CPLError(CE_Failure, CPLE_FileIO, "Read error to jpeg data stream.");
    3699           0 :         *pbError = true;
    3700           0 :         return 0;
    3701             :     }
    3702             : 
    3703             :     /* -------------------------------------------------------------------- */
    3704             :     /*      Scan ahead for jpeg magic code.  In some files (eg. NSIF)       */
    3705             :     /*      there seems to be some extra junk before the image data stream. */
    3706             :     /* -------------------------------------------------------------------- */
    3707          24 :     GUInt32 nOffset = 0;
    3708          24 :     while (nOffset < sizeof(abyHeader) - 23 &&
    3709          24 :            (abyHeader[nOffset + 0] != 0xff || abyHeader[nOffset + 1] != 0xd8 ||
    3710          24 :             abyHeader[nOffset + 2] != 0xff))
    3711           0 :         nOffset++;
    3712             : 
    3713          24 :     if (nOffset >= sizeof(abyHeader) - 23)
    3714             :     {
    3715           0 :         *pbError = true;
    3716           0 :         return 0;
    3717             :     }
    3718             : 
    3719          24 :     *pbError = false;
    3720          24 :     *pnDataStart += nOffset;
    3721             : 
    3722          24 :     if (nOffset > 0)
    3723           0 :         CPLDebug("NITF",
    3724             :                  "JPEG data stream at offset %d from start of data segment, "
    3725             :                  "NSIF?",
    3726             :                  nOffset);
    3727             : 
    3728             :     /* -------------------------------------------------------------------- */
    3729             :     /*      Do we have an NITF app tag?  If so, pull out the Q level.       */
    3730             :     /* -------------------------------------------------------------------- */
    3731          24 :     if (memcmp(abyHeader + nOffset + 6, "NITF\0", 5) != 0)
    3732           5 :         return 0;
    3733             : 
    3734          19 :     return abyHeader[22 + nOffset];
    3735             : }
    3736             : 
    3737             : /************************************************************************/
    3738             : /*                           ScanJPEGBlocks()                           */
    3739             : /************************************************************************/
    3740             : 
    3741           2 : CPLErr NITFDataset::ScanJPEGBlocks()
    3742             : 
    3743             : {
    3744           2 :     GUIntBig nJPEGStart =
    3745           2 :         psFile->pasSegmentInfo[psImage->iSegment].nSegmentStart;
    3746           2 :     bool bError = false;
    3747           2 :     nQLevel = ScanJPEGQLevel(&nJPEGStart, &bError);
    3748           2 :     if (bError)
    3749             :     {
    3750           0 :         return CE_Failure;
    3751             :     }
    3752             : 
    3753             :     /* -------------------------------------------------------------------- */
    3754             :     /*      Allocate offset array                                           */
    3755             :     /* -------------------------------------------------------------------- */
    3756           2 :     panJPEGBlockOffset = static_cast<vsi_l_offset *>(VSI_CALLOC_VERBOSE(
    3757             :         sizeof(vsi_l_offset), static_cast<size_t>(psImage->nBlocksPerRow) *
    3758             :                                   psImage->nBlocksPerColumn));
    3759           2 :     if (panJPEGBlockOffset == nullptr)
    3760             :     {
    3761           0 :         return CE_Failure;
    3762             :     }
    3763           2 :     panJPEGBlockOffset[0] = nJPEGStart;
    3764             : 
    3765           2 :     if (psImage->nBlocksPerRow * psImage->nBlocksPerColumn == 1)
    3766           0 :         return CE_None;
    3767             : 
    3768           2 :     for (int iBlock = psImage->nBlocksPerRow * psImage->nBlocksPerColumn - 1;
    3769          44 :          iBlock > 0; iBlock--)
    3770          42 :         panJPEGBlockOffset[iBlock] = -1;
    3771             : 
    3772             :     /* -------------------------------------------------------------------- */
    3773             :     /*      Scan through the whole image data stream identifying all        */
    3774             :     /*      block boundaries.  Each block starts with 0xFFD8 (SOI).         */
    3775             :     /*      They also end with 0xFFD9, but we don't currently look for      */
    3776             :     /*      that.                                                           */
    3777             :     /* -------------------------------------------------------------------- */
    3778           2 :     int iNextBlock = 1;
    3779           2 :     GIntBig iSegOffset = 2;
    3780           2 :     if (psFile->pasSegmentInfo[psImage->iSegment].nSegmentSize <
    3781           2 :         nJPEGStart - psFile->pasSegmentInfo[psImage->iSegment].nSegmentStart)
    3782           0 :         return CE_Failure;
    3783           2 :     GIntBig iSegSize =
    3784           2 :         psFile->pasSegmentInfo[psImage->iSegment].nSegmentSize -
    3785           2 :         (nJPEGStart - psFile->pasSegmentInfo[psImage->iSegment].nSegmentStart);
    3786             :     GByte abyBlock[512];
    3787           2 :     int ignoreBytes = 0;
    3788             : 
    3789          65 :     while (iSegOffset < iSegSize - 1)
    3790             :     {
    3791             :         const size_t nReadSize = std::min(
    3792          65 :             sizeof(abyBlock), static_cast<size_t>(iSegSize - iSegOffset));
    3793             : 
    3794          65 :         if (VSIFSeekL(psFile->fp, panJPEGBlockOffset[0] + iSegOffset,
    3795          65 :                       SEEK_SET) != 0)
    3796             :         {
    3797           0 :             CPLError(CE_Failure, CPLE_FileIO,
    3798             :                      "Seek error to jpeg data stream.");
    3799           0 :             return CE_Failure;
    3800             :         }
    3801             : 
    3802          65 :         if (VSIFReadL(abyBlock, 1, nReadSize, psFile->fp) < nReadSize)
    3803             :         {
    3804           0 :             CPLError(CE_Failure, CPLE_FileIO,
    3805             :                      "Read error to jpeg data stream.");
    3806           0 :             return CE_Failure;
    3807             :         }
    3808             : 
    3809       32563 :         for (size_t i = 0; i < nReadSize - 1; i++)
    3810             :         {
    3811       32500 :             if (ignoreBytes == 0)
    3812             :             {
    3813       32446 :                 if (abyBlock[i] == 0xff)
    3814             :                 {
    3815             :                     /* start-of-image marker */
    3816         759 :                     if (abyBlock[i + 1] == 0xd8)
    3817             :                     {
    3818          42 :                         panJPEGBlockOffset[iNextBlock++] =
    3819          42 :                             panJPEGBlockOffset[0] + iSegOffset + i;
    3820             : 
    3821          42 :                         if (iNextBlock ==
    3822          42 :                             psImage->nBlocksPerRow * psImage->nBlocksPerColumn)
    3823             :                         {
    3824           2 :                             return CE_None;
    3825             :                         }
    3826             :                     }
    3827             :                     /* Skip application-specific data to avoid false positive
    3828             :                      * while detecting */
    3829             :                     /* start-of-image markers (#2927). The size of the
    3830             :                      * application data is */
    3831             :                     /* found in the two following bytes */
    3832             :                     /* We need this complex mechanism of ignoreBytes for dealing
    3833             :                      * with */
    3834             :                     /* application data crossing several abyBlock ... */
    3835         717 :                     else if (abyBlock[i + 1] >= 0xe0 && abyBlock[i + 1] < 0xf0)
    3836             :                     {
    3837           2 :                         ignoreBytes = -2;
    3838             :                     }
    3839             :                 }
    3840             :             }
    3841          54 :             else if (ignoreBytes < 0)
    3842             :             {
    3843           4 :                 if (ignoreBytes == -1)
    3844             :                 {
    3845             :                     /* Size of the application data */
    3846           2 :                     ignoreBytes = abyBlock[i] * 256 + abyBlock[i + 1];
    3847             :                 }
    3848             :                 else
    3849           2 :                     ignoreBytes++;
    3850             :             }
    3851             :             else
    3852             :             {
    3853          50 :                 ignoreBytes--;
    3854             :             }
    3855             :         }
    3856             : 
    3857          63 :         iSegOffset += nReadSize - 1;
    3858             :     }
    3859             : 
    3860           0 :     return CE_None;
    3861             : }
    3862             : 
    3863             : /************************************************************************/
    3864             : /*                           ReadJPEGBlock()                            */
    3865             : /************************************************************************/
    3866             : 
    3867         128 : CPLErr NITFDataset::ReadJPEGBlock(int iBlockX, int iBlockY)
    3868             : 
    3869             : {
    3870             :     CPLErr eErr;
    3871             : 
    3872             :     /* -------------------------------------------------------------------- */
    3873             :     /*      If this is our first request, do a scan for block boundaries.   */
    3874             :     /* -------------------------------------------------------------------- */
    3875         128 :     if (panJPEGBlockOffset == nullptr)
    3876             :     {
    3877           3 :         if (EQUAL(psImage->szIC, "M3"))
    3878             :         {
    3879             :             /* --------------------------------------------------------------------
    3880             :              */
    3881             :             /*      When a data mask subheader is present, we don't need to scan
    3882             :              */
    3883             :             /*      the whole file. We just use the psImage->panBlockStart table
    3884             :              */
    3885             :             /* --------------------------------------------------------------------
    3886             :              */
    3887           1 :             panJPEGBlockOffset = static_cast<vsi_l_offset *>(
    3888           1 :                 VSI_CALLOC_VERBOSE(sizeof(vsi_l_offset),
    3889             :                                    static_cast<size_t>(psImage->nBlocksPerRow) *
    3890             :                                        psImage->nBlocksPerColumn));
    3891           1 :             if (panJPEGBlockOffset == nullptr)
    3892             :             {
    3893           0 :                 return CE_Failure;
    3894             :             }
    3895           5 :             for (int i = 0;
    3896           5 :                  i < psImage->nBlocksPerRow * psImage->nBlocksPerColumn; i++)
    3897             :             {
    3898           4 :                 panJPEGBlockOffset[i] = psImage->panBlockStart[i];
    3899           4 :                 if (panJPEGBlockOffset[i] != static_cast<vsi_l_offset>(-1) &&
    3900           4 :                     panJPEGBlockOffset[i] != UINT_MAX)
    3901             :                 {
    3902           4 :                     vsi_l_offset nOffset = panJPEGBlockOffset[i];
    3903           4 :                     bool bError = false;
    3904           4 :                     nQLevel = ScanJPEGQLevel(&nOffset, &bError);
    3905             :                     /* The beginning of the JPEG stream should be the offset */
    3906             :                     /* from the panBlockStart table */
    3907           4 :                     if (bError || nOffset != panJPEGBlockOffset[i])
    3908             :                     {
    3909           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    3910             :                                  "JPEG block doesn't start at expected offset");
    3911           0 :                         return CE_Failure;
    3912             :                     }
    3913             :                 }
    3914             :             }
    3915             :         }
    3916             :         else /* 'C3' case */
    3917             :         {
    3918             :             /* --------------------------------------------------------------------
    3919             :              */
    3920             :             /*      Scan through the whole image data stream identifying all */
    3921             :             /*      block boundaries. */
    3922             :             /* --------------------------------------------------------------------
    3923             :              */
    3924           2 :             eErr = ScanJPEGBlocks();
    3925           2 :             if (eErr != CE_None)
    3926           0 :                 return eErr;
    3927             :         }
    3928             :     }
    3929             : 
    3930             :     /* -------------------------------------------------------------------- */
    3931             :     /*    Allocate image data block (where the uncompressed image will go)  */
    3932             :     /* -------------------------------------------------------------------- */
    3933         128 :     if (pabyJPEGBlock == nullptr)
    3934             :     {
    3935             :         /* Allocate enough memory to hold 12bit JPEG data */
    3936           3 :         pabyJPEGBlock = static_cast<GByte *>(VSI_CALLOC_VERBOSE(
    3937             :             psImage->nBands, static_cast<size_t>(psImage->nBlockWidth) *
    3938             :                                  psImage->nBlockHeight * 2));
    3939           3 :         if (pabyJPEGBlock == nullptr)
    3940             :         {
    3941           0 :             return CE_Failure;
    3942             :         }
    3943             :     }
    3944             : 
    3945             :     /* -------------------------------------------------------------------- */
    3946             :     /*      Read JPEG Chunk.                                                */
    3947             :     /* -------------------------------------------------------------------- */
    3948         128 :     const int iBlock = iBlockX + iBlockY * psImage->nBlocksPerRow;
    3949             : 
    3950         128 :     if (panJPEGBlockOffset[iBlock] == static_cast<vsi_l_offset>(-1) ||
    3951         128 :         panJPEGBlockOffset[iBlock] == UINT_MAX)
    3952             :     {
    3953           0 :         memset(pabyJPEGBlock, 0,
    3954           0 :                static_cast<size_t>(psImage->nBands) * psImage->nBlockWidth *
    3955           0 :                    psImage->nBlockHeight * 2);
    3956           0 :         return CE_None;
    3957             :     }
    3958             : 
    3959         256 :     CPLString osFilename;
    3960             :     osFilename.Printf("JPEG_SUBFILE:Q%d," CPL_FRMT_GUIB ",%d,%s", nQLevel,
    3961         128 :                       panJPEGBlockOffset[iBlock], 0, osNITFFilename.c_str());
    3962             : 
    3963             :     GDALDataset *poDS =
    3964         128 :         GDALDataset::Open(osFilename, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR);
    3965         128 :     if (poDS == nullptr)
    3966           0 :         return CE_Failure;
    3967             : 
    3968         256 :     if (poDS->GetRasterXSize() != psImage->nBlockWidth ||
    3969         128 :         poDS->GetRasterYSize() != psImage->nBlockHeight)
    3970             :     {
    3971           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3972             :                  "JPEG block %d not same size as NITF blocksize.", iBlock);
    3973           0 :         delete poDS;
    3974           0 :         return CE_Failure;
    3975             :     }
    3976             : 
    3977         128 :     if (poDS->GetRasterCount() < psImage->nBands)
    3978             :     {
    3979           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3980             :                  "JPEG block %d has not enough bands.", iBlock);
    3981           0 :         delete poDS;
    3982           0 :         return CE_Failure;
    3983             :     }
    3984             : 
    3985         128 :     if (poDS->GetRasterBand(1)->GetRasterDataType() !=
    3986         128 :         GetRasterBand(1)->GetRasterDataType())
    3987             :     {
    3988           0 :         CPLError(
    3989             :             CE_Failure, CPLE_AppDefined,
    3990             :             "JPEG block %d data type (%s) not consistent with band data type "
    3991             :             "(%s).",
    3992             :             iBlock,
    3993             :             GDALGetDataTypeName(poDS->GetRasterBand(1)->GetRasterDataType()),
    3994             :             GDALGetDataTypeName(GetRasterBand(1)->GetRasterDataType()));
    3995           0 :         delete poDS;
    3996           0 :         return CE_Failure;
    3997             :     }
    3998             : 
    3999         128 :     int anBands[3] = {1, 2, 3};
    4000         128 :     eErr = poDS->RasterIO(GF_Read, 0, 0, psImage->nBlockWidth,
    4001         128 :                           psImage->nBlockHeight, pabyJPEGBlock,
    4002         128 :                           psImage->nBlockWidth, psImage->nBlockHeight,
    4003             :                           GetRasterBand(1)->GetRasterDataType(),
    4004         128 :                           psImage->nBands, anBands, 0, 0, 0, nullptr);
    4005             : 
    4006         128 :     delete poDS;
    4007             : 
    4008         128 :     return eErr;
    4009             : }
    4010             : 
    4011             : /************************************************************************/
    4012             : /*                            GetFileList()                             */
    4013             : /************************************************************************/
    4014             : 
    4015          73 : char **NITFDataset::GetFileList()
    4016             : 
    4017             : {
    4018          73 :     char **papszFileList = GDALPamDataset::GetFileList();
    4019             : 
    4020             :     // Small optimization to avoid useless file probing.
    4021          73 :     if (CSLCount(papszFileList) == 0)
    4022           0 :         return papszFileList;
    4023             : 
    4024             :     /* -------------------------------------------------------------------- */
    4025             :     /*      Check for .imd file.                                            */
    4026             :     /* -------------------------------------------------------------------- */
    4027          73 :     papszFileList = AddFile(papszFileList, "IMD", "imd");
    4028             : 
    4029             :     /* -------------------------------------------------------------------- */
    4030             :     /*      Check for .rpb file.                                            */
    4031             :     /* -------------------------------------------------------------------- */
    4032          73 :     papszFileList = AddFile(papszFileList, "RPB", "rpb");
    4033             : 
    4034          73 :     if (!m_osRPCTXTFilename.empty())
    4035           3 :         papszFileList = CSLAddString(papszFileList, m_osRPCTXTFilename);
    4036             : 
    4037             :     /* -------------------------------------------------------------------- */
    4038             :     /*      Check for other files.                                          */
    4039             :     /* -------------------------------------------------------------------- */
    4040          73 :     papszFileList = AddFile(papszFileList, "ATT", "att");
    4041          73 :     papszFileList = AddFile(papszFileList, "EPH", "eph");
    4042          73 :     papszFileList = AddFile(papszFileList, "GEO", "geo");
    4043          73 :     papszFileList = AddFile(papszFileList, "XML", "xml");
    4044             : 
    4045          73 :     return papszFileList;
    4046             : }
    4047             : 
    4048             : /************************************************************************/
    4049             : /*                              AddFile()                               */
    4050             : /*                                                                      */
    4051             : /*      Helper method for GetFileList()                                 */
    4052             : /************************************************************************/
    4053         438 : char **NITFDataset::AddFile(char **papszFileList, const char *EXTENSION,
    4054             :                             const char *extension)
    4055             : {
    4056             :     VSIStatBufL sStatBuf;
    4057         438 :     CPLString osTarget = CPLResetExtensionSafe(osNITFFilename, EXTENSION);
    4058         438 :     if (oOvManager.GetSiblingFiles() != nullptr)
    4059             :     {
    4060         438 :         if (CSLFindStringCaseSensitive(oOvManager.GetSiblingFiles(),
    4061         438 :                                        CPLGetFilename(osTarget)) >= 0)
    4062           0 :             papszFileList = CSLAddString(papszFileList, osTarget);
    4063             :         else
    4064             :         {
    4065         438 :             osTarget = CPLResetExtensionSafe(osNITFFilename, extension);
    4066         438 :             if (CSLFindStringCaseSensitive(oOvManager.GetSiblingFiles(),
    4067         438 :                                            CPLGetFilename(osTarget)) >= 0)
    4068           0 :                 papszFileList = CSLAddString(papszFileList, osTarget);
    4069             :         }
    4070             :     }
    4071             :     else
    4072             :     {
    4073           0 :         if (VSIStatL(osTarget, &sStatBuf) == 0)
    4074           0 :             papszFileList = CSLAddString(papszFileList, osTarget);
    4075             :         else
    4076             :         {
    4077           0 :             osTarget = CPLResetExtensionSafe(osNITFFilename, extension);
    4078           0 :             if (VSIStatL(osTarget, &sStatBuf) == 0)
    4079           0 :                 papszFileList = CSLAddString(papszFileList, osTarget);
    4080             :         }
    4081             :     }
    4082             : 
    4083         876 :     return papszFileList;
    4084             : }
    4085             : 
    4086             : /************************************************************************/
    4087             : /*                         GDALToNITFDataType()                         */
    4088             : /************************************************************************/
    4089             : 
    4090         314 : static const char *GDALToNITFDataType(GDALDataType eType)
    4091             : 
    4092             : {
    4093         314 :     const char *pszPVType = nullptr;
    4094             : 
    4095         314 :     switch (eType)
    4096             :     {
    4097         277 :         case GDT_UInt8:
    4098             :         case GDT_UInt16:
    4099             :         case GDT_UInt32:
    4100         277 :             pszPVType = "INT";
    4101         277 :             break;
    4102             : 
    4103          10 :         case GDT_Int16:
    4104             :         case GDT_Int32:
    4105          10 :             pszPVType = "SI";
    4106          10 :             break;
    4107             : 
    4108           9 :         case GDT_Float32:
    4109             :         case GDT_Float64:
    4110           9 :             pszPVType = "R";
    4111           9 :             break;
    4112             : 
    4113           6 :         case GDT_CInt16:
    4114             :         case GDT_CInt32:
    4115           6 :             CPLError(CE_Failure, CPLE_AppDefined,
    4116             :                      "NITF format does not support complex integer data.");
    4117           6 :             return nullptr;
    4118             : 
    4119           3 :         case GDT_CFloat32:
    4120           3 :             pszPVType = "C";
    4121           3 :             break;
    4122             : 
    4123           9 :         default:
    4124           9 :             CPLError(CE_Failure, CPLE_AppDefined,
    4125             :                      "Unsupported raster pixel type (%s).",
    4126             :                      GDALGetDataTypeName(eType));
    4127           9 :             return nullptr;
    4128             :     }
    4129             : 
    4130         299 :     return pszPVType;
    4131             : }
    4132             : 
    4133             : /************************************************************************/
    4134             : /*                          NITFJP2ECWOptions()                         */
    4135             : /*                                                                      */
    4136             : /*      Prepare JP2-in-NITF creation options based in part of the       */
    4137             : /*      NITF creation options.                                          */
    4138             : /************************************************************************/
    4139             : 
    4140           4 : static CPLStringList NITFJP2ECWOptions(const CPLStringList &aosOptionsIn)
    4141             : 
    4142             : {
    4143           4 :     CPLStringList aoJP2ECWOptions;
    4144           4 :     aoJP2ECWOptions.AddString("PROFILE=NPJE");
    4145           4 :     aoJP2ECWOptions.AddString("CODESTREAM_ONLY=TRUE");
    4146             : 
    4147          19 :     for (int i = 0; i < aosOptionsIn.size(); ++i)
    4148             :     {
    4149          15 :         if (STARTS_WITH_CI(aosOptionsIn[i], "PROFILE="))
    4150             :         {
    4151             :             aoJP2ECWOptions.SetNameValue("PROFILE",
    4152           0 :                                          aosOptionsIn[i] + strlen("PROFILE="));
    4153             :         }
    4154          15 :         else if (STARTS_WITH_CI(aosOptionsIn[i], "TARGET="))
    4155           3 :             aoJP2ECWOptions.AddString(aosOptionsIn[i]);
    4156             :     }
    4157             : 
    4158           4 :     return aoJP2ECWOptions;
    4159             : }
    4160             : 
    4161             : /************************************************************************/
    4162             : /*                           NITFJP2KAKOptions()                        */
    4163             : /*                                                                      */
    4164             : /*      Prepare JP2-in-NITF creation options based in part of the       */
    4165             : /*      NITF creation options.                                          */
    4166             : /************************************************************************/
    4167             : 
    4168           0 : static CPLStringList NITFJP2KAKOptions(const CPLStringList &aosOptionsIn,
    4169             :                                        int nABPP)
    4170             : 
    4171             : {
    4172           0 :     CPLStringList aoJP2KAKOptions;
    4173           0 :     aoJP2KAKOptions.AddString("CODEC=J2K");
    4174             : 
    4175           0 :     for (int i = 0; i < aosOptionsIn.size(); ++i)
    4176             :     {
    4177           0 :         if (STARTS_WITH_CI(aosOptionsIn[i], "QUALITY=") ||
    4178           0 :             STARTS_WITH_CI(aosOptionsIn[i], "BLOCKXSIZE=") ||
    4179           0 :             STARTS_WITH_CI(aosOptionsIn[i], "BLOCKYSIZE=") ||
    4180           0 :             STARTS_WITH_CI(aosOptionsIn[i], "LAYERS=") ||
    4181           0 :             STARTS_WITH_CI(aosOptionsIn[i], "ROI="))
    4182             :         {
    4183           0 :             aoJP2KAKOptions.AddString(aosOptionsIn[i]);
    4184             :         }
    4185             :     }
    4186             : 
    4187           0 :     aoJP2KAKOptions.SetNameValue(GDALMD_NBITS, CPLSPrintf("%d", nABPP));
    4188             : 
    4189           0 :     return aoJP2KAKOptions;
    4190             : }
    4191             : 
    4192             : /************************************************************************/
    4193             : /*                      NITFJP2OPENJPEGOptions()                        */
    4194             : /*                                                                      */
    4195             : /*      Prepare JP2-in-NITF creation options based in part of the       */
    4196             : /*      NITF creation options.                                          */
    4197             : /************************************************************************/
    4198             : 
    4199           8 : static CPLStringList NITFJP2OPENJPEGOptions(GDALDriver *poJ2KDriver,
    4200             :                                             const CPLStringList &aosOptionsIn,
    4201             :                                             int nABPP)
    4202             : 
    4203             : {
    4204           8 :     CPLStringList aoJP2OJPOptions;
    4205           8 :     aoJP2OJPOptions.AddString("CODEC=J2K");
    4206             : 
    4207           8 :     const char *pszQuality = aosOptionsIn.FetchNameValue("QUALITY");
    4208           8 :     double dfQuality = 0;
    4209           8 :     if (pszQuality)
    4210             :     {
    4211          16 :         for (const char *pszVal :
    4212          13 :              CPLStringList(CSLTokenizeString2(pszQuality, ",", 0)))
    4213           8 :             dfQuality = std::max(dfQuality, CPLAtof(pszVal));
    4214             :     }
    4215             : 
    4216           8 :     double dfTarget = CPLAtof(aosOptionsIn.FetchNameValueDef("TARGET", "0"));
    4217             : 
    4218           8 :     if (dfTarget > 0 && dfTarget < 100)
    4219           0 :         dfQuality = 100. - dfTarget;
    4220             : 
    4221          63 :     for (int i = 0; i < aosOptionsIn.size(); ++i)
    4222             :     {
    4223         105 :         if (STARTS_WITH_CI(aosOptionsIn[i], "BLOCKXSIZE=") ||
    4224          50 :             STARTS_WITH_CI(aosOptionsIn[i], "BLOCKYSIZE="))
    4225             :         {
    4226          10 :             aoJP2OJPOptions.AddString(aosOptionsIn[i]);
    4227             :         }
    4228             :     }
    4229             : 
    4230             :     // Set it now before the NPJE profiles have a chance to override it
    4231           8 :     if (pszQuality)
    4232             :     {
    4233           5 :         aoJP2OJPOptions.SetNameValue("QUALITY", pszQuality);
    4234             :     }
    4235             : 
    4236           8 :     const char *pszProfile = aosOptionsIn.FetchNameValueDef("PROFILE", "");
    4237           8 :     if (STARTS_WITH_CI(pszProfile, "NPJE"))
    4238             :     {
    4239             :         // Follow STDI-0006 NCDRD "2.3 Data Compression - JPEG 2000" and
    4240             :         // ISO/IEC BIIF Profile BPJ2K01.10
    4241             :         // (https://nsgreg.nga.mil/doc/view?i=2031&month=3&day=22&year=2021),
    4242             :         // for NPJE (Appendix D ) profile
    4243             : 
    4244           4 :         if (pszQuality && strchr(pszQuality, ','))
    4245             :         {
    4246           1 :             CPLError(CE_Warning, CPLE_AppDefined,
    4247             :                      "Only largest value of QUALITY used when PROFILE=%s "
    4248             :                      "is specified",
    4249             :                      pszProfile);
    4250             :         }
    4251             : 
    4252           4 :         aoJP2OJPOptions.AddString("@BLOCKSIZE_STRICT=YES");
    4253             : 
    4254             :         // Empty PRECINCTS option to ask for no custom precincts
    4255           4 :         aoJP2OJPOptions.AddString("PRECINCTS=");
    4256             : 
    4257             : #if defined(__GNUC__)
    4258             : #pragma GCC diagnostic push
    4259             : #pragma GCC diagnostic ignored "-Warray-bounds"
    4260             : #endif
    4261             :         // See Table 2.3-3 - Target Bit Rates for Each Tile in Panchromatic
    4262             :         // Image Segments of STDI-0006
    4263             :         std::vector<double> adfBPP = {
    4264             :             0.03125, 0.0625, 0.125, 0.25, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0,
    4265           8 :             1.1,     1.2,    1.3,   1.5,  1.7, 2.0, 2.3, 3.5, 3.9};
    4266           4 :         if (STARTS_WITH_CI(pszProfile, "NPJE_NUMERICALLY_LOSSLESS"))
    4267             :         {
    4268             :             // given that we consider a compression ratio afterwards, we
    4269             :             // arbitrarily consider a Byte datatype, and thus lossless quality
    4270             :             // is achieved at worse with 8 bpp
    4271           2 :             adfBPP.push_back(8.0);
    4272             : 
    4273             :             // Lossless 5x3 wavelet
    4274           2 :             aoJP2OJPOptions.AddString("REVERSIBLE=YES");
    4275             :         }
    4276             : #if defined(__GNUC__)
    4277             : #pragma GCC diagnostic pop
    4278             : #endif
    4279             : 
    4280           8 :         std::string osQuality;
    4281          80 :         for (double dfBPP : adfBPP)
    4282             :         {
    4283          77 :             if (!osQuality.empty())
    4284          73 :                 osQuality += ',';
    4285             :             // the JP2OPENJPEG QUALITY setting is 100. / compression_ratio
    4286             :             // and compression_ratio = 8 / bpp
    4287          77 :             double dfLayerQuality = 100.0 / (8.0 / dfBPP);
    4288          77 :             if (dfLayerQuality > dfQuality && dfQuality != 0.0)
    4289             :             {
    4290           1 :                 osQuality += CPLSPrintf("%f", dfQuality);
    4291           1 :                 break;
    4292             :             }
    4293          76 :             osQuality += CPLSPrintf("%f", dfLayerQuality);
    4294             :         }
    4295           4 :         aoJP2OJPOptions.SetNameValue("QUALITY", osQuality.c_str());
    4296             : 
    4297           4 :         aoJP2OJPOptions.AddString("PROGRESSION=LRCP");
    4298             : 
    4299             :         // Disable MCT
    4300           4 :         aoJP2OJPOptions.AddString("YCC=NO");
    4301             : 
    4302             :         // TLM option added in OpenJPEG 2.5
    4303           4 :         if (strstr(poJ2KDriver->GetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST),
    4304           4 :                    "TLM") != nullptr)
    4305             :         {
    4306           0 :             aoJP2OJPOptions.AddString("PLT=YES");
    4307           0 :             aoJP2OJPOptions.AddString("TLM=YES");
    4308             :         }
    4309             :         else
    4310             :         {
    4311           4 :             CPLError(CE_Warning, CPLE_AppDefined,
    4312             :                      "TLM option not available in JP2OPENJPEG driver. "
    4313             :                      "Use OpenJPEG 2.5 or later");
    4314             :         }
    4315             : 
    4316           4 :         aoJP2OJPOptions.AddString("RESOLUTIONS=6");
    4317             :     }
    4318           4 :     else if (EQUAL(pszProfile, "PROFILE_1"))
    4319             :     {
    4320           0 :         aoJP2OJPOptions.AddString("PROFILE=PROFILE_1");
    4321             :     }
    4322           4 :     else if (EQUAL(pszProfile, "PROFILE_2"))
    4323             :     {
    4324           0 :         aoJP2OJPOptions.AddString("PROFILE=UNRESTRICTED");
    4325             :     }
    4326             : 
    4327           8 :     aoJP2OJPOptions.SetNameValue(GDALMD_NBITS, CPLSPrintf("%d", nABPP));
    4328             : 
    4329          16 :     return aoJP2OJPOptions;
    4330             : }
    4331             : 
    4332             : /************************************************************************/
    4333             : /*                NITFExtractTEXTAndCGMCreationOption()                 */
    4334             : /************************************************************************/
    4335             : 
    4336         313 : static CPLStringList NITFExtractTEXTAndCGMCreationOption(
    4337             :     GDALDataset *poSrcDS, CSLConstList papszOptions, CPLStringList &aosTextMD,
    4338             :     CPLStringList &aosCgmMD)
    4339             : {
    4340         313 :     CPLStringList aosOptions(CSLDuplicate(papszOptions));
    4341             : 
    4342             :     /* -------------------------------------------------------------------- */
    4343             :     /*      Prepare for text segments.                                      */
    4344             :     /* -------------------------------------------------------------------- */
    4345         313 :     aosTextMD = CSLFetchNameValueMultiple(papszOptions, "TEXT");
    4346             :     // Notice: CSLFetchNameValueMultiple remove the leading "TEXT=" when
    4347             :     // returning the list, which is what we want.
    4348             : 
    4349             :     // Use TEXT information from original image if no creation option is passed
    4350             :     // in.
    4351         313 :     if (poSrcDS != nullptr && aosTextMD.empty())
    4352             :     {
    4353             :         // Read CGM adata from original image, duplicate the list because
    4354             :         // we frees papszCgmMD at end of the function.
    4355         154 :         aosTextMD = CSLDuplicate(poSrcDS->GetMetadata("TEXT"));
    4356             :     }
    4357             : 
    4358         313 :     int nNUMT = 0;
    4359         323 :     for (int iOpt = 0; iOpt < aosTextMD.size(); iOpt++)
    4360             :     {
    4361          10 :         if (!STARTS_WITH_CI(aosTextMD[iOpt], "DATA_"))
    4362           4 :             continue;
    4363             : 
    4364           6 :         nNUMT++;
    4365             :     }
    4366             : 
    4367         313 :     if (nNUMT > 0)
    4368             :     {
    4369           5 :         aosOptions.SetNameValue("NUMT", std::to_string(nNUMT).c_str());
    4370             :     }
    4371             : 
    4372             :     /* -------------------------------------------------------------------- */
    4373             :     /*      Prepare for CGM segments.                                       */
    4374             :     /* -------------------------------------------------------------------- */
    4375         313 :     aosCgmMD = CSLFetchNameValueMultiple(papszOptions, "CGM");
    4376             :     // Notice: CSLFetchNameValueMultiple remove the leading "CGM=" when
    4377             :     // returning the list, which is what we want.
    4378             : 
    4379             :     // Use CGM information from original image if no creation option is passed
    4380             :     // in.
    4381         313 :     if (poSrcDS != nullptr && aosCgmMD.empty())
    4382             :     {
    4383             :         // Read CGM adata from original image, duplicate the list because
    4384             :         // we frees papszCgmMD at end of the function.
    4385         154 :         aosCgmMD = CSLDuplicate(poSrcDS->GetMetadata("CGM"));
    4386             :     }
    4387             : 
    4388             :     // Set NUMS based on the number of segments
    4389             :     const char *pszNUMS;  // graphic segment option string
    4390         313 :     int nNUMS = 0;
    4391         313 :     if (!aosCgmMD.empty())
    4392             :     {
    4393          14 :         pszNUMS = aosCgmMD.FetchNameValue("SEGMENT_COUNT");
    4394             : 
    4395          14 :         if (pszNUMS != nullptr)
    4396             :         {
    4397          14 :             nNUMS = atoi(pszNUMS);
    4398             :         }
    4399          14 :         aosOptions.SetNameValue("NUMS", std::to_string(nNUMS).c_str());
    4400             :     }
    4401             : 
    4402         313 :     return aosOptions;
    4403             : }
    4404             : 
    4405             : /************************************************************************/
    4406             : /*                         NITFDatasetCreate()                          */
    4407             : /************************************************************************/
    4408             : 
    4409         170 : GDALDataset *NITFDataset::NITFDatasetCreate(const char *pszFilename, int nXSize,
    4410             :                                             int nYSize, int nBandsIn,
    4411             :                                             GDALDataType eType,
    4412             :                                             CSLConstList papszOptions)
    4413             : 
    4414             : {
    4415         170 :     const char *pszPVType = GDALToNITFDataType(eType);
    4416         170 :     if (pszPVType == nullptr)
    4417          12 :         return nullptr;
    4418             : 
    4419         158 :     const char *pszProduct = CSLFetchNameValue(papszOptions, "PRODUCT_TYPE");
    4420         158 :     if (pszProduct && EQUAL(pszProduct, "CADRG"))
    4421             :     {
    4422           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    4423             :                  "CADRG creation only supported in CreateCopy()");
    4424           1 :         return nullptr;
    4425             :     }
    4426             : 
    4427         157 :     const char *pszIC = CSLFetchNameValue(papszOptions, "IC");
    4428             : 
    4429             :     /* -------------------------------------------------------------------- */
    4430             :     /*      We disallow any IC value except NC when creating this way.      */
    4431             :     /* -------------------------------------------------------------------- */
    4432         157 :     GDALDriver *poJ2KDriver = nullptr;
    4433             : 
    4434         157 :     if (pszIC != nullptr && EQUAL(pszIC, "C8"))
    4435             :     {
    4436           1 :         bool bHasCreate = false;
    4437             : 
    4438           1 :         poJ2KDriver = GetGDALDriverManager()->GetDriverByName("JP2ECW");
    4439           1 :         if (poJ2KDriver != nullptr)
    4440           1 :             bHasCreate = poJ2KDriver->GetMetadataItem(GDAL_DCAP_CREATE,
    4441           1 :                                                       nullptr) != nullptr;
    4442           1 :         if (!bHasCreate)
    4443             :         {
    4444           0 :             CPLError(
    4445             :                 CE_Failure, CPLE_AppDefined,
    4446             :                 "Unable to create JPEG2000 encoded NITF files.  The\n"
    4447             :                 "JP2ECW driver is unavailable, or missing Create support.");
    4448           0 :             return nullptr;
    4449             :         }
    4450             : 
    4451           1 :         if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "J2KLRA", "NO")))
    4452             :         {
    4453           0 :             CPLError(CE_Warning, CPLE_NotSupported,
    4454             :                      "J2KLRA TRE can only be written in CreateCopy() mode, and "
    4455             :                      "when using the JP2OPENJPEG driver in NPJE profiles");
    4456           1 :         }
    4457             :     }
    4458             : 
    4459         156 :     else if (pszIC != nullptr && !EQUAL(pszIC, "NC"))
    4460             :     {
    4461           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4462             :                  "Unsupported compression (IC=%s) used in direct\n"
    4463             :                  "NITF File creation",
    4464             :                  pszIC);
    4465           0 :         return nullptr;
    4466             :     }
    4467             : 
    4468         157 :     const char *const apszIgnoredOptions[] = {"SDE_TRE", "RPC00B", "RPCTXT",
    4469             :                                               nullptr};
    4470         628 :     for (int i = 0; apszIgnoredOptions[i] != nullptr; ++i)
    4471             :     {
    4472         471 :         if (CSLFetchNameValue(papszOptions, apszIgnoredOptions[i]))
    4473             :         {
    4474           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    4475             :                      "%s creation option ignored by Create() method "
    4476             :                      "(only valid in CreateCopy())",
    4477           0 :                      apszIgnoredOptions[i]);
    4478             :         }
    4479             :     }
    4480             : 
    4481             :     /* -------------------------------------------------------------------- */
    4482             :     /*      Prepare for text and CGM segments.                              */
    4483             :     /* -------------------------------------------------------------------- */
    4484         314 :     CPLStringList aosTextMD, aosCgmMD;
    4485             :     CPLStringList aosOptions(NITFExtractTEXTAndCGMCreationOption(
    4486         314 :         nullptr, papszOptions, aosTextMD, aosCgmMD));
    4487             : 
    4488         157 :     const char *pszBlockSize = aosOptions.FetchNameValue("BLOCKSIZE");
    4489         157 :     if (pszBlockSize != nullptr &&
    4490           0 :         aosOptions.FetchNameValue("BLOCKXSIZE") == nullptr)
    4491             :     {
    4492           0 :         aosOptions.SetNameValue("BLOCKXSIZE", pszBlockSize);
    4493             :     }
    4494         157 :     if (pszBlockSize != nullptr &&
    4495           0 :         aosOptions.FetchNameValue("BLOCKYSIZE") == nullptr)
    4496             :     {
    4497           0 :         aosOptions.SetNameValue("BLOCKYSIZE", pszBlockSize);
    4498             :     }
    4499             : 
    4500         157 :     if (const char *pszNBITS = aosOptions.FetchNameValue(GDALMD_NBITS))
    4501             :     {
    4502           1 :         aosOptions.SetNameValue("ABPP", pszNBITS);
    4503             :     }
    4504             : 
    4505             :     /* -------------------------------------------------------------------- */
    4506             :     /*      Create the file.                                                */
    4507             :     /* -------------------------------------------------------------------- */
    4508             : 
    4509         157 :     int nIMIndex = 0;
    4510         157 :     int nImageCount = 0;
    4511         157 :     vsi_l_offset nImageOffset = 0;
    4512         157 :     vsi_l_offset nICOffset = 0;
    4513         157 :     if (!NITFCreateEx(pszFilename, nXSize, nYSize, nBandsIn,
    4514             :                       GDALGetDataTypeSizeBits(eType), pszPVType,
    4515         157 :                       aosOptions.List(), &nIMIndex, &nImageCount, &nImageOffset,
    4516             :                       &nICOffset, nullptr))
    4517             :     {
    4518           3 :         return nullptr;
    4519             :     }
    4520             : 
    4521             :     /* -------------------------------------------------------------------- */
    4522             :     /*      Various special hacks related to JPEG2000 encoded files.        */
    4523             :     /* -------------------------------------------------------------------- */
    4524         154 :     GDALDataset *poWritableJ2KDataset = nullptr;
    4525         154 :     if (poJ2KDriver)
    4526             :     {
    4527           1 :         CPLString osDSName;
    4528             : 
    4529             :         osDSName.Printf("/vsisubfile/" CPL_FRMT_GUIB "_%d,%s",
    4530           1 :                         static_cast<GUIntBig>(nImageOffset), -1, pszFilename);
    4531             : 
    4532             :         poWritableJ2KDataset =
    4533           1 :             poJ2KDriver->Create(osDSName, nXSize, nYSize, nBandsIn, eType,
    4534           2 :                                 NITFJP2ECWOptions(aosOptions).List());
    4535             : 
    4536           1 :         if (poWritableJ2KDataset == nullptr)
    4537             :         {
    4538           0 :             return nullptr;
    4539             :         }
    4540             :     }
    4541             : 
    4542             :     /* -------------------------------------------------------------------- */
    4543             :     /*      Open the dataset in update mode.                                */
    4544             :     /* -------------------------------------------------------------------- */
    4545         154 :     GDALOpenInfo oOpenInfo(pszFilename, GA_Update);
    4546         154 :     NITFDataset *poDS = NITFDataset::OpenInternal(
    4547             :         &oOpenInfo, poWritableJ2KDataset, true, nIMIndex);
    4548         154 :     if (poDS)
    4549             :     {
    4550         154 :         poDS->m_nImageOffset = nImageOffset;
    4551         154 :         poDS->m_nIMIndex = nIMIndex;
    4552         154 :         poDS->m_nImageCount = nImageCount;
    4553         154 :         poDS->m_nICOffset = nICOffset;
    4554         154 :         poDS->papszTextMDToWrite = aosTextMD.StealList();
    4555         154 :         poDS->papszCgmMDToWrite = aosCgmMD.StealList();
    4556         154 :         poDS->aosCreationOptions.Assign(CSLDuplicate(papszOptions), true);
    4557             :     }
    4558         154 :     return poDS;
    4559             : }
    4560             : 
    4561             : /************************************************************************/
    4562             : /*                           NITFCreateCopy()                           */
    4563             : /************************************************************************/
    4564             : 
    4565         163 : GDALDataset *NITFDataset::NITFCreateCopy(const char *pszFilename,
    4566             :                                          GDALDataset *poSrcDS, int bStrict,
    4567             :                                          CSLConstList papszOptions,
    4568             :                                          GDALProgressFunc pfnProgress,
    4569             :                                          void *pProgressData)
    4570             : {
    4571         163 :     CADRGCreateCopyContext copyContext;
    4572         326 :     return CreateCopy(pszFilename, poSrcDS, bStrict, papszOptions, pfnProgress,
    4573             :                       pProgressData, /* nRecLevel = */ 0, &copyContext)
    4574         326 :         .release();
    4575             : }
    4576             : 
    4577             : /************************************************************************/
    4578             : /*                      NITFDataset::CreateCopy()                       */
    4579             : /************************************************************************/
    4580             : 
    4581             : std::unique_ptr<GDALDataset>
    4582         195 : NITFDataset::CreateCopy(const char *pszFilename, GDALDataset *poSrcDS,
    4583             :                         int bStrict, CSLConstList papszOptions,
    4584             :                         GDALProgressFunc pfnProgress, void *pProgressData,
    4585             :                         int nRecLevel, CADRGCreateCopyContext *copyContext)
    4586             : 
    4587             : {
    4588         195 :     if (nRecLevel == 3)
    4589             :     {
    4590           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4591             :                  "NITFDataset::CreateCopy(): programming error: too deep "
    4592             :                  "recursion");
    4593           0 :         return nullptr;
    4594             :     }
    4595             : 
    4596         195 :     int nBands = poSrcDS->GetRasterCount();
    4597         195 :     if (nBands == 0)
    4598             :     {
    4599           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    4600             :                  "Unable to export files with zero bands.");
    4601           1 :         return nullptr;
    4602             :     }
    4603             : 
    4604         194 :     GDALRasterBand *poBand1 = poSrcDS->GetRasterBand(1);
    4605         194 :     if (poBand1 == nullptr)
    4606             :     {
    4607           0 :         return nullptr;
    4608             :     }
    4609             : 
    4610             :     const char *pszProductType =
    4611         194 :         CSLFetchNameValue(papszOptions, "PRODUCT_TYPE");
    4612         194 :     const bool bIsCADRG = (pszProductType && EQUAL(pszProductType, "CADRG"));
    4613             : 
    4614             :     /* -------------------------------------------------------------------- */
    4615             :     /*      Only allow supported compression values.                        */
    4616             :     /* -------------------------------------------------------------------- */
    4617         194 :     bool bJPEG2000 = false;
    4618         194 :     bool bJPEG = false;
    4619         194 :     GDALDriver *poJ2KDriver = nullptr;
    4620             :     const char *pszJPEG2000_DRIVER =
    4621         194 :         CSLFetchNameValue(papszOptions, "JPEG2000_DRIVER");
    4622         194 :     if (pszJPEG2000_DRIVER != nullptr)
    4623             :         poJ2KDriver =
    4624           4 :             GetGDALDriverManager()->GetDriverByName(pszJPEG2000_DRIVER);
    4625             : 
    4626         194 :     const char *pszIC = CSLFetchNameValue(papszOptions, "IC");
    4627         194 :     if (pszIC != nullptr)
    4628             :     {
    4629          26 :         if (bIsCADRG)
    4630             :         {
    4631           5 :             if (!EQUAL(pszIC, "C4"))
    4632             :             {
    4633           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    4634             :                          "CADRG only supports IC=C4");
    4635           0 :                 return nullptr;
    4636             :             }
    4637             :         }
    4638          21 :         else if (EQUAL(pszIC, "C4"))
    4639             :         {
    4640           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    4641             :                      "IC=C4 only supported for PRODUCT_TYPE=CADRG");
    4642           0 :             return nullptr;
    4643             :         }
    4644             : 
    4645          21 :         else if (EQUAL(pszIC, "NC"))
    4646             :             /* ok */;
    4647          21 :         else if (EQUAL(pszIC, "C8"))
    4648             :         {
    4649          11 :             if (pszJPEG2000_DRIVER == nullptr)
    4650             :             {
    4651           7 :                 poJ2KDriver = GetGDALDriverManager()->GetDriverByName("JP2ECW");
    4652          10 :                 if (poJ2KDriver == nullptr ||
    4653           3 :                     poJ2KDriver->GetMetadataItem(GDAL_DCAP_CREATECOPY,
    4654           3 :                                                  nullptr) == nullptr)
    4655             :                 {
    4656             :                     /* Try with  JP2KAK as an alternate driver */
    4657             :                     poJ2KDriver =
    4658           4 :                         GetGDALDriverManager()->GetDriverByName("JP2KAK");
    4659             :                 }
    4660           7 :                 if (poJ2KDriver == nullptr)
    4661             :                 {
    4662             :                     /* Try with JP2OPENJPEG as an alternate driver */
    4663             :                     poJ2KDriver =
    4664           4 :                         GetGDALDriverManager()->GetDriverByName("JP2OPENJPEG");
    4665             :                 }
    4666             :             }
    4667          11 :             if (poJ2KDriver == nullptr)
    4668             :             {
    4669           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    4670             :                          "Unable to write JPEG2000 compressed NITF file.\n"
    4671             :                          "No 'subfile' JPEG2000 write supporting drivers are\n"
    4672             :                          "configured.");
    4673           0 :                 return nullptr;
    4674             :             }
    4675             : 
    4676          11 :             if (CPLTestBool(CSLFetchNameValueDef(papszOptions, "J2KLRA", "NO")))
    4677             :             {
    4678           0 :                 if (!EQUAL(poJ2KDriver->GetDescription(), "JP2OPENJPEG"))
    4679             :                 {
    4680           0 :                     CPLError(
    4681             :                         CE_Warning, CPLE_NotSupported,
    4682             :                         "J2KLRA TRE can only be written "
    4683             :                         "when using the JP2OPENJPEG driver in NPJE profiles");
    4684             :                 }
    4685           0 :                 else if (!STARTS_WITH_CI(
    4686             :                              CSLFetchNameValueDef(papszOptions, "PROFILE", ""),
    4687             :                              "NPJE"))
    4688             :                 {
    4689           0 :                     CPLError(CE_Warning, CPLE_NotSupported,
    4690             :                              "J2KLRA TRE can only be written in NPJE profiles");
    4691             :                 }
    4692             :             }
    4693          11 :             bJPEG2000 = TRUE;
    4694             :         }
    4695          10 :         else if (EQUAL(pszIC, "C3") || EQUAL(pszIC, "M3"))
    4696             :         {
    4697          10 :             bJPEG = TRUE;
    4698             : #ifndef JPEG_SUPPORTED
    4699             :             CPLError(CE_Failure, CPLE_AppDefined,
    4700             :                      "Unable to write JPEG compressed NITF file.\n"
    4701             :                      "Libjpeg is not configured into build.");
    4702             :             return nullptr;
    4703             : #endif
    4704             :         }
    4705             :         else
    4706             :         {
    4707           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    4708             :                      "Only IC=NC (uncompressed), IC=C3/M3 (JPEG) and IC=C8 "
    4709             :                      "(JPEG2000)\n"
    4710             :                      "allowed with NITF CreateCopy method.");
    4711           0 :             return nullptr;
    4712             :         }
    4713             :     }
    4714             : 
    4715             :     /* -------------------------------------------------------------------- */
    4716             :     /*      Get the data type.  Complex integers isn't supported by         */
    4717             :     /*      NITF, so map that to complex float if we aren't in strict       */
    4718             :     /*      mode.                                                           */
    4719             :     /* -------------------------------------------------------------------- */
    4720         194 :     GDALDataType eType = poBand1->GetRasterDataType();
    4721         194 :     if (!bStrict && (eType == GDT_CInt16 || eType == GDT_CInt32))
    4722           0 :         eType = GDT_CFloat32;
    4723             : 
    4724             :     /* -------------------------------------------------------------------- */
    4725             :     /*      CADRG related checks and pre-processing                         */
    4726             :     /* -------------------------------------------------------------------- */
    4727         194 :     if (bIsCADRG)
    4728             :     {
    4729             :         auto ret =
    4730             :             CADRGCreateCopy(pszFilename, poSrcDS, bStrict, papszOptions,
    4731          71 :                             pfnProgress, pProgressData, nRecLevel, copyContext);
    4732          71 :         if (std::holds_alternative<std::unique_ptr<GDALDataset>>(ret))
    4733             :         {
    4734          24 :             return std::move(std::get<std::unique_ptr<GDALDataset>>(ret));
    4735             :         }
    4736          47 :         CPLAssert(std::holds_alternative<bool>(ret));
    4737          47 :         const bool bGoOn = std::get<bool>(ret);
    4738          47 :         if (!bGoOn)
    4739             :         {
    4740          14 :             return nullptr;
    4741             :         }
    4742             :     }
    4743             : 
    4744             :     /* -------------------------------------------------------------------- */
    4745             :     /*      Prepare for text and CGM segments.                              */
    4746             :     /* -------------------------------------------------------------------- */
    4747         312 :     CPLStringList aosTextMD, aosCgmMD;
    4748             :     CPLStringList aosOptions(NITFExtractTEXTAndCGMCreationOption(
    4749         312 :         poSrcDS, papszOptions, aosTextMD, aosCgmMD));
    4750             : 
    4751         156 :     const char *pszBlockSize = aosOptions.FetchNameValue("BLOCKSIZE");
    4752         156 :     if (bIsCADRG)
    4753             :     {
    4754          33 :         if (pszBlockSize && atoi(pszBlockSize) != 256)
    4755             :         {
    4756           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    4757             :                      "CADRG only supports BLOCKSIZE=256");
    4758           0 :             return nullptr;
    4759             :         }
    4760             :         else
    4761             :         {
    4762          33 :             pszBlockSize = "256";
    4763             :         }
    4764             :     }
    4765         192 :     if (pszBlockSize != nullptr &&
    4766          36 :         aosOptions.FetchNameValue("BLOCKXSIZE") == nullptr)
    4767             :     {
    4768          36 :         aosOptions.SetNameValue("BLOCKXSIZE", pszBlockSize);
    4769             :     }
    4770         192 :     if (pszBlockSize != nullptr &&
    4771          36 :         aosOptions.FetchNameValue("BLOCKYSIZE") == nullptr)
    4772             :     {
    4773          36 :         aosOptions.SetNameValue("BLOCKYSIZE", pszBlockSize);
    4774             :     }
    4775             : 
    4776             :     /* -------------------------------------------------------------------- */
    4777             :     /*      Copy over other source metadata items as creation options       */
    4778             :     /*      that seem useful, unless they are already set as creation       */
    4779             :     /*      options.                                                        */
    4780             :     /* -------------------------------------------------------------------- */
    4781             :     const bool bUseSrcNITFMetadata =
    4782         156 :         CPLFetchBool(papszOptions, "USE_SRC_NITF_METADATA", true);
    4783         156 :     CSLConstList papszSrcMD = poSrcDS->GetMetadata();
    4784             : 
    4785        1100 :     for (int iMD = 0; bUseSrcNITFMetadata && papszSrcMD && papszSrcMD[iMD];
    4786             :          iMD++)
    4787             :     {
    4788         944 :         bool bPreserveSrcMDAsCreationOption = false;
    4789         944 :         if (STARTS_WITH_CI(papszSrcMD[iMD], "NITF_BLOCKA"))
    4790             :         {
    4791          30 :             bPreserveSrcMDAsCreationOption =
    4792          50 :                 CSLPartialFindString(papszOptions, "BLOCKA_") < 0 &&
    4793          20 :                 CSLPartialFindString(papszOptions, "TRE=BLOCKA=") < 0;
    4794             :         }
    4795         914 :         else if (STARTS_WITH_CI(papszSrcMD[iMD], "NITF_FHDR"))
    4796             :         {
    4797          14 :             bPreserveSrcMDAsCreationOption =
    4798          14 :                 CSLFetchNameValue(papszOptions, "FHDR") == nullptr;
    4799             :         }
    4800         944 :         if (bPreserveSrcMDAsCreationOption)
    4801             :         {
    4802          23 :             char *pszName = nullptr;
    4803          23 :             const char *pszValue = CPLParseNameValue(papszSrcMD[iMD], &pszName);
    4804          23 :             if (pszName && aosOptions.FetchNameValue(pszName + 5) == nullptr)
    4805             :             {
    4806          23 :                 aosOptions.SetNameValue(pszName + 5, pszValue);
    4807             :             }
    4808          23 :             CPLFree(pszName);
    4809             :         }
    4810             :     }
    4811             : 
    4812             :     /* -------------------------------------------------------------------- */
    4813             :     /*      Copy TRE definitions as creation options, unless they are       */
    4814             :     /*      already set as creation options.                                */
    4815             :     /* -------------------------------------------------------------------- */
    4816         156 :     papszSrcMD = poSrcDS->GetMetadata("TRE");
    4817             : 
    4818         163 :     for (int iMD = 0; bUseSrcNITFMetadata && papszSrcMD && papszSrcMD[iMD];
    4819             :          iMD++)
    4820             :     {
    4821           7 :         CPLString osTRE;
    4822             : 
    4823           7 :         if (STARTS_WITH_CI(papszSrcMD[iMD], "RPFHDR") ||
    4824           6 :             STARTS_WITH_CI(papszSrcMD[iMD], "RPFIMG") ||
    4825           5 :             STARTS_WITH_CI(papszSrcMD[iMD], "RPFDES"))
    4826             :         {
    4827             :             /* Do not copy RPF TRE. They contain absolute offsets */
    4828             :             /* No chance that they make sense in the new NITF file */
    4829           2 :             continue;
    4830             :         }
    4831           8 :         if (STARTS_WITH_CI(papszSrcMD[iMD], "BLOCKA") &&
    4832           3 :             CSLPartialFindString(papszOptions, "BLOCKA_") >= 0)
    4833             :         {
    4834             :             /* Do not copy BLOCKA TRE if there are BLOCKA_ creation options */
    4835           1 :             continue;
    4836             :         }
    4837             : 
    4838           4 :         osTRE = "TRE=";
    4839           4 :         osTRE += papszSrcMD[iMD];
    4840             : 
    4841           4 :         char *pszName = nullptr;
    4842           4 :         CPLParseNameValue(papszSrcMD[iMD], &pszName);
    4843           8 :         if (pszName != nullptr &&
    4844           4 :             CSLPartialFindString(papszOptions, CPLSPrintf("TRE=%s", pszName)) <
    4845             :                 0)
    4846             :         {
    4847           3 :             aosOptions.AddString(osTRE);
    4848             :         }
    4849           4 :         CPLFree(pszName);
    4850             :     }
    4851             : 
    4852             :     /* -------------------------------------------------------------------- */
    4853             :     /*      Set if we can set IREP.                                         */
    4854             :     /* -------------------------------------------------------------------- */
    4855         156 :     if (aosOptions.FetchNameValue("IREP") == nullptr)
    4856             :     {
    4857         175 :         if (((poSrcDS->GetRasterCount() == 3 && bJPEG) ||
    4858         150 :              (poSrcDS->GetRasterCount() >= 3 && !bJPEG)) &&
    4859          23 :             poSrcDS->GetRasterBand(1)->GetColorInterpretation() ==
    4860          16 :                 GCI_RedBand &&
    4861          16 :             poSrcDS->GetRasterBand(2)->GetColorInterpretation() ==
    4862         312 :                 GCI_GreenBand &&
    4863          16 :             poSrcDS->GetRasterBand(3)->GetColorInterpretation() == GCI_BlueBand)
    4864             :         {
    4865          16 :             if (bJPEG)
    4866           5 :                 aosOptions.SetNameValue("IREP", "YCbCr601");
    4867             :             else
    4868          11 :                 aosOptions.SetNameValue("IREP", "RGB");
    4869             :         }
    4870         147 :         else if (poSrcDS->GetRasterCount() >= 3 && !bJPEG &&
    4871           6 :                  poSrcDS->GetRasterBand(1)->GetColorInterpretation() ==
    4872           2 :                      GCI_BlueBand &&
    4873           2 :                  poSrcDS->GetRasterBand(2)->GetColorInterpretation() ==
    4874           2 :                      GCI_GreenBand &&
    4875           2 :                  poSrcDS->GetRasterBand(3)->GetColorInterpretation() ==
    4876         147 :                      GCI_RedBand &&
    4877           2 :                  aosOptions.FetchNameValue("IREPBAND") == nullptr)
    4878             :         {
    4879           2 :             aosOptions.SetNameValue("IREP", "MULTI");
    4880           4 :             std::string osIREPBAND = "B,G,R";
    4881           3 :             for (int i = 4; i <= poSrcDS->GetRasterCount(); ++i)
    4882           1 :                 osIREPBAND += ",M";
    4883           2 :             aosOptions.SetNameValue("IREPBAND", osIREPBAND.c_str());
    4884             :         }
    4885         248 :         else if (poSrcDS->GetRasterCount() == 1 && eType == GDT_UInt8 &&
    4886         110 :                  poBand1->GetColorTable() != nullptr)
    4887             :         {
    4888          34 :             aosOptions.SetNameValue("IREP", "RGB/LUT");
    4889             :             aosOptions.SetNameValue(
    4890             :                 "LUT_SIZE",
    4891          68 :                 CPLString().Printf(
    4892          34 :                     "%d", poBand1->GetColorTable()->GetColorEntryCount()));
    4893             :         }
    4894         104 :         else if (GDALDataTypeIsComplex(eType))
    4895           4 :             aosOptions.SetNameValue("IREP", "NODISPLY");
    4896             : 
    4897             :         else
    4898         100 :             aosOptions.SetNameValue("IREP", "MONO");
    4899             :     }
    4900             : 
    4901             :     /* -------------------------------------------------------------------- */
    4902             :     /*      Do we have lat/long georeferencing information?                 */
    4903             :     /* -------------------------------------------------------------------- */
    4904         156 :     GDALGeoTransform gt;
    4905         156 :     bool bWriteGeoTransform = false;
    4906         156 :     bool bWriteGCPs = false;
    4907         156 :     int nZone = 0;
    4908             : 
    4909         156 :     int nGCIFFlags = GCIF_PAM_DEFAULT;
    4910         156 :     double dfIGEOLOULX = 0;
    4911         156 :     double dfIGEOLOULY = 0;
    4912         156 :     double dfIGEOLOURX = 0;
    4913         156 :     double dfIGEOLOURY = 0;
    4914         156 :     double dfIGEOLOLRX = 0;
    4915         156 :     double dfIGEOLOLRY = 0;
    4916         156 :     double dfIGEOLOLLX = 0;
    4917         156 :     double dfIGEOLOLLY = 0;
    4918         156 :     bool bManualWriteOfIGEOLO = false;
    4919             : 
    4920         156 :     const OGRSpatialReference *poSrcSRS = poSrcDS->GetSpatialRef();
    4921         156 :     if (!poSrcSRS)
    4922          49 :         poSrcSRS = poSrcDS->GetGCPSpatialRef();
    4923         156 :     if (poSrcSRS)
    4924             :     {
    4925         108 :         OGRSpatialReference oSRS_WGS84;
    4926         108 :         oSRS_WGS84.SetWellKnownGeogCS("WGS84");
    4927         108 :         oSRS_WGS84.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    4928             : 
    4929             :         const bool bIsCADRGPolarAzimuthalEquidistant =
    4930          33 :             bIsCADRG && poSrcSRS->IsProjected() &&
    4931           4 :             EQUAL(poSrcSRS->GetAttrValue("PROJECTION"),
    4932           4 :                   SRS_PT_AZIMUTHAL_EQUIDISTANT) &&
    4933           4 :             poSrcSRS->GetSemiMajor() == SRS_WGS84_SEMIMAJOR &&
    4934           4 :             poSrcSRS->GetInvFlattening() == 0.0 &&
    4935           4 :             poSrcSRS->GetPrimeMeridian() == 0.0 &&
    4936         145 :             poSrcSRS->GetProjParm(SRS_PP_LONGITUDE_OF_CENTER) == 0.0 &&
    4937           4 :             std::fabs(poSrcSRS->GetProjParm(SRS_PP_LATITUDE_OF_CENTER)) == 90.0;
    4938         112 :         if (bIsCADRG && !poSrcSRS->IsGeographic() &&
    4939           4 :             !bIsCADRGPolarAzimuthalEquidistant)
    4940             :         {
    4941           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    4942             :                      "CADRG only support geographic CRS or polar azimuthal "
    4943             :                      "equidistant");
    4944           0 :             return nullptr;
    4945             :         }
    4946             : 
    4947         108 :         if (!bIsCADRGPolarAzimuthalEquidistant)
    4948             :         {
    4949             :             /* NITF is only WGS84 */
    4950         104 :             if (!poSrcSRS->IsSameGeogCS(&oSRS_WGS84))
    4951             :             {
    4952          17 :                 CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
    4953             :                          "NITF only supports WGS84 geographic and UTM "
    4954             :                          "projections.");
    4955          17 :                 if (bStrict)
    4956             :                 {
    4957           0 :                     return nullptr;
    4958             :                 }
    4959             :             }
    4960             :         }
    4961             : 
    4962         108 :         const char *pszICORDS = aosOptions.FetchNameValue("ICORDS");
    4963             : 
    4964             :         /* --------------------------------------------------------------------
    4965             :          */
    4966             :         /*      Should we write DIGEST Spatial Data Extension TRE ? */
    4967             :         /* --------------------------------------------------------------------
    4968             :          */
    4969         108 :         const char *pszSDE_TRE = aosOptions.FetchNameValue("SDE_TRE");
    4970         108 :         const bool bSDE_TRE = pszSDE_TRE && CPLTestBool(pszSDE_TRE);
    4971         108 :         if (bSDE_TRE)
    4972             :         {
    4973           1 :             if (poSrcSRS->IsGeographic() &&
    4974           1 :                 poSrcSRS->GetPrimeMeridian() == 0.0 &&
    4975           1 :                 poSrcDS->GetGeoTransform(gt) == CE_None && gt.xrot == 0.0 &&
    4976           2 :                 gt.yrot == 0.0 && gt.yscale < 0.0)
    4977             :             {
    4978             :                 /* Override ICORDS to G if necessary */
    4979           1 :                 if (pszICORDS != nullptr && EQUAL(pszICORDS, "D"))
    4980             :                 {
    4981           0 :                     aosOptions.SetNameValue("ICORDS", "G");
    4982           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    4983             :                              "Forcing ICORDS=G when writing GEOLOB");
    4984             :                 }
    4985             :                 else
    4986             :                 {
    4987             :                     /* Code a bit below will complain with other ICORDS value */
    4988             :                 }
    4989             : 
    4990           1 :                 if (CSLPartialFindString(aosOptions.List(), "TRE=GEOLOB=") !=
    4991             :                     -1)
    4992             :                 {
    4993           0 :                     CPLDebug("NITF", "GEOLOB TRE was explicitly defined "
    4994             :                                      "before.  Overriding it with current "
    4995             :                                      "georeferencing info.");
    4996             :                 }
    4997             : 
    4998             :                 /* Structure of SDE TRE documented here */
    4999             :                 // http://www.gwg.nga.mil/ntb/baseline/docs/digest/part2_annex_d.pdf
    5000             : 
    5001             :                 /* --------------------------------------------------------------------
    5002             :                  */
    5003             :                 /*      Write GEOLOB TRE */
    5004             :                 /* --------------------------------------------------------------------
    5005             :                  */
    5006             :                 // Extra (useless) bytes to avoid CLang 18 erroneous -Wformat-truncation
    5007           1 :                 constexpr int MARGIN_FOR_CLANG_18 = 2;
    5008             :                 char szGEOLOB[48 + 1 + MARGIN_FOR_CLANG_18];
    5009           1 :                 const double dfARV = 360.0 / gt.xscale;
    5010           1 :                 const double dfBRV = 360.0 / -gt.yscale;
    5011           1 :                 const double dfLSO = gt.xorig;
    5012           1 :                 const double dfPSO = gt.yorig;
    5013           1 :                 CPLsnprintf(szGEOLOB, sizeof(szGEOLOB),
    5014             :                             "%09d%09d%#+015.10f%#+015.10f",
    5015           1 :                             static_cast<int>(dfARV + 0.5),
    5016           1 :                             static_cast<int>(dfBRV + 0.5), dfLSO, dfPSO);
    5017             : 
    5018           2 :                 CPLString osGEOLOB("TRE=GEOLOB=");
    5019           1 :                 osGEOLOB += szGEOLOB;
    5020           1 :                 aosOptions.AddString(osGEOLOB);
    5021             : 
    5022             :                 /* --------------------------------------------------------------------
    5023             :                  */
    5024             :                 /*      Write GEOPSB TRE if not already explicitly provided */
    5025             :                 /* --------------------------------------------------------------------
    5026             :                  */
    5027           1 :                 if (CSLPartialFindString(aosOptions.List(),
    5028           2 :                                          "FILE_TRE=GEOPSB=") == -1 &&
    5029           1 :                     CSLPartialFindString(aosOptions.List(), "TRE=GEOPSB=") ==
    5030             :                         -1)
    5031             :                 {
    5032             :                     char szGEOPSB[443 + 1];
    5033           1 :                     memset(szGEOPSB, ' ', 443);
    5034           1 :                     szGEOPSB[443] = 0;
    5035             : #define WRITE_STR_NOSZ(dst, src) memcpy(dst, src, strlen(src))
    5036           1 :                     char *pszGEOPSB = szGEOPSB;
    5037           1 :                     WRITE_STR_NOSZ(pszGEOPSB, "GEO");
    5038           1 :                     pszGEOPSB += 3;
    5039           1 :                     WRITE_STR_NOSZ(pszGEOPSB, "DEG");
    5040           1 :                     pszGEOPSB += 3;
    5041           1 :                     WRITE_STR_NOSZ(pszGEOPSB, "World Geodetic System 1984");
    5042           1 :                     pszGEOPSB += 80;
    5043           1 :                     WRITE_STR_NOSZ(pszGEOPSB, "WGE");
    5044           1 :                     pszGEOPSB += 4;
    5045           1 :                     WRITE_STR_NOSZ(pszGEOPSB, "World Geodetic System 1984");
    5046           1 :                     pszGEOPSB += 80;
    5047           1 :                     WRITE_STR_NOSZ(pszGEOPSB, "WE");
    5048           1 :                     pszGEOPSB += 3;
    5049           1 :                     WRITE_STR_NOSZ(pszGEOPSB, "Geodetic");
    5050           1 :                     pszGEOPSB += 80; /* DVR */
    5051           1 :                     WRITE_STR_NOSZ(pszGEOPSB, "GEOD");
    5052           1 :                     pszGEOPSB += 4; /* VDCDVR */
    5053           1 :                     WRITE_STR_NOSZ(pszGEOPSB, "Mean Sea");
    5054           1 :                     pszGEOPSB += 80; /* SDA */
    5055           1 :                     WRITE_STR_NOSZ(pszGEOPSB, "MSL");
    5056           1 :                     pszGEOPSB += 4; /* VDCSDA */
    5057           1 :                     WRITE_STR_NOSZ(pszGEOPSB, "000000000000000");
    5058           1 :                     pszGEOPSB += 15; /* ZOR */
    5059           1 :                     pszGEOPSB += 3;  /* GRD */
    5060           1 :                     pszGEOPSB += 80; /* GRN */
    5061           1 :                     WRITE_STR_NOSZ(pszGEOPSB, "0000");
    5062           1 :                     pszGEOPSB += 4; /* ZNA */
    5063           1 :                     CPL_IGNORE_RET_VAL(pszGEOPSB);
    5064           1 :                     CPLAssert(pszGEOPSB == szGEOPSB + 443);
    5065             : 
    5066           2 :                     CPLString osGEOPSB("FILE_TRE=GEOPSB=");
    5067           1 :                     osGEOPSB += szGEOPSB;
    5068           1 :                     aosOptions.AddString(osGEOPSB);
    5069             :                 }
    5070             :                 else
    5071             :                 {
    5072           0 :                     CPLDebug("NITF", "GEOPSB TRE was explicitly defined "
    5073             :                                      "before. Keeping it.");
    5074             :                 }
    5075             :             }
    5076             :             else
    5077             :             {
    5078           0 :                 CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
    5079             :                          "Georeferencing info isn't compatible with writing a "
    5080             :                          "GEOLOB TRE (only geographic SRS handled for now)");
    5081           0 :                 if (bStrict)
    5082             :                 {
    5083           0 :                     return nullptr;
    5084             :                 }
    5085             :             }
    5086             :         }
    5087             : 
    5088         108 :         bWriteGeoTransform = (poSrcDS->GetGeoTransform(gt) == CE_None);
    5089         108 :         bWriteGCPs = (!bWriteGeoTransform && poSrcDS->GetGCPCount() == 4);
    5090             : 
    5091             :         int bNorth;
    5092         108 :         const bool bHasIGEOLO = aosOptions.FetchNameValue("IGEOLO") != nullptr;
    5093         108 :         if (bHasIGEOLO && pszICORDS == nullptr)
    5094             :         {
    5095           1 :             CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_AppDefined,
    5096             :                      "IGEOLO specified, but ICORDS not.%s",
    5097             :                      bStrict ? "" : " Ignoring IGEOLO");
    5098           1 :             if (bStrict)
    5099             :             {
    5100           1 :                 return nullptr;
    5101             :             }
    5102             :         }
    5103             : 
    5104         107 :         if (aosOptions.FetchNameValue("IGEOLO") != nullptr &&
    5105             :             pszICORDS != nullptr)
    5106             :         {
    5107             :             // if both IGEOLO and ICORDS are specified, do not try to write
    5108             :             // computed values
    5109             : 
    5110           1 :             bWriteGeoTransform = false;
    5111           1 :             bWriteGCPs = false;
    5112           1 :             nGCIFFlags &= ~GCIF_PROJECTION;
    5113           1 :             nGCIFFlags &= ~GCIF_GEOTRANSFORM;
    5114             :         }
    5115         187 :         else if (poSrcSRS->IsGeographic() &&
    5116          81 :                  poSrcSRS->GetPrimeMeridian() == 0.0)
    5117             :         {
    5118          81 :             if (pszICORDS == nullptr)
    5119             :             {
    5120          79 :                 aosOptions.SetNameValue("ICORDS", "G");
    5121             :             }
    5122           2 :             else if (EQUAL(pszICORDS, "G") || EQUAL(pszICORDS, "D"))
    5123             :             {
    5124             :                 /* Do nothing */
    5125             :             }
    5126             :             else
    5127             :             {
    5128           0 :                 CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
    5129             :                          "Inconsistent ICORDS value with SRS : %s%s.\n",
    5130             :                          pszICORDS,
    5131             :                          (!bStrict) ? ". Setting it to G instead" : "");
    5132           0 :                 if (bStrict)
    5133             :                 {
    5134           0 :                     return nullptr;
    5135             :                 }
    5136           0 :                 aosOptions.SetNameValue("ICORDS", "G");
    5137             :             }
    5138             :         }
    5139             : 
    5140          25 :         else if (poSrcSRS->GetUTMZone(&bNorth) > 0)
    5141             :         {
    5142          21 :             const char *pszComputedICORDS = bNorth ? "N" : "S";
    5143          21 :             nZone = poSrcSRS->GetUTMZone(nullptr);
    5144          21 :             if (pszICORDS == nullptr)
    5145             :             {
    5146          20 :                 aosOptions.SetNameValue("ICORDS", pszComputedICORDS);
    5147             :             }
    5148           1 :             else if (EQUAL(pszICORDS, pszComputedICORDS))
    5149             :             {
    5150             :                 // ok
    5151             :             }
    5152           1 :             else if ((EQUAL(pszICORDS, "G") || EQUAL(pszICORDS, "D")) &&
    5153             :                      bWriteGeoTransform)
    5154             :             {
    5155             :                 // Reproject UTM corner coordinates to geographic.
    5156             :                 // This can be used when there is no way to write an
    5157             :                 // equatorial image whose one of the northing value is below
    5158             :                 // -1e6
    5159             : 
    5160           1 :                 const int nXSize = poSrcDS->GetRasterXSize();
    5161           1 :                 const int nYSize = poSrcDS->GetRasterYSize();
    5162             : 
    5163           1 :                 dfIGEOLOULX = gt.xorig + 0.5 * gt.xscale + 0.5 * gt.xrot;
    5164           1 :                 dfIGEOLOULY = gt.yorig + 0.5 * gt.yrot + 0.5 * gt.yscale;
    5165           1 :                 dfIGEOLOURX = dfIGEOLOULX + gt.xscale * (nXSize - 1);
    5166           1 :                 dfIGEOLOURY = dfIGEOLOULY + gt.yrot * (nXSize - 1);
    5167           1 :                 dfIGEOLOLRX = dfIGEOLOULX + gt.xscale * (nXSize - 1) +
    5168           1 :                               gt.xrot * (nYSize - 1);
    5169           1 :                 dfIGEOLOLRY = dfIGEOLOULY + gt.yrot * (nXSize - 1) +
    5170           1 :                               gt.yscale * (nYSize - 1);
    5171           1 :                 dfIGEOLOLLX = dfIGEOLOULX + gt.xrot * (nYSize - 1);
    5172           1 :                 dfIGEOLOLLY = dfIGEOLOULY + gt.yscale * (nYSize - 1);
    5173             : 
    5174             :                 auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
    5175           1 :                     OGRCreateCoordinateTransformation(poSrcSRS, &oSRS_WGS84));
    5176           2 :                 if (poCT && poCT->Transform(1, &dfIGEOLOULX, &dfIGEOLOULY) &&
    5177           1 :                     poCT->Transform(1, &dfIGEOLOURX, &dfIGEOLOURY) &&
    5178           3 :                     poCT->Transform(1, &dfIGEOLOLRX, &dfIGEOLOLRY) &&
    5179           1 :                     poCT->Transform(1, &dfIGEOLOLLX, &dfIGEOLOLLY))
    5180             :                 {
    5181           1 :                     nZone = 0;
    5182           1 :                     bWriteGeoTransform = false;
    5183           1 :                     bManualWriteOfIGEOLO = true;
    5184           1 :                     nGCIFFlags &= ~GCIF_PROJECTION;
    5185           1 :                     nGCIFFlags &= ~GCIF_GEOTRANSFORM;
    5186             :                 }
    5187             :                 else
    5188             :                 {
    5189           0 :                     CPLError(
    5190             :                         CE_Failure, CPLE_AppDefined,
    5191             :                         "Cannot reproject UTM coordinates to geographic ones");
    5192           0 :                     return nullptr;
    5193           1 :                 }
    5194             :             }
    5195             :             else
    5196             :             {
    5197           0 :                 CPLError((bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
    5198             :                          "Inconsistent ICORDS value with SRS : %s.", pszICORDS);
    5199           0 :                 if (bStrict)
    5200             :                 {
    5201           0 :                     return nullptr;
    5202             :                 }
    5203             :             }
    5204             :         }
    5205           4 :         else if (bIsCADRGPolarAzimuthalEquidistant)
    5206             :         {
    5207           4 :             OGREnvelope sExtent;
    5208           4 :             poSrcDS->GetExtent(&sExtent);
    5209             : 
    5210           4 :             aosOptions.SetNameValue("ICORDS", "G");
    5211           4 :             dfIGEOLOULX = sExtent.MinX;
    5212           4 :             dfIGEOLOULY = sExtent.MaxY;
    5213           4 :             dfIGEOLOURX = sExtent.MaxX;
    5214           4 :             dfIGEOLOURY = sExtent.MaxY;
    5215           4 :             dfIGEOLOLLX = sExtent.MinX;
    5216           4 :             dfIGEOLOLLY = sExtent.MinY;
    5217           4 :             dfIGEOLOLRX = sExtent.MaxX;
    5218           4 :             dfIGEOLOLRY = sExtent.MinY;
    5219             :             auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
    5220           4 :                 OGRCreateCoordinateTransformation(poSrcSRS, &oSRS_WGS84));
    5221           8 :             if (poCT && poCT->Transform(1, &dfIGEOLOULX, &dfIGEOLOULY) &&
    5222           4 :                 poCT->Transform(1, &dfIGEOLOURX, &dfIGEOLOURY) &&
    5223          12 :                 poCT->Transform(1, &dfIGEOLOLRX, &dfIGEOLOLRY) &&
    5224           4 :                 poCT->Transform(1, &dfIGEOLOLLX, &dfIGEOLOLLY))
    5225             :             {
    5226           4 :                 nZone = 0;
    5227           4 :                 bWriteGeoTransform = false;
    5228           4 :                 bManualWriteOfIGEOLO = true;
    5229           4 :                 nGCIFFlags &= ~GCIF_PROJECTION;
    5230           4 :                 nGCIFFlags &= ~GCIF_GEOTRANSFORM;
    5231             :             }
    5232             :             else
    5233             :             {
    5234           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5235             :                          "Cannot reproject azimuthal equidistant coordinates "
    5236             :                          "to geographic ones");
    5237           0 :                 return nullptr;
    5238             :             }
    5239             :         }
    5240             :         else
    5241             :         {
    5242           0 :             CPLError(
    5243             :                 (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
    5244             :                 "NITF only supports WGS84 geographic and UTM projections.");
    5245           0 :             if (bStrict)
    5246             :             {
    5247           0 :                 return nullptr;
    5248             :             }
    5249             :         }
    5250             :     }
    5251             : 
    5252             :     /* -------------------------------------------------------------------- */
    5253             :     /*      Do we have RPC information?                                     */
    5254             :     /* -------------------------------------------------------------------- */
    5255         155 :     if (!bUseSrcNITFMetadata)
    5256           1 :         nGCIFFlags &= ~GCIF_METADATA;
    5257             : 
    5258         155 :     CSLConstList papszRPC = poSrcDS->GetMetadata(GDAL_MDD_RPC);
    5259         183 :     if (papszRPC != nullptr && bUseSrcNITFMetadata &&
    5260          28 :         CPLFetchBool(aosOptions.List(), "RPC00B", true))
    5261             :     {
    5262          27 :         if (CSLPartialFindString(aosOptions.List(), "TRE=RPC00B=") >= 0)
    5263             :         {
    5264           1 :             CPLDebug("NITF",
    5265             :                      "Both TRE=RPC00B and RPC metadata are available. "
    5266             :                      "Ignoring RPC metadata and re-using source TRE=RPC00B");
    5267             :         }
    5268             :         else
    5269             :         {
    5270          26 :             int bPrecisionLoss = FALSE;
    5271             :             char *pszRPC =
    5272          26 :                 NITFFormatRPC00BFromMetadata(papszRPC, &bPrecisionLoss);
    5273          26 :             if (pszRPC == nullptr)
    5274             :             {
    5275          11 :                 CPLError(
    5276             :                     (bStrict) ? CE_Failure : CE_Warning, CPLE_NotSupported,
    5277             :                     "Cannot format a valid RPC00B TRE from the RPC metadata");
    5278          11 :                 if (bStrict)
    5279             :                 {
    5280          11 :                     return nullptr;
    5281             :                 }
    5282             :             }
    5283             :             else
    5284             :             {
    5285          30 :                 CPLString osRPC00B("TRE=RPC00B=");
    5286          15 :                 osRPC00B += pszRPC;
    5287          15 :                 aosOptions.AddString(osRPC00B);
    5288             : 
    5289             :                 // If no precision loss occurred during RPC conversion, then
    5290             :                 // we can suppress it from PAM
    5291          15 :                 if (!bPrecisionLoss)
    5292           3 :                     nGCIFFlags &= ~GCIF_METADATA;
    5293             :             }
    5294          15 :             CPLFree(pszRPC);
    5295             :         }
    5296             :     }
    5297         128 :     else if (!CPLFetchBool(aosOptions.List(), "RPC00B", true))
    5298             :     {
    5299           1 :         int nIdx = CSLPartialFindString(aosOptions.List(), "TRE=RPC00B=");
    5300           1 :         if (nIdx >= 0)
    5301             :         {
    5302             :             aosOptions =
    5303           0 :                 CSLRemoveStrings(aosOptions.StealList(), nIdx, 1, nullptr);
    5304             :         }
    5305             :     }
    5306             : 
    5307         144 :     if (papszRPC != nullptr && CPLFetchBool(aosOptions.List(), "RPCTXT", false))
    5308             :     {
    5309           1 :         GDALWriteRPCTXTFile(pszFilename, papszRPC);
    5310             :     }
    5311             : 
    5312             :     /* -------------------------------------------------------------------- */
    5313             :     /*      Create the output file.                                         */
    5314             :     /* -------------------------------------------------------------------- */
    5315         144 :     const int nXSize = poSrcDS->GetRasterXSize();
    5316         144 :     const int nYSize = poSrcDS->GetRasterYSize();
    5317         144 :     const char *pszPVType = GDALToNITFDataType(eType);
    5318             : 
    5319         144 :     if (pszPVType == nullptr)
    5320             :     {
    5321           3 :         return nullptr;
    5322             :     }
    5323             : 
    5324         141 :     int nABPP = GDALGetDataTypeSizeBits(eType);
    5325         141 :     if (const char *pszABPP = aosOptions.FetchNameValue("ABPP"))
    5326             :     {
    5327           1 :         nABPP = atoi(pszABPP);
    5328             :     }
    5329         140 :     else if (const char *pszNBITS = aosOptions.FetchNameValueDef(
    5330             :                  GDALMD_NBITS, poBand1->GetMetadataItem(
    5331         140 :                                    GDALMD_NBITS, GDAL_MDD_IMAGE_STRUCTURE)))
    5332             :     {
    5333           2 :         aosOptions.SetNameValue("ABPP", pszNBITS);
    5334           2 :         nABPP = atoi(pszNBITS);
    5335             :     }
    5336             : 
    5337         152 :     if (poJ2KDriver != nullptr &&
    5338          11 :         EQUAL(poJ2KDriver->GetDescription(), "JP2ECW"))
    5339             :     {
    5340           3 :         if (STARTS_WITH_CI(aosOptions.FetchNameValueDef("PROFILE", "NPJE"),
    5341           5 :                            "NPJE") &&
    5342           2 :             (nXSize >= 1024 || nYSize >= 1024))
    5343             :         {
    5344             :             int nBlockXSize =
    5345           1 :                 atoi(aosOptions.FetchNameValueDef("BLOCKXSIZE", "0"));
    5346             :             int nBlockYSize =
    5347           1 :                 atoi(aosOptions.FetchNameValueDef("BLOCKYSIZE", "0"));
    5348           1 :             if (nBlockXSize > 0 && nBlockXSize != 1024)
    5349             :             {
    5350           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    5351             :                          "BLOCKXSIZE != 1024 inconsistent with PROFILE=NPJE");
    5352             :             }
    5353           1 :             if (nBlockYSize > 0 && nBlockYSize != 1024)
    5354             :             {
    5355           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    5356             :                          "BLOCKYSIZE != 1024 inconsistent with PROFILE=NPJE");
    5357             :             }
    5358           1 :             if (nBlockXSize == 0)
    5359             :             {
    5360           1 :                 aosOptions.SetNameValue("BLOCKXSIZE", "1024");
    5361             :             }
    5362           1 :             if (nBlockYSize == 0)
    5363             :             {
    5364           1 :                 aosOptions.SetNameValue("BLOCKYSIZE", "1024");
    5365             :             }
    5366             :         }
    5367             :     }
    5368         146 :     else if (poJ2KDriver != nullptr &&
    5369           8 :              EQUAL(poJ2KDriver->GetDescription(), "JP2OPENJPEG"))
    5370             :     {
    5371           8 :         const char *pszProfile = aosOptions.FetchNameValue("PROFILE");
    5372           8 :         if (pszProfile && EQUAL(pszProfile, "EPJE"))
    5373             :         {
    5374           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    5375             :                      "PROFILE=EPJE not handled by JP2OPENJPEG driver");
    5376             :         }
    5377             : 
    5378           8 :         int nBlockXSize = atoi(aosOptions.FetchNameValueDef("BLOCKXSIZE", "0"));
    5379           8 :         int nBlockYSize = atoi(aosOptions.FetchNameValueDef("BLOCKYSIZE", "0"));
    5380           8 :         if (pszProfile && STARTS_WITH_CI(pszProfile, "NPJE") &&
    5381           4 :             ((nBlockXSize != 0 && nBlockXSize != 1024) ||
    5382           0 :              (nBlockYSize != 0 && nBlockYSize != 1024)))
    5383             :         {
    5384           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    5385             :                      "PROFILE=NPJE implies 1024x1024 tiles");
    5386             :         }
    5387             : 
    5388           8 :         if (nXSize >= 1024 || nYSize >= 1024 ||
    5389           4 :             (pszProfile && STARTS_WITH_CI(pszProfile, "NPJE")))
    5390             :         {
    5391             :             // The JP2OPENJPEG driver uses 1024 block size by default. Set it
    5392             :             // explicitly for NITFCreate() purposes.
    5393           5 :             if (nBlockXSize == 0)
    5394             :             {
    5395           5 :                 aosOptions.SetNameValue("BLOCKXSIZE", "1024");
    5396             :             }
    5397           5 :             if (nBlockYSize == 0)
    5398             :             {
    5399           5 :                 aosOptions.SetNameValue("BLOCKYSIZE", "1024");
    5400             :             }
    5401             :         }
    5402             : 
    5403             :         // Compose J2KLRA TRE for NPJE profiles
    5404          12 :         if (pszProfile && STARTS_WITH_CI(pszProfile, "NPJE") &&
    5405           4 :             CPLTestBool(aosOptions.FetchNameValueDef("J2KLRA", "YES")))
    5406             :         {
    5407             : #if defined(__GNUC__)
    5408             : #pragma GCC diagnostic push
    5409             : #pragma GCC diagnostic ignored "-Warray-bounds"
    5410             : #endif
    5411             :             // See Table 2.3-3 - Target Bit Rates for Each Tile in Panchromatic
    5412             :             // Image Segments of STDI-0006
    5413             :             std::vector<double> adfBPP = {
    5414             :                 0.03125, 0.0625, 0.125, 0.25, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0,
    5415           8 :                 1.1,     1.2,    1.3,   1.5,  1.7, 2.0, 2.3, 3.5, 3.9};
    5416             : 
    5417           4 :             if (EQUAL(pszProfile, "NPJE") ||
    5418           4 :                 EQUAL(pszProfile, "NPJE_NUMERICALLY_LOSSLESS"))
    5419             :             {
    5420           2 :                 adfBPP.push_back(nABPP);
    5421             :             }
    5422             : #if defined(__GNUC__)
    5423             : #pragma GCC diagnostic pop
    5424             : #endif
    5425             : 
    5426             :             double dfQuality =
    5427           4 :                 CPLAtof(aosOptions.FetchNameValueDef("QUALITY", "0"));
    5428             :             double dfTarget =
    5429           4 :                 CPLAtof(aosOptions.FetchNameValueDef("TARGET", "0"));
    5430           4 :             if (dfTarget > 0 && dfTarget < 100)
    5431           0 :                 dfQuality = 100. - dfTarget;
    5432             : 
    5433           4 :             if (dfQuality != 0.0)
    5434             :             {
    5435          27 :                 for (size_t i = 0; i < adfBPP.size(); ++i)
    5436             :                 {
    5437             :                     // the JP2OPENJPEG QUALITY setting is 100. /
    5438             :                     // compression_ratio and compression_ratio = 8 / bpp
    5439          27 :                     double dfLayerQuality = 100.0 / (8.0 / adfBPP[i]);
    5440          27 :                     if (dfLayerQuality > dfQuality)
    5441             :                     {
    5442           2 :                         adfBPP[i] = dfQuality / 100.0 * nABPP;
    5443           2 :                         adfBPP.resize(i + 1);
    5444           2 :                         break;
    5445             :                     }
    5446             :                 }
    5447             :             }
    5448             : 
    5449           8 :             CPLString osJ2KLRA("TRE=J2KLRA=");
    5450           4 :             osJ2KLRA += '0';   // ORIG: 0=Original NPJE
    5451           4 :             osJ2KLRA += "05";  // Number of wavelets decompositions.
    5452             :                                // This corresponds to the value of the
    5453             :                                // RESOLUTIONS JP2OPENJPEG creation option - 1
    5454           4 :             osJ2KLRA += CPLSPrintf("%05d", poSrcDS->GetRasterCount());
    5455           4 :             osJ2KLRA += CPLSPrintf("%03d", static_cast<int>(adfBPP.size()));
    5456          70 :             for (size_t i = 0; i < adfBPP.size(); ++i)
    5457             :             {
    5458          66 :                 osJ2KLRA += CPLSPrintf("%03d", static_cast<int>(i));
    5459          66 :                 osJ2KLRA += CPLSPrintf("%09.6f", adfBPP[i]);
    5460             :             }
    5461           4 :             aosOptions.AddString(osJ2KLRA);
    5462             :         }
    5463             :     }
    5464             : 
    5465         282 :     GDALOffsetPatcher::OffsetPatcher offsetPatcher;
    5466         141 :     std::unique_ptr<CADRGInformation> CADRGInfo;
    5467             : 
    5468         141 :     if (bIsCADRG)
    5469             :     {
    5470          33 :         aosOptions.SetNameValue("FHDR", "NITF02.00");
    5471          33 :         pszIC = "C4";
    5472          33 :         aosOptions.SetNameValue("IC", pszIC);
    5473          33 :         if (aosOptions.FetchNameValue("IID1") == nullptr)
    5474             :         {
    5475          33 :             aosOptions.SetNameValue("IID1", "CADRG");
    5476             :         }
    5477          33 :         if (aosOptions.FetchNameValue("FTITLE") == nullptr)
    5478             :         {
    5479          33 :             aosOptions.SetNameValue("FTITLE", CPLGetFilename(pszFilename));
    5480             :         }
    5481          33 :         if (aosOptions.FetchNameValue("ITITLE") == nullptr)
    5482             :         {
    5483          33 :             aosOptions.SetNameValue("ITITLE", CPLGetFilename(pszFilename));
    5484             :         }
    5485          33 :         if (aosOptions.FetchNameValue("ICAT") == nullptr)
    5486             :         {
    5487          33 :             aosOptions.SetNameValue("ICAT", "MAP");
    5488             :         }
    5489          33 :         if (aosOptions.FetchNameValue("IMAG") == nullptr)
    5490             :         {
    5491             :             // 0.67 = 100 (microns ADRG) / 150 (microns CADRG)
    5492          33 :             aosOptions.SetNameValue("IMAG", "0.67");
    5493             :         }
    5494             : 
    5495          33 :         const int nRPFDESIdx = aosOptions.PartialFindString("DES=RPFDES=");
    5496          33 :         if (nRPFDESIdx >= 0)
    5497           0 :             aosOptions.RemoveStrings(nRPFDESIdx);
    5498             :         // +1 for the created RPFDES
    5499             :         const int nDES =
    5500             :             1 +
    5501          33 :             CPLStringList(CSLFetchNameValueMultiple(aosOptions.List(), "DES"))
    5502          33 :                 .size();
    5503          33 :         aosOptions.SetNameValue("NUMDES", CPLSPrintf("%d", nDES));
    5504             : 
    5505          33 :         if (copyContext->nReciprocalScale == 0)
    5506             :         {
    5507           0 :             bool bGotDPI = false;
    5508             :             {
    5509           0 :                 CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    5510           0 :                 copyContext->nReciprocalScale =
    5511           0 :                     RPFGetCADRGClosestReciprocalScale(poSrcDS, 0, bGotDPI);
    5512             :             }
    5513           0 :             if (!bGotDPI)
    5514             :             {
    5515             :                 // the one from ADRG typically
    5516           0 :                 constexpr double DEFAULT_DPI = 254;
    5517           0 :                 copyContext->nReciprocalScale =
    5518           0 :                     RPFGetCADRGClosestReciprocalScale(poSrcDS, DEFAULT_DPI,
    5519             :                                                       bGotDPI);
    5520           0 :                 if (!copyContext->nReciprocalScale)
    5521           0 :                     return nullptr;
    5522             :             }
    5523             :         }
    5524             : 
    5525          66 :         CADRGInfo = RPFFrameCreateCADRG_TREs(&offsetPatcher, pszFilename,
    5526          33 :                                              poSrcDS, aosOptions, *copyContext);
    5527          33 :         if (!CADRGInfo)
    5528             :         {
    5529           0 :             return nullptr;
    5530             :         }
    5531             : 
    5532             :         aosOptions.SetNameValue(
    5533             :             "LUT_SIZE",
    5534             :             CPLSPrintf("%d", CADRG_MAX_COLOR_ENTRY_COUNT +
    5535          33 :                                  (CADRGInfo->HasTransparentPixels() ? 1 : 0)));
    5536             :     }
    5537             : 
    5538         141 :     int nIMIndex = 0;
    5539         141 :     int nImageCount = 0;
    5540         141 :     vsi_l_offset nImageOffset = 0;
    5541         141 :     vsi_l_offset nICOffset = 0;
    5542         141 :     if (!NITFCreateEx(pszFilename, nXSize, nYSize, poSrcDS->GetRasterCount(),
    5543             :                       GDALGetDataTypeSizeBits(eType), pszPVType,
    5544         141 :                       aosOptions.List(), &nIMIndex, &nImageCount, &nImageOffset,
    5545             :                       &nICOffset, &offsetPatcher))
    5546             :     {
    5547          14 :         return nullptr;
    5548             :     }
    5549             : 
    5550             :     /* ==================================================================== */
    5551             :     /*      JPEG2000 case.  We need to write the data through a J2K         */
    5552             :     /*      driver in pixel interleaved form.                               */
    5553             :     /* ==================================================================== */
    5554         127 :     std::unique_ptr<NITFDataset> poDstDS;
    5555             : 
    5556         127 :     if (bJPEG2000)
    5557             :     {
    5558          11 :         CPLString osDSName;
    5559             :         osDSName.Printf("/vsisubfile/" CPL_FRMT_GUIB "_%d,%s",
    5560          11 :                         static_cast<GUIntBig>(nImageOffset), -1, pszFilename);
    5561             : 
    5562           0 :         std::unique_ptr<GDALDataset> poJ2KDataset;
    5563          11 :         if (EQUAL(poJ2KDriver->GetDescription(), "JP2ECW"))
    5564             :         {
    5565           3 :             poJ2KDataset.reset(poJ2KDriver->CreateCopy(
    5566           6 :                 osDSName, poSrcDS, FALSE, NITFJP2ECWOptions(aosOptions).List(),
    5567             :                 pfnProgress, pProgressData));
    5568             :         }
    5569           8 :         else if (EQUAL(poJ2KDriver->GetDescription(), "JP2KAK"))
    5570             :         {
    5571           0 :             poJ2KDataset.reset(poJ2KDriver->CreateCopy(
    5572             :                 osDSName, poSrcDS, FALSE,
    5573           0 :                 NITFJP2KAKOptions(aosOptions, nABPP).List(), pfnProgress,
    5574             :                 pProgressData));
    5575             :         }
    5576           8 :         else if (EQUAL(poJ2KDriver->GetDescription(), "JP2OPENJPEG"))
    5577             :         {
    5578           8 :             poJ2KDataset.reset(poJ2KDriver->CreateCopy(
    5579             :                 osDSName, poSrcDS, FALSE,
    5580          16 :                 NITFJP2OPENJPEGOptions(poJ2KDriver, aosOptions, nABPP).List(),
    5581             :                 pfnProgress, pProgressData));
    5582             :         }
    5583          11 :         if (poJ2KDataset == nullptr)
    5584             :         {
    5585           0 :             return nullptr;
    5586             :         }
    5587             : 
    5588          11 :         poJ2KDataset.reset();
    5589             : 
    5590             :         // Now we need to figure out the actual length of the file
    5591             :         // and correct the image segment size information.
    5592             :         const GIntBig nPixelCount =
    5593          11 :             static_cast<GIntBig>(nXSize) * nYSize * poSrcDS->GetRasterCount();
    5594             : 
    5595          11 :         bool bOK = NITFPatchImageLength(pszFilename, nullptr, nIMIndex,
    5596             :                                         nImageOffset, nPixelCount, "C8",
    5597          11 :                                         nICOffset, aosOptions.List());
    5598          11 :         if (nIMIndex + 1 == nImageCount)
    5599             :         {
    5600          11 :             bOK &= NITFWriteExtraSegments(pszFilename, nullptr, aosCgmMD.List(),
    5601          11 :                                           aosTextMD.List(), nullptr, aosOptions,
    5602          11 :                                           0);
    5603             :         }
    5604          11 :         if (!bOK)
    5605             :         {
    5606           0 :             return nullptr;
    5607             :         }
    5608             : 
    5609          11 :         GDALOpenInfo oOpenInfo(pszFilename, GA_Update);
    5610          11 :         poDstDS.reset(OpenInternal(&oOpenInfo, nullptr, true,
    5611          11 :                                    nImageCount == 1 ? -1 : nIMIndex));
    5612             : 
    5613          11 :         if (poDstDS == nullptr)
    5614             :         {
    5615           0 :             return nullptr;
    5616             :         }
    5617             :     }
    5618             : 
    5619             :     /* ==================================================================== */
    5620             :     /*      Loop copying bands to an uncompressed file.                     */
    5621             :     /* ==================================================================== */
    5622         116 :     else if (bJPEG)
    5623             :     {
    5624             : #ifdef JPEG_SUPPORTED
    5625             :         std::unique_ptr<NITFFile, decltype(&NITFClose)> psFile(
    5626          10 :             NITFOpen(pszFilename, TRUE), NITFClose);
    5627          10 :         if (psFile == nullptr)
    5628             :         {
    5629           0 :             return nullptr;
    5630             :         }
    5631             : 
    5632             :         const bool bSuccess =
    5633          10 :             NITFWriteJPEGImage(poSrcDS, psFile->fp, nImageOffset,
    5634          10 :                                aosOptions.List(), pfnProgress, pProgressData);
    5635             : 
    5636          10 :         if (!bSuccess)
    5637             :         {
    5638           0 :             return nullptr;
    5639             :         }
    5640             : 
    5641             :         // Now we need to figure out the actual length of the file
    5642             :         // and correct the image segment size information.
    5643             :         const GIntBig nPixelCount =
    5644          10 :             static_cast<GIntBig>(nXSize) * nYSize * poSrcDS->GetRasterCount();
    5645             : 
    5646          10 :         psFile.reset();
    5647             : 
    5648          10 :         bool bOK = NITFPatchImageLength(pszFilename, nullptr, nIMIndex,
    5649             :                                         nImageOffset, nPixelCount, pszIC,
    5650          10 :                                         nICOffset, aosOptions.List());
    5651          10 :         if (nIMIndex + 1 == nImageCount)
    5652             :         {
    5653           8 :             bOK &= NITFWriteExtraSegments(pszFilename, nullptr, aosCgmMD.List(),
    5654           8 :                                           aosTextMD.List(), nullptr, aosOptions,
    5655           8 :                                           0);
    5656             :         }
    5657          10 :         if (!bOK)
    5658             :         {
    5659           0 :             return nullptr;
    5660             :         }
    5661             : 
    5662          10 :         GDALOpenInfo oOpenInfo(pszFilename, GA_Update);
    5663          10 :         poDstDS.reset(OpenInternal(&oOpenInfo, nullptr, true,
    5664          10 :                                    nImageCount == 1 ? -1 : nIMIndex));
    5665             : 
    5666          10 :         if (poDstDS == nullptr)
    5667             :         {
    5668           0 :             return nullptr;
    5669             :         }
    5670             : #endif /* def JPEG_SUPPORTED */
    5671             :     }
    5672             : 
    5673         106 :     else if (bIsCADRG)
    5674             :     {
    5675          32 :         auto fp = VSIFilesystemHandler::OpenStatic(pszFilename, "rb+");
    5676          32 :         if (!fp)
    5677             :         {
    5678           0 :             CPLError(CE_Failure, CPLE_FileIO,
    5679             :                      "Cannot re-open %s in read/write mode", pszFilename);
    5680           0 :             return nullptr;
    5681             :         }
    5682             : 
    5683          32 :         if (!RPFFrameWriteCADRG_ImageContent(&offsetPatcher, fp.get(), poSrcDS,
    5684             :                                              CADRGInfo.get()))
    5685             :         {
    5686           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    5687             :                      "RPFFrameWriteCADRG_ImageContent() failed");
    5688           0 :             return nullptr;
    5689             :         }
    5690             : 
    5691             :         // Now we need to figure out the actual length of the file
    5692             :         // and correct the image segment size information.
    5693             :         const GIntBig nPixelCount =
    5694          32 :             static_cast<GIntBig>(nXSize) * nYSize * poSrcDS->GetRasterCount();
    5695             : 
    5696          32 :         bool bOK = NITFPatchImageLength(pszFilename, fp.get(), nIMIndex,
    5697             :                                         nImageOffset, nPixelCount, pszIC,
    5698          32 :                                         nICOffset, aosOptions.List());
    5699             : 
    5700             :         // This will call RPFFrameWriteCADRG_RPFDES()
    5701          32 :         bOK &= NITFWriteExtraSegments(
    5702          32 :             pszFilename, fp.get(), aosCgmMD.List(), aosTextMD.List(),
    5703          32 :             &offsetPatcher, aosOptions, copyContext->nReciprocalScale);
    5704             : 
    5705          32 :         if (!bOK || !offsetPatcher.Finalize(fp.get()))
    5706           0 :             return nullptr;
    5707             : 
    5708          32 :         fp.reset();
    5709             : 
    5710          32 :         GDALOpenInfo oOpenInfo(pszFilename, GA_Update);
    5711          32 :         poDstDS.reset(OpenInternal(&oOpenInfo, nullptr, true, -1));
    5712          32 :         if (poDstDS == nullptr)
    5713             :         {
    5714           0 :             return nullptr;
    5715             :         }
    5716             : 
    5717          32 :         const GDALColorTable *poCT = poSrcDS->GetRasterBand(1)->GetColorTable();
    5718          64 :         GDALColorTable oDstCT;
    5719             :         const int nMaxCTEntries = CADRG_MAX_COLOR_ENTRY_COUNT +
    5720          32 :                                   (CADRGInfo->HasTransparentPixels() ? 1 : 0);
    5721        6972 :         for (int i = 0; i < nMaxCTEntries; ++i)
    5722             :         {
    5723        6940 :             if (i < poCT->GetColorEntryCount())
    5724        6940 :                 oDstCT.SetColorEntry(i, poCT->GetColorEntry(i));
    5725             :             else
    5726             :             {
    5727           0 :                 GDALColorEntry entry = {0, 0, 0, 0};
    5728           0 :                 oDstCT.SetColorEntry(i, &entry);
    5729             :             }
    5730             :         }
    5731          32 :         poDstDS->GetRasterBand(1)->SetColorTable(&oDstCT);
    5732             :     }
    5733             : 
    5734             :     /* ==================================================================== */
    5735             :     /*      Loop copying bands to an uncompressed file.                     */
    5736             :     /* ==================================================================== */
    5737             :     else
    5738             :     {
    5739          74 :         if (nIMIndex + 1 == nImageCount)
    5740             :         {
    5741          71 :             bool bOK = NITFWriteExtraSegments(pszFilename, nullptr,
    5742          71 :                                               aosCgmMD.List(), aosTextMD.List(),
    5743             :                                               nullptr, aosOptions, 0);
    5744          71 :             if (!bOK)
    5745             :             {
    5746           0 :                 return nullptr;
    5747             :             }
    5748             :         }
    5749             : 
    5750             :         // Save error state to restore it afterwards since some operations
    5751             :         // in Open() might reset it.
    5752          74 :         CPLErr eLastErr = CPLGetLastErrorType();
    5753          74 :         int nLastErrNo = CPLGetLastErrorNo();
    5754          74 :         CPLString osLastErrorMsg = CPLGetLastErrorMsg();
    5755             : 
    5756          74 :         GDALOpenInfo oOpenInfo(pszFilename, GA_Update);
    5757          74 :         poDstDS.reset(OpenInternal(&oOpenInfo, nullptr, true,
    5758          74 :                                    nImageCount == 1 ? -1 : nIMIndex));
    5759             : 
    5760          74 :         if (CPLGetLastErrorType() == CE_None && eLastErr != CE_None)
    5761           0 :             CPLErrorSetState(eLastErr, nLastErrNo, osLastErrorMsg.c_str());
    5762             : 
    5763          74 :         if (poDstDS == nullptr)
    5764             :         {
    5765           0 :             return nullptr;
    5766             :         }
    5767             : 
    5768             :         std::unique_ptr<void, VSIFreeReleaser> pData(
    5769          74 :             VSIMalloc2(nXSize, GDALGetDataTypeSizeBytes(eType)));
    5770          74 :         if (pData == nullptr)
    5771             :         {
    5772           0 :             return nullptr;
    5773             :         }
    5774             : 
    5775          74 :         CPLErr eErr = CE_None;
    5776             : 
    5777         363 :         for (int iBand = 0; nIMIndex >= 0 && eErr == CE_None &&
    5778         181 :                             iBand < poSrcDS->GetRasterCount();
    5779             :              iBand++)
    5780             :         {
    5781         108 :             GDALRasterBand *poSrcBand = poSrcDS->GetRasterBand(iBand + 1);
    5782         108 :             GDALRasterBand *poDstBand = poDstDS->GetRasterBand(iBand + 1);
    5783             : 
    5784             :             /* --------------------------------------------------------------------
    5785             :              */
    5786             :             /*      Do we need to copy a colortable or other metadata? */
    5787             :             /* --------------------------------------------------------------------
    5788             :              */
    5789         108 :             GDALColorTable *poCT = poSrcBand->GetColorTable();
    5790         108 :             if (poCT != nullptr)
    5791           1 :                 poDstBand->SetColorTable(poCT);
    5792             : 
    5793             :             /* --------------------------------------------------------------------
    5794             :              */
    5795             :             /*      Copy image data. */
    5796             :             /* --------------------------------------------------------------------
    5797             :              */
    5798        4816 :             for (int iLine = 0; iLine < nYSize; iLine++)
    5799             :             {
    5800        4708 :                 eErr = poSrcBand->RasterIO(GF_Read, 0, iLine, nXSize, 1,
    5801             :                                            pData.get(), nXSize, 1, eType, 0, 0,
    5802             :                                            nullptr);
    5803        4708 :                 if (eErr != CE_None)
    5804           0 :                     break;
    5805             : 
    5806        4708 :                 eErr = poDstBand->RasterIO(GF_Write, 0, iLine, nXSize, 1,
    5807             :                                            pData.get(), nXSize, 1, eType, 0, 0,
    5808             :                                            nullptr);
    5809             : 
    5810        4708 :                 if (eErr != CE_None)
    5811           0 :                     break;
    5812             : 
    5813        4708 :                 if (!pfnProgress(
    5814        4708 :                         (iBand + (iLine + 1) / static_cast<double>(nYSize)) /
    5815        4708 :                             static_cast<double>(poSrcDS->GetRasterCount()),
    5816             :                         nullptr, pProgressData))
    5817             :                 {
    5818           0 :                     CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
    5819           0 :                     eErr = CE_Failure;
    5820           0 :                     break;
    5821             :                 }
    5822             :             }
    5823             :         }
    5824             : 
    5825          74 :         if (eErr != CE_None)
    5826             :         {
    5827           0 :             return nullptr;
    5828             :         }
    5829             :     }
    5830             : 
    5831             :     /* -------------------------------------------------------------------- */
    5832             :     /*      Set the georeferencing.                                         */
    5833             :     /* -------------------------------------------------------------------- */
    5834         127 :     if (poDstDS->psImage == nullptr)
    5835             :     {
    5836             :         // do nothing
    5837             :     }
    5838         126 :     else if (bManualWriteOfIGEOLO)
    5839             :     {
    5840           5 :         if (!NITFWriteIGEOLO(poDstDS->psImage, poDstDS->psImage->chICORDS,
    5841           5 :                              poDstDS->psImage->nZone, dfIGEOLOULX, dfIGEOLOULY,
    5842             :                              dfIGEOLOURX, dfIGEOLOURY, dfIGEOLOLRX, dfIGEOLOLRY,
    5843             :                              dfIGEOLOLLX, dfIGEOLOLLY))
    5844             :         {
    5845           0 :             return nullptr;
    5846             :         }
    5847             :     }
    5848         121 :     else if (bWriteGeoTransform)
    5849             :     {
    5850          82 :         poDstDS->psImage->nZone = nZone;
    5851          82 :         poDstDS->SetGeoTransform(gt);
    5852             :     }
    5853          39 :     else if (bWriteGCPs)
    5854             :     {
    5855           1 :         poDstDS->psImage->nZone = nZone;
    5856           2 :         poDstDS->SetGCPs(poSrcDS->GetGCPCount(), poSrcDS->GetGCPs(),
    5857           1 :                          poSrcDS->GetGCPSpatialRef());
    5858             :     }
    5859             : 
    5860         127 :     poDstDS->CloneInfo(poSrcDS, nGCIFFlags);
    5861             : 
    5862         127 :     if ((nGCIFFlags & GCIF_METADATA) == 0)
    5863             :     {
    5864           4 :         const int nSavedMOFlags = poDstDS->GetMOFlags();
    5865           4 :         papszSrcMD = poSrcDS->GetMetadata();
    5866           4 :         if (papszSrcMD != nullptr)
    5867             :         {
    5868           1 :             if (!bUseSrcNITFMetadata)
    5869             :             {
    5870           2 :                 CPLStringList aosNewMD(CSLDuplicate(poDstDS->GetMetadata()));
    5871           1 :                 bool bAdded = false;
    5872          76 :                 for (const char *pszKeyValue : cpl::Iterate(papszSrcMD))
    5873             :                 {
    5874          75 :                     if (!STARTS_WITH(pszKeyValue, "NITF_"))
    5875             :                     {
    5876           0 :                         bAdded = true;
    5877           0 :                         aosNewMD.AddString(pszKeyValue);
    5878             :                     }
    5879             :                 }
    5880           1 :                 if (bAdded)
    5881             :                 {
    5882           0 :                     poDstDS->SetMetadata(aosNewMD.List());
    5883             :                 }
    5884             :             }
    5885           0 :             else if (CSLCount(poDstDS->GetMetadata()) != CSLCount(papszSrcMD))
    5886             :             {
    5887           0 :                 poDstDS->SetMetadata(papszSrcMD);
    5888             :             }
    5889             :         }
    5890           4 :         poDstDS->SetMOFlags(nSavedMOFlags);
    5891             :     }
    5892             : 
    5893         127 :     if (bIsCADRG)
    5894             :     {
    5895          64 :         GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
    5896          32 :         poDstDS.reset();
    5897          32 :         poDstDS.reset(OpenInternal(&oOpenInfo, nullptr, true, -1));
    5898             :     }
    5899             : 
    5900         127 :     if (poDstDS && pfnProgress && !pfnProgress(1.0, "", pProgressData))
    5901             :     {
    5902           0 :         CPLError(CE_Failure, CPLE_UserInterrupt, "User terminated");
    5903           0 :         poDstDS.reset();
    5904             :     }
    5905             : 
    5906         127 :     return poDstDS;
    5907             : }
    5908             : 
    5909             : /************************************************************************/
    5910             : /*                         NITFHasFSDWNGField()                         */
    5911             : /************************************************************************/
    5912             : 
    5913         174 : static bool NITFHasFSDWNGField(VSILFILE *fpVSIL)
    5914             : {
    5915         174 :     char szFHDR[9 + 1] = {0};
    5916         174 :     bool bOK = VSIFSeekL(fpVSIL, 0, SEEK_SET) == 0;
    5917         174 :     bOK &= VSIFReadL(szFHDR, 9, 1, fpVSIL) == 1;
    5918         174 :     if (EQUAL(szFHDR, "NITF02.00"))
    5919             :     {
    5920             :         // Read FSDWNG field
    5921         101 :         char szFSDWNG[6 + 1] = {0};
    5922         101 :         bOK &= VSIFSeekL(fpVSIL, 280, SEEK_SET) == 0;
    5923         101 :         bOK &= VSIFReadL(szFSDWNG, 1, 6, fpVSIL) == 6;
    5924         101 :         if (bOK && EQUAL(szFSDWNG, "999998"))
    5925             :         {
    5926           4 :             return true;
    5927             :         }
    5928             :     }
    5929         170 :     return false;
    5930             : }
    5931             : 
    5932             : /************************************************************************/
    5933             : /*                        NITFPatchImageLength()                        */
    5934             : /*                                                                      */
    5935             : /*      Fixup various stuff we don't know till we have written the      */
    5936             : /*      imagery.  In particular the file length, image data length      */
    5937             : /*      and the compression ratio achieved.                             */
    5938             : /************************************************************************/
    5939             : 
    5940          54 : static bool NITFPatchImageLength(const char *pszFilename, VSILFILE *fp,
    5941             :                                  int nIMIndex, GUIntBig nImageOffset,
    5942             :                                  GIntBig nPixelCount, const char *pszIC,
    5943             :                                  vsi_l_offset nICOffset,
    5944             :                                  CSLConstList papszCreationOptions)
    5945             : 
    5946             : {
    5947          54 :     VSILFILE *fpVSIL = fp ? fp : VSIFOpenL(pszFilename, "r+b");
    5948          54 :     if (fpVSIL == nullptr)
    5949           0 :         return false;
    5950             : 
    5951          54 :     CPL_IGNORE_RET_VAL(VSIFSeekL(fpVSIL, 0, SEEK_END));
    5952          54 :     GUIntBig nFileLen = VSIFTellL(fpVSIL);
    5953             : 
    5954             :     /* -------------------------------------------------------------------- */
    5955             :     /*      Update total file length.                                       */
    5956             :     /* -------------------------------------------------------------------- */
    5957          54 :     if (nFileLen >= NITF_MAX_FILE_SIZE)
    5958             :     {
    5959           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    5960             :                  "Too big file : " CPL_FRMT_GUIB
    5961             :                  ". Truncating to " CPL_FRMT_GUIB,
    5962             :                  nFileLen, NITF_MAX_FILE_SIZE - 1);
    5963           0 :         nFileLen = NITF_MAX_FILE_SIZE - 1;
    5964             :     }
    5965             :     CPLString osLen =
    5966         108 :         CPLString().Printf("%012" CPL_FRMT_GB_WITHOUT_PREFIX "u", nFileLen);
    5967          54 :     const int nExtraOffset = NITFHasFSDWNGField(fpVSIL) ? 40 : 0;
    5968         108 :     if (VSIFSeekL(fpVSIL, 342 + nExtraOffset, SEEK_SET) != 0 ||
    5969          54 :         VSIFWriteL(reinterpret_cast<const void *>(osLen.c_str()), 12, 1,
    5970             :                    fpVSIL) != 1)
    5971             :     {
    5972           0 :         CPLError(CE_Failure, CPLE_FileIO, "Write error");
    5973           0 :         if (!fp)
    5974           0 :             CPL_IGNORE_RET_VAL(VSIFCloseL(fpVSIL));
    5975           0 :         return false;
    5976             :     }
    5977             : 
    5978             :     /* -------------------------------------------------------------------- */
    5979             :     /*      Update the image data length.                                   */
    5980             :     /* -------------------------------------------------------------------- */
    5981          54 :     GUIntBig nImageSize = nFileLen - nImageOffset;
    5982          54 :     if (nImageSize >= 9999999999ULL)
    5983             :     {
    5984           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    5985             :                  "Too big image size : " CPL_FRMT_GUIB
    5986             :                  ". Truncating to 9999999998",
    5987             :                  nImageSize);
    5988           0 :         nImageSize = 9999999998ULL;
    5989             :     }
    5990             :     osLen =
    5991          54 :         CPLString().Printf("%010" CPL_FRMT_GB_WITHOUT_PREFIX "u", nImageSize);
    5992         108 :     if (VSIFSeekL(fpVSIL, 369 + nExtraOffset + 16 * nIMIndex, SEEK_SET) != 0 ||
    5993          54 :         VSIFWriteL(reinterpret_cast<const void *>(osLen.c_str()), 10, 1,
    5994             :                    fpVSIL) != 1)
    5995             :     {
    5996           0 :         CPLError(CE_Failure, CPLE_FileIO, "Write error");
    5997           0 :         if (!fp)
    5998           0 :             CPL_IGNORE_RET_VAL(VSIFCloseL(fpVSIL));
    5999           0 :         return false;
    6000             :     }
    6001             : 
    6002             :     /* -------------------------------------------------------------------- */
    6003             :     /*      Update COMRAT, the compression rate variable, and CLEVEL        */
    6004             :     /* -------------------------------------------------------------------- */
    6005             : 
    6006             :     /* Set to IC position */
    6007          54 :     bool bOK = VSIFSeekL(fpVSIL, nICOffset, SEEK_SET) == 0;
    6008             : 
    6009             :     /* Read IC */
    6010             :     char szICBuf[2];
    6011          54 :     bOK &= VSIFReadL(szICBuf, 2, 1, fpVSIL) == 1;
    6012             : 
    6013             :     /* The following line works around a "feature" of *BSD libc (at least
    6014             :      * PC-BSD 7.1) */
    6015             :     /* that makes the position of the file offset unreliable when executing a */
    6016             :     /* "seek, read and write" sequence. After the read(), the file offset seen
    6017             :      * by */
    6018             :     /* the write() is approximately the size of a block further... */
    6019          54 :     bOK &= VSIFSeekL(fpVSIL, VSIFTellL(fpVSIL), SEEK_SET) == 0;
    6020             : 
    6021          54 :     if (!EQUALN(szICBuf, pszIC, 2))
    6022             :     {
    6023           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    6024             :                  "Unable to locate COMRAT to update in NITF header.");
    6025             :     }
    6026             :     else
    6027             :     {
    6028          54 :         char szCOMRAT[5] = "00.0";
    6029             : 
    6030          54 :         if (EQUAL(pszIC, "C8")) /* jpeg2000 */
    6031             :         {
    6032          12 :             double dfRate = static_cast<GIntBig>(nFileLen - nImageOffset) * 8 /
    6033          12 :                             static_cast<double>(nPixelCount);
    6034             : 
    6035             :             const char *pszProfile =
    6036          12 :                 CSLFetchNameValueDef(papszCreationOptions, "PROFILE", "");
    6037          12 :             if (STARTS_WITH_CI(pszProfile, "NPJE"))
    6038             :             {
    6039           4 :                 dfRate = std::max(0.1, std::min(99.9, dfRate));
    6040             : 
    6041             :                 // We emit in Vxyz or Nxyz format with an implicit decimal place
    6042             :                 // between yz and z as per spec.
    6043           4 :                 snprintf(szCOMRAT, sizeof(szCOMRAT), "%c%03u",
    6044           4 :                          EQUAL(pszProfile, "NPJE_VISUALLY_LOSSLESS") ? 'V'
    6045             :                                                                      : 'N',
    6046             :                          // % 1000 to please -Wformat-truncation
    6047           4 :                          static_cast<unsigned>(dfRate * 10) % 1000);
    6048             :             }
    6049             :             else
    6050             :             {
    6051           8 :                 dfRate = std::max(0.01, std::min(99.99, dfRate));
    6052             : 
    6053             :                 // We emit in wxyz format with an implicit decimal place
    6054             :                 // between wx and yz as per spec for lossy compression.
    6055             :                 // We really should have a special case for lossless
    6056             :                 // compression.
    6057           8 :                 snprintf(szCOMRAT, sizeof(szCOMRAT), "%04u",
    6058             :                          // % 10000 to please -Wformat-truncation
    6059           8 :                          static_cast<unsigned>(dfRate * 100) % 10000);
    6060             :             }
    6061             :         }
    6062          42 :         else if (EQUAL(pszIC, "C4") || EQUAL(pszIC, "M4"))  // VQ
    6063             :         {
    6064          32 :             snprintf(szCOMRAT, sizeof(szCOMRAT), "0.75");
    6065             :         }
    6066             : 
    6067          54 :         bOK &= VSIFWriteL(szCOMRAT, 4, 1, fpVSIL) == 1;
    6068             : 
    6069             :         /* ---------------------------------------------------------------- */
    6070             :         /*      Update CLEVEL.                                              */
    6071             :         /* ---------------------------------------------------------------- */
    6072             :         // Get existing CLEVEL
    6073          54 :         constexpr vsi_l_offset OFFSET_CLEVEL = 9;
    6074          54 :         constexpr int SIZE_CLEVEL = 2;
    6075          54 :         bOK &= VSIFSeekL(fpVSIL, OFFSET_CLEVEL, SEEK_SET) == 0;
    6076          54 :         char szCLEVEL[SIZE_CLEVEL + 1] = {0};
    6077          54 :         bOK &= VSIFReadL(szCLEVEL, 1, SIZE_CLEVEL, fpVSIL) != 0;
    6078          54 :         unsigned int nCLevel = static_cast<unsigned>(atoi(szCLEVEL));
    6079          54 :         if (nCLevel >= 3 && nCLevel <= 7)
    6080             :         {
    6081          54 :             const unsigned int nCLevelOri = nCLevel;
    6082          54 :             if (nFileLen > 2147483647)
    6083             :             {
    6084           0 :                 nCLevel = std::max(nCLevel, 7U);
    6085             :             }
    6086          54 :             else if (nFileLen > 1073741833)
    6087             :             {
    6088           0 :                 nCLevel = std::max(nCLevel, 6U);
    6089             :             }
    6090          54 :             else if (nFileLen > 52428799)
    6091             :             {
    6092           1 :                 nCLevel = std::max(nCLevel, 5U);
    6093             :             }
    6094          54 :             if (nCLevel != nCLevelOri)
    6095             :             {
    6096           0 :                 CPLDebug("NITF", "Updating CLEVEL from %02u to %02u",
    6097             :                          nCLevelOri, nCLevel);
    6098             :                 // %100 to please -Wformat-truncation
    6099           0 :                 snprintf(szCLEVEL, sizeof(szCLEVEL), "%02u", nCLevel % 100);
    6100           0 :                 bOK &= VSIFSeekL(fpVSIL, OFFSET_CLEVEL, SEEK_SET) == 0;
    6101           0 :                 bOK &= VSIFWriteL(szCLEVEL, 1, SIZE_CLEVEL, fpVSIL) != 0;
    6102          54 :             }
    6103             :         }
    6104             :         else
    6105             :         {
    6106           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    6107             :                      "Invalid CLEVEL=%s value found when updating NITF header.",
    6108             :                      szCLEVEL);
    6109             :         }
    6110             :     }
    6111             : 
    6112          54 :     if (!fp && VSIFCloseL(fpVSIL) != 0)
    6113           0 :         bOK = false;
    6114             : 
    6115          54 :     if (!bOK)
    6116             :     {
    6117           0 :         CPLError(CE_Failure, CPLE_FileIO, "I/O error");
    6118             :     }
    6119             : 
    6120          54 :     return bOK;
    6121             : }
    6122             : 
    6123             : /************************************************************************/
    6124             : /*                        NITFWriteCGMSegments()                        */
    6125             : /************************************************************************/
    6126         275 : static bool NITFWriteCGMSegments(const char *pszFilename, VSILFILE *&fpVSIL,
    6127             :                                  CSLConstList papszList)
    6128             : {
    6129         275 :     char errorMessage[255] = "";
    6130             : 
    6131             :     // size of each Cgm header entry (LS (4) + LSSH (6))
    6132         275 :     const int nCgmHdrEntrySz = 10;
    6133             : 
    6134         275 :     if (papszList == nullptr)
    6135         261 :         return true;
    6136             : 
    6137          14 :     int nNUMS = 0;
    6138          14 :     const char *pszNUMS = CSLFetchNameValue(papszList, "SEGMENT_COUNT");
    6139          14 :     if (pszNUMS != nullptr)
    6140             :     {
    6141          14 :         nNUMS = atoi(pszNUMS);
    6142             :     }
    6143             : 
    6144             :     /* -------------------------------------------------------------------- */
    6145             :     /*      Open the target file if not already done.                       */
    6146             :     /* -------------------------------------------------------------------- */
    6147          14 :     if (fpVSIL == nullptr)
    6148          13 :         fpVSIL = VSIFOpenL(pszFilename, "r+b");
    6149          14 :     if (fpVSIL == nullptr)
    6150           0 :         return false;
    6151             : 
    6152             :     // Calculates the offset for NUMS so we can update header data
    6153             :     char achNUMI[4];  // 3 digits plus null character
    6154          14 :     achNUMI[3] = '\0';
    6155             : 
    6156             :     // NUMI offset is at a fixed offset 360
    6157             :     const vsi_l_offset nNumIOffset =
    6158          14 :         360 + (NITFHasFSDWNGField(fpVSIL) ? 40 : 0);
    6159          14 :     bool bOK = VSIFSeekL(fpVSIL, nNumIOffset, SEEK_SET) == 0;
    6160          14 :     bOK &= VSIFReadL(achNUMI, 3, 1, fpVSIL) == 1;
    6161          14 :     const int nIM = atoi(achNUMI);
    6162             : 
    6163             :     // 6 for size of LISH and 10 for size of LI
    6164             :     // NUMS offset is NumI offset plus the size of NumI + size taken up each
    6165             :     // the header data multiply by the number of data
    6166             : 
    6167          14 :     const vsi_l_offset nNumSOffset = nNumIOffset + 3 + nIM * (6 + 10);
    6168             : 
    6169             :     /* -------------------------------------------------------------------- */
    6170             :     /*      Confirm that the NUMS in the file header already matches the    */
    6171             :     /*      number of graphic segments we want to write                     */
    6172             :     /* -------------------------------------------------------------------- */
    6173             :     char achNUMS[4];
    6174             : 
    6175          14 :     bOK &= VSIFSeekL(fpVSIL, nNumSOffset, SEEK_SET) == 0;
    6176          14 :     bOK &= VSIFReadL(achNUMS, 3, 1, fpVSIL) == 1;
    6177          14 :     achNUMS[3] = '\0';
    6178             : 
    6179          14 :     if (!bOK || atoi(achNUMS) != nNUMS)
    6180             :     {
    6181           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6182             :                  "It appears an attempt was made to add or update graphic\n"
    6183             :                  "segments on an NITF file with existing segments.  This\n"
    6184             :                  "is not currently supported by the GDAL NITF driver.");
    6185             : 
    6186           0 :         return false;
    6187             :     }
    6188             : 
    6189             :     // allocate space for graphic header.
    6190             :     // Size of LS = 4, size of LSSH = 6, and 1 for null character
    6191             :     char *pachLS =
    6192          14 :         static_cast<char *>(CPLCalloc(nNUMS * nCgmHdrEntrySz + 1, 1));
    6193             : 
    6194             :     /* -------------------------------------------------------------------- */
    6195             :     /*  Assume no extended data such as SXSHDL, SXSHD                       */
    6196             :     /* -------------------------------------------------------------------- */
    6197             : 
    6198             :     /* ==================================================================== */
    6199             :     /*      Write the Graphics segments at the end of the file.             */
    6200             :     /* ==================================================================== */
    6201             : 
    6202             : #define PLACE(location, name, text) memcpy(location, text, strlen(text))
    6203             : 
    6204          18 :     for (int i = 0; bOK && i < nNUMS; i++)
    6205             :     {
    6206             : 
    6207             :         // Get all the fields for current CGM segment
    6208           4 :         const char *pszSlocRow = CSLFetchNameValue(
    6209           8 :             papszList, CPLString().Printf("SEGMENT_%d_SLOC_ROW", i));
    6210           4 :         const char *pszSlocCol = CSLFetchNameValue(
    6211           8 :             papszList, CPLString().Printf("SEGMENT_%d_SLOC_COL", i));
    6212           4 :         const char *pszSdlvl = CSLFetchNameValue(
    6213           8 :             papszList, CPLString().Printf("SEGMENT_%d_SDLVL", i));
    6214           4 :         const char *pszSalvl = CSLFetchNameValue(
    6215           8 :             papszList, CPLString().Printf("SEGMENT_%d_SALVL", i));
    6216           4 :         const char *pszData = CSLFetchNameValue(
    6217           8 :             papszList, CPLString().Printf("SEGMENT_%d_DATA", i));
    6218             : 
    6219             :         // Error checking
    6220           4 :         if (pszSlocRow == nullptr)
    6221             :         {
    6222           0 :             snprintf(errorMessage, sizeof(errorMessage),
    6223             :                      "NITF graphic segment writing error: SLOC_ROW for segment "
    6224             :                      "%d is not defined",
    6225             :                      i);
    6226           0 :             break;
    6227             :         }
    6228           4 :         if (pszSlocCol == nullptr)
    6229             :         {
    6230           0 :             snprintf(errorMessage, sizeof(errorMessage),
    6231             :                      "NITF graphic segment writing error: SLOC_COL for segment "
    6232             :                      "%d is not defined",
    6233             :                      i);
    6234           0 :             break;
    6235             :         }
    6236           4 :         if (pszSdlvl == nullptr)
    6237             :         {
    6238           0 :             snprintf(errorMessage, sizeof(errorMessage),
    6239             :                      "NITF graphic segment writing error: SDLVL for segment %d "
    6240             :                      "is not defined",
    6241             :                      i);
    6242           0 :             break;
    6243             :         }
    6244           4 :         if (pszSalvl == nullptr)
    6245             :         {
    6246           0 :             snprintf(errorMessage, sizeof(errorMessage),
    6247             :                      "NITF graphic segment writing error: SALVLfor segment %d "
    6248             :                      "is not defined",
    6249             :                      i);
    6250           0 :             break;
    6251             :         }
    6252           4 :         if (pszData == nullptr)
    6253             :         {
    6254           0 :             snprintf(errorMessage, sizeof(errorMessage),
    6255             :                      "NITF graphic segment writing error: DATA for segment %d "
    6256             :                      "is not defined",
    6257             :                      i);
    6258           0 :             break;
    6259             :         }
    6260             : 
    6261           4 :         const int nSlocCol = atoi(pszSlocRow);
    6262           4 :         const int nSlocRow = atoi(pszSlocCol);
    6263           4 :         const int nSdlvl = atoi(pszSdlvl);
    6264           4 :         const int nSalvl = atoi(pszSalvl);
    6265             : 
    6266             :         // Create a buffer for graphics segment header, 258 is the size of
    6267             :         // the header that we will be writing.
    6268             :         char achGSH[258];
    6269             : 
    6270           4 :         memset(achGSH, ' ', sizeof(achGSH));
    6271             : 
    6272           4 :         PLACE(achGSH + 0, SY, "SY");
    6273           4 :         PLACE(achGSH + 2, SID, CPLSPrintf("%010d", i));
    6274           4 :         PLACE(achGSH + 12, SNAME, "DEFAULT NAME        ");
    6275           4 :         PLACE(achGSH + 32, SSCLAS, "U");
    6276           4 :         PLACE(achGSH + 33, SSCLASY, "0");
    6277           4 :         PLACE(achGSH + 199, ENCRYP, "0");
    6278           4 :         PLACE(achGSH + 200, SFMT, "C");
    6279           4 :         PLACE(achGSH + 201, SSTRUCT, "0000000000000");
    6280           4 :         PLACE(achGSH + 214, SDLVL, CPLSPrintf("%03d", nSdlvl));  // size3
    6281           4 :         PLACE(achGSH + 217, SALVL, CPLSPrintf("%03d", nSalvl));  // size3
    6282           4 :         PLACE(achGSH + 220, SLOC,
    6283             :               CPLSPrintf("%05d%05d", nSlocRow, nSlocCol));  // size 10
    6284           4 :         PLACE(achGSH + 230, SBAND1, "0000000000");
    6285           4 :         PLACE(achGSH + 240, SCOLOR, "C");
    6286           4 :         PLACE(achGSH + 241, SBAND2, "0000000000");
    6287           4 :         PLACE(achGSH + 251, SRES2, "00");
    6288           4 :         PLACE(achGSH + 253, SXSHDL, "00000");
    6289             : 
    6290             :         // Move to the end of the file
    6291           4 :         bOK &= VSIFSeekL(fpVSIL, 0, SEEK_END) == 0;
    6292           4 :         bOK &= VSIFWriteL(achGSH, sizeof(achGSH), 1, fpVSIL) == 1;
    6293             : 
    6294             :         /* ------------------------------------------------------------------ */
    6295             :         /*      Prepare and write CGM segment data.                           */
    6296             :         /* ------------------------------------------------------------------ */
    6297           4 :         int nCGMSize = 0;
    6298             :         char *pszCgmToWrite =
    6299           4 :             CPLUnescapeString(pszData, &nCGMSize, CPLES_BackslashQuotable);
    6300             : 
    6301           4 :         if (nCGMSize > 999998)
    6302             :         {
    6303           0 :             CPLError(CE_Warning, CPLE_NotSupported,
    6304             :                      "Length of SEGMENT_%d_DATA is %d, which is greater than "
    6305             :                      "999998. Truncating...",
    6306             :                      i + 1, nCGMSize);
    6307           0 :             nCGMSize = 999998;
    6308             :         }
    6309             : 
    6310           4 :         bOK &= static_cast<int>(
    6311           4 :                    VSIFWriteL(pszCgmToWrite, 1, nCGMSize, fpVSIL)) == nCGMSize;
    6312             : 
    6313             :         /* --------------------------------------------------------------------
    6314             :          */
    6315             :         /*      Update the subheader and data size info in the file header. */
    6316             :         /* --------------------------------------------------------------------
    6317             :          */
    6318           4 :         snprintf(pachLS + nCgmHdrEntrySz * i, nCgmHdrEntrySz + 1, "%04d%06d",
    6319             :                  static_cast<int>(sizeof(achGSH)), nCGMSize);
    6320             : 
    6321           4 :         CPLFree(pszCgmToWrite);
    6322             :     }  // End For
    6323             : 
    6324             :     /* -------------------------------------------------------------------- */
    6325             :     /*      Write out the graphic segment info.                             */
    6326             :     /* -------------------------------------------------------------------- */
    6327             : 
    6328          14 :     bOK &= VSIFSeekL(fpVSIL, nNumSOffset + 3, SEEK_SET) == 0;
    6329          14 :     bOK &= static_cast<int>(VSIFWriteL(pachLS, 1, nNUMS * nCgmHdrEntrySz,
    6330          14 :                                        fpVSIL)) == nNUMS * nCgmHdrEntrySz;
    6331             : 
    6332          14 :     CPLFree(pachLS);
    6333             : 
    6334          14 :     if (strlen(errorMessage) != 0)
    6335             :     {
    6336           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", errorMessage);
    6337           0 :         bOK = false;
    6338             :     }
    6339             : 
    6340          14 :     return bOK;
    6341             : 
    6342             : #undef PLACE
    6343             : }
    6344             : 
    6345             : /************************************************************************/
    6346             : /*                       NITFWriteTextSegments()                        */
    6347             : /************************************************************************/
    6348             : 
    6349         275 : static bool NITFWriteTextSegments(const char *pszFilename, VSILFILE *&fpVSIL,
    6350             :                                   CSLConstList papszList)
    6351             : 
    6352             : {
    6353             :     /* -------------------------------------------------------------------- */
    6354             :     /*      Count the number of apparent text segments to write.  There     */
    6355             :     /*      is nothing at all to do if there are none to write.             */
    6356             :     /* -------------------------------------------------------------------- */
    6357         275 :     int nNUMT = 0;
    6358             : 
    6359         285 :     for (int iOpt = 0; papszList != nullptr && papszList[iOpt] != nullptr;
    6360             :          iOpt++)
    6361             :     {
    6362          10 :         if (STARTS_WITH_CI(papszList[iOpt], "DATA_"))
    6363           6 :             nNUMT++;
    6364             :     }
    6365             : 
    6366         275 :     if (nNUMT == 0)
    6367         270 :         return true;
    6368             : 
    6369             :     /* -------------------------------------------------------------------- */
    6370             :     /*      Open the target file if not already done.                       */
    6371             :     /* -------------------------------------------------------------------- */
    6372           5 :     if (fpVSIL == nullptr)
    6373           1 :         fpVSIL = VSIFOpenL(pszFilename, "r+b");
    6374           5 :     if (fpVSIL == nullptr)
    6375           0 :         return false;
    6376             : 
    6377             :     // Get number of text field.  Since there there could be multiple images
    6378             :     // or graphic segment, the  offset need to be calculated dynamically.
    6379             : 
    6380             :     char achNUMI[4];  // 3 digits plus null character
    6381           5 :     achNUMI[3] = '\0';
    6382             :     // NUMI offset is at a fixed offset 360
    6383             :     const vsi_l_offset nNumIOffset =
    6384           5 :         360 + (NITFHasFSDWNGField(fpVSIL) ? 40 : 0);
    6385           5 :     bool bOK = VSIFSeekL(fpVSIL, nNumIOffset, SEEK_SET) == 0;
    6386           5 :     bOK &= VSIFReadL(achNUMI, 3, 1, fpVSIL) == 1;
    6387           5 :     int nIM = atoi(achNUMI);
    6388             : 
    6389             :     char achNUMG[4];  // 3 digits plus null character
    6390           5 :     achNUMG[3] = '\0';
    6391             : 
    6392             :     // 3 for size of NUMI.  6 and 10 are the field size for LISH and LI
    6393           5 :     const vsi_l_offset nNumGOffset = nNumIOffset + 3 + nIM * (6 + 10);
    6394           5 :     bOK &= VSIFSeekL(fpVSIL, nNumGOffset, SEEK_SET) == 0;
    6395           5 :     bOK &= VSIFReadL(achNUMG, 3, 1, fpVSIL) == 1;
    6396           5 :     const int nGS = atoi(achNUMG);
    6397             : 
    6398             :     // NUMT offset
    6399             :     // 3 for size of NUMG.  4 and 6 are filed size of LSSH and LS.
    6400             :     // the last + 3 is for NUMX field, which is not used
    6401           5 :     const vsi_l_offset nNumTOffset = nNumGOffset + 3 + nGS * (4 + 6) + 3;
    6402             : 
    6403             :     /* -------------------------------------------------------------------- */
    6404             :     /*      Confirm that the NUMT in the file header already matches the    */
    6405             :     /*      number of text segments we want to write, and that the          */
    6406             :     /*      segment header/data size info is blank.                         */
    6407             :     /* -------------------------------------------------------------------- */
    6408             :     char achNUMT[4];
    6409           5 :     char *pachLT = static_cast<char *>(CPLCalloc(nNUMT * 9 + 1, 1));
    6410             : 
    6411           5 :     bOK &= VSIFSeekL(fpVSIL, nNumTOffset, SEEK_SET) == 0;
    6412           5 :     bOK &= VSIFReadL(achNUMT, 3, 1, fpVSIL) == 1;
    6413           5 :     achNUMT[3] = '\0';
    6414             : 
    6415           5 :     bOK &= VSIFReadL(pachLT, nNUMT * 9, 1, fpVSIL) == 1;
    6416             : 
    6417           5 :     if (!bOK || atoi(achNUMT) != nNUMT)
    6418             :     {
    6419           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6420             :                  "It appears an attempt was made to add or update text\n"
    6421             :                  "segments on an NITF file with existing segments.  This\n"
    6422             :                  "is not currently supported by the GDAL NITF driver.");
    6423             : 
    6424           0 :         CPLFree(pachLT);
    6425           0 :         return false;
    6426             :     }
    6427             : 
    6428           5 :     if (!STARTS_WITH_CI(pachLT, "         "))
    6429             :     {
    6430           0 :         CPLFree(pachLT);
    6431             :         // presumably the text segments are already written, do nothing.
    6432           0 :         return true;
    6433             :     }
    6434             : 
    6435             : /* -------------------------------------------------------------------- */
    6436             : /*      At this point we likely ought to confirm NUMDES, NUMRES,        */
    6437             : /*      UDHDL and XHDL are zero.  Consider adding later...              */
    6438             : /* -------------------------------------------------------------------- */
    6439             : 
    6440             : /* ==================================================================== */
    6441             : /*      Write the text segments at the end of the file.                 */
    6442             : /* ==================================================================== */
    6443             : #define PLACE(location, name, text) memcpy(location, text, strlen(text))
    6444           5 :     int iTextSeg = 0;
    6445             : 
    6446          15 :     for (int iOpt = 0;
    6447          15 :          bOK && papszList != nullptr && papszList[iOpt] != nullptr; iOpt++)
    6448             :     {
    6449          10 :         if (!STARTS_WITH_CI(papszList[iOpt], "DATA_"))
    6450           4 :             continue;
    6451             : 
    6452             :         const char *pszTextToWrite =
    6453           6 :             CPLParseNameValue(papszList[iOpt], nullptr);
    6454           6 :         if (pszTextToWrite == nullptr)
    6455           0 :             continue;
    6456             : 
    6457             :         /* --------------------------------------------------------------------
    6458             :          */
    6459             :         /*      Locate corresponding header data in the buffer */
    6460             :         /* --------------------------------------------------------------------
    6461             :          */
    6462             : 
    6463           6 :         const char *pszHeaderBuffer = nullptr;
    6464          13 :         for (int iOpt2 = 0; papszList[iOpt2] != nullptr; iOpt2++)
    6465             :         {
    6466          11 :             if (!STARTS_WITH_CI(papszList[iOpt2], "HEADER_"))
    6467           7 :                 continue;
    6468             : 
    6469           4 :             char *pszHeaderKey = nullptr;
    6470           4 :             CPLParseNameValue(papszList[iOpt2], &pszHeaderKey);
    6471           4 :             char *pszDataKey = nullptr;
    6472           4 :             CPLParseNameValue(papszList[iOpt], &pszDataKey);
    6473           4 :             if (pszHeaderKey == nullptr || pszDataKey == nullptr)
    6474             :             {
    6475           0 :                 CPLFree(pszHeaderKey);
    6476           0 :                 CPLFree(pszDataKey);
    6477           0 :                 continue;
    6478             :             }
    6479             : 
    6480             :             // Point to header and data number.
    6481           4 :             char *pszHeaderId = pszHeaderKey + 7;
    6482           4 :             char *pszDataId = pszDataKey + 5;
    6483             : 
    6484           4 :             const bool bIsSameId = strcmp(pszHeaderId, pszDataId) == 0;
    6485           4 :             CPLFree(pszHeaderKey);
    6486           4 :             CPLFree(pszDataKey);
    6487             : 
    6488             :             // if ID matches, read the header information and exit the loop
    6489           4 :             if (bIsSameId)
    6490             :             {
    6491           4 :                 pszHeaderBuffer = CPLParseNameValue(papszList[iOpt2], nullptr);
    6492           4 :                 break;
    6493             :             }
    6494             :         }
    6495             : 
    6496             :         /* --------------------------------------------------------------------
    6497             :          */
    6498             :         /*      Prepare and write text header. */
    6499             :         /* --------------------------------------------------------------------
    6500             :          */
    6501             :         char achTSH[282];
    6502           6 :         memset(achTSH, ' ', sizeof(achTSH));
    6503           6 :         bOK &= VSIFSeekL(fpVSIL, 0, SEEK_END) == 0;
    6504             : 
    6505           6 :         if (pszHeaderBuffer != nullptr)
    6506             :         {
    6507           4 :             memcpy(achTSH, pszHeaderBuffer,
    6508           4 :                    std::min(strlen(pszHeaderBuffer), sizeof(achTSH)));
    6509             : 
    6510             :             // Take care NITF2.0 date format changes
    6511           4 :             const char chTimeZone = achTSH[20];
    6512             : 
    6513             :             // Check for Zulu time zone character.  IpachLTf that exist, then
    6514             :             // it is NITF2.0 format.
    6515           4 :             if (chTimeZone == 'Z')
    6516             :             {
    6517           0 :                 char *achOrigDate = achTSH + 12;  // original date string
    6518             : 
    6519             :                 // The date value taken from default NITF file date
    6520             :                 char achYear[3];
    6521             : 
    6522             :                 // Offset to the year
    6523           0 :                 strncpy(achYear, achOrigDate + 12, 2);
    6524           0 :                 achYear[2] = '\0';
    6525           0 :                 const int nYear = atoi(achYear);
    6526             : 
    6527             :                 // Set century.
    6528             :                 // Since NITF2.0 does not track the century, we are going to
    6529             :                 // assume any year number greater then 94 (the year NITF2.0
    6530             :                 // spec published), will be 1900s, otherwise, it is 2000s.
    6531           0 :                 char achNewDate[] = "20021216151629";
    6532           0 :                 if (nYear > 94)
    6533           0 :                     memcpy(achNewDate, "19", 2);
    6534             :                 else
    6535           0 :                     memcpy(achNewDate, "20", 2);
    6536             : 
    6537           0 :                 memcpy(achNewDate + 6, achOrigDate, 8);  // copy cover DDhhmmss
    6538           0 :                 memcpy(achNewDate + 2, achOrigDate + 12, 2);  // copy over years
    6539             : 
    6540             :                 // Perform month conversion
    6541           0 :                 char *pszOrigMonth = achOrigDate + 9;
    6542           0 :                 char *pszNewMonth = achNewDate + 4;
    6543             : 
    6544           0 :                 if (STARTS_WITH(pszOrigMonth, "JAN"))
    6545           0 :                     memcpy(pszNewMonth, "01", 2);
    6546           0 :                 else if (STARTS_WITH(pszOrigMonth, "FEB"))
    6547           0 :                     memcpy(pszNewMonth, "02", 2);
    6548           0 :                 else if (STARTS_WITH(pszOrigMonth, "MAR"))
    6549           0 :                     memcpy(pszNewMonth, "03", 2);
    6550           0 :                 else if (STARTS_WITH(pszOrigMonth, "APR"))
    6551           0 :                     memcpy(pszNewMonth, "04", 2);
    6552           0 :                 else if (STARTS_WITH(pszOrigMonth, "MAY"))
    6553           0 :                     memcpy(pszNewMonth, "05", 2);
    6554           0 :                 else if (STARTS_WITH(pszOrigMonth, "JUN"))
    6555           0 :                     memcpy(pszNewMonth, "07", 2);
    6556           0 :                 else if (STARTS_WITH(pszOrigMonth, "AUG"))
    6557           0 :                     memcpy(pszNewMonth, "08", 2);
    6558           0 :                 else if (STARTS_WITH(pszOrigMonth, "SEP"))
    6559           0 :                     memcpy(pszNewMonth, "09", 2);
    6560           0 :                 else if (STARTS_WITH(pszOrigMonth, "OCT"))
    6561           0 :                     memcpy(pszNewMonth, "10", 2);
    6562           0 :                 else if (STARTS_WITH(pszOrigMonth, "NOV"))
    6563           0 :                     memcpy(pszNewMonth, "11", 2);
    6564           0 :                 else if (STARTS_WITH(pszOrigMonth, "DEC"))
    6565           0 :                     memcpy(pszNewMonth, "12", 2);
    6566             : 
    6567           0 :                 PLACE(achTSH + 12, TXTDT, achNewDate);
    6568             :             }
    6569             :         }
    6570             :         else
    6571             :         {  // Use default value if header information is not found
    6572           2 :             PLACE(achTSH + 0, TE, "TE");
    6573           2 :             PLACE(achTSH + 9, TXTALVL, "000");
    6574           2 :             PLACE(achTSH + 12, TXTDT, "20021216151629");
    6575           2 :             PLACE(achTSH + 106, TSCLAS, "U");
    6576           2 :             PLACE(achTSH + 273, ENCRYP, "0");
    6577           2 :             PLACE(achTSH + 274, TXTFMT, "STA");
    6578           2 :             PLACE(achTSH + 277, TXSHDL, "00000");
    6579             :         }
    6580             : 
    6581           6 :         bOK &= VSIFWriteL(achTSH, sizeof(achTSH), 1, fpVSIL) == 1;
    6582             : 
    6583             :         /* --------------------------------------------------------------------
    6584             :          */
    6585             :         /*      Prepare and write text segment data. */
    6586             :         /* --------------------------------------------------------------------
    6587             :          */
    6588             : 
    6589           6 :         int nTextLength = static_cast<int>(strlen(pszTextToWrite));
    6590           6 :         if (nTextLength > 99998)
    6591             :         {
    6592           0 :             CPLError(CE_Warning, CPLE_NotSupported,
    6593             :                      "Length of DATA_%d is %d, which is greater than 99998. "
    6594             :                      "Truncating...",
    6595             :                      iTextSeg + 1, nTextLength);
    6596           0 :             nTextLength = 99998;
    6597             :         }
    6598             : 
    6599           6 :         bOK &= static_cast<int>(VSIFWriteL(pszTextToWrite, 1, nTextLength,
    6600           6 :                                            fpVSIL)) == nTextLength;
    6601             : 
    6602             :         /* --------------------------------------------------------------------
    6603             :          */
    6604             :         /*      Update the subheader and data size info in the file header. */
    6605             :         /* --------------------------------------------------------------------
    6606             :          */
    6607           6 :         CPLsnprintf(pachLT + 9 * iTextSeg + 0, 9 + 1, "%04d%05d",
    6608             :                     static_cast<int>(sizeof(achTSH)), nTextLength);
    6609             : 
    6610           6 :         iTextSeg++;
    6611             :     }
    6612             : 
    6613             :     /* -------------------------------------------------------------------- */
    6614             :     /*      Write out the text segment info.                                */
    6615             :     /* -------------------------------------------------------------------- */
    6616             : 
    6617           5 :     bOK &= VSIFSeekL(fpVSIL, nNumTOffset + 3, SEEK_SET) == 0;
    6618           5 :     bOK &=
    6619           5 :         static_cast<int>(VSIFWriteL(pachLT, 1, nNUMT * 9, fpVSIL)) == nNUMT * 9;
    6620             : 
    6621           5 :     CPLFree(pachLT);
    6622             : 
    6623           5 :     return bOK;
    6624             : #undef PLACE
    6625             : }
    6626             : 
    6627             : /************************************************************************/
    6628             : /*                            NITFWriteDES()                            */
    6629             : /************************************************************************/
    6630             : 
    6631          13 : static bool NITFWriteDES(VSILFILE *&fp, const char *pszFilename,
    6632             :                          vsi_l_offset nOffsetLDSH, int iDES,
    6633             :                          const char *pszDESName, const GByte *pabyDESData,
    6634             :                          int nArrayLen)
    6635             : {
    6636          13 :     constexpr int LEN_DE = 2;
    6637          13 :     constexpr int LEN_DESID = 25;
    6638          13 :     constexpr int LEN_DESOFLW = 6;
    6639          13 :     constexpr int LEN_DESITEM = 3;
    6640          13 :     const int nTotalLen = LEN_DE + LEN_DESID + nArrayLen;
    6641             : 
    6642          13 :     const bool bIsTRE_OVERFLOW = (strcmp(pszDESName, "TRE_OVERFLOW") == 0);
    6643          13 :     const int MIN_LEN_DES_SUBHEADER =
    6644          13 :         200 + (bIsTRE_OVERFLOW ? LEN_DESOFLW + LEN_DESITEM : 0);
    6645             : 
    6646          13 :     if (nTotalLen < MIN_LEN_DES_SUBHEADER)
    6647             :     {
    6648           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6649             :                  "DES does not contain enough data");
    6650           0 :         return false;
    6651             :     }
    6652             : 
    6653          13 :     int nDESITEM = 0;
    6654          13 :     GUIntBig nIXSOFLOffset = 0;
    6655          13 :     if (bIsTRE_OVERFLOW)
    6656             :     {
    6657             :         char szDESITEM[LEN_DESITEM + 1];
    6658           3 :         memcpy(szDESITEM, pabyDESData + 169 + LEN_DESOFLW, LEN_DESITEM);
    6659           3 :         szDESITEM[LEN_DESITEM] = '\0';
    6660           3 :         if (!isdigit(static_cast<unsigned char>(szDESITEM[0])) ||
    6661           3 :             !isdigit(static_cast<unsigned char>(szDESITEM[1])) ||
    6662           3 :             !isdigit(static_cast<unsigned char>(szDESITEM[2])))
    6663             :         {
    6664           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    6665             :                      "Invalid value for DESITEM: '%s'", szDESITEM);
    6666           2 :             return false;
    6667             :         }
    6668           3 :         nDESITEM = atoi(szDESITEM);
    6669             : 
    6670             :         char szDESOFLW[LEN_DESOFLW + 1];
    6671           3 :         memcpy(szDESOFLW, pabyDESData + 169, LEN_DESOFLW);
    6672           3 :         szDESOFLW[LEN_DESOFLW] = '\0';
    6673           3 :         if (strcmp(szDESOFLW, "IXSHD ") == 0)
    6674             :         {
    6675           3 :             auto psFile = NITFOpenEx(fp, pszFilename);
    6676           3 :             if (psFile == nullptr)
    6677             :             {
    6678           0 :                 fp = nullptr;
    6679           0 :                 return false;
    6680             :             }
    6681             : 
    6682           3 :             int nImageIdx = 1;
    6683           5 :             for (int iSegment = 0; iSegment < psFile->nSegmentCount; ++iSegment)
    6684             :             {
    6685           4 :                 const auto psSegInfo = psFile->pasSegmentInfo + iSegment;
    6686           4 :                 if (!EQUAL(psSegInfo->szSegmentType, "IM"))
    6687           1 :                     continue;
    6688           3 :                 if (nImageIdx == nDESITEM)
    6689             :                 {
    6690           2 :                     auto psImage = NITFImageAccess(psFile, iSegment);
    6691           2 :                     if (psImage == nullptr)
    6692             :                     {
    6693           0 :                         nImageIdx = -1;
    6694           0 :                         break;
    6695             :                     }
    6696             : 
    6697           2 :                     if (psImage->nIXSOFL == -1)
    6698             :                     {
    6699           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    6700             :                                  "Missing IXSOFL field in image %d. "
    6701             :                                  "RESERVE_SPACE_FOR_TRE_OVERFLOW=YES creation "
    6702             :                                  "option likely missing.",
    6703             :                                  nImageIdx);
    6704             :                     }
    6705           1 :                     else if (psImage->nIXSOFL != 0)
    6706             :                     {
    6707           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    6708             :                                  "Expected IXSOFL of image %d to be 0. Got %d",
    6709             :                                  nImageIdx, psImage->nIXSOFL);
    6710             :                     }
    6711             :                     else
    6712             :                     {
    6713           1 :                         nIXSOFLOffset = psSegInfo->nSegmentHeaderStart +
    6714           1 :                                         psImage->nIXSOFLOffsetInSubfileHeader;
    6715             :                     }
    6716             : 
    6717           2 :                     NITFImageDeaccess(psImage);
    6718           2 :                     break;
    6719             :                 }
    6720           1 :                 ++nImageIdx;
    6721             :             }
    6722             : 
    6723           3 :             psFile->fp = nullptr;
    6724           3 :             NITFClose(psFile);
    6725             : 
    6726           3 :             if (nImageIdx != nDESITEM)
    6727             :             {
    6728           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    6729             :                          "Cannot find image matching DESITEM = %d value",
    6730             :                          nDESITEM);
    6731           0 :                 return false;
    6732             :             }
    6733           3 :             if (nIXSOFLOffset == 0)
    6734             :             {
    6735           2 :                 return false;
    6736             :             }
    6737             :         }
    6738           0 :         else if (strcmp(szDESOFLW, "UDHD  ") == 0 ||
    6739           0 :                  strcmp(szDESOFLW, "UDID  ") == 0 ||
    6740           0 :                  strcmp(szDESOFLW, "XHD   ") == 0 ||
    6741           0 :                  strcmp(szDESOFLW, "SXSHD ") == 0 ||
    6742           0 :                  strcmp(szDESOFLW, "TXSHD ") == 0)
    6743             :         {
    6744           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    6745             :                      "Unhandled value for DESOFLW: '%s'. "
    6746             :                      "Segment subheader fields will not be updated.",
    6747             :                      szDESOFLW);
    6748             :         }
    6749             :         else
    6750             :         {
    6751           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    6752             :                      "Invalid value for DESOFLW: '%s'", szDESOFLW);
    6753           0 :             return false;
    6754             :         }
    6755             :     }
    6756             : 
    6757             :     // Extract DESSHL value
    6758          11 :     constexpr int LEN_DESSHL = 4;
    6759             :     char szDESSHL[LEN_DESSHL + 1];
    6760          11 :     const int OFFSET_DESSHL =
    6761          11 :         169 + (bIsTRE_OVERFLOW ? LEN_DESOFLW + LEN_DESITEM : 0);
    6762          11 :     memcpy(szDESSHL, pabyDESData + OFFSET_DESSHL, LEN_DESSHL);
    6763          11 :     szDESSHL[LEN_DESSHL] = '\0';
    6764          11 :     if (!isdigit(static_cast<unsigned char>(szDESSHL[0])) ||
    6765          11 :         !isdigit(static_cast<unsigned char>(szDESSHL[1])) ||
    6766          11 :         !isdigit(static_cast<unsigned char>(szDESSHL[2])) ||
    6767          11 :         !isdigit(static_cast<unsigned char>(szDESSHL[3])))
    6768             :     {
    6769           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid value for DESSHL: '%s'",
    6770             :                  szDESSHL);
    6771           0 :         return false;
    6772             :     }
    6773          11 :     const int nDESSHL = atoi(szDESSHL);
    6774          11 :     const int nSubHeadLen = nDESSHL + MIN_LEN_DES_SUBHEADER;
    6775          11 :     const int nDataLen =
    6776             :         nTotalLen - nSubHeadLen;  // Length of DESDATA field only
    6777          11 :     if (nDataLen < 0)
    6778             :     {
    6779           0 :         CPLError(
    6780             :             CE_Failure, CPLE_AppDefined,
    6781             :             "Value of DESSHL = '%s' is not consistent with provided DESData",
    6782             :             szDESSHL);
    6783           0 :         return false;
    6784             :     }
    6785             : 
    6786          11 :     if (nSubHeadLen > 9998 || nDataLen > 999999998)
    6787             :     {
    6788           0 :         CPLError(CE_Failure, CPLE_AppDefined, "DES is too big to be written");
    6789           0 :         return false;
    6790             :     }
    6791             : 
    6792          11 :     bool bOK = VSIFSeekL(fp, 0, SEEK_END) == 0;
    6793          11 :     bOK &= VSIFWriteL("DE", 1, 2, fp) == 2;
    6794          11 :     bOK &= VSIFWriteL(CPLSPrintf("%-25s", pszDESName), 1, 25, fp) == 25;
    6795          11 :     bOK &= VSIFWriteL(pabyDESData, 1, nArrayLen, fp) ==
    6796          11 :            static_cast<size_t>(nArrayLen);
    6797             : 
    6798             :     // Update LDSH and LD in the NITF Header
    6799          11 :     bOK &= VSIFSeekL(fp, nOffsetLDSH + iDES * 13, SEEK_SET) == 0;
    6800          11 :     bOK &= VSIFWriteL(CPLSPrintf("%04d", nSubHeadLen), 1, 4, fp) == 4;
    6801          11 :     bOK &= VSIFWriteL(CPLSPrintf("%09d", nDataLen), 1, 9, fp) == 9;
    6802             : 
    6803          11 :     if (nIXSOFLOffset > 0)
    6804             :     {
    6805           1 :         CPLDebug("NITF", "Patching IXSOFL of image %d to %d", iDES + 1,
    6806             :                  nDESITEM);
    6807           1 :         bOK &= VSIFSeekL(fp, nIXSOFLOffset, SEEK_SET) == 0;
    6808           1 :         bOK &= VSIFWriteL(CPLSPrintf("%03d", nDESITEM), 1, 3, fp) == 3;
    6809             :     }
    6810             : 
    6811          11 :     return bOK;
    6812             : }
    6813             : 
    6814             : /************************************************************************/
    6815             : /*                            NITFWriteDES()                            */
    6816             : /************************************************************************/
    6817             : 
    6818         275 : static bool NITFWriteDES(const char *pszFilename, VSILFILE *&fpVSIL,
    6819             :                          GDALOffsetPatcher::OffsetPatcher *offsetPatcher,
    6820             :                          const CPLStringList &aosOptions, int nReciprocalScale)
    6821             : {
    6822         275 :     int nDESFound = offsetPatcher ? 1 : 0;
    6823        1297 :     for (const char *pszOpt : aosOptions)
    6824             :     {
    6825        1022 :         if (cpl::starts_with(std::string_view(pszOpt),
    6826             :                              std::string_view("DES=")))
    6827             :         {
    6828          13 :             nDESFound++;
    6829             :         }
    6830             :     }
    6831         275 :     if (nDESFound == 0)
    6832             :     {
    6833         231 :         return true;
    6834             :     }
    6835             : 
    6836             :     /* -------------------------------------------------------------------- */
    6837             :     /*      Open the target file if not already done.                       */
    6838             :     /* -------------------------------------------------------------------- */
    6839          44 :     if (fpVSIL == nullptr)
    6840          11 :         fpVSIL = VSIFOpenL(pszFilename, "r+b");
    6841          44 :     if (fpVSIL == nullptr)
    6842           0 :         return false;
    6843             : 
    6844             :     // NUMI offset is at a fixed offset 360, unless there is a FSDWNG field
    6845             :     const vsi_l_offset nNumIOffset =
    6846          44 :         360 + (NITFHasFSDWNGField(fpVSIL) ? 40 : 0);
    6847             : 
    6848             :     char achNUMI[4];  // 3 digits plus null character
    6849          44 :     achNUMI[3] = '\0';
    6850          44 :     bool bOK = VSIFSeekL(fpVSIL, nNumIOffset, SEEK_SET) == 0;
    6851          44 :     bOK &= VSIFReadL(achNUMI, 3, 1, fpVSIL) == 1;
    6852          44 :     int nIM = atoi(achNUMI);
    6853             : 
    6854             :     char achNUMG[4];  // 3 digits plus null character
    6855          44 :     achNUMG[3] = '\0';
    6856             : 
    6857             :     // 3 for size of NUMI.  6 and 10 are the field size for LISH and LI
    6858          44 :     const vsi_l_offset nNumGOffset = nNumIOffset + 3 + nIM * (6 + 10);
    6859          44 :     bOK &= VSIFSeekL(fpVSIL, nNumGOffset, SEEK_SET) == 0;
    6860          44 :     bOK &= VSIFReadL(achNUMG, 3, 1, fpVSIL) == 1;
    6861          44 :     const int nGS = atoi(achNUMG);
    6862             : 
    6863             :     // NUMT offset
    6864             :     // 3 for size of NUMG.  4 and 6 are the field size of LSSH and LS.
    6865             :     // the last + 3 is for NUMX field, which is not used
    6866          44 :     const vsi_l_offset nNumTOffset = nNumGOffset + 3 + nGS * (4 + 6) + 3;
    6867             :     char achNUMT[4];
    6868          44 :     bOK &= VSIFSeekL(fpVSIL, nNumTOffset, SEEK_SET) == 0;
    6869          44 :     bOK &= VSIFReadL(achNUMT, 3, 1, fpVSIL) == 1;
    6870          44 :     achNUMT[3] = '\0';
    6871          44 :     const int nNUMT = atoi(achNUMT);
    6872             : 
    6873             :     // NUMDES offset
    6874             :     // 3 for size of NUMT. 4 and 5 are the field size of LTSH and LT.
    6875          44 :     const vsi_l_offset nNumDESOffset = nNumTOffset + 3 + (4 + 5) * nNUMT;
    6876             :     char achNUMDES[4];
    6877          44 :     bOK &= VSIFSeekL(fpVSIL, nNumDESOffset, SEEK_SET) == 0;
    6878          44 :     bOK &= VSIFReadL(achNUMDES, 3, 1, fpVSIL) == 1;
    6879          44 :     achNUMDES[3] = '\0';
    6880             : 
    6881          44 :     if (!bOK || atoi(achNUMDES) != nDESFound)
    6882             :     {
    6883           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6884             :                  "It appears an attempt was made to add or update DE\n"
    6885             :                  "segments on an NITF file with existing segments.  This\n"
    6886             :                  "is not currently supported by the GDAL NITF driver.");
    6887           0 :         return false;
    6888             :     }
    6889             : 
    6890          44 :     const auto nOffsetLDSH = nNumDESOffset + 3;
    6891             : 
    6892          44 :     int iDES = 0;
    6893          44 :     if (offsetPatcher)
    6894             :     {
    6895          32 :         bOK = RPFFrameWriteCADRG_RPFDES(offsetPatcher, fpVSIL, nOffsetLDSH,
    6896             :                                         aosOptions, nReciprocalScale);
    6897          32 :         iDES = 1;
    6898             :     }
    6899             : 
    6900         649 :     for (int iOption = 0; bOK && iOption < aosOptions.size(); iOption++)
    6901             :     {
    6902         607 :         if (!cpl::starts_with(std::string_view(aosOptions[iOption]),
    6903             :                               std::string_view("DES=")))
    6904             :         {
    6905         594 :             continue;
    6906             :         }
    6907             : 
    6908             :         /* We don't use CPLParseNameValue() as it removes leading spaces */
    6909             :         /* from the value (see #3088) */
    6910          13 :         const char *pszDESOpt = aosOptions[iOption] + 4;
    6911          13 :         const char *pszDelim = strchr(pszDESOpt, '=');
    6912          13 :         if (pszDelim == nullptr)
    6913             :         {
    6914           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    6915             :                      "Could not parse creation options %s", pszDESOpt);
    6916           2 :             return false;
    6917             :         }
    6918             : 
    6919          13 :         const size_t nNameLength = strlen(pszDESOpt) - strlen(pszDelim);
    6920          13 :         if (nNameLength > 25)
    6921             :         {
    6922           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    6923             :                      "Specified DESID is too long %s", pszDESOpt);
    6924           0 :             return false;
    6925             :         }
    6926             : 
    6927          13 :         char *pszDESName = static_cast<char *>(CPLMalloc(nNameLength + 1));
    6928          13 :         memcpy(pszDESName, pszDESOpt, nNameLength);
    6929          13 :         pszDESName[nNameLength] = '\0';
    6930             : 
    6931          13 :         const char *pszEscapedContents = pszDelim + 1;
    6932             : 
    6933          13 :         int nContentLength = 0;
    6934             :         GByte *pabyUnescapedContents =
    6935          13 :             reinterpret_cast<GByte *>(CPLUnescapeString(
    6936             :                 pszEscapedContents, &nContentLength, CPLES_BackslashQuotable));
    6937             : 
    6938          13 :         if (!NITFWriteDES(fpVSIL, pszFilename, nOffsetLDSH, iDES, pszDESName,
    6939             :                           pabyUnescapedContents, nContentLength))
    6940             :         {
    6941           2 :             CPLFree(pszDESName);
    6942           2 :             CPLFree(pabyUnescapedContents);
    6943           2 :             CPLError(CE_Failure, CPLE_AppDefined, "Could not write DES %d",
    6944             :                      iDES);
    6945           2 :             return false;
    6946             :         }
    6947             : 
    6948          11 :         CPLFree(pszDESName);
    6949          11 :         CPLFree(pabyUnescapedContents);
    6950             : 
    6951          11 :         iDES++;
    6952             :     }
    6953             : 
    6954          42 :     return bOK;
    6955             : }
    6956             : 
    6957             : /************************************************************************/
    6958             : /*                          UpdateFileLength()                          */
    6959             : /************************************************************************/
    6960             : 
    6961          57 : static bool UpdateFileLength(VSILFILE *fp)
    6962             : {
    6963             : 
    6964             :     /* -------------------------------------------------------------------- */
    6965             :     /*      Update total file length.                                       */
    6966             :     /* -------------------------------------------------------------------- */
    6967          57 :     bool bOK = VSIFSeekL(fp, 0, SEEK_END) == 0;
    6968          57 :     GUIntBig nFileLen = VSIFTellL(fp);
    6969             : 
    6970             :     // Offset to file length entry
    6971          57 :     const vsi_l_offset nFLOffset = 342 + (NITFHasFSDWNGField(fp) ? 40 : 0);
    6972          57 :     bOK &= VSIFSeekL(fp, nFLOffset, SEEK_SET) == 0;
    6973          57 :     if (nFileLen >= NITF_MAX_FILE_SIZE)
    6974             :     {
    6975           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6976             :                  "Too big file : " CPL_FRMT_GUIB
    6977             :                  ". Truncating to " CPL_FRMT_GUIB,
    6978             :                  nFileLen, NITF_MAX_FILE_SIZE - 1);
    6979           0 :         nFileLen = NITF_MAX_FILE_SIZE - 1;
    6980             :     }
    6981             :     CPLString osLen =
    6982          57 :         CPLString().Printf("%012" CPL_FRMT_GB_WITHOUT_PREFIX "u", nFileLen);
    6983          57 :     bOK &= VSIFWriteL(reinterpret_cast<const void *>(osLen.c_str()), 12, 1,
    6984          57 :                       fp) == 1;
    6985         114 :     return bOK;
    6986             : }
    6987             : 
    6988             : /************************************************************************/
    6989             : /*                       NITFWriteExtraSegments()                       */
    6990             : /************************************************************************/
    6991             : 
    6992             : static bool
    6993         275 : NITFWriteExtraSegments(const char *pszFilename, VSILFILE *fpIn,
    6994             :                        CSLConstList papszCgmMD, CSLConstList papszTextMD,
    6995             :                        GDALOffsetPatcher::OffsetPatcher *offsetPatcher,
    6996             :                        const CPLStringList &aosOptions, int nReciprocalScale)
    6997             : {
    6998         275 :     VSILFILE *fp = fpIn;
    6999         275 :     bool bOK = NITFWriteCGMSegments(pszFilename, fp, papszCgmMD);
    7000         275 :     bOK &= NITFWriteTextSegments(pszFilename, fp, papszTextMD);
    7001         275 :     bOK &= NITFWriteDES(pszFilename, fp, offsetPatcher, aosOptions,
    7002         275 :                         nReciprocalScale);
    7003         275 :     if (fp)
    7004             :     {
    7005          57 :         bOK &= UpdateFileLength(fp);
    7006             : 
    7007          57 :         if (fp != fpIn && VSIFCloseL(fp) != 0)
    7008           0 :             bOK = false;
    7009             : 
    7010          57 :         if (!bOK)
    7011             :         {
    7012           2 :             CPLError(CE_Failure, CPLE_FileIO, "I/O error");
    7013             :         }
    7014             :     }
    7015         275 :     return bOK;
    7016             : }
    7017             : 
    7018             : /************************************************************************/
    7019             : /*                         NITFWriteJPEGImage()                         */
    7020             : /************************************************************************/
    7021             : 
    7022             : #ifdef JPEG_SUPPORTED
    7023             : 
    7024             : int NITFWriteJPEGBlock(GDALDataset *poSrcDS, VSILFILE *fp, int nBlockXOff,
    7025             :                        int nBlockYOff, int nBlockXSize, int nBlockYSize,
    7026             :                        int bProgressive, int nQuality, const GByte *pabyAPP6,
    7027             :                        int nRestartInterval, GDALProgressFunc pfnProgress,
    7028             :                        void *pProgressData);
    7029             : 
    7030          10 : static bool NITFWriteJPEGImage(GDALDataset *poSrcDS, VSILFILE *fp,
    7031             :                                vsi_l_offset nStartOffset,
    7032             :                                CSLConstList papszOptions,
    7033             :                                GDALProgressFunc pfnProgress,
    7034             :                                void *pProgressData)
    7035             : {
    7036          10 :     if (!pfnProgress(0.0, nullptr, pProgressData))
    7037           0 :         return false;
    7038             : 
    7039             :     /* -------------------------------------------------------------------- */
    7040             :     /*      Some some rudimentary checks                                    */
    7041             :     /* -------------------------------------------------------------------- */
    7042          10 :     const int nBands = poSrcDS->GetRasterCount();
    7043          10 :     if (nBands != 1 && nBands != 3)
    7044             :     {
    7045           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    7046             :                  "JPEG driver doesn't support %d bands.  Must be 1 (grey) "
    7047             :                  "or 3 (RGB) bands.\n",
    7048             :                  nBands);
    7049             : 
    7050           0 :         return false;
    7051             :     }
    7052             : 
    7053          10 :     GDALDataType eDT = poSrcDS->GetRasterBand(1)->GetRasterDataType();
    7054             : 
    7055             : #if defined(JPEG_LIB_MK1) || defined(JPEG_DUAL_MODE_8_12)
    7056          10 :     if (eDT != GDT_UInt8 && eDT != GDT_UInt16)
    7057             :     {
    7058           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    7059             :                  "JPEG driver doesn't support data type %s. "
    7060             :                  "Only eight and twelve bit bands supported (Mk1 libjpeg).\n",
    7061             :                  GDALGetDataTypeName(
    7062             :                      poSrcDS->GetRasterBand(1)->GetRasterDataType()));
    7063             : 
    7064           0 :         return false;
    7065             :     }
    7066             : 
    7067          10 :     if (eDT == GDT_UInt16 || eDT == GDT_Int16)
    7068           2 :         eDT = GDT_UInt16;
    7069             :     else
    7070           8 :         eDT = GDT_UInt8;
    7071             : 
    7072             : #else
    7073             :     if (eDT != GDT_UInt8)
    7074             :     {
    7075             :         CPLError(CE_Failure, CPLE_NotSupported,
    7076             :                  "JPEG driver doesn't support data type %s. "
    7077             :                  "Only eight bit byte bands supported.\n",
    7078             :                  GDALGetDataTypeName(
    7079             :                      poSrcDS->GetRasterBand(1)->GetRasterDataType()));
    7080             : 
    7081             :         return false;
    7082             :     }
    7083             : 
    7084             :     eDT = GDT_UInt8;  // force to 8bit.
    7085             : #endif
    7086             : 
    7087             :     /* -------------------------------------------------------------------- */
    7088             :     /*      What options has the user selected?                             */
    7089             :     /* -------------------------------------------------------------------- */
    7090          10 :     int nQuality = 75;
    7091          10 :     if (CSLFetchNameValue(papszOptions, "QUALITY") != nullptr)
    7092             :     {
    7093           2 :         nQuality = atoi(CSLFetchNameValue(papszOptions, "QUALITY"));
    7094           2 :         if (nQuality < 10 || nQuality > 100)
    7095             :         {
    7096           0 :             CPLError(CE_Failure, CPLE_IllegalArg,
    7097             :                      "QUALITY=%s is not a legal value in the range 10-100.",
    7098             :                      CSLFetchNameValue(papszOptions, "QUALITY"));
    7099           0 :             return false;
    7100             :         }
    7101             :     }
    7102             : 
    7103          10 :     int nRestartInterval = -1;
    7104          10 :     if (CSLFetchNameValue(papszOptions, "RESTART_INTERVAL") != nullptr)
    7105             :     {
    7106             :         nRestartInterval =
    7107           0 :             atoi(CSLFetchNameValue(papszOptions, "RESTART_INTERVAL"));
    7108             :     }
    7109             : 
    7110          10 :     const bool bProgressive = CPLFetchBool(papszOptions, "PROGRESSIVE", false);
    7111             : 
    7112             :     /* -------------------------------------------------------------------- */
    7113             :     /*      Compute blocking factors                                        */
    7114             :     /* -------------------------------------------------------------------- */
    7115          10 :     const int nXSize = poSrcDS->GetRasterXSize();
    7116          10 :     const int nYSize = poSrcDS->GetRasterYSize();
    7117          10 :     int nNPPBH = nXSize;
    7118          10 :     int nNPPBV = nYSize;
    7119             : 
    7120          10 :     if (CSLFetchNameValue(papszOptions, "BLOCKXSIZE") != nullptr)
    7121           3 :         nNPPBH = atoi(CSLFetchNameValue(papszOptions, "BLOCKXSIZE"));
    7122             : 
    7123          10 :     if (CSLFetchNameValue(papszOptions, "BLOCKYSIZE") != nullptr)
    7124           3 :         nNPPBV = atoi(CSLFetchNameValue(papszOptions, "BLOCKYSIZE"));
    7125             : 
    7126          10 :     if (CSLFetchNameValue(papszOptions, "NPPBH") != nullptr)
    7127           0 :         nNPPBH = atoi(CSLFetchNameValue(papszOptions, "NPPBH"));
    7128             : 
    7129          10 :     if (CSLFetchNameValue(papszOptions, "NPPBV") != nullptr)
    7130           0 :         nNPPBV = atoi(CSLFetchNameValue(papszOptions, "NPPBV"));
    7131             : 
    7132          10 :     if (nNPPBH <= 0 || nNPPBV <= 0 || nNPPBH > 9999 || nNPPBV > 9999)
    7133             :     {
    7134           1 :         nNPPBH = 256;
    7135           1 :         nNPPBV = 256;
    7136             :     }
    7137             : 
    7138          10 :     const int nNBPR = DIV_ROUND_UP(nXSize, nNPPBH);
    7139          10 :     const int nNBPC = DIV_ROUND_UP(nYSize, nNPPBV);
    7140             : 
    7141             :     /* -------------------------------------------------------------------- */
    7142             :     /*  Creates APP6 NITF application segment (required by MIL-STD-188-198) */
    7143             :     /*  see #3345                                                           */
    7144             :     /* -------------------------------------------------------------------- */
    7145             :     GByte abyAPP6[23];
    7146          10 :     memcpy(abyAPP6, "NITF", 4);
    7147          10 :     abyAPP6[4] = 0;
    7148          10 :     int nOffset = 5;
    7149             : 
    7150             :     /* Version : 2.0 */
    7151          10 :     GUInt16 nUInt16 = 0x0200;
    7152          10 :     CPL_MSBPTR16(&nUInt16);
    7153          10 :     memcpy(abyAPP6 + nOffset, &nUInt16, sizeof(nUInt16));
    7154          10 :     nOffset += sizeof(nUInt16);
    7155             : 
    7156             :     /* IMODE */
    7157          10 :     abyAPP6[nOffset] = (nBands == 1) ? 'B' : 'P';
    7158          10 :     nOffset++;
    7159             : 
    7160             :     /* Number of image blocks per row */
    7161          10 :     nUInt16 = static_cast<GUInt16>(nNBPR);
    7162          10 :     CPL_MSBPTR16(&nUInt16);
    7163          10 :     memcpy(abyAPP6 + nOffset, &nUInt16, sizeof(nUInt16));
    7164          10 :     nOffset += sizeof(nUInt16);
    7165             : 
    7166             :     /* Number of image blocks per column */
    7167          10 :     nUInt16 = static_cast<GUInt16>(nNBPC);
    7168          10 :     CPL_MSBPTR16(&nUInt16);
    7169          10 :     memcpy(abyAPP6 + nOffset, &nUInt16, sizeof(nUInt16));
    7170          10 :     nOffset += sizeof(nUInt16);
    7171             : 
    7172             :     /* Image color */
    7173          10 :     abyAPP6[nOffset] = (nBands == 1) ? 0 : 1;
    7174          10 :     nOffset++;
    7175             : 
    7176             :     /* Original sample precision */
    7177             :     /* coverity[dead_error_line] */
    7178          10 :     abyAPP6[nOffset] = (eDT == GDT_UInt16) ? 12 : 8;
    7179          10 :     nOffset++;
    7180             : 
    7181             :     /* Image class */
    7182          10 :     abyAPP6[nOffset] = 0;
    7183          10 :     nOffset++;
    7184             : 
    7185             :     /* JPEG coding process */
    7186             :     /* coverity[dead_error_line] */
    7187          10 :     abyAPP6[nOffset] = (eDT == GDT_UInt16) ? 4 : 1;
    7188          10 :     nOffset++;
    7189             : 
    7190             :     /* Quality */
    7191          10 :     abyAPP6[nOffset] = 0;
    7192          10 :     nOffset++;
    7193             : 
    7194             :     /* Stream color */
    7195          10 :     abyAPP6[nOffset] = (nBands == 1) ? 0 /* Monochrome */ : 2 /* YCbCr*/;
    7196          10 :     nOffset++;
    7197             : 
    7198             :     /* Stream bits */
    7199             :     /* coverity[dead_error_line] */
    7200          10 :     abyAPP6[nOffset] = (eDT == GDT_UInt16) ? 12 : 8;
    7201          10 :     nOffset++;
    7202             : 
    7203             :     /* Horizontal filtering */
    7204          10 :     abyAPP6[nOffset] = 1;
    7205          10 :     nOffset++;
    7206             : 
    7207             :     /* Vertical filtering */
    7208          10 :     abyAPP6[nOffset] = 1;
    7209          10 :     nOffset++;
    7210             : 
    7211             :     /* Reserved */
    7212          10 :     abyAPP6[nOffset] = 0;
    7213          10 :     nOffset++;
    7214          10 :     abyAPP6[nOffset] = 0;
    7215          10 :     nOffset++;
    7216             :     (void)nOffset;
    7217             : 
    7218          10 :     CPLAssert(nOffset == sizeof(abyAPP6));
    7219             : 
    7220             :     /* -------------------------------------------------------------------- */
    7221             :     /*      Prepare block map if necessary                                  */
    7222             :     /* -------------------------------------------------------------------- */
    7223             : 
    7224          10 :     bool bOK = VSIFSeekL(fp, nStartOffset, SEEK_SET) == 0;
    7225             : 
    7226          10 :     const char *pszIC = CSLFetchNameValue(papszOptions, "IC");
    7227          10 :     GUInt32 nIMDATOFF = 0;
    7228          10 :     constexpr GUInt32 BLOCKMAP_HEADER_SIZE = 4 + 2 + 2 + 2;
    7229          10 :     if (EQUAL(pszIC, "M3"))
    7230             :     {
    7231             :         /* Prepare the block map */
    7232           1 :         GUInt32 nIMDATOFF_MSB = BLOCKMAP_HEADER_SIZE + nNBPC * nNBPR * 4;
    7233           1 :         nIMDATOFF = nIMDATOFF_MSB;
    7234           1 :         GUInt16 nBMRLNTH = 4;
    7235           1 :         GUInt16 nTMRLNTH = 0;
    7236           1 :         GUInt16 nTPXCDLNTH = 0;
    7237             : 
    7238           1 :         CPL_MSBPTR32(&nIMDATOFF_MSB);
    7239           1 :         CPL_MSBPTR16(&nBMRLNTH);
    7240           1 :         CPL_MSBPTR16(&nTMRLNTH);
    7241           1 :         CPL_MSBPTR16(&nTPXCDLNTH);
    7242             : 
    7243           1 :         bOK &= VSIFWriteL(&nIMDATOFF_MSB, 4, 1, fp) == 1;
    7244           1 :         bOK &= VSIFWriteL(&nBMRLNTH, 2, 1, fp) == 1;
    7245           1 :         bOK &= VSIFWriteL(&nTMRLNTH, 2, 1, fp) == 1;
    7246           1 :         bOK &= VSIFWriteL(&nTPXCDLNTH, 2, 1, fp) == 1;
    7247             : 
    7248             :         /* Reserve space for the table itself */
    7249           1 :         bOK &= VSIFSeekL(fp, static_cast<vsi_l_offset>(nNBPC) * nNBPR * 4,
    7250           1 :                          SEEK_CUR) == 0;
    7251             :     }
    7252             : 
    7253             :     /* -------------------------------------------------------------------- */
    7254             :     /*      Copy each block                                                 */
    7255             :     /* -------------------------------------------------------------------- */
    7256          22 :     for (int nBlockYOff = 0; bOK && nBlockYOff < nNBPC; nBlockYOff++)
    7257             :     {
    7258          67 :         for (int nBlockXOff = 0; bOK && nBlockXOff < nNBPR; nBlockXOff++)
    7259             :         {
    7260             : #ifdef DEBUG_VERBOSE
    7261             :             CPLDebug("NITF", "nBlockXOff=%d/%d, nBlockYOff=%d/%d", nBlockXOff,
    7262             :                      nNBPR, nBlockYOff, nNBPC);
    7263             : #endif
    7264          55 :             if (EQUAL(pszIC, "M3"))
    7265             :             {
    7266             :                 /* Write block offset for current block */
    7267             : 
    7268           4 :                 const GUIntBig nCurPos = VSIFTellL(fp);
    7269           8 :                 bOK &= VSIFSeekL(fp,
    7270           4 :                                  nStartOffset + BLOCKMAP_HEADER_SIZE +
    7271           4 :                                      4 * (nBlockYOff * nNBPR + nBlockXOff),
    7272           4 :                                  SEEK_SET) == 0;
    7273           4 :                 const GUIntBig nBlockOffset =
    7274           4 :                     nCurPos - nStartOffset - nIMDATOFF;
    7275           4 :                 if (nBlockOffset <= UINT_MAX)
    7276             :                 {
    7277           4 :                     GUInt32 nBlockOffset32 = static_cast<GUInt32>(nBlockOffset);
    7278           4 :                     CPL_MSBPTR32(&nBlockOffset32);
    7279           4 :                     bOK &= VSIFWriteL(&nBlockOffset32, 4, 1, fp) == 1;
    7280             :                 }
    7281             :                 else
    7282             :                 {
    7283           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    7284             :                              "Offset for block (%d, %d) = " CPL_FRMT_GUIB
    7285             :                              ". Cannot fit into 32 bits...",
    7286             :                              nBlockXOff, nBlockYOff, nBlockOffset);
    7287             : 
    7288           0 :                     GUInt32 nBlockOffset32 = UINT_MAX;
    7289           0 :                     for (int i = nBlockYOff * nNBPR + nBlockXOff;
    7290           0 :                          bOK && i < nNBPC * nNBPR; i++)
    7291             :                     {
    7292           0 :                         bOK &= VSIFWriteL(&nBlockOffset32, 4, 1, fp) == 1;
    7293             :                     }
    7294           0 :                     if (!bOK)
    7295             :                     {
    7296           0 :                         CPLError(CE_Failure, CPLE_FileIO, "I/O error");
    7297             :                     }
    7298           0 :                     return bOK;
    7299             :                 }
    7300           4 :                 bOK &= VSIFSeekL(fp, nCurPos, SEEK_SET) == 0;
    7301             :             }
    7302             : 
    7303         110 :             if (bOK &&
    7304          67 :                 !NITFWriteJPEGBlock(
    7305             :                     poSrcDS, fp, nBlockXOff, nBlockYOff, nNPPBH, nNPPBV,
    7306             :                     bProgressive, nQuality,
    7307          12 :                     (nBlockXOff == 0 && nBlockYOff == 0) ? abyAPP6 : nullptr,
    7308             :                     nRestartInterval, pfnProgress, pProgressData))
    7309             :             {
    7310           0 :                 return false;
    7311             :             }
    7312             :         }
    7313             :     }
    7314          10 :     if (!bOK)
    7315             :     {
    7316           0 :         CPLError(CE_Failure, CPLE_FileIO, "I/O error");
    7317             :     }
    7318          10 :     return true;
    7319             : }
    7320             : 
    7321             : #endif /* def JPEG_SUPPORTED */
    7322             : 
    7323             : /************************************************************************/
    7324             : /*                         GDALRegister_NITF()                          */
    7325             : /************************************************************************/
    7326             : 
    7327             : typedef struct
    7328             : {
    7329             :     int nMaxLen;
    7330             :     const char *pszName;
    7331             :     const char *pszDescription;
    7332             : } NITFFieldDescription;
    7333             : 
    7334             : /* Keep in sync with NITFCreate */
    7335             : static const NITFFieldDescription asFieldDescription[] = {
    7336             :     {2, "CLEVEL", "Complexity level"},
    7337             :     {10, "OSTAID", "Originating Station ID"},
    7338             :     {14, "FDT", "File Date and Time"},
    7339             :     {80, "FTITLE", "File Title"},
    7340             :     {1, "FSCLAS", "File Security Classification"},
    7341             :     {2, "FSCLSY", "File Classification Security System (NITF02.10/NSIF only)"},
    7342             :     {11, "FSCODE", "File Codewords"},
    7343             :     {2, "FSCTLH", "File Control and Handling"},
    7344             :     {20, "FSREL", "File Releasing Instructions"},
    7345             :     {2, "FSDCTP", "File Declassification Type (NITF02.10/NSIF only)"},
    7346             :     {8, "FSDCDT", "File Declassification Date (NITF02.10/NSIF only)"},
    7347             :     {4, "FSDCXM", "File Declassification Exemption (NITF02.10/NSIF only)"},
    7348             :     {1, "FSDG", "File Downgrade (NITF02.10/NSIF only)"},
    7349             :     {8, "FSDGDT", "File Downgrade Date (NITF02.10/NSIF only)"},
    7350             :     {6, "FSDWNG", "File Security Downgrade (NITF02.00 only)"},
    7351             :     {40, "FSDEVT", "File Downgrading event (NITF02.00 only)"},
    7352             :     {43, "FSCLTX", "File Classification Text (NITF02.10/NSIF only)"},
    7353             :     {1, "FSCATP", "File Classification Authority Type (NITF02.10/NSIF only)"},
    7354             :     {40, "FSCAUT", "File Classification Authority"},
    7355             :     {1, "FSCRSN", "File Classification Reason (NITF02.10/NSIF only)"},
    7356             :     {8, "FSSRDT", "File Security Source Date (NITF02.10/NSIF only)"},
    7357             :     {15, "FSCTLN", "File Security Control Number"},
    7358             :     {5, "FSCOP", "File Copy Number"},
    7359             :     {5, "FSCPYS", "File Number of Copies"},
    7360             :     {24, "ONAME", "Originator Name"},
    7361             :     {18, "OPHONE", "Originator Phone Number"},
    7362             :     {10, "IID1", "Image Identifier 1"},
    7363             :     {14, "IDATIM", "Image Date and Time"},
    7364             :     {17, "TGTID", "Target Identifier"},
    7365             :     {80, "IID2", "Image Identifier 2 (NITF02.10/NSIF only)"},
    7366             :     {80, "ITITLE", "Image Title (NITF02.00 only)"},
    7367             :     {1, "ISCLAS", "Image Security Classification"},
    7368             :     {2, "ISCLSY", "Image Classification Security System (NITF02.10/NSIF only)"},
    7369             :     {11, "ISCODE", "Image Codewords"},
    7370             :     {2, "ISCTLH", "Image Control and Handling"},
    7371             :     {20, "ISREL", "Image Releasing Instructions (NITF02.10/NSIF only)"},
    7372             :     {2, "ISDCTP", "Image Declassification Type (NITF02.10/NSIF only)"},
    7373             :     {8, "ISDCDT", "Image Declassification Date (NITF02.10/NSIF only)"},
    7374             :     {4, "ISDCXM", "Image Declassification Exemption (NITF02.10/NSIF only)"},
    7375             :     {1, "ISDG", "Image Downgrade (NITF02.10/NSIF only)"},
    7376             :     {8, "ISDGDT", "Image Downgrade Date (NITF02.10/NSIF only)"},
    7377             :     {6, "ISDWNG", "Image Security Downgrade (NITF02.00 only)"},
    7378             :     {40, "ISDEVT", "Image Downgrading event (NITF02.00 only)"},
    7379             :     {43, "ISCLTX", "Image Classification Text (NITF02.10/NSIF only)"},
    7380             :     {1, "ISCATP", "Image Classification Authority Type (NITF02.10/NSIF only)"},
    7381             :     {40, "ISCAUT", "Image Classification Authority"},
    7382             :     {1, "ISCRSN", "Image Classification Reason (NITF02.10/NSIF only)"},
    7383             :     {8, "ISSRDT", "Image Security Source Date (NITF02.10/NSIF only)"},
    7384             :     {15, "ISCTLN", "Image Security Control Number (NITF02.10/NSIF only)"},
    7385             :     {42, "ISORCE", "Image Source"},
    7386             :     {8, "ICAT", "Image Category"},
    7387             :     {2, "ABPP", "Actual Bits-Per-Pixel Per Band"},
    7388             :     {1, "PJUST", "Pixel Justification"},
    7389             :     {720, "ICOM", "Image Comments (up to 9x80 characters)"},
    7390             :     {3, "IDLVL", "Image Display Level"},
    7391             :     {3, "IALVL", "Image Attachment Level"},
    7392             :     {5, "ILOCROW", "Image Location Row"},
    7393             :     {5, "ILOCCOL", "Image Location Column"},
    7394             : };
    7395             : 
    7396             : /* Keep in sync with NITFWriteBLOCKA */
    7397             : static const char *const apszFieldsBLOCKA[] = {
    7398             :     "BLOCK_INSTANCE", "0",     "2",    "N_GRAY",        "2",  "5",
    7399             :     "L_LINES",        "7",     "5",    "LAYOVER_ANGLE", "12", "3",
    7400             :     "SHADOW_ANGLE",   "15",    "3",    "BLANKS",        "18", "16",
    7401             :     "FRLC_LOC",       "34",    "21",   "LRLC_LOC",      "55", "21",
    7402             :     "LRFC_LOC",       "76",    "21",   "FRFC_LOC",      "97", "21",
    7403             :     nullptr,          nullptr, nullptr};
    7404             : 
    7405             : /************************************************************************/
    7406             : /*                              NITFDriver                              */
    7407             : /************************************************************************/
    7408             : 
    7409             : class NITFDriver final : public GDALDriver
    7410             : {
    7411             :     std::recursive_mutex m_oMutex{};
    7412             :     bool m_bCreationOptionListInitialized = false;
    7413             :     void InitCreationOptionList();
    7414             : 
    7415             :   public:
    7416             :     const char *GetMetadataItem(const char *pszName,
    7417             :                                 const char *pszDomain) override;
    7418             : 
    7419         458 :     CSLConstList GetMetadata(const char *pszDomain) override
    7420             :     {
    7421         916 :         std::lock_guard oLock(m_oMutex);
    7422         458 :         InitCreationOptionList();
    7423         916 :         return GDALDriver::GetMetadata(pszDomain);
    7424             :     }
    7425             : };
    7426             : 
    7427             : /************************************************************************/
    7428             : /*                    NITFDriver::GetMetadataItem()                     */
    7429             : /************************************************************************/
    7430             : 
    7431       74715 : const char *NITFDriver::GetMetadataItem(const char *pszName,
    7432             :                                         const char *pszDomain)
    7433             : {
    7434      149430 :     std::lock_guard oLock(m_oMutex);
    7435       74715 :     if (EQUAL(pszName, GDAL_DMD_CREATIONOPTIONLIST))
    7436             :     {
    7437         585 :         InitCreationOptionList();
    7438             :     }
    7439      149430 :     return GDALDriver::GetMetadataItem(pszName, pszDomain);
    7440             : }
    7441             : 
    7442             : /************************************************************************/
    7443             : /*                       InitCreationOptionList()                       */
    7444             : /************************************************************************/
    7445             : 
    7446        1043 : void NITFDriver::InitCreationOptionList()
    7447             : {
    7448        1043 :     if (m_bCreationOptionListInitialized)
    7449         833 :         return;
    7450         210 :     m_bCreationOptionListInitialized = true;
    7451             : 
    7452         210 :     const bool bHasJP2ECW = GDALGetDriverByName("JP2ECW") != nullptr;
    7453         210 :     const bool bHasJP2KAK = GDALGetDriverByName("JP2KAK") != nullptr;
    7454         210 :     const bool bHasJP2OPENJPEG = GDALGetDriverByName("JP2OPENJPEG") != nullptr;
    7455         210 :     const bool bHasJPEG2000Drivers =
    7456         210 :         bHasJP2ECW || bHasJP2KAK || bHasJP2OPENJPEG;
    7457             : 
    7458             :     CPLString osCreationOptions =
    7459             :         "<CreationOptionList>"
    7460             :         "   <Option name='IC' type='string-select' default='NC' "
    7461             :         "description='Compression mode. NC=no compression. "
    7462             : #ifdef JPEG_SUPPORTED
    7463             :         "C3/M3=JPEG compression. "
    7464             : #endif
    7465         420 :         "C4=VQ compression (only for PRODUCT_TYPE=CADRG). ";
    7466             : 
    7467         210 :     if (bHasJPEG2000Drivers)
    7468             :         osCreationOptions +=
    7469         210 :             "C8=JP2 compression through the JPEG2000 write capable drivers";
    7470             : 
    7471             :     osCreationOptions += "'>"
    7472             :                          "       <Value>NC</Value>"
    7473             : #ifdef JPEG_SUPPORTED
    7474             :                          "       <Value>C3</Value>"
    7475             :                          "       <Value>M3</Value>"
    7476             : #endif
    7477         210 :                          "       <Value>C4</Value>";
    7478             : 
    7479         210 :     if (bHasJPEG2000Drivers)
    7480         210 :         osCreationOptions += "       <Value>C8</Value>";
    7481             : 
    7482         210 :     osCreationOptions += "   </Option>";
    7483             : 
    7484             : #if !defined(JPEG_SUPPORTED)
    7485             :     if (bHasJPEG2000Drivers)
    7486             : #endif
    7487             :     {
    7488             :         osCreationOptions +=
    7489             :             "   <Option name='QUALITY' type='string' "
    7490             :             "description='JPEG (10-100) or JPEG2000 quality, possibly as a"
    7491             :             "separated list of values for JPEG2000_DRIVER=JP2OPENJPEG' "
    7492         210 :             "default='75'/>";
    7493             :     }
    7494             : 
    7495             : #ifdef JPEG_SUPPORTED
    7496             :     osCreationOptions +=
    7497             :         "   <Option name='PROGRESSIVE' type='boolean' description='JPEG "
    7498             :         "progressive mode'/>"
    7499             :         "   <Option name='RESTART_INTERVAL' type='int' description='Restart "
    7500             :         "interval (in MCUs). -1 for auto, 0 for none, > 0 for user specified' "
    7501         210 :         "default='-1'/>";
    7502             : #endif
    7503             :     osCreationOptions +=
    7504             :         "   <Option name='NUMI' type='int' default='1' description='Number of "
    7505             :         "images to create (1-999). Only works with IC=NC if "
    7506             :         "WRITE_ONLY_FIRST_IMAGE=NO'/>"
    7507             :         "   <Option name='WRITE_ONLY_FIRST_IMAGE' type='boolean' default='NO' "
    7508             :         "description='To be used with NUMI. If YES, only write first image. "
    7509         210 :         "Subsequent one must be written with APPEND_SUBDATASET=YES'/>";
    7510             : 
    7511         210 :     if (bHasJPEG2000Drivers)
    7512             :     {
    7513             :         osCreationOptions +=
    7514             :             "   <Option name='TARGET' type='float' description='For JP2 only. "
    7515             :             "Compression Percentage'/>"
    7516             :             "   <Option name='PROFILE' type='string-select' description='For "
    7517         210 :             "JP2 only.'>";
    7518             : 
    7519         210 :         if (bHasJP2ECW)
    7520             :         {
    7521         210 :             osCreationOptions += "       <Value>BASELINE_0</Value>";
    7522             :         }
    7523         210 :         if (bHasJP2ECW || bHasJP2OPENJPEG)
    7524             :         {
    7525             :             osCreationOptions +=
    7526             :                 "       <Value>BASELINE_1</Value>"
    7527             :                 "       <Value>BASELINE_2</Value>"
    7528             :                 "       <Value>NPJE</Value>"
    7529             :                 "       <Value>NPJE_VISUALLY_LOSSLESS</Value>"
    7530         210 :                 "       <Value>NPJE_NUMERICALLY_LOSSLESS</Value>";
    7531             :         }
    7532         210 :         if (bHasJP2ECW)
    7533             :         {
    7534         210 :             osCreationOptions += "       <Value>EPJE</Value>";
    7535             :         }
    7536             :         osCreationOptions +=
    7537             :             "   </Option>"
    7538             :             "   <Option name='JPEG2000_DRIVER' type='string-select' "
    7539         210 :             "description='Short name of the JPEG2000 driver'>";
    7540         210 :         if (bHasJP2OPENJPEG)
    7541         210 :             osCreationOptions += "       <Value>JP2OPENJPEG</Value>";
    7542         210 :         if (bHasJP2ECW)
    7543         210 :             osCreationOptions += "       <Value>JP2ECW</Value>";
    7544         210 :         if (bHasJP2KAK)
    7545           0 :             osCreationOptions += "       <Value>JP2KAK</Value>";
    7546             :         osCreationOptions += "   </Option>"
    7547             :                              "   <Option name='J2KLRA' type='boolean' "
    7548         210 :                              "description='Write J2KLRA TRE'/>";
    7549             :     }
    7550             : 
    7551             :     osCreationOptions +=
    7552             :         "   <Option name='ICORDS' type='string-select' description='To ensure "
    7553             :         "that space will be reserved for geographic corner coordinates in DMS "
    7554             :         "(G), in decimal degrees (D), UTM North (N) or UTM South (S)'>"
    7555             :         "       <Value>G</Value>"
    7556             :         "       <Value>D</Value>"
    7557             :         "       <Value>N</Value>"
    7558             :         "       <Value>S</Value>"
    7559             :         "   </Option>"
    7560             :         "   <Option name='IGEOLO' type='string' description='Image corner "
    7561             :         "coordinates. "
    7562             :         "Normally automatically set. If specified, ICORDS must also be "
    7563             :         "specified'/>"
    7564             :         "   <Option name='FHDR' type='string-select' description='File "
    7565             :         "version' default='NITF02.10'>"
    7566             :         "       <Value>NITF02.10</Value>"
    7567             :         "       <Value>NSIF01.00</Value>"
    7568             :         "       <Value>NITF02.00</Value>"
    7569             :         "   </Option>"
    7570             :         "   <Option name='IREP' type='string' description='Set to RGB/LUT to "
    7571             :         "reserve space for a color table for each output band. (Only needed "
    7572             :         "for Create() method, not CreateCopy())'/>"
    7573             :         "   <Option name='IREPBAND' type='string' description='Comma separated "
    7574             :         "list of band IREPBANDs in band order'/>"
    7575             :         "   <Option name='ISUBCAT' type='string' description='Comma separated "
    7576             :         "list of band ISUBCATs in band order'/>"
    7577             :         "   <Option name='LUT_SIZE' type='integer' description='Set to control "
    7578             :         "the size of pseudocolor tables for RGB/LUT bands' default='256'/>"
    7579             :         "   <Option name='BLOCKXSIZE' type='int' description='Set the block "
    7580             :         "width'/>"
    7581             :         "   <Option name='BLOCKYSIZE' type='int' description='Set the block "
    7582             :         "height'/>"
    7583             :         "   <Option name='BLOCKSIZE' type='int' description='Set the block "
    7584             :         "with and height. Overridden by BLOCKXSIZE and BLOCKYSIZE'/>"
    7585             :         "   <Option name='TEXT' type='string' description='TEXT options as "
    7586             :         "text-option-name=text-option-content'/>"
    7587             :         "   <Option name='CGM' type='string' description='CGM options in "
    7588         210 :         "cgm-option-name=cgm-option-content'/>";
    7589             : 
    7590       12390 :     for (unsigned int i = 0;
    7591       12390 :          i < sizeof(asFieldDescription) / sizeof(asFieldDescription[0]); i++)
    7592             :     {
    7593       12180 :         if (EQUAL(asFieldDescription[i].pszName, "ABPP"))
    7594             :         {
    7595             :             osCreationOptions +=
    7596         420 :                 CPLString().Printf("   <Option name='%s' alias='NBITS' "
    7597             :                                    "type='string' description='%s' "
    7598             :                                    "maxsize='%d'/>",
    7599         210 :                                    asFieldDescription[i].pszName,
    7600         210 :                                    asFieldDescription[i].pszDescription,
    7601         210 :                                    asFieldDescription[i].nMaxLen);
    7602             :         }
    7603             :         else
    7604             :         {
    7605       23940 :             osCreationOptions += CPLString().Printf(
    7606             :                 "   <Option name='%s' type='string' description='%s' "
    7607             :                 "maxsize='%d'/>",
    7608       11970 :                 asFieldDescription[i].pszName,
    7609       11970 :                 asFieldDescription[i].pszDescription,
    7610       11970 :                 asFieldDescription[i].nMaxLen);
    7611             :         }
    7612             :     }
    7613             : 
    7614             :     osCreationOptions +=
    7615             :         "   <Option name='TRE' type='string' description='Under the format "
    7616             :         "TRE=tre-name,tre-contents'/>"
    7617             :         "   <Option name='FILE_TRE' type='string' description='Under the "
    7618             :         "format FILE_TRE=tre-name,tre-contents'/>"
    7619             :         "   <Option name='RESERVE_SPACE_FOR_TRE_OVERFLOW' type='boolean' "
    7620             :         "description='Set to true to reserve space for IXSOFL when writing a "
    7621             :         "TRE_OVERFLOW DES'/>"
    7622             :         "   <Option name='BLOCKA_BLOCK_COUNT' type='int'/>"
    7623             :         "   <Option name='DES' type='string' description='Under the format "
    7624             :         "DES=des-name=des-contents'/>"
    7625             :         "   <Option name='NUMDES' type='int' default='0' description='Number "
    7626         210 :         "of DES segments. Only to be used on first image segment'/>";
    7627        2310 :     for (unsigned int i = 0; apszFieldsBLOCKA[i] != nullptr; i += 3)
    7628             :     {
    7629             :         char szFieldDescription[128];
    7630        2100 :         snprintf(szFieldDescription, sizeof(szFieldDescription),
    7631             :                  "   <Option name='BLOCKA_%s_*' type='string' maxsize='%d'/>",
    7632        2100 :                  apszFieldsBLOCKA[i], atoi(apszFieldsBLOCKA[i + 2]));
    7633        2100 :         osCreationOptions += szFieldDescription;
    7634             :     }
    7635             :     osCreationOptions +=
    7636             :         "   <Option name='SDE_TRE' type='boolean' description='Write GEOLOB "
    7637             :         "and GEOPSB TREs (only geographic SRS for now)' default='NO'/>"
    7638             :         "   <Option name='RPC00B' type='boolean' description='Write RPC00B TRE "
    7639             :         "(either from source TRE, or from RPC metadata)' default='YES'/>"
    7640             :         "   <Option name='RPCTXT' type='boolean' description='Write out "
    7641             :         "_RPC.TXT file' default='NO'/>"
    7642             :         "   <Option name='USE_SRC_NITF_METADATA' type='boolean' "
    7643             :         "description='Whether to use NITF source metadata in NITF-to-NITF "
    7644             :         "conversions' default='YES'/>"
    7645             :         "   <Option name='PRODUCT_TYPE' type='string-select' description='"
    7646             :         "Sub-specification the output dataset should respect' "
    7647             :         "default='REGULAR'>"
    7648             :         "       <Value>REGULAR</Value>"
    7649             :         "       <Value>CADRG</Value>"
    7650             :         "   </Option>"
    7651             :         "   <Option name='SCALE' type='string' min='1000' max='20000000' "
    7652             :         "description='Reciprocal scale to use when generating output frames. "
    7653             :         "Special value GUESS can be used. Only used when PRODUCT_TYPE=CADRG'/>"
    7654             :         "   <Option name='COLOR_QUANTIZATION_BITS' type='int' min='5' max='8' "
    7655             :         "default='5' description='"
    7656             :         "Number of bits per R,G,B color component used during color palette "
    7657             :         "computation. The higher the better quality and slower computation "
    7658             :         "time. Only used when PRODUCT_TYPE=CADRG'/>"
    7659             :         "   <Option name='COLOR_TABLE_PER_FRAME' type='boolean' default='NO' "
    7660             :         "description='Whether the color table should be optimized on the whole "
    7661             :         "input dataset, or per output frame. "
    7662             :         "Only used when PRODUCT_TYPE=CADRG'/>"
    7663             :         "   <Option name='DPI' type='float' description='"
    7664             :         "Dot-Per-Inch value that may need to be specified together with SCALE. "
    7665             :         "Only used when PRODUCT_TYPE=CADRG' min='1' max='7200'/>"
    7666             :         "   <Option name='ZONE' type='string' description='"
    7667             :         "ARC Zone to which restrict generation of CADRG frames (1 to 9, A to "
    7668             :         "H, J)."
    7669             :         "Only used when PRODUCT_TYPE=CADRG'/>"
    7670             :         "   <Option name='SERIES_CODE' type='string-select' description='"
    7671             :         "Two-letter code specifying the map/chart type. Only used when "
    7672             :         "PRODUCT_TYPE=CADRG' default='MM'>"
    7673             :         "       <Value>GN</Value>"
    7674             :         "       <Value>JN</Value>"
    7675             :         "       <Value>ON</Value>"
    7676             :         "       <Value>TP</Value>"
    7677             :         "       <Value>LF</Value>"
    7678             :         "       <Value>JG</Value>"
    7679             :         "       <Value>JA</Value>"
    7680             :         "       <Value>JR</Value>"
    7681             :         "       <Value>TF</Value>"
    7682             :         "       <Value>AT</Value>"
    7683             :         "       <Value>TC</Value>"
    7684             :         "       <Value>TL</Value>"
    7685             :         "       <Value>HA</Value>"
    7686             :         "       <Value>CO</Value>"
    7687             :         "       <Value>OA</Value>"
    7688             :         "       <Value>CG</Value>"
    7689             :         "       <Value>CM</Value>"
    7690             :         "       <Value>MM</Value>"
    7691             :         "       <OtherValues/>"
    7692             :         "   </Option>"
    7693             :         "   <Option name='RESAMPLING' type='string-select' description='"
    7694             :         "Resampling method used during CADRG frame creation' "
    7695             :         "default='CUBIC'>"
    7696             :         "       <Value>CUBIC</Value>"
    7697             :         "       <Value>BILINEAR</Value>"
    7698             :         "       <Value>LANCZOS</Value>"
    7699             :         "       <Value>NEAREST</Value>"
    7700             :         "   </Option>"
    7701             :         "   <Option name='VERSION_NUMBER' type='string' description='"
    7702             :         "Two letter version number (using letters among 0-9, A-H and J). "
    7703             :         "Only used when PRODUCT_TYPE=CADRG' default='01'/>"
    7704             :         "   <Option name='PRODUCER_CODE_ID' type='string' description='"
    7705             :         "One letter code indicating the data producer. Only used when "
    7706             :         "PRODUCT_TYPE=CADRG' default='0'/>"
    7707             :         "   <Option name='SECURITY_COUNTRY_CODE' type='string' description='"
    7708             :         "Two letter country ISO code of the security classification'/>"
    7709             :         "   <Option name='CURRENCY_DATE' type='string' description='"
    7710             :         "Date of the most recent revision to the RPF product, as YYYYMMDD. "
    7711             :         "Can also be set to empty string or special value NOW. "
    7712             :         "Only used when PRODUCT_TYPE=CADRG'/>"
    7713             :         "   <Option name='PRODUCTION_DATE' type='string' description='"
    7714             :         "Date that the source data was transferred to RPF format, as YYYYMMDD. "
    7715             :         "Can also be set to empty string or special value NOW. "
    7716             :         "Only used when PRODUCT_TYPE=CADRG'/>"
    7717             :         "   <Option name='SIGNIFICANT_DATE' type='string' description='"
    7718             :         "Date describing the basic date of the source produc, as YYYYMMDD. "
    7719             :         "Can also be set to empty string or special value NOW. "
    7720             :         "Only used when PRODUCT_TYPE=CADRG'/>"
    7721             :         "   <Option name='DATA_SERIES_DESIGNATION' type='string' "
    7722             :         "description='Short title for the identification of a group of products"
    7723             :         " usually having the same scale and/or cartographic specification "
    7724             :         "(e.g. JOG 1501A). Up to 10 characters. Only used when "
    7725             :         "PRODUCT_TYPE=CADRG'/>"
    7726             :         "   <Option name='MAP_DESIGNATION' type='string' "
    7727             :         "description='Designation, within the data series, of the hardcopy "
    7728             :         "source (e.g. G18 if the hardcopy source is ONC G18). Up to 8 "
    7729             :         "characters. Only used when PRODUCT_TYPE=CADRG'/>"
    7730         210 :         "</CreationOptionList>";
    7731             : 
    7732         210 :     SetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST, osCreationOptions);
    7733             : }
    7734             : 
    7735        2135 : void GDALRegister_NITF()
    7736             : 
    7737             : {
    7738        2135 :     if (GDALGetDriverByName(NITF_DRIVER_NAME) != nullptr)
    7739         263 :         return;
    7740             : 
    7741        1872 :     GDALDriver *poDriver = new NITFDriver();
    7742        1872 :     NITFDriverSetCommonMetadata(poDriver);
    7743             : 
    7744        1872 :     poDriver->pfnOpen = NITFDataset::Open;
    7745        1872 :     poDriver->pfnCreate = NITFDataset::NITFDatasetCreate;
    7746        1872 :     poDriver->pfnCreateCopy = NITFDataset::NITFCreateCopy;
    7747             : 
    7748        1872 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    7749             : 
    7750             : #ifdef NITF_PLUGIN
    7751             :     GDALRegister_RPFTOC();
    7752             :     GDALRegister_ECRGTOC();
    7753             : #endif
    7754             : }

Generated by: LCOV version 1.14