LCOV - code coverage report
Current view: top level - frmts/nitf - nitfdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 2427 3015 80.5 %
Date: 2026-02-12 23:49:34 Functions: 56 58 96.6 %

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

Generated by: LCOV version 1.14