LCOV - code coverage report
Current view: top level - frmts/nitf - nitffile.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1455 1795 81.1 %
Date: 2026-10-01 05:58:56 Functions: 36 39 92.3 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  NITF Read/Write Library
       4             :  * Purpose:  Module responsible for opening NITF file, populating NITFFile
       5             :  *           structure, and instantiating segment specific access objects.
       6             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       7             :  *
       8             :  **********************************************************************
       9             :  * Copyright (c) 2002, Frank Warmerdam
      10             :  * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ****************************************************************************/
      14             : 
      15             : #include "nitflib.h"
      16             : #include "cpl_vsi.h"
      17             : #include "cpl_conv.h"
      18             : #include "cpl_string.h"
      19             : #include "cpl_time.h"
      20             : #include <stdbool.h>
      21             : 
      22             : #include <algorithm>
      23             : #include <cstdlib>
      24             : #include <map>
      25             : 
      26             : #ifdef EMBED_RESOURCE_FILES
      27             : #include "embedded_resources.h"
      28             : #endif
      29             : 
      30             : #include "offsetpatcher.h"
      31             : #include "rpfframewriter.h"
      32             : 
      33             : static bool NITFWriteBLOCKA(VSILFILE *fp, vsi_l_offset nOffsetIXSHDL,
      34             :                             int *pnOffset, CSLConstList papszOptions);
      35             : static bool
      36             : NITFWriteTREsFromOptions(VSILFILE *fp, vsi_l_offset nOffsetHDL, int *pnOffset,
      37             :                          CSLConstList papszOptions, const char *pszTREPrefix,
      38             :                          GDALOffsetPatcher::OffsetPatcher *offsetPatcher);
      39             : 
      40             : static int NITFCollectSegmentInfo(NITFFile *psFile, int nFileHeaderLenSize,
      41             :                                   int nOffset, const char szType[3],
      42             :                                   int nHeaderLenSize, int nDataLenSize,
      43             :                                   GUIntBig *pnNextData);
      44             : 
      45             : static void NITFExtractAndRecodeMetadata(char ***ppapszMetadata,
      46             :                                          const char *pachHeader, int nStart,
      47             :                                          int nLength, const char *pszName,
      48             :                                          const char *pszSrcEncoding);
      49             : 
      50             : static bool NITFWriteOption(VSILFILE *fp, CSLConstList papszOptions,
      51             :                             size_t nWidth, GUIntBig nLocation,
      52             :                             const char *pszName, const char *pszText);
      53             : 
      54             : /************************************************************************/
      55             : /*                              NITFOpen()                              */
      56             : /************************************************************************/
      57             : 
      58          33 : NITFFile *NITFOpen(const char *pszFilename, int bUpdatable)
      59             : 
      60             : {
      61             :     VSILFILE *fp;
      62             : 
      63             :     /* -------------------------------------------------------------------- */
      64             :     /*      Open the file.                                                  */
      65             :     /* -------------------------------------------------------------------- */
      66          33 :     if (bUpdatable)
      67          14 :         fp = VSIFOpenL(pszFilename, "r+b");
      68             :     else
      69          19 :         fp = VSIFOpenL(pszFilename, "rb");
      70             : 
      71          33 :     if (fp == nullptr)
      72             :     {
      73           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open file %s.",
      74             :                  pszFilename);
      75           0 :         return nullptr;
      76             :     }
      77             : 
      78          33 :     return NITFOpenEx(fp, pszFilename);
      79             : }
      80             : 
      81             : /************************************************************************/
      82             : /*                             NITFOpenEx()                             */
      83             : /************************************************************************/
      84             : 
      85         782 : NITFFile *NITFOpenEx(VSILFILE *fp, const char *pszFilename)
      86             : 
      87             : {
      88             :     char *pachHeader;
      89             :     NITFFile *psFile;
      90             :     int nHeaderLen, nOffset;
      91             :     GUIntBig nNextData;
      92             :     char szTemp[128], achFSDWNG[6];
      93             :     GIntBig currentPos;
      94         782 :     int bTriedStreamingFileHeader = FALSE;
      95             : 
      96             :     /* -------------------------------------------------------------------- */
      97             :     /*      Check file type.                                                */
      98             :     /* -------------------------------------------------------------------- */
      99        1564 :     if (VSIFSeekL(fp, 0, SEEK_SET) != 0 || VSIFReadL(szTemp, 1, 9, fp) != 9 ||
     100         782 :         (!STARTS_WITH_CI(szTemp, "NITF") && !STARTS_WITH_CI(szTemp, "NSIF")))
     101             :     {
     102           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     103             :                  "The file %s is not an NITF file.", pszFilename);
     104           0 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     105           0 :         return nullptr;
     106             :     }
     107             : 
     108             :     /* -------------------------------------------------------------------- */
     109             :     /*      Read the FSDWNG field.                                          */
     110             :     /* -------------------------------------------------------------------- */
     111        1564 :     if (VSIFSeekL(fp, 280, SEEK_SET) != 0 ||
     112         782 :         VSIFReadL(achFSDWNG, 1, 6, fp) != 6)
     113             :     {
     114           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     115             :                  "Unable to read FSDWNG field from NITF file.  File is either "
     116             :                  "corrupt\n"
     117             :                  "or empty.");
     118           0 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     119           0 :         return nullptr;
     120             :     }
     121             : 
     122             :     /* -------------------------------------------------------------------- */
     123             :     /*      Get header length.                                              */
     124             :     /* -------------------------------------------------------------------- */
     125        2345 :     const int nHeaderLenOffset = (STARTS_WITH_CI(szTemp, "NITF01.") ||
     126         781 :                                   STARTS_WITH_CI(achFSDWNG, "999998"))
     127        1563 :                                      ? 394
     128             :                                      : 354;
     129             : 
     130         782 :     if (VSIFSeekL(fp, static_cast<vsi_l_offset>(nHeaderLenOffset), SEEK_SET) !=
     131        1564 :             0 ||
     132         782 :         VSIFReadL(szTemp, 1, 6, fp) != 6)
     133             :     {
     134           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     135             :                  "Unable to read header length from NITF file.  File is either "
     136             :                  "corrupt\n"
     137             :                  "or empty.");
     138           1 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     139           1 :         return nullptr;
     140             :     }
     141             : 
     142         781 :     szTemp[6] = '\0';
     143         781 :     nHeaderLen = atoi(szTemp);
     144             : 
     145         781 :     if (VSIFSeekL(fp, static_cast<vsi_l_offset>(nHeaderLen), SEEK_SET) != 0)
     146           0 :         currentPos = 0;
     147             :     else
     148         781 :         currentPos = VSIFTellL(fp);
     149         781 :     if (nHeaderLen < nHeaderLenOffset || nHeaderLen > currentPos)
     150             :     {
     151           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     152             :                  "NITF Header Length (%d) seems to be corrupt.", nHeaderLen);
     153           0 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     154           0 :         return nullptr;
     155             :     }
     156             : 
     157             :     /* -------------------------------------------------------------------- */
     158             :     /*      Read the whole file header.                                     */
     159             :     /* -------------------------------------------------------------------- */
     160         781 :     pachHeader = static_cast<char *>(VSI_MALLOC_VERBOSE(nHeaderLen));
     161         781 :     if (pachHeader == nullptr)
     162             :     {
     163           0 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     164           0 :         return nullptr;
     165             :     }
     166        1562 :     if (VSIFSeekL(fp, 0, SEEK_SET) != 0 ||
     167         781 :         static_cast<int>(VSIFReadL(pachHeader, 1, nHeaderLen, fp)) !=
     168             :             nHeaderLen)
     169             :     {
     170           0 :         CPLError(CE_Failure, CPLE_FileIO,
     171             :                  "Cannot read %d bytes for NITF header", (nHeaderLen));
     172           0 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     173           0 :         CPLFree(pachHeader);
     174           0 :         return nullptr;
     175             :     }
     176             : 
     177             :     /* -------------------------------------------------------------------- */
     178             :     /*      Create and initialize info structure about file.                */
     179             :     /* -------------------------------------------------------------------- */
     180         781 :     psFile = static_cast<NITFFile *>(CPLCalloc(sizeof(NITFFile), 1));
     181         781 :     psFile->fp = fp;
     182         781 :     psFile->pachHeader = pachHeader;
     183             : 
     184         781 : retry_read_header:
     185             :     /* -------------------------------------------------------------------- */
     186             :     /*      Get version.                                                    */
     187             :     /* -------------------------------------------------------------------- */
     188         781 :     NITFGetField(psFile->szVersion, pachHeader, 0, 9);
     189             : 
     190             : /* -------------------------------------------------------------------- */
     191             : /*      Collect a variety of information as metadata.                   */
     192             : /* -------------------------------------------------------------------- */
     193             : #define GetMD(target, hdr, start, length, name)                                \
     194             :     NITFExtractMetadata(&(target->papszMetadata), hdr, start, length,          \
     195             :                         "NITF_" #name);
     196             : 
     197         781 :     if (EQUAL(psFile->szVersion, "NITF02.10") ||
     198         199 :         EQUAL(psFile->szVersion, "NSIF01.00"))
     199             :     {
     200             :         char szWork[100];
     201             : 
     202         609 :         GetMD(psFile, pachHeader, 0, 9, FHDR);
     203         609 :         GetMD(psFile, pachHeader, 9, 2, CLEVEL);
     204         609 :         GetMD(psFile, pachHeader, 11, 4, STYPE);
     205         609 :         GetMD(psFile, pachHeader, 15, 10, OSTAID);
     206         609 :         GetMD(psFile, pachHeader, 25, 14, FDT);
     207         609 :         GetMD(psFile, pachHeader, 39, 80, FTITLE);
     208         609 :         GetMD(psFile, pachHeader, 119, 1, FSCLAS);
     209         609 :         GetMD(psFile, pachHeader, 120, 2, FSCLSY);
     210         609 :         GetMD(psFile, pachHeader, 122, 11, FSCODE);
     211         609 :         GetMD(psFile, pachHeader, 133, 2, FSCTLH);
     212         609 :         GetMD(psFile, pachHeader, 135, 20, FSREL);
     213         609 :         GetMD(psFile, pachHeader, 155, 2, FSDCTP);
     214         609 :         GetMD(psFile, pachHeader, 157, 8, FSDCDT);
     215         609 :         GetMD(psFile, pachHeader, 165, 4, FSDCXM);
     216         609 :         GetMD(psFile, pachHeader, 169, 1, FSDG);
     217         609 :         GetMD(psFile, pachHeader, 170, 8, FSDGDT);
     218         609 :         GetMD(psFile, pachHeader, 178, 43, FSCLTX);
     219         609 :         GetMD(psFile, pachHeader, 221, 1, FSCATP);
     220         609 :         GetMD(psFile, pachHeader, 222, 40, FSCAUT);
     221         609 :         GetMD(psFile, pachHeader, 262, 1, FSCRSN);
     222         609 :         GetMD(psFile, pachHeader, 263, 8, FSSRDT);
     223         609 :         GetMD(psFile, pachHeader, 271, 15, FSCTLN);
     224         609 :         GetMD(psFile, pachHeader, 286, 5, FSCOP);
     225         609 :         GetMD(psFile, pachHeader, 291, 5, FSCPYS);
     226         609 :         GetMD(psFile, pachHeader, 296, 1, ENCRYP);
     227         609 :         snprintf(szWork, sizeof(szWork), "%3d,%3d,%3d",
     228         609 :                  reinterpret_cast<GByte *>(pachHeader)[297],
     229         609 :                  reinterpret_cast<GByte *>(pachHeader)[298],
     230         609 :                  reinterpret_cast<GByte *>(pachHeader)[299]);
     231         609 :         GetMD(psFile, szWork, 0, 11, FBKGC);
     232         609 :         GetMD(psFile, pachHeader, 300, 24, ONAME);
     233         609 :         GetMD(psFile, pachHeader, 324, 18, OPHONE);
     234         609 :         NITFGetField(szTemp, pachHeader, 342, 12);
     235             :     }
     236         172 :     else if (EQUAL(psFile->szVersion, "NITF02.00"))
     237             :     {
     238         171 :         int nCOff = 0;
     239             : 
     240         171 :         GetMD(psFile, pachHeader, 0, 9, FHDR);
     241         171 :         GetMD(psFile, pachHeader, 9, 2, CLEVEL);
     242         171 :         GetMD(psFile, pachHeader, 11, 4, STYPE);
     243         171 :         GetMD(psFile, pachHeader, 15, 10, OSTAID);
     244         171 :         GetMD(psFile, pachHeader, 25, 14, FDT);
     245         171 :         GetMD(psFile, pachHeader, 39, 80, FTITLE);
     246         171 :         GetMD(psFile, pachHeader, 119, 1, FSCLAS);
     247         171 :         GetMD(psFile, pachHeader, 120, 40, FSCODE);
     248         171 :         GetMD(psFile, pachHeader, 160, 40, FSCTLH);
     249         171 :         GetMD(psFile, pachHeader, 200, 40, FSREL);
     250         171 :         GetMD(psFile, pachHeader, 240, 20, FSCAUT);
     251         171 :         GetMD(psFile, pachHeader, 260, 20, FSCTLN);
     252         171 :         GetMD(psFile, pachHeader, 280, 6, FSDWNG);
     253         171 :         if (STARTS_WITH_CI(pachHeader + 280, "999998"))
     254             :         {
     255           3 :             GetMD(psFile, pachHeader, 286, 40, FSDEVT);
     256           3 :             nCOff += 40;
     257             :         }
     258         171 :         GetMD(psFile, pachHeader, 286 + nCOff, 5, FSCOP);
     259         171 :         GetMD(psFile, pachHeader, 291 + nCOff, 5, FSCPYS);
     260         171 :         GetMD(psFile, pachHeader, 296 + nCOff, 1, ENCRYP);
     261         171 :         GetMD(psFile, pachHeader, 297 + nCOff, 27, ONAME);
     262         171 :         GetMD(psFile, pachHeader, 324 + nCOff, 18, OPHONE);
     263         171 :         NITFGetField(szTemp, pachHeader, 342 + nCOff, 12);
     264             :     }
     265             : #undef GetMD
     266             : 
     267         781 :     if (!bTriedStreamingFileHeader && EQUAL(szTemp, "999999999999"))
     268             :     {
     269             :         GUIntBig nFileSize;
     270           0 :         GByte abyDELIM2_L2[12] = {0};
     271           0 :         GByte abyL1_DELIM1[11] = {0};
     272             :         bool bOK;
     273             : 
     274           0 :         bTriedStreamingFileHeader = TRUE;
     275           0 :         CPLDebug("NITF",
     276             :                  "Total file unknown. Trying to get a STREAMING_FILE_HEADER");
     277             : 
     278           0 :         bOK = VSIFSeekL(fp, 0, SEEK_END) == 0;
     279           0 :         nFileSize = VSIFTellL(fp);
     280             : 
     281           0 :         bOK &= VSIFSeekL(fp, nFileSize - 11, SEEK_SET) == 0;
     282           0 :         abyDELIM2_L2[11] = '\0';
     283             : 
     284           0 :         if (bOK && VSIFReadL(abyDELIM2_L2, 1, 11, fp) == 11 &&
     285           0 :             abyDELIM2_L2[0] == 0x0E && abyDELIM2_L2[1] == 0xCA &&
     286           0 :             abyDELIM2_L2[2] == 0x14 && abyDELIM2_L2[3] == 0xBF)
     287             :         {
     288             :             int SFHL2 =
     289           0 :                 atoi(reinterpret_cast<const char *>((abyDELIM2_L2 + 4)));
     290           0 :             if (SFHL2 > 0 && (nFileSize > static_cast<size_t>(11 + SFHL2 + 11)))
     291             :             {
     292           0 :                 bOK &=
     293           0 :                     VSIFSeekL(fp, nFileSize - 11 - SFHL2 - 11, SEEK_SET) == 0;
     294             : 
     295           0 :                 if (bOK && VSIFReadL(abyL1_DELIM1, 1, 11, fp) == 11 &&
     296           0 :                     abyL1_DELIM1[7] == 0x0A && abyL1_DELIM1[8] == 0x6E &&
     297           0 :                     abyL1_DELIM1[9] == 0x1D && abyL1_DELIM1[10] == 0x97 &&
     298           0 :                     memcmp(abyL1_DELIM1, abyDELIM2_L2 + 4, 7) == 0)
     299             :                 {
     300           0 :                     if (SFHL2 == nHeaderLen)
     301             :                     {
     302           0 :                         CSLDestroy(psFile->papszMetadata);
     303           0 :                         psFile->papszMetadata = nullptr;
     304             : 
     305           0 :                         if (static_cast<int>(
     306           0 :                                 VSIFReadL(pachHeader, 1, SFHL2, fp)) != SFHL2)
     307             :                         {
     308           0 :                             CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     309           0 :                             CPLFree(pachHeader);
     310           0 :                             CPLFree(psFile);
     311           0 :                             return nullptr;
     312             :                         }
     313             : 
     314           0 :                         goto retry_read_header;
     315             :                     }
     316             :                 }
     317             :             }
     318             :         }
     319           0 :         if (!bOK)
     320             :         {
     321           0 :             NITFClose(psFile);
     322           0 :             return nullptr;
     323             :         }
     324             :     }
     325             : 
     326             :     /* -------------------------------------------------------------------- */
     327             :     /*      Collect segment info for the types we care about.               */
     328             :     /* -------------------------------------------------------------------- */
     329         781 :     nNextData = nHeaderLen;
     330             : 
     331         781 :     nOffset = nHeaderLenOffset + 6;
     332             : 
     333         781 :     nOffset = NITFCollectSegmentInfo(psFile, nHeaderLen, nOffset, "IM", 6, 10,
     334             :                                      &nNextData);
     335             : 
     336         781 :     if (nOffset != -1)
     337         781 :         nOffset = NITFCollectSegmentInfo(psFile, nHeaderLen, nOffset, "GR", 4,
     338             :                                          6, &nNextData);
     339             : 
     340             :     /* LA Called NUMX in NITF 2.1 */
     341         781 :     if (nOffset != -1)
     342         781 :         nOffset = NITFCollectSegmentInfo(psFile, nHeaderLen, nOffset, "LA", 4,
     343             :                                          3, &nNextData);
     344             : 
     345         781 :     if (nOffset != -1)
     346         781 :         nOffset = NITFCollectSegmentInfo(psFile, nHeaderLen, nOffset, "TX", 4,
     347             :                                          5, &nNextData);
     348             : 
     349         781 :     if (nOffset != -1)
     350         781 :         nOffset = NITFCollectSegmentInfo(psFile, nHeaderLen, nOffset, "DE", 4,
     351             :                                          9, &nNextData);
     352             : 
     353         781 :     if (nOffset != -1)
     354         781 :         nOffset = NITFCollectSegmentInfo(psFile, nHeaderLen, nOffset, "RE", 4,
     355             :                                          7, &nNextData);
     356             : 
     357         781 :     if (nOffset < 0)
     358             :     {
     359           1 :         NITFClose(psFile);
     360           1 :         return nullptr;
     361             :     }
     362             : 
     363             :     /* -------------------------------------------------------------------- */
     364             :     /*      Is there User Define Header Data? (TREs)                        */
     365             :     /* -------------------------------------------------------------------- */
     366         780 :     if (nHeaderLen < nOffset + 5)
     367             :     {
     368           0 :         CPLError(CE_Failure, CPLE_AppDefined, "NITF header too small");
     369           0 :         NITFClose(psFile);
     370           0 :         return nullptr;
     371             :     }
     372             : 
     373         780 :     psFile->nTREBytes = atoi(NITFGetField(szTemp, pachHeader, nOffset, 5));
     374         780 :     if (psFile->nTREBytes < 0)
     375             :     {
     376           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid TRE size : %d",
     377             :                  psFile->nTREBytes);
     378           0 :         NITFClose(psFile);
     379           0 :         return nullptr;
     380             :     }
     381         780 :     nOffset += 5;
     382             : 
     383         780 :     if (psFile->nTREBytes == 3)
     384             :     {
     385           0 :         nOffset += 3; /* UDHOFL */
     386           0 :         psFile->nTREBytes = 0;
     387             :     }
     388         780 :     else if (psFile->nTREBytes > 3)
     389             :     {
     390         160 :         nOffset += 3; /* UDHOFL */
     391         160 :         psFile->nTREBytes -= 3;
     392             : 
     393         160 :         if (nHeaderLen < nOffset + psFile->nTREBytes)
     394             :         {
     395           0 :             CPLError(CE_Failure, CPLE_AppDefined, "NITF header too small");
     396           0 :             NITFClose(psFile);
     397           0 :             return nullptr;
     398             :         }
     399             : 
     400         160 :         psFile->pachTRE =
     401         160 :             static_cast<char *>(VSI_MALLOC_VERBOSE(psFile->nTREBytes));
     402         160 :         if (psFile->pachTRE == nullptr)
     403             :         {
     404           0 :             NITFClose(psFile);
     405           0 :             return nullptr;
     406             :         }
     407         160 :         memcpy(psFile->pachTRE, pachHeader + nOffset, psFile->nTREBytes);
     408         160 :         nOffset += psFile->nTREBytes;
     409             :     }
     410             : 
     411             :     /* -------------------------------------------------------------------- */
     412             :     /*      Is there Extended Header Data?  (More TREs)                     */
     413             :     /* -------------------------------------------------------------------- */
     414         780 :     if (nHeaderLen > nOffset + 8)
     415             :     {
     416          45 :         int nXHDL = atoi(NITFGetField(szTemp, pachHeader, nOffset, 5));
     417          45 :         if (nXHDL < 0)
     418             :         {
     419           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid XHDL value : %d",
     420             :                      nXHDL);
     421           0 :             NITFClose(psFile);
     422           0 :             return nullptr;
     423             :         }
     424             : 
     425          45 :         nOffset += 5; /* XHDL */
     426             : 
     427          45 :         if (nXHDL > 3)
     428             :         {
     429             :             char *pachNewTRE;
     430             : 
     431          11 :             nOffset += 3; /* XHDLOFL */
     432          11 :             nXHDL -= 3;
     433             : 
     434          11 :             if (nHeaderLen < nOffset + nXHDL)
     435             :             {
     436           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "NITF header too small");
     437           0 :                 NITFClose(psFile);
     438           0 :                 return nullptr;
     439             :             }
     440             : 
     441          11 :             pachNewTRE = static_cast<char *>(VSI_REALLOC_VERBOSE(
     442             :                 psFile->pachTRE, psFile->nTREBytes + nXHDL));
     443          11 :             if (pachNewTRE == nullptr)
     444             :             {
     445           0 :                 NITFClose(psFile);
     446           0 :                 return nullptr;
     447             :             }
     448          11 :             psFile->pachTRE = pachNewTRE;
     449          11 :             memcpy(psFile->pachTRE + psFile->nTREBytes, pachHeader + nOffset,
     450             :                    nXHDL);
     451          11 :             psFile->nTREBytes += nXHDL;
     452             :         }
     453             :     }
     454             : 
     455         780 :     return psFile;
     456             : }
     457             : 
     458             : /************************************************************************/
     459             : /*                             NITFClose()                              */
     460             : /************************************************************************/
     461             : 
     462         781 : bool NITFClose(NITFFile *psFile)
     463             : 
     464             : {
     465         781 :     bool ret = true;
     466             :     int iSegment;
     467             : 
     468       10762 :     for (iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++)
     469             :     {
     470        9981 :         NITFSegmentInfo *psSegInfo = psFile->pasSegmentInfo + iSegment;
     471             : 
     472        9981 :         if (psSegInfo->hAccess == nullptr)
     473        1228 :             continue;
     474             : 
     475        8753 :         if (EQUAL(psSegInfo->szSegmentType, "IM"))
     476        8753 :             NITFImageDeaccess(static_cast<NITFImage *>(psSegInfo->hAccess));
     477           0 :         else if (EQUAL(psSegInfo->szSegmentType, "DE"))
     478           0 :             NITFDESDeaccess(static_cast<NITFDES *>(psSegInfo->hAccess));
     479             :         else
     480             :         {
     481           0 :             CPLAssert(FALSE);
     482             :         }
     483             :     }
     484             : 
     485         781 :     CPLFree(psFile->pasSegmentInfo);
     486         781 :     if (psFile->fp != nullptr)
     487         773 :         ret = VSIFCloseL(psFile->fp) == 0;
     488         781 :     CPLFree(psFile->pachHeader);
     489         781 :     CSLDestroy(psFile->papszMetadata);
     490         781 :     CPLFree(psFile->pachTRE);
     491             : 
     492         781 :     if (psFile->psNITFSpecNode)
     493         754 :         CPLDestroyXMLNode(psFile->psNITFSpecNode);
     494             : 
     495         781 :     CPLFree(psFile);
     496         781 :     return ret;
     497             : }
     498             : 
     499      315797 : static bool NITFGotoOffset(VSILFILE *fp, GUIntBig nLocation)
     500             : {
     501      315797 :     bool bOK = true;
     502      315797 :     GUIntBig nCurrentLocation = VSIFTellL(fp);
     503      315797 :     if (nLocation > nCurrentLocation)
     504             :     {
     505             :         GUIntBig nFileSize;
     506             :         size_t iFill;
     507      190033 :         char cSpace = ' ';
     508             : 
     509      190033 :         bOK &= VSIFSeekL(fp, 0, SEEK_END) == 0;
     510      190033 :         nFileSize = VSIFTellL(fp);
     511      190033 :         if (bOK && nLocation > nFileSize)
     512             :         {
     513     1396860 :             for (iFill = 0; bOK && iFill < nLocation - nFileSize; iFill++)
     514     1221450 :                 bOK &= VSIFWriteL(&cSpace, 1, 1, fp) == 1;
     515             :         }
     516             :         else
     517       14630 :             bOK &= VSIFSeekL(fp, nLocation, SEEK_SET) == 0;
     518             :     }
     519      125764 :     else if (nLocation < nCurrentLocation)
     520             :     {
     521        9138 :         bOK &= VSIFSeekL(fp, nLocation, SEEK_SET) == 0;
     522             :     }
     523      315797 :     if (!bOK)
     524             :     {
     525         522 :         CPLError(CE_Failure, CPLE_FileIO, "I/O error");
     526             :     }
     527      315797 :     return bOK;
     528             : }
     529             : 
     530             : /************************************************************************/
     531             : /*                             NITFCreate()                             */
     532             : /*                                                                      */
     533             : /*      Create a new uncompressed NITF file.                            */
     534             : /************************************************************************/
     535             : 
     536           0 : int NITFCreate(const char *pszFilename, int nPixels, int nLines, int nBands,
     537             :                int nBitsPerSample, const char *pszPVType,
     538             :                CSLConstList papszOptions)
     539             : 
     540             : {
     541           0 :     return NITFCreateEx(pszFilename, nPixels, nLines, nBands, nBitsPerSample,
     542             :                         pszPVType, papszOptions, nullptr, nullptr, nullptr,
     543           0 :                         nullptr, nullptr);
     544             : }
     545             : 
     546         317 : int NITFCreateEx(const char *pszFilename, int nPixels, int nLines, int nBands,
     547             :                  int nBitsPerSample, const char *pszPVType,
     548             :                  CSLConstList papszOptions, int *pnIndex, int *pnImageCount,
     549             :                  vsi_l_offset *pnImageOffset, vsi_l_offset *pnICOffset,
     550             :                  GDALOffsetPatcher::OffsetPatcher *offsetPatcher)
     551             : 
     552             : {
     553             :     VSILFILE *fp;
     554         317 :     GUIntBig nCur = 0;
     555         317 :     int nOffset = 0, iBand, nIHSize, nNPPBH, nNPPBV;
     556         317 :     GUIntBig nImageSize = 0;
     557             :     int nNBPR, nNBPC;
     558             :     const char *pszIREP;
     559         317 :     const char *pszIC = CSLFetchNameValue(papszOptions, "IC");
     560             :     int nCLevel;
     561             :     const char *pszNUMT;
     562         317 :     int nNUMT = 0;
     563             :     vsi_l_offset nOffsetIXSHDL;
     564             :     const char *pszVersion;
     565         317 :     int iIM, nIM = 1;
     566             :     const char *pszNUMI;
     567         317 :     int iGS, nGS = 0;     // number of graphic segment
     568             :     const char *pszNUMS;  // graphic segment option string
     569         317 :     int iDES, nDES = 0;
     570             :     bool bOK;
     571             : 
     572         317 :     if (pnIndex)
     573         297 :         *pnIndex = 0;
     574             : 
     575         317 :     if (pszIC == nullptr)
     576         261 :         pszIC = "NC";
     577             : 
     578             :     /* -------------------------------------------------------------------- */
     579             :     /*      Fetch some parameter overrides.                                 */
     580             :     /* -------------------------------------------------------------------- */
     581         317 :     pszIREP = CSLFetchNameValue(papszOptions, "IREP");
     582         317 :     if (pszIREP == nullptr)
     583         176 :         pszIREP = "MONO";
     584             : 
     585         317 :     pszNUMT = CSLFetchNameValue(papszOptions, "NUMT");
     586         317 :     if (pszNUMT != nullptr)
     587             :     {
     588           5 :         nNUMT = atoi(pszNUMT);
     589           5 :         if (nNUMT < 0 || nNUMT > 999)
     590             :         {
     591           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid NUMT value : %s",
     592             :                      pszNUMT);
     593           0 :             return FALSE;
     594             :         }
     595             :     }
     596             : 
     597             :     const bool bAppendSubdataset =
     598         317 :         CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "APPEND_SUBDATASET",
     599         317 :                                             "NO")) == TRUE;
     600             :     const bool bWriteAllImages =
     601         317 :         CSLTestBoolean(CSLFetchNameValueDef(papszOptions, "WRITE_ALL_IMAGES",
     602         317 :                                             "NO")) == TRUE;
     603         317 :     pszNUMI = CSLFetchNameValue(papszOptions, "NUMI");
     604         317 :     if (pszNUMI != nullptr)
     605             :     {
     606          27 :         if (bAppendSubdataset)
     607             :         {
     608           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     609             :                      "NUMI not supported with APPEND_SUBDATASET");
     610           0 :             return FALSE;
     611             :         }
     612          27 :         nIM = atoi(pszNUMI);
     613          27 :         if (nIM == 0)
     614             :         {
     615          21 :             if (pnIndex)
     616           1 :                 *pnIndex = -1;
     617             :         }
     618           6 :         else if (nIM < 0 || nIM > 999)
     619             :         {
     620           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid NUMI value : %s",
     621             :                      pszNUMI);
     622           0 :             return FALSE;
     623             :         }
     624          27 :         if (nIM != 1 && !EQUAL(pszIC, "NC") && bWriteAllImages)
     625             :         {
     626           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     627             :                      "Unable to create file with multiple images and "
     628             :                      "compression at the same time");
     629           0 :             return FALSE;
     630             :         }
     631             :     }
     632         290 :     else if (bAppendSubdataset && bWriteAllImages)
     633             :     {
     634           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     635             :                  "WRITE_ALL_IMAGES=YES only supported for first image");
     636             :     }
     637             : 
     638         317 :     if (pnImageCount)
     639         297 :         *pnImageCount = nIM;
     640             : 
     641         317 :     if (nIM > 0)
     642             :     {
     643         296 :         if (nBands <= 0 || nBands > 99999)
     644             :         {
     645           1 :             CPLError(CE_Failure, CPLE_NotSupported, "Invalid band number : %d",
     646             :                      nBands);
     647           1 :             return FALSE;
     648             :         }
     649         295 :         if (nLines > 99999999 || nPixels > 99999999)
     650             :         {
     651           0 :             CPLError(
     652             :                 CE_Failure, CPLE_NotSupported,
     653             :                 "NITF does not support image whose dimension is larger than "
     654             :                 "99999999");
     655           0 :             return FALSE;
     656             :         }
     657             :     }
     658             : 
     659             :     // Reads and validates graphics segment number option
     660         316 :     pszNUMS = CSLFetchNameValue(papszOptions, "NUMS");
     661         316 :     if (pszNUMS != nullptr)
     662             :     {
     663          14 :         nGS = atoi(pszNUMS);
     664          14 :         if (nGS < 0 || nGS > 999)
     665             :         {
     666           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid NUMS value : %s",
     667             :                      pszNUMS);
     668           0 :             return FALSE;
     669             :         }
     670             :     }
     671             : 
     672         316 :     const char *pszNUMDES = CSLFetchNameValue(papszOptions, "NUMDES");
     673         316 :     if (pszNUMDES)
     674          55 :         nDES = atoi(pszNUMDES);
     675             :     else
     676             :     {
     677         522 :         nDES = CPLStringList(CSLFetchNameValueMultiple(papszOptions, "DES"))
     678         261 :                    .size();
     679             :     }
     680             : 
     681             :     /* -------------------------------------------------------------------- */
     682             :     /*      Compute raw image size, blocking factors and so forth.          */
     683             :     /* -------------------------------------------------------------------- */
     684         316 :     nNPPBH = nPixels;
     685         316 :     nNPPBV = nLines;
     686             : 
     687         316 :     if (CSLFetchNameValue(papszOptions, "BLOCKXSIZE") != nullptr)
     688          52 :         nNPPBH = atoi(CSLFetchNameValue(papszOptions, "BLOCKXSIZE"));
     689             : 
     690         316 :     if (CSLFetchNameValue(papszOptions, "BLOCKYSIZE") != nullptr)
     691          50 :         nNPPBV = atoi(CSLFetchNameValue(papszOptions, "BLOCKYSIZE"));
     692             : 
     693         316 :     if (CSLFetchNameValue(papszOptions, "NPPBH") != nullptr)
     694           0 :         nNPPBH = atoi(CSLFetchNameValue(papszOptions, "NPPBH"));
     695             : 
     696         316 :     if (CSLFetchNameValue(papszOptions, "NPPBV") != nullptr)
     697           0 :         nNPPBV = atoi(CSLFetchNameValue(papszOptions, "NPPBV"));
     698             : 
     699         316 :     if ((EQUAL(pszIC, "NC") || EQUAL(pszIC, "C8")) &&
     700         273 :         (nPixels > 8192 || nLines > 8192) && nNPPBH == nPixels &&
     701             :         nNPPBV == nLines)
     702             :     {
     703             :         /* See MIL-STD-2500-C, paragraph 5.4.2.2-d (#3263) */
     704           4 :         nNBPR = 1;
     705           4 :         nNBPC = 1;
     706           4 :         nNPPBH = 0;
     707           4 :         nNPPBV = 0;
     708             : 
     709           4 :         if (EQUAL(pszIC, "NC"))
     710             :         {
     711           4 :             nImageSize = ((nBitsPerSample) / 8) *
     712           4 :                          (static_cast<GUIntBig>(nPixels) * nLines) * nBands;
     713             :         }
     714             :     }
     715         312 :     else if ((EQUAL(pszIC, "NC") || EQUAL(pszIC, "C8")) && nPixels > 8192 &&
     716             :              nNPPBH == nPixels)
     717             :     {
     718           0 :         if (nNPPBV <= 0)
     719           0 :             nNPPBV = 256;
     720             : 
     721             :         /* See MIL-STD-2500-C, paragraph 5.4.2.2-d */
     722           0 :         nNBPR = 1;
     723           0 :         nNPPBH = 0;
     724           0 :         nNBPC = nLines / nNPPBV + ((nLines % nNPPBV) == 0 ? 0 : 1);
     725             : 
     726           0 :         if (nNBPC > 9999)
     727             :         {
     728           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     729             :                      "Unable to create file %s,\n"
     730             :                      "Too many blocks : %d x %d",
     731             :                      pszFilename, nNBPR, nNBPC);
     732           0 :             return FALSE;
     733             :         }
     734             : 
     735           0 :         if (EQUAL(pszIC, "NC"))
     736             :         {
     737           0 :             nImageSize = ((nBitsPerSample) / 8) *
     738           0 :                          (static_cast<GUIntBig>(nPixels) * (nNBPC * nNPPBV)) *
     739           0 :                          nBands;
     740             :         }
     741             :     }
     742         312 :     else if ((EQUAL(pszIC, "NC") || EQUAL(pszIC, "C8")) && nLines > 8192 &&
     743             :              nNPPBV == nLines)
     744             :     {
     745           1 :         if (nNPPBH <= 0)
     746           0 :             nNPPBH = 256;
     747             : 
     748             :         /* See MIL-STD-2500-C, paragraph 5.4.2.2-d */
     749           1 :         nNBPC = 1;
     750           1 :         nNPPBV = 0;
     751           1 :         nNBPR = nPixels / nNPPBH + ((nPixels % nNPPBH) == 0 ? 0 : 1);
     752             : 
     753           1 :         if (nNBPR > 9999)
     754             :         {
     755           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     756             :                      "Unable to create file %s,\n"
     757             :                      "Too many blocks : %d x %d",
     758             :                      pszFilename, nNBPR, nNBPC);
     759           0 :             return FALSE;
     760             :         }
     761             : 
     762           1 :         if (EQUAL(pszIC, "NC"))
     763             :         {
     764           1 :             nImageSize = ((nBitsPerSample) / 8) *
     765           1 :                          (static_cast<GUIntBig>(nLines) * (nNBPR * nNPPBH)) *
     766           1 :                          nBands;
     767             :         }
     768             :     }
     769             :     else
     770             :     {
     771         311 :         if (nNPPBH <= 0 || nNPPBV <= 0 || nNPPBH > 9999 || nNPPBV > 9999)
     772          21 :             nNPPBH = nNPPBV = 256;
     773             : 
     774         311 :         nNBPR = nPixels / nNPPBH + ((nPixels % nNPPBH) == 0 ? 0 : 1);
     775         311 :         nNBPC = nLines / nNPPBV + ((nLines % nNPPBV) == 0 ? 0 : 1);
     776         311 :         if (nNBPR > 9999 || nNBPC > 9999)
     777             :         {
     778           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     779             :                      "Unable to create file %s,\n"
     780             :                      "Too many blocks : %d x %d",
     781             :                      pszFilename, nNBPR, nNBPC);
     782           0 :             return FALSE;
     783             :         }
     784             : 
     785         311 :         if (EQUAL(pszIC, "NC"))
     786             :         {
     787         256 :             nImageSize = ((nBitsPerSample) / 8) *
     788         256 :                          (static_cast<GUIntBig>(nNBPR) * nNBPC) * nNPPBH *
     789         256 :                          nNPPBV * nBands;
     790             :         }
     791             :     }
     792             : 
     793         316 :     if (EQUAL(pszIC, "NC"))
     794             :     {
     795         261 :         if (nImageSize >= NITF_MAX_IMAGE_SIZE)
     796             :         {
     797           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     798             :                      "Unable to create file %s,\n"
     799             :                      "Too big image size : " CPL_FRMT_GUIB,
     800             :                      pszFilename, nImageSize);
     801           1 :             return FALSE;
     802             :         }
     803         260 :         if (nImageSize * nIM >= NITF_MAX_FILE_SIZE)
     804             :         {
     805           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     806             :                      "Unable to create file %s,\n"
     807             :                      "Too big file size : " CPL_FRMT_GUIB,
     808           1 :                      pszFilename, nImageSize * nIM);
     809           1 :             return FALSE;
     810             :         }
     811             :     }
     812             : 
     813             :     /* -------------------------------------------------------------------- */
     814             :     /*      Open new file.                                                  */
     815             :     /* -------------------------------------------------------------------- */
     816         314 :     fp = VSIFOpenL(pszFilename, bAppendSubdataset ? "rb+" : "wb+");
     817         314 :     if (fp == nullptr)
     818             :     {
     819           4 :         CPLError(CE_Failure, CPLE_OpenFailed,
     820             :                  "Unable to create file %s,\n"
     821             :                  "check path and permissions.",
     822             :                  pszFilename);
     823           4 :         return FALSE;
     824             :     }
     825             : 
     826             :     /* -------------------------------------------------------------------- */
     827             :     /*      Work out the version we are producing.  For now we really       */
     828             :     /*      only support creating NITF02.00, NITF02.10 or the NATO analog   */
     829             :     /*      NSIF01.00.                                                      */
     830             :     /* -------------------------------------------------------------------- */
     831         310 :     pszVersion = CSLFetchNameValue(papszOptions, "FHDR");
     832         310 :     if (pszVersion == nullptr)
     833         247 :         pszVersion = "NITF02.10";
     834          63 :     else if (!EQUAL(pszVersion, "NITF02.00") &&
     835          10 :              !EQUAL(pszVersion, "NITF02.10") && !EQUAL(pszVersion, "NSIF01.00"))
     836             :     {
     837           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     838             :                  "FHDR=%s not supported, switching to NITF02.10.", pszVersion);
     839           0 :         pszVersion = "NITF02.10";
     840             :     }
     841             : 
     842             :     /* -------------------------------------------------------------------- */
     843             :     /*      Prepare the file header.                                        */
     844             :     /* -------------------------------------------------------------------- */
     845             : 
     846         310 :     bOK = VSIFSeekL(fp, 0, SEEK_SET) == 0;
     847             : 
     848             : #define PLACE(location, name, text)                                            \
     849             :     do                                                                         \
     850             :     {                                                                          \
     851             :         const char *_text = text;                                              \
     852             :         bOK &= NITFGotoOffset(fp, location);                                   \
     853             :         bOK &= VSIFWriteL(_text, 1, strlen(_text), fp) == strlen(_text);       \
     854             :     } while (0)
     855             : 
     856             : #define OVR(width, location, name, text)                                       \
     857             :     bOK &= NITFWriteOption(fp, papszOptions, width, location, #name, text);
     858             : 
     859             : #define WRITE_BYTE(location, val)                                              \
     860             :     do                                                                         \
     861             :     {                                                                          \
     862             :         char cVal = val;                                                       \
     863             :         bOK &= NITFGotoOffset(fp, location);                                   \
     864             :         bOK &= VSIFWriteL(&cVal, 1, 1, fp) == 1;                               \
     865             :     } while (0)
     866             : 
     867        3186 :     const auto FormatDate = [pszVersion, papszOptions](const char *pszItem)
     868             :     {
     869        1593 :         const char *pszV = CSLFetchNameValue(papszOptions, pszItem);
     870        1593 :         if (!pszV || EQUAL(pszV, "DEFAULT"))
     871             :         {
     872        1593 :             const char *pszDefaultDate = EQUAL(pszVersion, "NITF02.00")
     873             :                                              ? "01000000ZJAN26"
     874             :                                              : "20021216151629";
     875        1593 :             return pszDefaultDate;
     876             :         }
     877           0 :         if (EQUAL(pszV, "NOW"))
     878             :         {
     879             :             time_t now;
     880           0 :             time(&now);
     881             :             struct tm brokenDownTime;
     882           0 :             CPLUnixTimeToYMDHMS(now, &brokenDownTime);
     883           0 :             if (EQUAL(pszVersion, "NITF02.00"))
     884             :             {
     885             :                 // DDHHMMSSZMONYY
     886           0 :                 const char *const aszMonth[] = {"JAN", "FEB", "MAR", "APR",
     887             :                                                 "MAY", "JUN", "JUL", "AUG",
     888             :                                                 "SEP", "OCT", "NOV", "DEC"};
     889           0 :                 return CPLSPrintf(
     890             :                     "%02d%02d%02d%02dZ%s%02d", brokenDownTime.tm_mday,
     891             :                     brokenDownTime.tm_hour, brokenDownTime.tm_min,
     892             :                     brokenDownTime.tm_sec,
     893           0 :                     aszMonth[std::max(0, brokenDownTime.tm_mon) % 12],
     894           0 :                     brokenDownTime.tm_year % 100);
     895             :             }
     896             :             else
     897             :             {
     898             :                 // CCYYMMDDhhmmss
     899           0 :                 return CPLSPrintf(
     900           0 :                     "%04d%02d%02d%02d%02d%02d", brokenDownTime.tm_year + 1900,
     901           0 :                     brokenDownTime.tm_mon + 1, brokenDownTime.tm_mday,
     902             :                     brokenDownTime.tm_hour, brokenDownTime.tm_min,
     903           0 :                     brokenDownTime.tm_sec);
     904             :             }
     905             :         }
     906             : 
     907           0 :         return pszV;
     908         310 :     };
     909             : 
     910         310 :     int nCOff = 0;
     911         310 :     if (!bAppendSubdataset)
     912             :     {
     913         305 :         PLACE(0, FDHR_FVER, pszVersion);
     914         305 :         OVR(2, 9, CLEVEL, "03"); /* Patched at the end */
     915         305 :         PLACE(11, STYPE, EQUAL(pszVersion, "NITF02.00") ? "    " : "BF01");
     916         305 :         OVR(10, 15, OSTAID, "GDAL");
     917         305 :         OVR(14, 25, FDT, FormatDate("NITF_FDT"));
     918         305 :         OVR(80, 39, FTITLE, "");
     919         305 :         OVR(1, 119, FSCLAS, "U");
     920             : 
     921         305 :         if (EQUAL(pszVersion, "NITF02.00"))
     922             :         {
     923          53 :             OVR(40, 120, FSCODE, "");
     924          53 :             OVR(40, 160, FSCTLH, "");
     925          53 :             OVR(40, 200, FSREL, "");
     926          53 :             OVR(20, 240, FSCAUT, "");
     927          53 :             OVR(20, 260, FSCTLN, "");
     928          53 :             OVR(6, 280, FSDWNG, "");
     929          53 :             if (EQUAL(CSLFetchNameValueDef(papszOptions, "FSDWNG", ""),
     930             :                       "999998"))
     931             :             {
     932           1 :                 OVR(40, 286, FSDEVT, "");
     933           1 :                 nCOff += 40;
     934             :             }
     935          53 :             OVR(5, 286 + nCOff, FSCOP, "00000");
     936          53 :             OVR(5, 291 + nCOff, FSCPYS, "00000");
     937          53 :             PLACE(296 + nCOff, ENCRYP, "0");
     938          53 :             OVR(27, 297 + nCOff, ONAME, "");
     939          53 :             OVR(18, 324 + nCOff, OPHONE, "");
     940             :         }
     941             :         else
     942             :         {
     943         252 :             OVR(2, 120, FSCLSY, "");
     944         252 :             OVR(11, 122, FSCODE, "");
     945         252 :             OVR(2, 133, FSCTLH, "");
     946         252 :             OVR(20, 135, FSREL, "");
     947         252 :             OVR(2, 155, FSDCTP, "");
     948         252 :             OVR(8, 157, FSDCDT, "");
     949         252 :             OVR(4, 165, FSDCXM, "");
     950         252 :             OVR(1, 169, FSDG, "");
     951         252 :             OVR(8, 170, FSDGDT, "");
     952         252 :             OVR(43, 178, FSCLTX, "");
     953         252 :             OVR(1, 221, FSCATP, "");
     954         252 :             OVR(40, 222, FSCAUT, "");
     955         252 :             OVR(1, 262, FSCRSN, "");
     956         252 :             OVR(8, 263, FSSRDT, "");
     957         252 :             OVR(15, 271, FSCTLN, "");
     958         252 :             OVR(5, 286, FSCOP, "00000");
     959         252 :             OVR(5, 291, FSCPYS, "00000");
     960         252 :             PLACE(296, ENCRYP, "0");
     961         252 :             WRITE_BYTE(297, 0x00); /* FBKGC */
     962         252 :             WRITE_BYTE(298, 0x00);
     963         252 :             WRITE_BYTE(299, 0x00);
     964         252 :             OVR(24, 300, ONAME, "");
     965         252 :             OVR(18, 324, OPHONE, "");
     966             :         }
     967         305 :         PLACE(342 + nCOff, FL, "????????????");
     968         305 :         PLACE(354 + nCOff, HL, "??????");
     969         305 :         PLACE(360 + nCOff, NUMI, CPLSPrintf("%03d", nIM));
     970             : 
     971         305 :         int nHL = 363 + nCOff;
     972        1593 :         for (iIM = 0; iIM < nIM; iIM++)
     973             :         {
     974             :             /* Patched when image segments are written. */
     975        1288 :             PLACE(nHL, LISHi, "??????");
     976        1288 :             PLACE(nHL + 6, LIi, "??????????");
     977        1288 :             nHL += 6 + 10;
     978             :         }
     979             : 
     980             :         // Creates Header entries for graphic segment
     981             :         //    NUMS: number of segment
     982             :         // For each segment:
     983             :         // LSSH[i]: subheader length (4 byte), set to be 258, the size for
     984             :         //          minimal amount of information.
     985             :         // LS[i] data length (6 byte)
     986         305 :         PLACE(nHL, NUMS, CPLSPrintf("%03d", nGS));
     987         305 :         nHL += 3;  // Move three characters
     988         309 :         for (iGS = 0; iGS < nGS; iGS++)
     989             :         {
     990             :             /* Patched when graphic segments are written. */
     991           4 :             PLACE(nHL, LSSHi, "????");
     992           4 :             nHL += 4;
     993           4 :             PLACE(nHL, LSi, "??????");
     994           4 :             nHL += 6;
     995             :         }
     996             : 
     997         305 :         PLACE(nHL, NUMX, "000");
     998         305 :         PLACE(nHL + 3, NUMT, CPLSPrintf("%03d", nNUMT));
     999             : 
    1000             :         /* Patched when text segments are written. */
    1001         305 :         PLACE(nHL + 6, LTSHnLTn, "");
    1002             : 
    1003         305 :         nHL += 6 + (4 + 5) * nNUMT;
    1004             : 
    1005         305 :         PLACE(nHL, NUMDES, CPLSPrintf("%03d", nDES));
    1006         305 :         nHL += 3;
    1007             : 
    1008         369 :         for (iDES = 0; iDES < nDES; iDES++)
    1009             :         {
    1010             :             /* Patched when DESs are written. */
    1011          64 :             PLACE(nHL, LDSH, "????");
    1012          64 :             nHL += 4;
    1013          64 :             PLACE(nHL, LD, "?????????");
    1014          64 :             nHL += 9;
    1015             :         }
    1016             : 
    1017         305 :         PLACE(nHL, NUMRES, "000");
    1018         305 :         nHL += 3;
    1019             : 
    1020             :         // The RPFHDR TRE must be written in UDID and not in IXSHD
    1021             :         const int nRPFHDRPos =
    1022         305 :             CSLPartialFindString(papszOptions, "FILE_TRE=RPFHDR=");
    1023         305 :         if (nRPFHDRPos >= 0)
    1024             :         {
    1025          51 :             int nContentLength = 0;
    1026         102 :             char *pszUnescapedContents = CPLUnescapeString(
    1027          51 :                 papszOptions[nRPFHDRPos] + strlen("FILE_TRE=RPFHDR="),
    1028             :                 &nContentLength, CPLES_BackslashQuotable);
    1029          51 :             if (nContentLength != 48)
    1030             :             {
    1031           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1032             :                          "Invalid length for RPFHDR : %d", nContentLength);
    1033           0 :                 CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
    1034           0 :                 CPLFree(pszUnescapedContents);
    1035           0 :                 return FALSE;
    1036             :             }
    1037             : 
    1038          51 :             constexpr const char *pszUDOFL = "000";
    1039          51 :             const char *pszTREPrefix = CPLSPrintf("RPFHDR%05d", nContentLength);
    1040          51 :             PLACE(nHL, UDHDL,
    1041             :                   CPLSPrintf("%05d", static_cast<int>(strlen(pszUDOFL) +
    1042             :                                                       strlen(pszTREPrefix) +
    1043             :                                                       nContentLength)));
    1044          51 :             nHL += 5;
    1045             : 
    1046          51 :             PLACE(nHL, UDOFL, pszUDOFL);
    1047          51 :             nHL += static_cast<int>(strlen(pszUDOFL));
    1048             : 
    1049          51 :             PLACE(nHL, UDID, pszTREPrefix);
    1050          51 :             nHL += static_cast<int>(strlen(pszTREPrefix));
    1051             : 
    1052          51 :             if (offsetPatcher)
    1053             :             {
    1054          51 :                 auto poBuffer = offsetPatcher->GetBufferFromName("RPFHDR");
    1055          51 :                 if (poBuffer)
    1056             :                 {
    1057          51 :                     poBuffer->DeclareBufferWrittenAtPosition(VSIFTellL(fp));
    1058             :                 }
    1059             :             }
    1060             : 
    1061          51 :             bOK &= VSIFWriteL(pszUnescapedContents, 1, nContentLength, fp) ==
    1062          51 :                    static_cast<size_t>(nContentLength);
    1063          51 :             nHL += nContentLength;
    1064          51 :             CPLFree(pszUnescapedContents);
    1065             :         }
    1066             :         else
    1067             :         {
    1068         254 :             PLACE(nHL, UDHDL, "00000");
    1069         254 :             nHL += 5;
    1070             :         }
    1071         305 :         const int nOffsetXHDL = nHL;
    1072         305 :         PLACE(nHL, XHDL, "00000");
    1073         305 :         nHL += 5;
    1074             : 
    1075         305 :         if (CSLFetchNameValue(papszOptions, "FILE_TRE") != nullptr)
    1076             :         {
    1077          56 :             bOK &= NITFWriteTREsFromOptions(fp, nOffsetXHDL, &nHL, papszOptions,
    1078          56 :                                             "FILE_TRE=", offsetPatcher);
    1079             :         }
    1080             : 
    1081         305 :         if (nHL > 999999)
    1082             :         {
    1083           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1084             :                      "Too big file header length : %d", nHL);
    1085           0 :             CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
    1086           0 :             return FALSE;
    1087             :         }
    1088             : 
    1089             :         // update header length
    1090         305 :         PLACE(354 + nCOff, HL, CPLSPrintf("%06d", nHL));
    1091             : 
    1092         305 :         nCur = nHL;
    1093         305 :         iIM = 0;
    1094             :     }
    1095             :     else
    1096             :     {
    1097             :         // Append subdataset
    1098           5 :         NITFFile *psFile = NITFOpenEx(fp, pszFilename);
    1099           5 :         if (psFile == nullptr)
    1100           0 :             return FALSE;
    1101             : 
    1102           5 :         if (EQUAL(psFile->szVersion, "NITF02.00") &&
    1103           0 :             EQUAL(
    1104             :                 CSLFetchNameValueDef(psFile->papszMetadata, "NITF_FSDWNG", ""),
    1105             :                 "999998"))
    1106             :         {
    1107           0 :             nCOff = 40;
    1108             :         }
    1109             : 
    1110           5 :         iIM = -1;
    1111           5 :         nIM = 0;
    1112          20 :         for (int i = 0; i < psFile->nSegmentCount; i++)
    1113             :         {
    1114          15 :             if (strcmp(psFile->pasSegmentInfo[i].szSegmentType, "IM") == 0)
    1115             :             {
    1116          12 :                 nIM++;
    1117          12 :                 if (psFile->pasSegmentInfo[i].nSegmentHeaderSize == 0 &&
    1118             :                     iIM < 0)
    1119             :                 {
    1120           5 :                     iIM = i;
    1121           5 :                     if (pnIndex)
    1122           5 :                         *pnIndex = i;
    1123             :                 }
    1124             :             }
    1125             :         }
    1126           5 :         if (pnImageCount)
    1127           5 :             *pnImageCount = nIM;
    1128             : 
    1129           5 :         psFile->fp = nullptr;
    1130           5 :         NITFClose(psFile);
    1131             : 
    1132           5 :         if (iIM < 0)
    1133             :         {
    1134           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1135             :                      "Did not find free image segment");
    1136           0 :             CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
    1137           0 :             return FALSE;
    1138             :         }
    1139           5 :         nIM = iIM + 1;
    1140             : 
    1141           5 :         bOK &= VSIFSeekL(fp, 0, SEEK_END) == 0;
    1142           5 :         nCur = VSIFTellL(fp);
    1143             :     }
    1144             : 
    1145             :     /* -------------------------------------------------------------------- */
    1146             :     /*      Prepare the image header.                                       */
    1147             :     /* -------------------------------------------------------------------- */
    1148        1309 :     for (; iIM < nIM; iIM++)
    1149             :     {
    1150        1288 :         char **papszIREPBANDTokens = nullptr;
    1151        1288 :         char **papszISUBCATTokens = nullptr;
    1152             : 
    1153        1288 :         if (CSLFetchNameValue(papszOptions, "IREPBAND") != nullptr)
    1154             :         {
    1155           2 :             papszIREPBANDTokens = CSLTokenizeStringComplex(
    1156             :                 CSLFetchNameValue(papszOptions, "IREPBAND"), ",", 0, 0);
    1157           4 :             if (papszIREPBANDTokens != nullptr &&
    1158           2 :                 CSLCount(papszIREPBANDTokens) != nBands)
    1159             :             {
    1160           0 :                 CSLDestroy(papszIREPBANDTokens);
    1161           0 :                 papszIREPBANDTokens = nullptr;
    1162             :             }
    1163             :         }
    1164        1288 :         if (CSLFetchNameValue(papszOptions, "ISUBCAT") != nullptr)
    1165             :         {
    1166           1 :             papszISUBCATTokens = CSLTokenizeStringComplex(
    1167             :                 CSLFetchNameValue(papszOptions, "ISUBCAT"), ",", 0, 0);
    1168           2 :             if (papszISUBCATTokens != nullptr &&
    1169           1 :                 CSLCount(papszISUBCATTokens) != nBands)
    1170             :             {
    1171           0 :                 CSLDestroy(papszISUBCATTokens);
    1172           0 :                 papszISUBCATTokens = nullptr;
    1173             :             }
    1174             :         }
    1175             : 
    1176        1288 :         bOK &= VSIFSeekL(fp, nCur, SEEK_SET) == 0;
    1177             : 
    1178        1288 :         PLACE(nCur + 0, IM, "IM");
    1179        1288 :         OVR(10, nCur + 2, IID1, "Missing");
    1180        1288 :         OVR(14, nCur + 12, IDATIM, FormatDate("NITF_IDATIM"));
    1181        1288 :         OVR(17, nCur + 26, TGTID, "");
    1182        1288 :         if (EQUAL(pszVersion, "NITF02.00"))
    1183             :         {
    1184          34 :             OVR(80, nCur + 43, ITITLE, "");
    1185             :         }
    1186             :         else
    1187             :         {
    1188        1254 :             OVR(80, nCur + 43, IID2, "");
    1189             :         }
    1190        1288 :         OVR(1, nCur + 123, ISCLAS, "U");
    1191        1288 :         int nExtraOffset = 0;
    1192        1288 :         if (EQUAL(pszVersion, "NITF02.00"))
    1193             :         {
    1194          34 :             OVR(40, nCur + 124, ISCODE, "");
    1195          34 :             OVR(40, nCur + 164, ISCTLH, "");
    1196          34 :             OVR(40, nCur + 204, ISREL, "");
    1197          34 :             OVR(20, nCur + 244, ISCAUT, "");
    1198          34 :             OVR(20, nCur + 264, ISCTLN, "");
    1199          34 :             OVR(6, nCur + 284, ISDWNG, "");
    1200          34 :             if (EQUAL(CSLFetchNameValueDef(papszOptions, "ISDWNG", ""),
    1201             :                       "999998"))
    1202             :             {
    1203           1 :                 OVR(40, nCur + 290, ISDEVT, "");
    1204           1 :                 nExtraOffset = 40;
    1205           1 :                 nCur += 40;
    1206             :             }
    1207             :         }
    1208             :         else
    1209             :         {
    1210        1254 :             OVR(2, nCur + 124, ISCLSY, "");
    1211        1254 :             OVR(11, nCur + 126, ISCODE, "");
    1212        1254 :             OVR(2, nCur + 137, ISCTLH, "");
    1213        1254 :             OVR(20, nCur + 139, ISREL, "");
    1214        1254 :             OVR(2, nCur + 159, ISDCTP, "");
    1215        1254 :             OVR(8, nCur + 161, ISDCDT, "");
    1216        1254 :             OVR(4, nCur + 169, ISDCXM, "");
    1217        1254 :             OVR(1, nCur + 173, ISDG, "");
    1218        1254 :             OVR(8, nCur + 174, ISDGDT, "");
    1219        1254 :             OVR(43, nCur + 182, ISCLTX, "");
    1220        1254 :             OVR(1, nCur + 225, ISCATP, "");
    1221        1254 :             OVR(40, nCur + 226, ISCAUT, "");
    1222        1254 :             OVR(1, nCur + 266, ISCRSN, "");
    1223        1254 :             OVR(8, nCur + 267, ISSRDT, "");
    1224        1254 :             OVR(15, nCur + 275, ISCTLN, "");
    1225             :         }
    1226        1288 :         PLACE(nCur + 290, ENCRYP, "0");
    1227        1288 :         OVR(42, nCur + 291, ISORCE, "Unknown");
    1228        1288 :         PLACE(nCur + 333, NROWS, CPLSPrintf("%08d", nLines));
    1229        1288 :         PLACE(nCur + 341, NCOLS, CPLSPrintf("%08d", nPixels));
    1230        1288 :         PLACE(nCur + 349, PVTYPE, pszPVType);
    1231        1288 :         PLACE(nCur + 352, IREP, pszIREP);
    1232        1288 :         OVR(8, nCur + 360, ICAT, "VIS");
    1233             :         {
    1234        1288 :             const char *pszParamValue = CSLFetchNameValue(papszOptions, "ABPP");
    1235        1288 :             PLACE(nCur + 368, ABPP,
    1236             :                   CPLSPrintf("%02d", pszParamValue ? atoi(pszParamValue)
    1237             :                                                    : nBitsPerSample));
    1238             :         }
    1239        1288 :         OVR(1, nCur + 370, PJUST, "R");
    1240             : 
    1241        1288 :         bool bHasIGEOLO = false;
    1242             :         {
    1243             :             const char *pszParamValue =
    1244        1288 :                 CSLFetchNameValueDef(papszOptions, "ICORDS", " ");
    1245        1288 :             if (strlen(pszParamValue) != 1)
    1246           0 :                 pszParamValue = " ";
    1247        1288 :             if (EQUAL(pszVersion, "NITF02.00"))
    1248             :             {
    1249          34 :                 if (EQUAL(pszParamValue, "N") || EQUAL(pszParamValue, "S"))
    1250             :                 {
    1251           0 :                     bHasIGEOLO = true;
    1252           0 :                     pszParamValue = "U";  // UTM
    1253             :                 }
    1254          34 :                 else if (EQUAL(pszParamValue, " "))
    1255             :                 {
    1256           1 :                     pszParamValue = "N";  // In NITF02.00, N stands for Nothing
    1257             :                 }
    1258             :                 else
    1259             :                 {
    1260          33 :                     bHasIGEOLO = true;
    1261             :                 }
    1262             :             }
    1263             :             else
    1264             :             {
    1265        1254 :                 bHasIGEOLO = !EQUAL(pszParamValue, " ");
    1266             :             }
    1267        1288 :             PLACE(nCur + 371, ICORDS, pszParamValue);
    1268             :         }
    1269             : 
    1270        1288 :         nCur -= nExtraOffset;
    1271        1288 :         nOffset = 372 + nExtraOffset;
    1272             : 
    1273        1288 :         if (bHasIGEOLO)
    1274             :         {
    1275         128 :             OVR(60, nCur + nOffset, IGEOLO, "");
    1276         128 :             nOffset += 60;
    1277             :         }
    1278             : 
    1279             :         {
    1280        1288 :             const char *pszICOM = CSLFetchNameValue(papszOptions, "ICOM");
    1281        1288 :             if (pszICOM != nullptr)
    1282             :             {
    1283             :                 char *pszRecodedICOM =
    1284           2 :                     CPLRecode(pszICOM, CPL_ENC_UTF8, CPL_ENC_ISO8859_1);
    1285           2 :                 int nLenICOM = static_cast<int>(strlen(pszRecodedICOM));
    1286           2 :                 int nICOM = (79 + nLenICOM) / 80;
    1287             :                 size_t nToWrite;
    1288           2 :                 if (nICOM > 9)
    1289             :                 {
    1290           0 :                     CPLError(CE_Warning, CPLE_NotSupported,
    1291             :                              "ICOM will be truncated");
    1292           0 :                     nICOM = 9;
    1293             :                 }
    1294           2 :                 PLACE(nCur + nOffset, NICOM, CPLSPrintf("%01d", nICOM));
    1295           2 :                 nToWrite = std::min(nICOM * 80, nLenICOM);
    1296           2 :                 bOK &= VSIFWriteL(pszRecodedICOM, 1, nToWrite, fp) == nToWrite;
    1297           2 :                 nOffset += nICOM * 80;
    1298           2 :                 CPLFree(pszRecodedICOM);
    1299             :             }
    1300             :             else
    1301             :             {
    1302        1286 :                 PLACE(nCur + nOffset, NICOM, "0");
    1303             :             }
    1304             :         }
    1305             : 
    1306        1288 :         if (pnICOffset)
    1307             :         {
    1308        1288 :             if (iIM == 0 || bAppendSubdataset)
    1309         290 :                 *pnICOffset = nCur + nOffset + 1;
    1310             :         }
    1311        1288 :         OVR(2, nCur + nOffset + 1, IC, "NC");
    1312             : 
    1313        1288 :         if (pszIC[0] != 'N')
    1314             :         {
    1315          54 :             OVR(4, nCur + nOffset + 3, COMRAT, "    ");
    1316          54 :             nOffset += 4;
    1317             :         }
    1318             : 
    1319        1288 :         if (nBands <= 9)
    1320             :         {
    1321        1287 :             PLACE(nCur + nOffset + 3, NBANDS, CPLSPrintf("%d", nBands));
    1322             :         }
    1323             :         else
    1324             :         {
    1325           1 :             PLACE(nCur + nOffset + 3, NBANDS, "0");
    1326           1 :             PLACE(nCur + nOffset + 4, XBANDS, CPLSPrintf("%05d", nBands));
    1327           1 :             nOffset += 5;
    1328             :         }
    1329             : 
    1330        1288 :         nOffset += 4;
    1331             : 
    1332             :         /* --------------------------------------------------------------------
    1333             :          */
    1334             :         /*      Per band info */
    1335             :         /* --------------------------------------------------------------------
    1336             :          */
    1337       72694 :         for (iBand = 0; iBand < nBands; iBand++)
    1338             :         {
    1339       71406 :             const char *pszIREPBAND = "M";
    1340             : 
    1341       71406 :             if (papszIREPBANDTokens != nullptr)
    1342             :             {
    1343           7 :                 if (strlen(papszIREPBANDTokens[iBand]) > 2)
    1344             :                 {
    1345           0 :                     papszIREPBANDTokens[iBand][2] = '\0';
    1346           0 :                     CPLError(CE_Warning, CPLE_NotSupported,
    1347             :                              "Truncating IREPBAND[%d] to '%s'", iBand + 1,
    1348           0 :                              papszIREPBANDTokens[iBand]);
    1349             :                 }
    1350           7 :                 pszIREPBAND = papszIREPBANDTokens[iBand];
    1351             :             }
    1352       71399 :             else if (EQUAL(pszIREP, "RGB/LUT"))
    1353          34 :                 pszIREPBAND = "LU";
    1354       71365 :             else if (EQUAL(pszIREP, "RGB"))
    1355             :             {
    1356          34 :                 if (iBand == 0)
    1357          11 :                     pszIREPBAND = "R";
    1358          23 :                 else if (iBand == 1)
    1359          11 :                     pszIREPBAND = "G";
    1360          12 :                 else if (iBand == 2)
    1361          11 :                     pszIREPBAND = "B";
    1362             :             }
    1363       71331 :             else if (STARTS_WITH_CI(pszIREP, "YCbCr"))
    1364             :             {
    1365          15 :                 if (iBand == 0)
    1366           5 :                     pszIREPBAND = "Y";
    1367          10 :                 else if (iBand == 1)
    1368           5 :                     pszIREPBAND = "Cb";
    1369           5 :                 else if (iBand == 2)
    1370           5 :                     pszIREPBAND = "Cr";
    1371             :             }
    1372             : 
    1373       71406 :             PLACE(nCur + nOffset + 0, IREPBANDn, pszIREPBAND);
    1374             : 
    1375       71406 :             if (papszISUBCATTokens != nullptr)
    1376             :             {
    1377           2 :                 if (strlen(papszISUBCATTokens[iBand]) > 6)
    1378             :                 {
    1379           0 :                     papszISUBCATTokens[iBand][6] = '\0';
    1380           0 :                     CPLError(CE_Warning, CPLE_NotSupported,
    1381             :                              "Truncating ISUBCAT[%d] to '%s'", iBand + 1,
    1382           0 :                              papszISUBCATTokens[iBand]);
    1383             :                 }
    1384           2 :                 PLACE(nCur + nOffset + 2, ISUBCATn, papszISUBCATTokens[iBand]);
    1385             :             }
    1386             :             //      else
    1387             :             //          PLACE(nCur+nOffset+ 2, ISUBCATn, "" );
    1388             : 
    1389       71406 :             PLACE(nCur + nOffset + 8, IFCn, "N");
    1390             :             //      PLACE(nCur+nOffset+ 9, IMFLTn, "" );
    1391             : 
    1392       71406 :             if (!EQUAL(pszIREP, "RGB/LUT"))
    1393             :             {
    1394       71372 :                 PLACE(nCur + nOffset + 12, NLUTSn, "0");
    1395       71372 :                 nOffset += 13;
    1396             :             }
    1397             :             else
    1398             :             {
    1399          34 :                 int iC, nCount = 256;
    1400             : 
    1401          34 :                 if (CSLFetchNameValue(papszOptions, "LUT_SIZE") != nullptr)
    1402          34 :                     nCount = atoi(CSLFetchNameValue(papszOptions, "LUT_SIZE"));
    1403             : 
    1404          34 :                 if (!(nCount >= 0 && nCount <= 99999))
    1405             :                 {
    1406           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1407             :                              "Invalid LUT value : %d. Defaulting to 256",
    1408             :                              nCount);
    1409           0 :                     nCount = 256;
    1410             :                 }
    1411          34 :                 PLACE(nCur + nOffset + 12, NLUTSn, "3");
    1412          34 :                 PLACE(nCur + nOffset + 13, NELUTn, CPLSPrintf("%05d", nCount));
    1413             : 
    1414        7230 :                 for (iC = 0; iC < nCount; iC++)
    1415             :                 {
    1416        7196 :                     WRITE_BYTE(nCur + nOffset + 18 + iC + 0,
    1417             :                                static_cast<char>(iC));
    1418        7196 :                     WRITE_BYTE(nCur + nOffset + 18 + iC + nCount * 1,
    1419             :                                static_cast<char>(iC));
    1420        7196 :                     WRITE_BYTE(nCur + nOffset + 18 + iC + nCount * 2,
    1421             :                                static_cast<char>(iC));
    1422             :                 }
    1423          34 :                 nOffset += 18 + nCount * 3;
    1424             :             }
    1425             :         }
    1426             : 
    1427        1288 :         CSLDestroy(papszIREPBANDTokens);
    1428        1288 :         CSLDestroy(papszISUBCATTokens);
    1429             : 
    1430             :         /* --------------------------------------------------------------------
    1431             :          */
    1432             :         /*      Remainder of image header info. */
    1433             :         /* --------------------------------------------------------------------
    1434             :          */
    1435        1288 :         PLACE(nCur + nOffset + 0, ISYNC, "0");
    1436             : 
    1437             :         /* RGB JPEG compressed NITF requires IMODE=P (see #3345) */
    1438        1288 :         if (nBands >= 3 && (EQUAL(pszIC, "C3") || EQUAL(pszIC, "M3")))
    1439             :         {
    1440           6 :             PLACE(nCur + nOffset + 1, IMODE, "P");
    1441             :         }
    1442             :         else
    1443             :         {
    1444        1282 :             PLACE(nCur + nOffset + 1, IMODE, "B");
    1445             :         }
    1446        1288 :         PLACE(nCur + nOffset + 2, NBPR, CPLSPrintf("%04d", nNBPR));
    1447        1288 :         PLACE(nCur + nOffset + 6, NBPC, CPLSPrintf("%04d", nNBPC));
    1448        1288 :         PLACE(nCur + nOffset + 10, NPPBH, CPLSPrintf("%04d", nNPPBH));
    1449        1288 :         PLACE(nCur + nOffset + 14, NPPBV, CPLSPrintf("%04d", nNPPBV));
    1450        1288 :         PLACE(nCur + nOffset + 18, NBPP, CPLSPrintf("%02d", nBitsPerSample));
    1451        1288 :         PLACE(nCur + nOffset + 20, IDLVL,
    1452             :               CPLSPrintf("%03d", atoi(CSLFetchNameValueDef(papszOptions,
    1453             :                                                            "IDLVL", "1"))));
    1454        1288 :         PLACE(nCur + nOffset + 23, IALVL,
    1455             :               CPLSPrintf("%03d", atoi(CSLFetchNameValueDef(papszOptions,
    1456             :                                                            "IALVL", "0"))));
    1457        1288 :         PLACE(nCur + nOffset + 26, ILOCROW,
    1458             :               CPLSPrintf("%05d", atoi(CSLFetchNameValueDef(papszOptions,
    1459             :                                                            "ILOCROW", "0"))));
    1460        1288 :         PLACE(nCur + nOffset + 31, ILOCCOL,
    1461             :               CPLSPrintf("%05d", atoi(CSLFetchNameValueDef(papszOptions,
    1462             :                                                            "ILOCCOL", "0"))));
    1463        1288 :         OVR(4, nCur + nOffset + 36, IMAG, "1.0 ");
    1464             : 
    1465             :         // The RPFIMG TRE must be written in UDID and not in IXSHD
    1466        1288 :         int nUDIDL = 0;
    1467        1424 :         if (offsetPatcher &&
    1468        1424 :             offsetPatcher->GetBufferFromName("LocationComponent"))
    1469             :         {
    1470          32 :             bOK &= RPFFrameWriteCADRG_RPFIMG(offsetPatcher, fp, nUDIDL);
    1471             :         }
    1472             :         else
    1473             :         {
    1474        1256 :             PLACE(nCur + nOffset + 40, UDIDL, "00000");
    1475             :         }
    1476             : 
    1477        1288 :         PLACE(nCur + nOffset + 45 + nUDIDL, IXSHDL, "00000");
    1478             : 
    1479        1288 :         nOffsetIXSHDL = nCur + nOffset + 45 + nUDIDL;
    1480        1288 :         nOffset += 50 + nUDIDL;
    1481             : 
    1482             :         /* --------------------------------------------------------------------
    1483             :          */
    1484             :         /*      Add BLOCKA TRE if requested. */
    1485             :         /* --------------------------------------------------------------------
    1486             :          */
    1487        1288 :         if (CSLFetchNameValue(papszOptions, "BLOCKA_BLOCK_COUNT") != nullptr)
    1488             :         {
    1489           3 :             NITFWriteBLOCKA(fp, nOffsetIXSHDL, &nOffset, papszOptions);
    1490             :         }
    1491             : 
    1492        2512 :         if (CSLFetchNameValue(papszOptions, "TRE") != nullptr ||
    1493        1224 :             CSLFetchNameValue(papszOptions, "RESERVE_SPACE_FOR_TRE_OVERFLOW") !=
    1494             :                 nullptr)
    1495             :         {
    1496          66 :             bOK &=
    1497          66 :                 NITFWriteTREsFromOptions(fp, nOffsetIXSHDL, &nOffset,
    1498          66 :                                          papszOptions, "TRE=", offsetPatcher);
    1499             :         }
    1500             : 
    1501             :         /* --------------------------------------------------------------------
    1502             :          */
    1503             :         /*      Update the image header length in the file header. */
    1504             :         /* --------------------------------------------------------------------
    1505             :          */
    1506        1288 :         nIHSize = nOffset;
    1507             : 
    1508        1288 :         if (nIHSize > 999999)
    1509             :         {
    1510           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1511             :                      "Too big image header length : %d", nIHSize);
    1512           0 :             CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
    1513           0 :             return FALSE;
    1514             :         }
    1515             : 
    1516        1288 :         PLACE(363 + nCOff + iIM * 16, LISH1, CPLSPrintf("%06d", nIHSize));
    1517        1288 :         if (EQUAL(pszIC, "NC"))
    1518             :         {
    1519        1234 :             PLACE(
    1520             :                 369 + nCOff + iIM * 16, LIi,
    1521             :                 CPLSPrintf("%010" CPL_FRMT_GB_WITHOUT_PREFIX "u", nImageSize));
    1522             :         }
    1523             : 
    1524        1288 :         nCur += nIHSize;
    1525        1288 :         if (pnImageOffset)
    1526             :         {
    1527        1288 :             if (iIM == 0 || bAppendSubdataset)
    1528         290 :                 *pnImageOffset = nCur;
    1529             :         }
    1530        1288 :         nCur += nImageSize;
    1531             : 
    1532        1288 :         if (!bWriteAllImages)
    1533         289 :             break;
    1534             :     }
    1535             : 
    1536             :     /* -------------------------------------------------------------------- */
    1537             :     /*      Fill in image data by writing one byte at the end               */
    1538             :     /* -------------------------------------------------------------------- */
    1539         310 :     if (nIM > 0 && EQUAL(pszIC, "NC"))
    1540             :     {
    1541         236 :         char cNul = 0;
    1542         236 :         bOK &= VSIFSeekL(fp, nCur - 1, SEEK_SET) == 0;
    1543         236 :         bOK &= VSIFWriteL(&cNul, 1, 1, fp) == 1;
    1544             :     }
    1545             : 
    1546             :     /* -------------------------------------------------------------------- */
    1547             :     /*      Compute and update CLEVEL ("complexity" level).                 */
    1548             :     /*      See: http://164.214.2.51/ntb/baseline/docs/2500b/2500b_not2.pdf */
    1549             :     /*            page 96u                                                  */
    1550             :     /* -------------------------------------------------------------------- */
    1551         310 :     if (EQUAL(pszVersion, "NITF02.00") && EQUAL(pszIC, "NC") &&
    1552          21 :         nPixels <= 1024 && nLines <= 1024 && nPixels == nNPPBH &&
    1553             :         nLines == nNPPBV)
    1554             :     {
    1555           2 :         nCLevel = 2;
    1556             :     }
    1557             :     else
    1558             :     {
    1559         308 :         nCLevel = 3;
    1560             :     }
    1561         310 :     if (bAppendSubdataset)
    1562             :     {
    1563             :         // Get existing CLEVEL
    1564           5 :         bOK &= VSIFSeekL(fp, 9, SEEK_SET) == 0;
    1565           5 :         char szCLEVEL[3] = {0};
    1566           5 :         bOK &= VSIFReadL(szCLEVEL, 1, 2, fp) != 0;
    1567           5 :         nCLevel = atoi(szCLEVEL);
    1568             :     }
    1569         310 :     if (nBands > 9 || nIM > 20 || nPixels > 2048 || nLines > 2048 ||
    1570         293 :         nNPPBH > 2048 || nNPPBV > 2048 || nCur > 52428799)
    1571             :     {
    1572          18 :         nCLevel = std::max(nCLevel, 5);
    1573             :     }
    1574         310 :     if (nPixels > 8192 || nLines > 8192 || nNPPBH > 8192 || nNPPBV > 8192 ||
    1575         305 :         nCur > 1073741833 || nDES > 10)
    1576             :     {
    1577           5 :         nCLevel = std::max(nCLevel, 6);
    1578             :     }
    1579         310 :     if (nBands > 256 || nPixels > 65536 || nLines > 65536 ||
    1580         309 :         nCur > 2147483647 || nDES > 50)
    1581             :     {
    1582           1 :         nCLevel = std::max(nCLevel, 7);
    1583             :     }
    1584         310 :     OVR(2, 9, CLEVEL, CPLSPrintf("%02d", nCLevel));
    1585             : 
    1586             :     /* -------------------------------------------------------------------- */
    1587             :     /*      Update total file length                                        */
    1588             :     /* -------------------------------------------------------------------- */
    1589             : 
    1590             :     /* According to the spec, CLEVEL 7 supports up to 10,737,418,330 bytes */
    1591             :     /* but we can support technically much more */
    1592         310 :     if (EQUAL(pszIC, "NC") && nCur >= 999999999999ULL)
    1593             :     {
    1594           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Too big file : " CPL_FRMT_GUIB,
    1595             :                  nCur);
    1596           0 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
    1597           0 :         return FALSE;
    1598             :     }
    1599             : 
    1600         310 :     PLACE(342 + nCOff, FL,
    1601             :           CPLSPrintf("%012" CPL_FRMT_GB_WITHOUT_PREFIX "d", nCur));
    1602             : 
    1603         310 :     if (VSIFCloseL(fp) != 0)
    1604           0 :         bOK = FALSE;
    1605             : 
    1606         310 :     CPL_IGNORE_RET_VAL(nOffset);
    1607             : 
    1608         310 :     return bOK;
    1609             : }
    1610             : 
    1611       39231 : static bool NITFWriteOption(VSILFILE *psFile, CSLConstList papszOptions,
    1612             :                             size_t nWidth, GUIntBig nLocation,
    1613             :                             const char *pszName, const char *pszText)
    1614             : {
    1615             :     const char *pszParamValue;
    1616             :     char *pszRecodedValue;
    1617             :     size_t nToWrite;
    1618       39231 :     bool bOK = true;
    1619             : 
    1620       39231 :     pszParamValue = CSLFetchNameValue(papszOptions, pszName);
    1621       39231 :     if (pszParamValue == nullptr)
    1622             :     {
    1623       38969 :         pszRecodedValue = CPLRecode(pszText, CPL_ENC_UTF8, CPL_ENC_ISO8859_1);
    1624             :     }
    1625             :     else
    1626             :     {
    1627             :         pszRecodedValue =
    1628         262 :             CPLRecode(pszParamValue, CPL_ENC_UTF8, CPL_ENC_ISO8859_1);
    1629             :     }
    1630             : 
    1631       39231 :     bOK &= NITFGotoOffset(psFile, nLocation);
    1632       39231 :     nToWrite = std::min(nWidth, strlen(pszRecodedValue));
    1633       39231 :     bOK &= VSIFWriteL(pszRecodedValue, 1, nToWrite, psFile) == nToWrite;
    1634       39231 :     CPLFree(pszRecodedValue);
    1635       39231 :     return bOK;
    1636             : }
    1637             : 
    1638             : /************************************************************************/
    1639             : /*                            NITFWriteTRE()                            */
    1640             : /************************************************************************/
    1641             : 
    1642         130 : static bool NITFWriteTRE(VSILFILE *fp, vsi_l_offset nOffsetHDL, int *pnOffset,
    1643             :                          const char *pszTREName, char *pabyTREData,
    1644             :                          int nTREDataSize,
    1645             :                          GDALOffsetPatcher::OffsetPatcher *offsetPatcher)
    1646             : 
    1647             : {
    1648             :     char szTemp[12];
    1649             :     int nOldOffset;
    1650         130 :     bool bOK = true;
    1651             : 
    1652         130 :     if (EQUAL(pszTREName, "RPFHDR") || EQUAL(pszTREName, "RPFIMG"))
    1653          51 :         return true;
    1654             : 
    1655             :     /* -------------------------------------------------------------------- */
    1656             :     /*      Update XHDL or IXSHDL.                                          */
    1657             :     /* -------------------------------------------------------------------- */
    1658          79 :     bOK &= VSIFSeekL(fp, nOffsetHDL, SEEK_SET) == 0;
    1659          79 :     bOK &= VSIFReadL(szTemp, 1, 5, fp) == 5;
    1660          79 :     szTemp[5] = 0;
    1661          79 :     nOldOffset = atoi(szTemp);
    1662             : 
    1663          79 :     if (nOldOffset == 0)
    1664             :     {
    1665          71 :         nOldOffset = 3;
    1666          71 :         PLACE(nOffsetHDL + 5, IXSOFL, "000");
    1667          71 :         *pnOffset += 3;
    1668             :     }
    1669             : 
    1670          79 :     if (nOldOffset + 11 + nTREDataSize > 99999 || nTREDataSize < 0 ||
    1671             :         nTREDataSize > 99999)
    1672             :     {
    1673           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Too big TRE to be written");
    1674           0 :         return FALSE;
    1675             :     }
    1676             : 
    1677          79 :     snprintf(szTemp, sizeof(szTemp), "%05d", nOldOffset + 11 + nTREDataSize);
    1678          79 :     PLACE(nOffsetHDL, IXSHDL, szTemp);
    1679             : 
    1680             :     /* -------------------------------------------------------------------- */
    1681             :     /*      Create TRE prefix.                                              */
    1682             :     /* -------------------------------------------------------------------- */
    1683          79 :     snprintf(szTemp, sizeof(szTemp), "%-6s%05d", pszTREName, nTREDataSize);
    1684          79 :     bOK &= VSIFSeekL(fp, nOffsetHDL + 5 + nOldOffset, SEEK_SET) == 0;
    1685          79 :     bOK &= VSIFWriteL(szTemp, 11, 1, fp) == 1;
    1686             : 
    1687          79 :     if (offsetPatcher)
    1688             :     {
    1689          24 :         auto poBuffer = offsetPatcher->GetBufferFromName(pszTREName);
    1690          24 :         if (poBuffer)
    1691             :         {
    1692           0 :             poBuffer->DeclareBufferWrittenAtPosition(VSIFTellL(fp));
    1693             :         }
    1694             :     }
    1695             : 
    1696          79 :     bOK &= static_cast<int>(VSIFWriteL(pabyTREData, 1, nTREDataSize, fp)) ==
    1697          79 :            nTREDataSize;
    1698             : 
    1699             :     /* -------------------------------------------------------------------- */
    1700             :     /*      Increment values.                                               */
    1701             :     /* -------------------------------------------------------------------- */
    1702          79 :     *pnOffset += nTREDataSize + 11;
    1703             : 
    1704          79 :     return bOK;
    1705             : }
    1706             : 
    1707             : /************************************************************************/
    1708             : /*                      NITFWriteTREsFromOptions()                      */
    1709             : /************************************************************************/
    1710             : 
    1711             : /* nOffsetHDL might be the offset of the XHDL or IXSHDL field */
    1712             : static bool
    1713         122 : NITFWriteTREsFromOptions(VSILFILE *fp, vsi_l_offset nOffsetHDL, int *pnOffset,
    1714             :                          CSLConstList papszOptions, const char *pszTREPrefix,
    1715             :                          GDALOffsetPatcher::OffsetPatcher *offsetPatcher)
    1716             : 
    1717             : {
    1718             :     int bIgnoreBLOCKA =
    1719         122 :         CSLFetchNameValue(papszOptions, "BLOCKA_BLOCK_COUNT") != nullptr;
    1720             :     int iOption;
    1721             :     const bool bReserveSpaceForTREOverflow =
    1722         122 :         CSLFetchNameValue(papszOptions, "RESERVE_SPACE_FOR_TRE_OVERFLOW") !=
    1723         122 :         nullptr;
    1724             : 
    1725         122 :     if (papszOptions == nullptr)
    1726           0 :         return true;
    1727             : 
    1728        1001 :     for (iOption = 0; papszOptions[iOption] != nullptr; iOption++)
    1729             :     {
    1730             :         const char *pszEscapedContents;
    1731             :         char *pszUnescapedContents;
    1732             :         char *pszTREName;
    1733             :         int nContentLength;
    1734             :         const char *pszSpace;
    1735         879 :         int bIsHex = FALSE;
    1736         879 :         int nTREPrefixLen = static_cast<int>(strlen(pszTREPrefix));
    1737             : 
    1738         879 :         if (!EQUALN(papszOptions[iOption], pszTREPrefix, nTREPrefixLen))
    1739         752 :             continue;
    1740             : 
    1741         128 :         if (STARTS_WITH_CI(papszOptions[iOption] + nTREPrefixLen, "BLOCKA=") &&
    1742             :             bIgnoreBLOCKA)
    1743           1 :             continue;
    1744             : 
    1745         127 :         if (STARTS_WITH_CI(papszOptions[iOption] + nTREPrefixLen, "HEX/"))
    1746             :         {
    1747           5 :             bIsHex = TRUE;
    1748           5 :             nTREPrefixLen += 4;
    1749             :         }
    1750             : 
    1751             :         /* We do no longer use CPLParseNameValue() as it removes leading spaces
    1752             :          */
    1753             :         /* from the value (see #3088) */
    1754         127 :         pszSpace = strchr(papszOptions[iOption] + nTREPrefixLen, '=');
    1755         127 :         if (pszSpace == nullptr)
    1756             :         {
    1757           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1758             :                      "Could not parse creation options %s",
    1759           0 :                      papszOptions[iOption] + nTREPrefixLen);
    1760           0 :             return false;
    1761             :         }
    1762             : 
    1763         127 :         pszTREName = CPLStrdup(papszOptions[iOption] + nTREPrefixLen);
    1764         127 :         pszTREName[std::min<size_t>(
    1765         127 :             6, pszSpace - (papszOptions[iOption] + nTREPrefixLen))] = '\0';
    1766         127 :         pszEscapedContents = pszSpace + 1;
    1767             : 
    1768         127 :         pszUnescapedContents = CPLUnescapeString(
    1769             :             pszEscapedContents, &nContentLength, CPLES_BackslashQuotable);
    1770             : 
    1771         127 :         if (bIsHex)
    1772             :         {
    1773             :             int i;
    1774             :             char pszSubStr[3];
    1775             : 
    1776           5 :             if (nContentLength % 2)
    1777             :             {
    1778           0 :                 CPLError(
    1779             :                     CE_Failure, CPLE_AppDefined,
    1780             :                     "Could not parse creation options %s: invalid hex data",
    1781           0 :                     papszOptions[iOption] + nTREPrefixLen);
    1782           0 :                 CPLFree(pszTREName);
    1783           0 :                 CPLFree(pszUnescapedContents);
    1784           0 :                 return false;
    1785             :             }
    1786             : 
    1787           5 :             nContentLength = nContentLength / 2;
    1788        1388 :             for (i = 0; i < nContentLength; i++)
    1789             :             {
    1790        1383 :                 CPLStrlcpy(pszSubStr, pszUnescapedContents + 2 * i, 3);
    1791        1383 :                 reinterpret_cast<unsigned char *>(pszUnescapedContents)[i] =
    1792        1383 :                     static_cast<unsigned char>(strtoul(pszSubStr, nullptr, 16));
    1793             :             }
    1794           5 :             pszUnescapedContents[nContentLength] = '\0';
    1795             :         }
    1796             : 
    1797         127 :         if (!NITFWriteTRE(fp, nOffsetHDL, pnOffset, pszTREName,
    1798             :                           pszUnescapedContents, nContentLength, offsetPatcher))
    1799             :         {
    1800           0 :             CPLFree(pszTREName);
    1801           0 :             CPLFree(pszUnescapedContents);
    1802           0 :             return false;
    1803             :         }
    1804             : 
    1805         127 :         CPLFree(pszTREName);
    1806         127 :         CPLFree(pszUnescapedContents);
    1807             :     }
    1808             : 
    1809         122 :     if (bReserveSpaceForTREOverflow)
    1810             :     {
    1811             :         /* --------------------------------------------------------------------
    1812             :          */
    1813             :         /*      Update XHDL or IXSHDL. */
    1814             :         /* --------------------------------------------------------------------
    1815             :          */
    1816             :         int nOldOffset;
    1817             :         char szTemp[6];
    1818           2 :         bool bOK = VSIFSeekL(fp, nOffsetHDL, SEEK_SET) == 0;
    1819           2 :         bOK &= VSIFReadL(szTemp, 1, 5, fp) == 5;
    1820           2 :         szTemp[5] = 0;
    1821           2 :         nOldOffset = atoi(szTemp);
    1822             : 
    1823           2 :         if (nOldOffset == 0)
    1824             :         {
    1825           2 :             PLACE(nOffsetHDL, IXSHDL, "00003");
    1826             : 
    1827           2 :             PLACE(nOffsetHDL + 5, IXSOFL, "000");
    1828           2 :             *pnOffset += 3;
    1829             :         }
    1830             : 
    1831           2 :         return bOK;
    1832             :     }
    1833             : 
    1834         120 :     return true;
    1835             : }
    1836             : 
    1837             : /************************************************************************/
    1838             : /*                          NITFWriteBLOCKA()                           */
    1839             : /************************************************************************/
    1840             : 
    1841           3 : static bool NITFWriteBLOCKA(VSILFILE *fp, vsi_l_offset nOffsetIXSHDL,
    1842             :                             int *pnOffset, CSLConstList papszOptions)
    1843             : 
    1844             : {
    1845             :     static const char *const apszFields[] = {
    1846             :         "BLOCK_INSTANCE", "0",     "2",    "N_GRAY",        "2",  "5",
    1847             :         "L_LINES",        "7",     "5",    "LAYOVER_ANGLE", "12", "3",
    1848             :         "SHADOW_ANGLE",   "15",    "3",    "BLANKS",        "18", "16",
    1849             :         "FRLC_LOC",       "34",    "21",   "LRLC_LOC",      "55", "21",
    1850             :         "LRFC_LOC",       "76",    "21",   "FRFC_LOC",      "97", "21",
    1851             :         nullptr,          nullptr, nullptr};
    1852             :     int nBlockCount =
    1853           3 :         atoi(CSLFetchNameValue(papszOptions, "BLOCKA_BLOCK_COUNT"));
    1854             :     int iBlock;
    1855             : 
    1856             :     /* ==================================================================== */
    1857             :     /*      Loop over all the blocks we have metadata for.                  */
    1858             :     /* ==================================================================== */
    1859           6 :     for (iBlock = 1; iBlock <= nBlockCount; iBlock++)
    1860             :     {
    1861             :         char szBLOCKA[123];
    1862             :         int iField;
    1863             : 
    1864             :         /* --------------------------------------------------------------------
    1865             :          */
    1866             :         /*      Write all fields. */
    1867             :         /* --------------------------------------------------------------------
    1868             :          */
    1869          33 :         for (iField = 0; apszFields[iField * 3] != nullptr; iField++)
    1870             :         {
    1871             :             char szFullFieldName[64];
    1872          30 :             int iStart = atoi(apszFields[iField * 3 + 1]);
    1873          30 :             int iSize = atoi(apszFields[iField * 3 + 2]);
    1874             :             const char *pszValue;
    1875             : 
    1876          30 :             snprintf(szFullFieldName, sizeof(szFullFieldName), "BLOCKA_%s_%02d",
    1877          30 :                      apszFields[iField * 3 + 0], iBlock);
    1878             : 
    1879          30 :             pszValue = CSLFetchNameValue(papszOptions, szFullFieldName);
    1880          30 :             if (pszValue == nullptr)
    1881           6 :                 pszValue = "";
    1882             : 
    1883          30 :             if (iSize - static_cast<int>(strlen(pszValue)) < 0)
    1884             :             {
    1885           0 :                 CPLError(
    1886             :                     CE_Failure, CPLE_AppDefined,
    1887             :                     "Too much data for %s. Got %d bytes, max allowed is %d",
    1888           0 :                     szFullFieldName, static_cast<int>(strlen(pszValue)), iSize);
    1889           0 :                 return false;
    1890             :             }
    1891             : 
    1892             :             /* Right align value and left pad with spaces */
    1893          30 :             memset(szBLOCKA + iStart, ' ', iSize);
    1894             :             /* unsigned is always >= 0 */
    1895             :             /* memcpy( szBLOCKA + iStart +
    1896             :              * std::max((size_t)0,iSize-strlen(pszValue)), */
    1897          30 :             memcpy(szBLOCKA + iStart +
    1898          30 :                        (iSize - static_cast<int>(strlen(pszValue))),
    1899             :                    pszValue, strlen(pszValue));
    1900             :         }
    1901             : 
    1902             :         // required field - semantics unknown.
    1903           3 :         memcpy(szBLOCKA + 118, "010.0", 5);
    1904             : 
    1905           3 :         if (!NITFWriteTRE(fp, nOffsetIXSHDL, pnOffset, "BLOCKA", szBLOCKA, 123,
    1906             :                           nullptr))
    1907           0 :             return false;
    1908             :     }
    1909             : 
    1910           3 :     return true;
    1911             : }
    1912             : 
    1913             : /************************************************************************/
    1914             : /*                       NITFCollectSegmentInfo()                       */
    1915             : /*                                                                      */
    1916             : /*      Collect the information about a set of segments of a            */
    1917             : /*      particular type from the NITF file header, and add them to      */
    1918             : /*      the segment list in the NITFFile object.                        */
    1919             : /************************************************************************/
    1920             : 
    1921        4686 : static int NITFCollectSegmentInfo(NITFFile *psFile, int nFileHeaderLen,
    1922             :                                   int nOffset, const char szType[3],
    1923             :                                   int nHeaderLenSize, int nDataLenSize,
    1924             :                                   GUIntBig *pnNextData)
    1925             : 
    1926             : {
    1927             :     char szTemp[12];
    1928             :     int nCount, nSegDefSize, iSegment;
    1929             : 
    1930             :     /* -------------------------------------------------------------------- */
    1931             :     /*      Get the segment count, and grow the segmentinfo array           */
    1932             :     /*      accordingly.                                                    */
    1933             :     /* -------------------------------------------------------------------- */
    1934        4686 :     if (nFileHeaderLen < nOffset + 3)
    1935             :     {
    1936           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1937             :                  "Not enough bytes to read segment count");
    1938           0 :         return -1;
    1939             :     }
    1940             : 
    1941        4686 :     NITFGetField(szTemp, psFile->pachHeader, nOffset, 3);
    1942        4686 :     nCount = atoi(szTemp);
    1943             : 
    1944        4686 :     if (nCount <= 0)
    1945        3732 :         return nOffset + 3;
    1946             : 
    1947         954 :     nSegDefSize = nCount * (nHeaderLenSize + nDataLenSize);
    1948         954 :     if (nFileHeaderLen < nOffset + 3 + nSegDefSize)
    1949             :     {
    1950           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1951             :                  "Not enough bytes to read segment info");
    1952           1 :         return -1;
    1953             :     }
    1954             : 
    1955         953 :     if (psFile->pasSegmentInfo == nullptr)
    1956         777 :         psFile->pasSegmentInfo = static_cast<NITFSegmentInfo *>(
    1957         777 :             CPLMalloc(sizeof(NITFSegmentInfo) * nCount));
    1958             :     else
    1959         176 :         psFile->pasSegmentInfo = static_cast<NITFSegmentInfo *>(CPLRealloc(
    1960         176 :             psFile->pasSegmentInfo,
    1961         176 :             sizeof(NITFSegmentInfo) * (psFile->nSegmentCount + nCount)));
    1962             : 
    1963             :     /* -------------------------------------------------------------------- */
    1964             :     /*      Collect detailed about segment.                                 */
    1965             :     /* -------------------------------------------------------------------- */
    1966       10934 :     for (iSegment = 0; iSegment < nCount; iSegment++)
    1967             :     {
    1968        9981 :         NITFSegmentInfo *psInfo =
    1969        9981 :             psFile->pasSegmentInfo + psFile->nSegmentCount;
    1970             : 
    1971        9981 :         psInfo->nDLVL = -1;
    1972        9981 :         psInfo->nALVL = -1;
    1973        9981 :         psInfo->nLOC_R = -1;
    1974        9981 :         psInfo->nLOC_C = -1;
    1975        9981 :         psInfo->nCCS_R = -1;
    1976        9981 :         psInfo->nCCS_C = -1;
    1977             : 
    1978        9981 :         psInfo->hAccess = nullptr;
    1979        9981 :         strncpy(psInfo->szSegmentType, szType, sizeof(psInfo->szSegmentType));
    1980        9981 :         psInfo->szSegmentType[sizeof(psInfo->szSegmentType) - 1] = '\0';
    1981             : 
    1982       19962 :         psInfo->nSegmentHeaderSize = atoi(NITFGetField(
    1983        9981 :             szTemp, psFile->pachHeader,
    1984        9981 :             nOffset + 3 + iSegment * (nHeaderLenSize + nDataLenSize),
    1985             :             nHeaderLenSize));
    1986        9981 :         if (strchr(szTemp, '-') !=
    1987             :             nullptr) /* Avoid negative values being mapped
    1988             :                                             to huge unsigned values */
    1989             :         {
    1990           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1991             :                      "Invalid segment header size : %s", szTemp);
    1992           0 :             return -1;
    1993             :         }
    1994             : 
    1995        9981 :         if (strcmp(szType, "DE") == 0 && psInfo->nSegmentHeaderSize == 207)
    1996             :         {
    1997             :             /* DMAAC A.TOC files have a wrong header size. It says 207 but it is
    1998             :              * 209 really */
    1999           0 :             psInfo->nSegmentHeaderSize = 209;
    2000             :         }
    2001             : 
    2002        9981 :         psInfo->nSegmentSize = CPLScanUIntBig(
    2003        9981 :             NITFGetField(szTemp, psFile->pachHeader,
    2004        9981 :                          nOffset + 3 +
    2005        9981 :                              iSegment * (nHeaderLenSize + nDataLenSize) +
    2006             :                              nHeaderLenSize,
    2007             :                          nDataLenSize),
    2008             :             nDataLenSize);
    2009        9981 :         if (strchr(szTemp, '-') !=
    2010             :             nullptr) /* Avoid negative values being mapped
    2011             :                                             to huge unsigned values */
    2012             :         {
    2013           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid segment size : %s",
    2014             :                      szTemp);
    2015           0 :             return -1;
    2016             :         }
    2017             : 
    2018        9981 :         psInfo->nSegmentHeaderStart = *pnNextData;
    2019        9981 :         psInfo->nSegmentStart = *pnNextData + psInfo->nSegmentHeaderSize;
    2020             : 
    2021        9981 :         *pnNextData += (psInfo->nSegmentHeaderSize + psInfo->nSegmentSize);
    2022        9981 :         psFile->nSegmentCount++;
    2023             :     }
    2024             : 
    2025         953 :     return nOffset + nSegDefSize + 3;
    2026             : }
    2027             : 
    2028             : /************************************************************************/
    2029             : /*                            NITFGetField()                            */
    2030             : /*                                                                      */
    2031             : /*      Copy a field from a passed in header buffer into a temporary    */
    2032             : /*      buffer and zero terminate it.                                   */
    2033             : /************************************************************************/
    2034             : 
    2035      687495 : char *NITFGetField(char *pszTarget, const char *pszSource, int nStart,
    2036             :                    int nLength)
    2037             : 
    2038             : {
    2039      687495 :     memcpy(pszTarget, pszSource + nStart, nLength);
    2040      687495 :     pszTarget[nLength] = '\0';
    2041             : 
    2042      687495 :     return pszTarget;
    2043             : }
    2044             : 
    2045             : /************************************************************************/
    2046             : /*                            NITFFindTRE()                             */
    2047             : /************************************************************************/
    2048             : 
    2049       80553 : const char *NITFFindTRE(const char *pszTREData, int nTREBytes,
    2050             :                         const char *pszTag, int *pnFoundTRESize)
    2051             : 
    2052             : {
    2053             :     char szTemp[100];
    2054             : 
    2055       80553 :     while (nTREBytes >= 11)
    2056             :     {
    2057       14908 :         int nThisTRESize = atoi(NITFGetField(szTemp, pszTREData, 6, 5));
    2058       14908 :         if (nThisTRESize < 0)
    2059             :         {
    2060           0 :             NITFGetField(szTemp, pszTREData, 0, 6);
    2061           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2062             :                      "Invalid size (%d) for TRE %s", nThisTRESize, szTemp);
    2063           0 :             return nullptr;
    2064             :         }
    2065       14908 :         if (nTREBytes - 11 < nThisTRESize)
    2066             :         {
    2067           0 :             NITFGetField(szTemp, pszTREData, 0, 6);
    2068           0 :             if (STARTS_WITH_CI(szTemp, "RPFIMG"))
    2069             :             {
    2070             :                 /* See #3848 */
    2071           0 :                 CPLDebug("NITF",
    2072             :                          "Adjusting RPFIMG TRE size from %d to %d, which is "
    2073             :                          "the remaining size",
    2074             :                          nThisTRESize, nTREBytes - 11);
    2075           0 :                 nThisTRESize = nTREBytes - 11;
    2076             :             }
    2077             :             else
    2078             :             {
    2079           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2080             :                          "Cannot read %s TRE. Not enough bytes : remaining %d, "
    2081             :                          "expected %d",
    2082             :                          szTemp, nTREBytes - 11, nThisTRESize);
    2083           0 :                 return nullptr;
    2084             :             }
    2085             :         }
    2086             : 
    2087       14908 :         if (EQUALN(pszTREData, pszTag, 6))
    2088             :         {
    2089         321 :             if (pnFoundTRESize != nullptr)
    2090         321 :                 *pnFoundTRESize = nThisTRESize;
    2091             : 
    2092         321 :             return pszTREData + 11;
    2093             :         }
    2094             : 
    2095       14587 :         nTREBytes -= (nThisTRESize + 11);
    2096       14587 :         pszTREData += (nThisTRESize + 11);
    2097             :     }
    2098             : 
    2099       65645 :     return nullptr;
    2100             : }
    2101             : 
    2102             : /************************************************************************/
    2103             : /*                         NITFFindTREByIndex()                         */
    2104             : /************************************************************************/
    2105             : 
    2106        1118 : const char *NITFFindTREByIndex(const char *pszTREData, int nTREBytes,
    2107             :                                const char *pszTag, int nTreIndex,
    2108             :                                int *pnFoundTRESize)
    2109             : 
    2110             : {
    2111             :     char szTemp[100];
    2112             : 
    2113        1118 :     while (nTREBytes >= 11)
    2114             :     {
    2115         369 :         int nThisTRESize = atoi(NITFGetField(szTemp, pszTREData, 6, 5));
    2116         369 :         if (nThisTRESize < 0)
    2117             :         {
    2118           0 :             NITFGetField(szTemp, pszTREData, 0, 6);
    2119           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2120             :                      "Invalid size (%d) for TRE %s", nThisTRESize, szTemp);
    2121           0 :             return nullptr;
    2122             :         }
    2123         369 :         if (nTREBytes - 11 < nThisTRESize)
    2124             :         {
    2125           0 :             NITFGetField(szTemp, pszTREData, 0, 6);
    2126           0 :             if (STARTS_WITH_CI(szTemp, "RPFIMG"))
    2127             :             {
    2128             :                 /* See #3848 */
    2129           0 :                 CPLDebug("NITF",
    2130             :                          "Adjusting RPFIMG TRE size from %d to %d, which is "
    2131             :                          "the remaining size",
    2132             :                          nThisTRESize, nTREBytes - 11);
    2133           0 :                 nThisTRESize = nTREBytes - 11;
    2134             :             }
    2135             :             else
    2136             :             {
    2137           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2138             :                          "Cannot read %s TRE. Not enough bytes : remaining %d, "
    2139             :                          "expected %d",
    2140             :                          szTemp, nTREBytes - 11, nThisTRESize);
    2141           0 :                 return nullptr;
    2142             :             }
    2143             :         }
    2144             : 
    2145         369 :         if (EQUALN(pszTREData, pszTag, 6))
    2146             :         {
    2147          49 :             if (nTreIndex <= 0)
    2148             :             {
    2149          26 :                 if (pnFoundTRESize != nullptr)
    2150          26 :                     *pnFoundTRESize = nThisTRESize;
    2151             : 
    2152          26 :                 return pszTREData + 11;
    2153             :             }
    2154             : 
    2155             :             /* Found a previous one - skip it ... */
    2156          23 :             nTreIndex--;
    2157             :         }
    2158             : 
    2159         343 :         nTREBytes -= (nThisTRESize + 11);
    2160         343 :         pszTREData += (nThisTRESize + 11);
    2161             :     }
    2162             : 
    2163         749 :     return nullptr;
    2164             : }
    2165             : 
    2166             : /************************************************************************/
    2167             : /*                        NITFExtractMetadata()                         */
    2168             : /************************************************************************/
    2169             : 
    2170      260154 : static void NITFExtractAndRecodeMetadata(char ***ppapszMetadata,
    2171             :                                          const char *pachHeader, int nStart,
    2172             :                                          int nLength, const char *pszName,
    2173             :                                          const char *pszSrcEncoding)
    2174             : 
    2175             : {
    2176             :     char szWork[400];
    2177             :     char *pszWork;
    2178             :     char *pszRecode;
    2179             : 
    2180      260154 :     if (nLength <= 0)
    2181           0 :         return;
    2182             : 
    2183      260154 :     if (nLength >= static_cast<int>(sizeof(szWork) - 1))
    2184           1 :         pszWork = static_cast<char *>(CPLMalloc(nLength + 1));
    2185             :     else
    2186      260153 :         pszWork = szWork;
    2187             : 
    2188             :     /* trim white space */
    2189     3226670 :     while (nLength > 0 && pachHeader[nStart + nLength - 1] == ' ')
    2190     2966510 :         nLength--;
    2191             : 
    2192      260154 :     memcpy(pszWork, pachHeader + nStart, nLength);
    2193      260154 :     pszWork[nLength] = '\0';
    2194             : 
    2195      260154 :     if (strcmp(pszSrcEncoding, CPL_ENC_UTF8) != 0)
    2196             :     {
    2197      256457 :         pszRecode = CPLRecode(pszWork, pszSrcEncoding, CPL_ENC_UTF8);
    2198      256457 :         *ppapszMetadata = CSLSetNameValue(*ppapszMetadata, pszName, pszRecode);
    2199      256457 :         CPLFree(pszRecode);
    2200             :     }
    2201             :     else
    2202             :     {
    2203        3697 :         *ppapszMetadata = CSLSetNameValue(*ppapszMetadata, pszName, pszWork);
    2204             :     }
    2205             : 
    2206      260154 :     if (szWork != pszWork)
    2207           1 :         CPLFree(pszWork);
    2208             : }
    2209             : 
    2210      256457 : void NITFExtractMetadata(char ***ppapszMetadata, const char *pachHeader,
    2211             :                          int nStart, int nLength, const char *pszName)
    2212             : 
    2213             : {
    2214      256457 :     NITFExtractAndRecodeMetadata(ppapszMetadata, pachHeader, nStart, nLength,
    2215             :                                  pszName, CPL_ENC_ISO8859_1);
    2216      256457 : }
    2217             : 
    2218             : /************************************************************************/
    2219             : /*        NITF_WGS84_Geocentric_Latitude_To_Geodetic_Latitude()         */
    2220             : /*                                                                      */
    2221             : /*      The input is a geocentric latitude in degrees.  The output      */
    2222             : /*      is a geodetic latitude in degrees.                              */
    2223             : /************************************************************************/
    2224             : 
    2225             : /*
    2226             :  * "The angle L' is called "geocentric latitude" and is defined as the
    2227             :  * angle between the equatorial plane and the radius from the geocenter.
    2228             :  *
    2229             :  * The angle L is called "geodetic latitude" and is defined as the angle
    2230             :  * between the equatorial plane and the normal to the surface of the
    2231             :  * ellipsoid.  The word "latitude" usually means geodetic latitude.  This
    2232             :  * is the basis for most of the maps and charts we use.  The normal to the
    2233             :  * surface is the direction that a plumb bob would hang were it not for
    2234             :  * local anomalies in the earth's gravitational field."
    2235             :  */
    2236             : 
    2237           0 : double NITF_WGS84_Geocentric_Latitude_To_Geodetic_Latitude(double dfLat)
    2238             : 
    2239             : {
    2240             :     /* WGS84 Ellipsoid */
    2241           0 :     const double a = 6378137.0;
    2242           0 :     const double b = 6356752.3142;
    2243             : 
    2244             :     /* convert to radians */
    2245           0 :     dfLat = dfLat * M_PI / 180.0;
    2246             : 
    2247             :     /* convert to geodetic */
    2248           0 :     dfLat = atan(((a * a) / (b * b)) * tan(dfLat));
    2249             : 
    2250             :     /* convert back to degrees */
    2251           0 :     dfLat = dfLat * 180.0 / M_PI;
    2252             : 
    2253           0 :     return dfLat;
    2254             : }
    2255             : 
    2256             : /************************************************************************/
    2257             : /*                         NITFGetSeriesInfo()                          */
    2258             : /************************************************************************/
    2259             : 
    2260             : /* From
    2261             :  * http://trac.osgeo.org/gdal/attachment/ticket/5353/MIL-STD-2411_1_CHG-3.pdf */
    2262             : static const NITFSeries nitfSeries[] = {
    2263             :     {"A1", "CM", "1:10K", "Combat Charts (1:10K)", "CADRG"},
    2264             :     {"A2", "CM", "1:25K", "Combat Charts (1:25K)", "CADRG"},
    2265             :     {"A3", "CM", "1:50K", "Combat Charts (1:50K)", "CADRG"},
    2266             :     {"A4", "CM", "1:100K", "Combat Charts (1:100K)", "CADRG"},
    2267             :     {"AT", "ATC", "1:200K", "Series 200 Air Target Chart", "CADRG"},
    2268             :     {"C1", "CG", "1:10000", "City Graphics", "CADRG"},
    2269             :     {"C2", "CG", "1:10560", "City Graphics", "CADRG"},
    2270             :     {"C3", "CG", "1:11000", "City Graphics", "CADRG"},
    2271             :     {"C4", "CG", "1:11800", "City Graphics", "CADRG"},
    2272             :     {"C5", "CG", "1:12000", "City Graphics", "CADRG"},
    2273             :     {"C6", "CG", "1:12500", "City Graphics", "CADRG"},
    2274             :     {"C7", "CG", "1:12800", "City Graphics", "CADRG"},
    2275             :     {"C8", "CG", "1:14000", "City Graphics", "CADRG"},
    2276             :     {"C9", "CG", "1:14700", "City Graphics", "CADRG"},
    2277             :     {"CA", "CG", "1:15000", "City Graphics", "CADRG"},
    2278             :     {"CB", "CG", "1:15500", "City Graphics", "CADRG"},
    2279             :     {"CC", "CG", "1:16000", "City Graphics", "CADRG"},
    2280             :     {"CD", "CG", "1:16666", "City Graphics", "CADRG"},
    2281             :     {"CE", "CG", "1:17000", "City Graphics", "CADRG"},
    2282             :     {"CF", "CG", "1:17500", "City Graphics", "CADRG"},
    2283             :     {"CG", "CG", "Various", "City Graphics", "CADRG"},
    2284             :     {"CH", "CG", "1:18000", "City Graphics", "CADRG"},
    2285             :     {"CJ", "CG", "1:20000", "City Graphics", "CADRG"},
    2286             :     {"CK", "CG", "1:21000", "City Graphics", "CADRG"},
    2287             :     {"CL", "CG", "1:21120", "City Graphics", "CADRG"},
    2288             :     {"CM", "CM", "Various", "Combat Charts", "CADRG"},
    2289             :     {"CN", "CG", "1:22000", "City Graphics", "CADRG"},
    2290             :     {"CO", "CO", "Various", "Coastal Charts", "CADRG"},
    2291             :     {"CP", "CG", "1:23000", "City Graphics", "CADRG"},
    2292             :     {"CQ", "CG", "1:25000", "City Graphics", "CADRG"},
    2293             :     {"CR", "CG", "1:26000", "City Graphics", "CADRG"},
    2294             :     {"CS", "CG", "1:35000", "City Graphics", "CADRG"},
    2295             :     {"CT", "CG", "1:36000", "City Graphics", "CADRG"},
    2296             :     {"D1", "", "100m", "Elevation Data from DTED level 1", "CDTED"},
    2297             :     {"D2", "", "30m", "Elevation Data from DTED level 2", "CDTED"},
    2298             :     {"EG", "NARC", "1:11,000,000", "North Atlantic Route Chart", "CADRG"},
    2299             :     {"ES", "SEC", "1:500K", "VFR Sectional", "CADRG"},
    2300             :     {"ET", "SEC", "1:250K", "VFR Sectional Inserts", "CADRG"},
    2301             :     {"F1", "TFC-1", "1:250K", "Transit Flying Chart (TBD #1)", "CADRG"},
    2302             :     {"F2", "TFC-2", "1:250K", "Transit Flying Chart (TBD #2)", "CADRG"},
    2303             :     {"F3", "TFC-3", "1:250K", "Transit Flying Chart (TBD #3)", "CADRG"},
    2304             :     {"F4", "TFC-4", "1:250K", "Transit Flying Chart (TBD #4)", "CADRG"},
    2305             :     {"F5", "TFC-5", "1:250K", "Transit Flying Chart (TBD #5)", "CADRG"},
    2306             :     {"GN", "GNC", "1:5M", "Global Navigation Chart", "CADRG"},
    2307             :     {"HA", "HA", "Various", "Harbor and Approach Charts", "CADRG"},
    2308             :     {"I1", "", "10m", "Imagery, 10 meter resolution", "CIB"},
    2309             :     {"I2", "", "5m", "Imagery, 5 meter resolution", "CIB"},
    2310             :     {"I3", "", "2m", "Imagery, 2 meter resolution", "CIB"},
    2311             :     {"I4", "", "1m", "Imagery, 1 meter resolution", "CIB"},
    2312             :     {"I5", "", ".5m", "Imagery, .5 (half) meter resolution", "CIB"},
    2313             :     {"IV", "", "Various > 10m", "Imagery, greater than 10 meter resolution",
    2314             :      "CIB"},
    2315             :     {"JA", "JOG-A", "1:250K", "Joint Operation Graphic - Air", "CADRG"},
    2316             :     {"JG", "JOG", "1:250K", "Joint Operation Graphic", "CADRG"},
    2317             :     {"JN", "JNC", "1:2M", "Jet Navigation Chart", "CADRG"},
    2318             :     {"JO", "OPG", "1:250K", "Operational Planning Graphic", "CADRG"},
    2319             :     {"JR", "JOG-R", "1:250K", "Joint Operation Graphic - Radar", "CADRG"},
    2320             :     {"K1", "ICM", "1:8K", "Image City Maps", "CADRG"},
    2321             :     {"K2", "ICM", "1:10K", "Image City Maps", "CADRG"},
    2322             :     {"K3", "ICM", "1:10560", "Image City Maps", "CADRG"},
    2323             :     {"K7", "ICM", "1:12500", "Image City Maps", "CADRG"},
    2324             :     {"K8", "ICM", "1:12800", "Image City Maps", "CADRG"},
    2325             :     {"KB", "ICM", "1:15K", "Image City Maps", "CADRG"},
    2326             :     {"KE", "ICM", "1:16666", "Image City Maps", "CADRG"},
    2327             :     {"KM", "ICM", "1:21120", "Image City Maps", "CADRG"},
    2328             :     {"KR", "ICM", "1:25K", "Image City Maps", "CADRG"},
    2329             :     {"KS", "ICM", "1:26K", "Image City Maps", "CADRG"},
    2330             :     {"KU", "ICM", "1:36K", "Image City Maps", "CADRG"},
    2331             :     {"L1", "LFC-1", "1:500K", "Low Flying Chart (TBD #1)", "CADRG"},
    2332             :     {"L2", "LFC-2", "1:500K", "Low Flying Chart (TBD #2)", "CADRG"},
    2333             :     {"L3", "LFC-3", "1:500K", "Low Flying Chart (TBD #3)", "CADRG"},
    2334             :     {"L4", "LFC-4", "1:500K", "Low Flying Chart (TBD #4)", "CADRG"},
    2335             :     {"L5", "LFC-5", "1:500K", "Low Flying Chart (TBD #5)", "CADRG"},
    2336             :     {"LF", "LFC-FR (Day)", "1:500K", "Low Flying Chart (Day) - Host Nation",
    2337             :      "CADRG"},
    2338             :     {"LN", "LN (Night)", "1:500K", "Low Flying Chart (Night) - Host Nation",
    2339             :      "CADRG"},
    2340             :     {"M1", "MIM", "Various", "Military Installation Maps (TBD #1)", "CADRG"},
    2341             :     {"M2", "MIM", "Various", "Military Installation Maps (TBD #2)", "CADRG"},
    2342             :     {"MH", "MIM", "1:25K", "Military Installation Maps", "CADRG"},
    2343             :     {"MI", "MIM", "1:50K", "Military Installation Maps", "CADRG"},
    2344             :     {"MJ", "MIM", "1:100K", "Military Installation Maps", "CADRG"},
    2345             :     {"MM", "", "Various", "(Miscellaneous Maps & Charts)", "CADRG"},
    2346             :     {"OA", "OPAREA", "Various", "Naval Range Operation Area Chart", "CADRG"},
    2347             :     {"OH", "VHRC", "1:1M", "VFR Helicopter Route Chart", "CADRG"},
    2348             :     {"ON", "ONC", "1:1M", "Operational Navigation Chart", "CADRG"},
    2349             :     {"OW", "WAC", "1:1M", "High Flying Chart - Host Nation", "CADRG"},
    2350             :     {"P1", "", "1:25K", "Special Military Map - Overlay", "CADRG"},
    2351             :     {"P2", "", "1:25K", "Special Military Purpose", "CADRG"},
    2352             :     {"P3", "", "1:25K", "Special Military Purpose", "CADRG"},
    2353             :     {"P4", "", "1:25K", "Special Military Purpose", "CADRG"},
    2354             :     {"P5", "", "1:50K", "Special Military Map - Overlay", "CADRG"},
    2355             :     {"P6", "", "1:50K", "Special Military Purpose", "CADRG"},
    2356             :     {"P7", "", "1:50K", "Special Military Purpose", "CADRG"},
    2357             :     {"P8", "", "1:50K", "Special Military Purpose", "CADRG"},
    2358             :     {"P9", "", "1:100K", "Special Military Map - Overlay", "CADRG"},
    2359             :     {"PA", "", "1:100K", "Special Military Purpose", "CADRG"},
    2360             :     {"PB", "", "1:100K", "Special Military Purpose", "CADRG"},
    2361             :     {"PC", "", "1:100K", "Special Military Purpose", "CADRG"},
    2362             :     {"PD", "", "1:250K", "Special Military Map - Overlay", "CADRG"},
    2363             :     {"PE", "", "1:250K", "Special Military Purpose", "CADRG"},
    2364             :     {"PF", "", "1:250K", "Special Military Purpose", "CADRG"},
    2365             :     {"PG", "", "1:250K", "Special Military Purpose", "CADRG"},
    2366             :     {"PH", "", "1:500K", "Special Military Map - Overlay", "CADRG"},
    2367             :     {"PI", "", "1:500K", "Special Military Purpose", "CADRG"},
    2368             :     {"PJ", "", "1:500K", "Special Military Purpose", "CADRG"},
    2369             :     {"PK", "", "1:500K", "Special Military Purpose", "CADRG"},
    2370             :     {"PL", "", "1:1M", "Special Military Map - Overlay", "CADRG"},
    2371             :     {"PM", "", "1:1M", "Special Military Purpose", "CADRG"},
    2372             :     {"PN", "", "1:1M", "Special Military Purpose", "CADRG"},
    2373             :     {"PO", "", "1:1M", "Special Military Purpose", "CADRG"},
    2374             :     {"PP", "", "1:2M", "Special Military Map - Overlay", "CADRG"},
    2375             :     {"PQ", "", "1:2M", "Special Military Purpose", "CADRG"},
    2376             :     {"PR", "", "1:2M", "Special Military Purpose", "CADRG"},
    2377             :     {"PS", "", "1:5M", "Special Military Map - Overlay", "CADRG"},
    2378             :     {"PT", "", "1:5M", "Special Military Purpose", "CADRG"},
    2379             :     {"PU", "", "1:5M", "Special Military Purpose", "CADRG"},
    2380             :     {"PV", "", "1:5M", "Special Military Purpose", "CADRG"},
    2381             :     {"R1", "", "1:50K", "Range Charts", "CADRG"},
    2382             :     {"R2", "", "1:100K", "Range Charts", "CADRG"},
    2383             :     {"R3", "", "1:250K", "Range Charts", "CADRG"},
    2384             :     {"R4", "", "1:500K", "Range Charts", "CADRG"},
    2385             :     {"R5", "", "1:1M", "Range Charts", "CADRG"},
    2386             :     {"RC", "RGS-100", "1:100K", "Russian General Staff Maps", "CADRG"},
    2387             :     {"RL", "RGS-50", "1:50K", "Russian General Staff Maps", "CADRG"},
    2388             :     {"RR", "RGS-200", "1:200K", "Russian General Staff Maps", "CADRG"},
    2389             :     {"RV", "Riverine", "1:50K", "Riverine Map 1:50,000 scale", "CADRG"},
    2390             :     {"TC", "TLM 100", "1:100K", "Topographic Line Map 1:100,000 scale",
    2391             :      "CADRG"},
    2392             :     {"TF", "TFC (Day)", "1:250K", "Transit Flying Chart (Day)", "CADRG"},
    2393             :     {"TL", "TLM50", "1:50K", "Topographic Line Map", "CADRG"},
    2394             :     {"TN", "TFC (Night)", "1:250K",
    2395             :      "Transit Flying Chart (Night) - Host Nation", "CADRG"},
    2396             :     {"TP", "TPC", "1:500K", "Tactical Pilotage Chart", "CADRG"},
    2397             :     {"TQ", "TLM24", "1:24K", "Topographic Line Map 1:24,000 scale", "CADRG"},
    2398             :     {"TR", "TLM200", "1:200K", "Topographic Line Map 1:200,000 scale", "CADRG"},
    2399             :     {"TT", "TLM25", "1:25K", "Topographic Line Map 1:25,000 scale", "CADRG"},
    2400             :     {"UL", "TLM50 - Other", "1:50K",
    2401             :      "Topographic Line Map (other 1:50,000 scale)", "CADRG"},
    2402             :     {"V1", "Inset HRC", "1:50", "Helicopter Route Chart Inset", "CADRG"},
    2403             :     {"V2", "Inset HRC", "1:62500", "Helicopter Route Chart Inset", "CADRG"},
    2404             :     {"V3", "Inset HRC", "1:90K", "Helicopter Route Chart Inset", "CADRG"},
    2405             :     {"V4", "Inset HRC", "1:250K", "Helicopter Route Chart Inset", "CADRG"},
    2406             :     {"VH", "HRC", "1:125K", "Helicopter Route Chart", "CADRG"},
    2407             :     {"VN", "VNC", "1:500K", "Visual Navigation Charts", "CADRG"},
    2408             :     {"VT", "VTAC", "1:250K", "VFR Terminal Area Chart", "CADRG"},
    2409             :     {"WA", "", "1:250K", "IFR Enroute Low", "CADRG"},
    2410             :     {"WB", "", "1:500K", "IFR Enroute Low", "CADRG"},
    2411             :     {"WC", "", "1:750K", "IFR Enroute Low", "CADRG"},
    2412             :     {"WD", "", "1:1M", "IFR Enroute Low", "CADRG"},
    2413             :     {"WE", "", "1:1.5M", "IFR Enroute Low", "CADRG"},
    2414             :     {"WF", "", "1:2M", "IFR Enroute Low", "CADRG"},
    2415             :     {"WG", "", "1:2.5M", "IFR Enroute Low", "CADRG"},
    2416             :     {"WH", "", "1:3M", "IFR Enroute Low", "CADRG"},
    2417             :     {"WI", "", "1:3.5M", "IFR Enroute Low", "CADRG"},
    2418             :     {"WK", "", "1:4M", "IFR Enroute Low", "CADRG"},
    2419             :     {"XD", "", "1:1M", "IFR Enroute High", "CADRG"},
    2420             :     {"XE", "", "1:1.5M", "IFR Enroute High", "CADRG"},
    2421             :     {"XF", "", "1:2M", "IFR Enroute High", "CADRG"},
    2422             :     {"XG", "", "1:2.5M", "IFR Enroute High", "CADRG"},
    2423             :     {"XH", "", "1:3M", "IFR Enroute High", "CADRG"},
    2424             :     {"XI", "", "1:3.5M", "IFR Enroute High", "CADRG"},
    2425             :     {"XJ", "", "1:4M", "IFR Enroute High", "CADRG"},
    2426             :     {"XK", "", "1:4.5M", "IFR Enroute High", "CADRG"},
    2427             :     {"Y9", "", "1:16.5M", "IFR Enroute Area", "CADRG"},
    2428             :     {"YA", "", "1:250K", "IFR Enroute Area", "CADRG"},
    2429             :     {"YB", "", "1:500K", "IFR Enroute Area", "CADRG"},
    2430             :     {"YC", "", "1:750K", "IFR Enroute Area", "CADRG"},
    2431             :     {"YD", "", "1:1M", "IFR Enroute Area", "CADRG"},
    2432             :     {"YE", "", "1:1.5M", "IFR Enroute Area", "CADRG"},
    2433             :     {"YF", "", "1:2M", "IFR Enroute Area", "CADRG"},
    2434             :     {"YI", "", "1:3.5M", "IFR Enroute Area", "CADRG"},
    2435             :     {"YJ", "", "1:4M", "IFR Enroute Area", "CADRG"},
    2436             :     {"YZ", "", "1:12M", "IFR Enroute Area", "CADRG"},
    2437             :     {"ZA", "", "1:250K", "IFR Enroute High/Low", "CADRG"},
    2438             :     {"ZB", "", "1:500K", "IFR Enroute High/Low", "CADRG"},
    2439             :     {"ZC", "", "1:750K", "IFR Enroute High/Low", "CADRG"},
    2440             :     {"ZD", "", "1:1M", "IFR Enroute High/Low", "CADRG"},
    2441             :     {"ZE", "", "1:1.5M", "IFR Enroute High/Low", "CADRG"},
    2442             :     {"ZF", "", "1:2M", "IFR Enroute High/Low", "CADRG"},
    2443             :     {"ZG", "", "1:2.5M", "IFR Enroute High/Low", "CADRG"},
    2444             :     {"ZH", "", "1:3M", "IFR Enroute High/Low", "CADRG"},
    2445             :     {"ZI", "", "1:3.5M", "IFR Enroute High/Low", "CADRG"},
    2446             :     {"ZJ", "", "1:4M", "IFR Enroute High/Low", "CADRG"},
    2447             :     {"ZK", "", "1:4.5M", "IFR Enroute High/Low", "CADRG"},
    2448             :     {"ZT", "", "1:9M", "IFR Enroute High/Low", "CADRG"},
    2449             :     {"ZV", "", "1:10M", "IFR Enroute High/Low", "CADRG"},
    2450             :     {"ZZ", "", "1:12M", "IFR Enroute High/Low", "CADRG"}};
    2451             : 
    2452           0 : const NITFSeries *NITFGetRPFSeriesInfoFromIndex(int nIdx)
    2453             : {
    2454           0 :     if (nIdx >= 0 && static_cast<size_t>(nIdx) < CPL_ARRAYSIZE(nitfSeries))
    2455           0 :         return &nitfSeries[nIdx];
    2456           0 :     return nullptr;
    2457             : }
    2458             : 
    2459         811 : const NITFSeries *NITFGetRPFSeriesInfoFromCode(const char *pszCode)
    2460             : {
    2461      120866 :     for (const auto &series : nitfSeries)
    2462             :     {
    2463      120318 :         if (EQUAL(pszCode, series.code))
    2464             :         {
    2465         263 :             return &series;
    2466             :         }
    2467             :     }
    2468         548 :     return nullptr;
    2469             : }
    2470             : 
    2471          66 : bool NITFIsKnownRPFDataSeriesCode(const char *pszCode,
    2472             :                                   const char *pszProductType)
    2473             : {
    2474          66 :     return std::find_if(std::begin(nitfSeries), std::end(nitfSeries),
    2475        5220 :                         [pszCode, &pszProductType](const auto &sEntry)
    2476             :                         {
    2477        5154 :                             return EQUAL(pszCode, sEntry.code) &&
    2478          66 :                                    (!pszProductType ||
    2479        5154 :                                     EQUAL(pszProductType, sEntry.rpfDataType));
    2480          66 :                         }) != std::end(nitfSeries);
    2481             : }
    2482             : 
    2483          70 : int NITFGetScaleFromScaleResolution(const char *scaleResolution)
    2484             : {
    2485          70 :     int nVal = 0;
    2486          70 :     if (STARTS_WITH(scaleResolution, "1:"))
    2487             :     {
    2488          16 :         nVal = atoi(scaleResolution + strlen("1:"));
    2489          16 :         if (strchr(scaleResolution, 'K'))
    2490           6 :             nVal *= 1000;
    2491          10 :         else if (strchr(scaleResolution, 'M'))
    2492          10 :             nVal *= 1000 * 1000;
    2493             :     }
    2494          70 :     return nVal;
    2495             : }
    2496             : 
    2497             : /* See 24111CN1.pdf paragraph 5.1.4 */
    2498         774 : const NITFSeries *NITFGetSeriesInfo(const char *pszFilename)
    2499             : {
    2500             :     int i;
    2501         774 :     char seriesCode[3] = {0, 0, 0};
    2502         774 :     if (pszFilename == nullptr)
    2503           0 :         return nullptr;
    2504        5409 :     for (i = static_cast<int>(strlen(pszFilename)) - 1; i >= 0; i--)
    2505             :     {
    2506        5357 :         if (pszFilename[i] == '.')
    2507             :         {
    2508         727 :             if (i < static_cast<int>(strlen(pszFilename)) - 3)
    2509             :             {
    2510         722 :                 seriesCode[0] = pszFilename[i + 1];
    2511         722 :                 seriesCode[1] = pszFilename[i + 2];
    2512         722 :                 return NITFGetRPFSeriesInfoFromCode(seriesCode);
    2513             :             }
    2514             :         }
    2515             :     }
    2516          52 :     return nullptr;
    2517             : }
    2518             : 
    2519             : /************************************************************************/
    2520             : /*                       NITFCollectAttachments()                       */
    2521             : /*                                                                      */
    2522             : /*      Collect attachment, display level and location info into the    */
    2523             : /*      segmentinfo structures.                                         */
    2524             : /************************************************************************/
    2525             : 
    2526         446 : int NITFCollectAttachments(NITFFile *psFile)
    2527             : 
    2528             : {
    2529             :     int iSegment;
    2530             : 
    2531             :     /* ==================================================================== */
    2532             :     /*      Loop over all segments.                                         */
    2533             :     /* ==================================================================== */
    2534        8974 :     for (iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++)
    2535             :     {
    2536        8528 :         NITFSegmentInfo *psSegInfo = psFile->pasSegmentInfo + iSegment;
    2537             : 
    2538             :         /* --------------------------------------------------------------------
    2539             :          */
    2540             :         /*      For image segments, we use the normal image access stuff. */
    2541             :         /* --------------------------------------------------------------------
    2542             :          */
    2543        8528 :         if (EQUAL(psSegInfo->szSegmentType, "IM"))
    2544             :         {
    2545        8441 :             NITFImage *psImage = NITFImageAccess(psFile, iSegment);
    2546        8441 :             if (psImage == nullptr)
    2547           0 :                 return FALSE;
    2548             : 
    2549        8441 :             psSegInfo->nDLVL = psImage->nIDLVL;
    2550        8441 :             psSegInfo->nALVL = psImage->nIALVL;
    2551        8441 :             psSegInfo->nLOC_R = psImage->nILOCRow;
    2552        8441 :             psSegInfo->nLOC_C = psImage->nILOCColumn;
    2553             :         }
    2554             :         /* --------------------------------------------------------------------
    2555             :          */
    2556             :         /*      For graphic file we need to process the header. */
    2557             :         /* --------------------------------------------------------------------
    2558             :          */
    2559          87 :         else if (EQUAL(psSegInfo->szSegmentType, "SY") ||
    2560          87 :                  EQUAL(psSegInfo->szSegmentType, "GR"))
    2561             :         {
    2562             :             char achSubheader[298];
    2563             :             int nSTYPEOffset;
    2564             :             char szTemp[100];
    2565             : 
    2566             :             /* --------------------------------------------------------------------
    2567             :              */
    2568             :             /*      Load the graphic subheader. */
    2569             :             /* --------------------------------------------------------------------
    2570             :              */
    2571           2 :             if (VSIFSeekL(psFile->fp, psSegInfo->nSegmentHeaderStart,
    2572           4 :                           SEEK_SET) != 0 ||
    2573           2 :                 VSIFReadL(achSubheader, 1, sizeof(achSubheader), psFile->fp) <
    2574             :                     258)
    2575             :             {
    2576           0 :                 CPLError(CE_Warning, CPLE_FileIO,
    2577             :                          "Failed to read graphic subheader at " CPL_FRMT_GUIB
    2578             :                          ".",
    2579             :                          psSegInfo->nSegmentHeaderStart);
    2580           0 :                 continue;
    2581             :             }
    2582             : 
    2583             :             // NITF 2.0. (also works for NITF 2.1)
    2584           2 :             nSTYPEOffset = 200;
    2585           2 :             if (STARTS_WITH_CI(achSubheader + 193, "999998"))
    2586           0 :                 nSTYPEOffset += 40;
    2587             : 
    2588             :             /* --------------------------------------------------------------------
    2589             :              */
    2590             :             /*      Report some standard info. */
    2591             :             /* --------------------------------------------------------------------
    2592             :              */
    2593           2 :             psSegInfo->nDLVL =
    2594           2 :                 atoi(NITFGetField(szTemp, achSubheader, nSTYPEOffset + 14, 3));
    2595           2 :             psSegInfo->nALVL =
    2596           2 :                 atoi(NITFGetField(szTemp, achSubheader, nSTYPEOffset + 17, 3));
    2597           2 :             psSegInfo->nLOC_R =
    2598           2 :                 atoi(NITFGetField(szTemp, achSubheader, nSTYPEOffset + 20, 5));
    2599           2 :             psSegInfo->nLOC_C =
    2600           2 :                 atoi(NITFGetField(szTemp, achSubheader, nSTYPEOffset + 25, 5));
    2601             :         }
    2602             :     }
    2603             : 
    2604         446 :     return TRUE;
    2605             : }
    2606             : 
    2607             : /************************************************************************/
    2608             : /*                      NITFReconcileAttachments()                      */
    2609             : /*                                                                      */
    2610             : /*      Generate the CCS location information for all the segments      */
    2611             : /*      if possible.                                                    */
    2612             : /************************************************************************/
    2613             : 
    2614         446 : int NITFReconcileAttachments(NITFFile *psFile)
    2615             : 
    2616             : {
    2617             :     int iSegment;
    2618         446 :     int bSuccess = TRUE;
    2619         446 :     int bMadeProgress = FALSE;
    2620             : 
    2621        8974 :     for (iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++)
    2622             :     {
    2623        8528 :         NITFSegmentInfo *psSegInfo = psFile->pasSegmentInfo + iSegment;
    2624             :         int iOther;
    2625             : 
    2626             :         // already processed?
    2627        8528 :         if (psSegInfo->nCCS_R != -1)
    2628           0 :             continue;
    2629             : 
    2630             :         // unattached segments are straight forward.
    2631        8528 :         if (psSegInfo->nALVL < 1)
    2632             :         {
    2633        8526 :             psSegInfo->nCCS_R = psSegInfo->nLOC_R;
    2634        8526 :             psSegInfo->nCCS_C = psSegInfo->nLOC_C;
    2635        8526 :             if (psSegInfo->nCCS_R != -1)
    2636        8441 :                 bMadeProgress = TRUE;
    2637        8526 :             continue;
    2638             :         }
    2639             : 
    2640             :         // Loc for segment to which we are attached.
    2641           2 :         for (iOther = 0; iOther < psFile->nSegmentCount; iOther++)
    2642             :         {
    2643           2 :             NITFSegmentInfo *psOtherSegInfo = psFile->pasSegmentInfo + iOther;
    2644             : 
    2645           2 :             if (psSegInfo->nALVL == psOtherSegInfo->nDLVL)
    2646             :             {
    2647           2 :                 if (psOtherSegInfo->nCCS_R != -1)
    2648             :                 {
    2649           2 :                     psSegInfo->nCCS_R =
    2650           2 :                         psOtherSegInfo->nLOC_R + psSegInfo->nLOC_R;
    2651           2 :                     psSegInfo->nCCS_C =
    2652           2 :                         psOtherSegInfo->nLOC_C + psSegInfo->nLOC_C;
    2653           2 :                     if (psSegInfo->nCCS_R != -1)
    2654           2 :                         bMadeProgress = TRUE;
    2655             :                 }
    2656             :                 else
    2657             :                 {
    2658           0 :                     bSuccess = FALSE;
    2659             :                 }
    2660           2 :                 break;
    2661             :             }
    2662             :         }
    2663             : 
    2664           2 :         if (iOther == psFile->nSegmentCount)
    2665           0 :             bSuccess = FALSE;
    2666             :     }
    2667             : 
    2668             :     /* -------------------------------------------------------------------- */
    2669             :     /*      If succeeded or made no progress then return our success        */
    2670             :     /*      flag.  Otherwise make another pass, hopefully filling in        */
    2671             :     /*      more values.                                                    */
    2672             :     /* -------------------------------------------------------------------- */
    2673         446 :     if (bSuccess || !bMadeProgress)
    2674         446 :         return bSuccess;
    2675             :     else
    2676           0 :         return NITFReconcileAttachments(psFile);
    2677             : }
    2678             : 
    2679             : /************************************************************************/
    2680             : /*                         NITFFindValFromEnd()                         */
    2681             : /************************************************************************/
    2682             : 
    2683        2416 : static const char *NITFFindValFromEnd(CSLConstList papszMD, int nMDSize,
    2684             :                                       const char *pszVar,
    2685             :                                       const char * /*pszDefault*/)
    2686             : {
    2687        2416 :     const size_t nVarLen = strlen(pszVar);
    2688       95954 :     for (int nIter = nMDSize - 1; nIter >= 0; nIter--)
    2689             :     {
    2690       94522 :         if (strncmp(papszMD[nIter], pszVar, nVarLen) == 0 &&
    2691         986 :             papszMD[nIter][nVarLen] == '=')
    2692         984 :             return papszMD[nIter] + nVarLen + 1;
    2693             :     }
    2694        1432 :     return nullptr;
    2695             : }
    2696             : 
    2697             : /************************************************************************/
    2698             : /*                        NITFFindValRecursive()                        */
    2699             : /************************************************************************/
    2700             : 
    2701         972 : static const char *NITFFindValRecursive(CSLConstList papszMD, int nMDSize,
    2702             :                                         const char *pszMDPrefix,
    2703             :                                         const char *pszVar)
    2704             : {
    2705        1944 :     std::string osMDItemName = std::string(pszMDPrefix).append(pszVar);
    2706             :     const char *pszCondVal =
    2707         972 :         NITFFindValFromEnd(papszMD, nMDSize, osMDItemName.c_str(), nullptr);
    2708             : 
    2709         972 :     if (pszCondVal == nullptr)
    2710             :     {
    2711             :         /* Needed for SENSRB */
    2712             :         /* See https://github.com/OSGeo/gdal/issues/1520 */
    2713             :         /* If the condition variable is not found at this level, */
    2714             :         /* try to research it at upper levels by shortening on _ */
    2715             :         /* separators */
    2716        1016 :         std::string osMDPrefixShortened(pszMDPrefix);
    2717         508 :         auto pos = osMDPrefixShortened.rfind('_');
    2718         508 :         if (pos != std::string::npos)
    2719             :         {
    2720         508 :             osMDPrefixShortened.resize(pos);
    2721         508 :             pos = osMDPrefixShortened.rfind('_');
    2722             :         }
    2723        1432 :         while (pos != std::string::npos)
    2724             :         {
    2725        1429 :             osMDPrefixShortened.resize(pos);
    2726        1429 :             osMDItemName = osMDPrefixShortened;
    2727        1429 :             osMDItemName += '_';
    2728        1429 :             osMDItemName += pszVar;
    2729        1429 :             pszCondVal = NITFFindValFromEnd(papszMD, nMDSize,
    2730             :                                             osMDItemName.c_str(), nullptr);
    2731        1429 :             if (pszCondVal)
    2732         505 :                 break;
    2733         924 :             pos = osMDPrefixShortened.rfind('_');
    2734             :         }
    2735             : 
    2736         508 :         if (!pszCondVal)
    2737           3 :             pszCondVal = NITFFindValFromEnd(papszMD, nMDSize, pszVar, nullptr);
    2738             :     }
    2739             : 
    2740        1944 :     return pszCondVal;
    2741             : }
    2742             : 
    2743             : /************************************************************************/
    2744             : /*                              CSLSplit()                              */
    2745             : /************************************************************************/
    2746             : 
    2747           6 : static char **CSLSplit(const char *pszStr, const char *pszSplitter)
    2748             : {
    2749           6 :     char **papszRet = nullptr;
    2750           6 :     const char *pszIter = pszStr;
    2751             :     while (TRUE)
    2752             :     {
    2753          36 :         const char *pszNextSplitter = strstr(pszIter, pszSplitter);
    2754          36 :         if (pszNextSplitter == nullptr)
    2755             :         {
    2756           6 :             papszRet = CSLAddString(papszRet, pszIter);
    2757           6 :             break;
    2758             :         }
    2759          30 :         size_t nLen = static_cast<size_t>(pszNextSplitter - pszIter);
    2760          30 :         char *pszToken = static_cast<char *>(CPLMalloc(nLen + 1));
    2761          30 :         memcpy(pszToken, pszIter, nLen);
    2762          30 :         pszToken[nLen] = 0;
    2763          30 :         papszRet = CSLAddString(papszRet, pszToken);
    2764          30 :         CPLFree(pszToken);
    2765          30 :         pszIter = pszNextSplitter + strlen(pszSplitter);
    2766          30 :     }
    2767           6 :     return papszRet;
    2768             : }
    2769             : 
    2770             : /************************************************************************/
    2771             : /*                          NITFEvaluateCond()                          */
    2772             : /************************************************************************/
    2773             : 
    2774         742 : static int NITFEvaluateCond(const char *pszCond, char **papszMD, int *pnMDSize,
    2775             :                             const char *pszMDPrefix,
    2776             :                             const char *pszDESOrTREKind,
    2777             :                             const char *pszDESOrTREName)
    2778             : {
    2779         742 :     const char *pszAnd = strstr(pszCond, " AND ");
    2780         742 :     const char *pszOr = strstr(pszCond, " OR ");
    2781         742 :     if (pszAnd && pszOr)
    2782             :     {
    2783           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    2784             :                  "Unsupported if condition in %s %s in XML resource: %s. "
    2785             :                  "AND and OR conditions cannot be used at the same time",
    2786             :                  pszDESOrTREName, pszDESOrTREKind, pszCond);
    2787           0 :         return -1;
    2788             :     }
    2789             : 
    2790         742 :     int nRet = 0;
    2791             :     const char *pszOperator;
    2792         742 :     if (pszAnd)
    2793             :     {
    2794           0 :         char **papszTokens = CSLSplit(pszCond, " AND ");
    2795           0 :         for (char **papszIter = papszTokens; *papszIter; ++papszIter)
    2796             :         {
    2797           0 :             nRet = NITFEvaluateCond(*papszIter, papszMD, pnMDSize, pszMDPrefix,
    2798             :                                     pszDESOrTREKind, pszDESOrTREName);
    2799             :             // exit early as soon as we have a negative evaluation (or error)
    2800           0 :             if (nRet != 1)
    2801           0 :                 break;
    2802             :         }
    2803           0 :         CSLDestroy(papszTokens);
    2804             :     }
    2805         742 :     else if (pszOr)
    2806             :     {
    2807           6 :         char **papszTokens = CSLSplit(pszCond, " OR ");
    2808          15 :         for (char **papszIter = papszTokens; *papszIter; ++papszIter)
    2809             :         {
    2810          15 :             nRet = NITFEvaluateCond(*papszIter, papszMD, pnMDSize, pszMDPrefix,
    2811             :                                     pszDESOrTREKind, pszDESOrTREName);
    2812             :             // exit early as soon as we have a positive evaluation (or error)
    2813          15 :             if (nRet != 0)
    2814           6 :                 break;
    2815             :         }
    2816           6 :         CSLDestroy(papszTokens);
    2817             :     }
    2818         736 :     else if ((pszOperator = strchr(pszCond, '=')) != nullptr)
    2819             :     {
    2820             :         char *pszCondVar =
    2821         388 :             static_cast<char *>(CPLMalloc(pszOperator - pszCond + 1));
    2822         388 :         const char *pszCondExpectedVal = pszOperator + 1;
    2823             :         const char *pszCondVal;
    2824         388 :         int bTestEqual = FALSE;
    2825         388 :         int bTestNotEqual = FALSE;
    2826         388 :         int bTestGreaterOrEqual = FALSE;
    2827         388 :         memcpy(pszCondVar, pszCond, pszOperator - pszCond);
    2828         388 :         if (pszOperator - pszCond > 1 &&
    2829         388 :             pszCondVar[pszOperator - pszCond - 1] == '!')
    2830             :         {
    2831          64 :             bTestNotEqual = TRUE;
    2832          64 :             pszCondVar[pszOperator - pszCond - 1] = '\0';
    2833             :         }
    2834         324 :         else if (pszOperator - pszCond > 1 &&
    2835         324 :                  pszCondVar[pszOperator - pszCond - 1] == '>')
    2836             :         {
    2837           0 :             bTestGreaterOrEqual = TRUE;
    2838           0 :             pszCondVar[pszOperator - pszCond - 1] = '\0';
    2839             :         }
    2840             :         else
    2841             :         {
    2842         324 :             bTestEqual = TRUE;
    2843             :         }
    2844         388 :         pszCondVar[pszOperator - pszCond] = '\0';
    2845             :         pszCondVal =
    2846         388 :             NITFFindValRecursive(papszMD, *pnMDSize, pszMDPrefix, pszCondVar);
    2847         388 :         if (pszCondVal == nullptr)
    2848             :         {
    2849           0 :             CPLDebug("NITF", "Cannot find if cond variable %s", pszCondVar);
    2850             :         }
    2851         388 :         else if ((bTestEqual && strcmp(pszCondVal, pszCondExpectedVal) == 0) ||
    2852          64 :                  (bTestNotEqual &&
    2853         309 :                   strcmp(pszCondVal, pszCondExpectedVal) != 0) ||
    2854           0 :                  (bTestGreaterOrEqual &&
    2855           0 :                   strcmp(pszCondVal, pszCondExpectedVal) >= 0))
    2856             :         {
    2857         111 :             nRet = 1;
    2858             :         }
    2859         388 :         CPLFree(pszCondVar);
    2860             :     }
    2861         348 :     else if ((pszOperator = strchr(pszCond, ':')) != nullptr)
    2862             :     {
    2863             :         char *pszCondVar =
    2864         348 :             static_cast<char *>(CPLMalloc(pszOperator - pszCond + 1));
    2865         348 :         const char *pszCondTestBit = pszOperator + 1;
    2866             :         const char *pszCondVal;
    2867         348 :         memcpy(pszCondVar, pszCond, pszOperator - pszCond);
    2868         348 :         pszCondVar[pszOperator - pszCond] = '\0';
    2869             :         pszCondVal =
    2870         348 :             NITFFindValRecursive(papszMD, *pnMDSize, pszMDPrefix, pszCondVar);
    2871         348 :         if (pszCondVal == nullptr)
    2872             :         {
    2873           0 :             CPLDebug("NITF", "Cannot find if cond variable %s", pszCondVar);
    2874             :         }
    2875         348 :         else if (strtoul(pszCondVal, nullptr, 10) &
    2876         348 :                  (1U << static_cast<unsigned>(atoi(pszCondTestBit))))
    2877             :         {
    2878          51 :             nRet = 1;
    2879             :         }
    2880         348 :         CPLFree(pszCondVar);
    2881             :     }
    2882             :     else
    2883             :     {
    2884           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    2885             :                  "Invalid if construct in %s %s in XML resource: %s. "
    2886             :                  "invalid 'cond' attribute",
    2887             :                  pszDESOrTREName, pszDESOrTREKind, pszCond);
    2888           0 :         return -1;
    2889             :     }
    2890         742 :     return nRet;
    2891             : }
    2892             : 
    2893             : /************************************************************************/
    2894             : /*                 NITFGenericMetadataReadTREInternal()                 */
    2895             : /************************************************************************/
    2896             : 
    2897        3579 : static char **NITFGenericMetadataReadTREInternal(
    2898             :     char **papszMD, int *pnMDSize, int *pnMDAlloc, CPLXMLNode *psOutXMLNode,
    2899             :     const char *pszDESOrTREKind, const char *pszDESOrTREName,
    2900             :     const char *pachTRE, int nTRESize, const CPLXMLNode *psTreNode,
    2901             :     int *pnTreOffset, const char *pszMDPrefix, bool bValidate, VSILFILE *fp,
    2902             :     std::map<NITFLocId, const CPLXMLNode *> &oMapLocIdToXML, int *pbError)
    2903             : {
    2904        3579 :     const bool bRPFIMGOrDES =
    2905        6900 :         psOutXMLNode &&
    2906        3321 :         (EQUAL(pszDESOrTREName, "RPFIMG") || EQUAL(pszDESOrTREName, "RPFDES"));
    2907        3579 :     if (bRPFIMGOrDES && oMapLocIdToXML.empty())
    2908             :     {
    2909             : #define LOCATION_ENTRY(x)                                                      \
    2910             :     {                                                                          \
    2911             :         std::string(#x), LID_##x                                               \
    2912             :     }
    2913             :         static const std::map<std::string, NITFLocId> goMapLocationNameToID = {
    2914           2 :             LOCATION_ENTRY(HeaderComponent),
    2915           2 :             LOCATION_ENTRY(LocationComponent),
    2916           2 :             LOCATION_ENTRY(CoverageSectionSubheader),
    2917           2 :             LOCATION_ENTRY(CompressionSectionSubsection),
    2918           2 :             LOCATION_ENTRY(CompressionLookupSubsection),
    2919           2 :             LOCATION_ENTRY(CompressionParameterSubsection),
    2920           2 :             LOCATION_ENTRY(ColorGrayscaleSectionSubheader),
    2921           2 :             LOCATION_ENTRY(ColormapSubsection),
    2922           2 :             LOCATION_ENTRY(ImageDescriptionSubheader),
    2923           2 :             LOCATION_ENTRY(ImageDisplayParametersSubheader),
    2924           2 :             LOCATION_ENTRY(MaskSubsection),
    2925           2 :             LOCATION_ENTRY(ColorConverterSubsection),
    2926           2 :             LOCATION_ENTRY(SpatialDataSubsection),
    2927           2 :             LOCATION_ENTRY(AttributeSectionSubheader),
    2928           2 :             LOCATION_ENTRY(AttributeSubsection),
    2929           2 :             LOCATION_ENTRY(ExplicitArealCoverageTable),
    2930           2 :             LOCATION_ENTRY(RelatedImagesSectionSubheader),
    2931           2 :             LOCATION_ENTRY(RelatedImagesSubsection),
    2932           2 :             LOCATION_ENTRY(ReplaceUpdateSectionSubheader),
    2933           2 :             LOCATION_ENTRY(ReplaceUpdateTable),
    2934           2 :             LOCATION_ENTRY(BoundaryRectangleSectionSubheader),
    2935           2 :             LOCATION_ENTRY(BoundaryRectangleTable),
    2936           2 :             LOCATION_ENTRY(FrameFileIndexSectionSubHeader),
    2937           2 :             LOCATION_ENTRY(FrameFileIndexSubsection),
    2938           2 :             LOCATION_ENTRY(ColorTableIndexSectionSubheader),
    2939           2 :             LOCATION_ENTRY(ColorTableIndexRecord),
    2940          58 :         };
    2941             : #undef LOCATION_ENTRY
    2942             : 
    2943           6 :         for (const CPLXMLNode *psIter = psTreNode->psChild;
    2944         156 :              psIter != nullptr && *pbError == FALSE; psIter = psIter->psNext)
    2945             :         {
    2946         150 :             if (psIter->eType == CXT_Element && psIter->pszValue != nullptr &&
    2947         132 :                 strcmp(psIter->pszValue, "rpf_component") == 0)
    2948             :             {
    2949          96 :                 const char *pszId = CPLGetXMLValue(psIter, "id", nullptr);
    2950          96 :                 if (pszId)
    2951             :                 {
    2952          96 :                     auto oIterMap = goMapLocationNameToID.find(pszId);
    2953          96 :                     if (oIterMap != goMapLocationNameToID.end())
    2954             :                     {
    2955          96 :                         oMapLocIdToXML[oIterMap->second] = psIter;
    2956             :                     }
    2957             :                     else
    2958             :                     {
    2959           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
    2960             :                                  "rpf_component id=%s unknown", pszId);
    2961             :                     }
    2962             :                 }
    2963             :             }
    2964             :         }
    2965             :     }
    2966             : 
    2967        3579 :     int nRPFLocationId = 0;
    2968        3579 :     vsi_l_offset nRPFLocationOffset = 0;
    2969        3579 :     uint32_t nRPFLocationSize = 0;
    2970             : 
    2971        3579 :     for (const CPLXMLNode *psIter = psTreNode->psChild;
    2972       18067 :          psIter != nullptr && *pbError == FALSE; psIter = psIter->psNext)
    2973             :     {
    2974       14490 :         if (psIter->eType == CXT_Element && psIter->pszValue != nullptr &&
    2975        6945 :             strcmp(psIter->pszValue, "field") == 0)
    2976             :         {
    2977        5803 :             const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
    2978             :             const char *pszLongName =
    2979        5803 :                 CPLGetXMLValue(psIter, "longname", nullptr);
    2980        5803 :             const char *pszLength = CPLGetXMLValue(psIter, "length", nullptr);
    2981        5803 :             const char *pszType = CPLGetXMLValue(psIter, "type", "string");
    2982        5803 :             const char *pszMinVal = CPLGetXMLValue(psIter, "minval", nullptr);
    2983        5803 :             const char *pszMaxVal = CPLGetXMLValue(psIter, "maxval", nullptr);
    2984        5803 :             int nLength = -1;
    2985        5803 :             if (pszLength != nullptr)
    2986        5767 :                 nLength = atoi(pszLength);
    2987             :             else
    2988             :             {
    2989             :                 const char *pszLengthVar =
    2990          36 :                     CPLGetXMLValue(psIter, "length_var", nullptr);
    2991          36 :                 if (pszLengthVar != nullptr)
    2992             :                 {
    2993             :                     // Preferably look for item at the same level as ours.
    2994          36 :                     const char *pszLengthValue = CSLFetchNameValue(
    2995             :                         papszMD, CPLSPrintf("%s%s", pszMDPrefix, pszLengthVar));
    2996          36 :                     if (pszLengthValue != nullptr)
    2997             :                     {
    2998          33 :                         nLength = atoi(pszLengthValue);
    2999             :                     }
    3000             :                     else
    3001             :                     {
    3002           3 :                         char **papszMDIter = papszMD;
    3003         191 :                         while (papszMDIter != nullptr &&
    3004         191 :                                *papszMDIter != nullptr)
    3005             :                         {
    3006         188 :                             if (strstr(*papszMDIter, pszLengthVar) != nullptr)
    3007             :                             {
    3008             :                                 const char *pszEqual =
    3009           3 :                                     strchr(*papszMDIter, '=');
    3010           3 :                                 if (pszEqual != nullptr)
    3011             :                                 {
    3012           3 :                                     nLength = atoi(pszEqual + 1);
    3013             :                                     // Voluntary missing break so as to find the
    3014             :                                     // "closest" item to ours in case it is not
    3015             :                                     // defined in the same level
    3016             :                                 }
    3017             :                             }
    3018         188 :                             papszMDIter++;
    3019             :                         }
    3020             :                     }
    3021             :                 }
    3022             :             }
    3023        5803 :             if (pszName != nullptr && nLength > 0)
    3024             :             {
    3025        5755 :                 char **papszTmp = nullptr;
    3026        5755 :                 char *pszValue = nullptr;
    3027             : 
    3028        5755 :                 if (*pnTreOffset + nLength > nTRESize)
    3029             :                 {
    3030           2 :                     *pbError = TRUE;
    3031           2 :                     CPLError(bValidate ? CE_Failure : CE_Warning,
    3032             :                              CPLE_AppDefined,
    3033             :                              "Not enough bytes when reading %s %s "
    3034             :                              "(at least %d needed, only %d available)",
    3035             :                              pszDESOrTREName, pszDESOrTREKind,
    3036           2 :                              *pnTreOffset + nLength, nTRESize);
    3037           2 :                     break;
    3038             :                 }
    3039             : 
    3040             :                 const std::string osMDItemName =
    3041        5753 :                     CPLSPrintf("%s%s", pszMDPrefix, pszName);
    3042             : 
    3043        5753 :                 if (strcmp(pszType, "IEEE754_Float32_BigEndian") == 0)
    3044             :                 {
    3045          15 :                     if (nLength == 4)
    3046             :                     {
    3047          15 :                         const size_t nBufferSize = 128;
    3048             :                         float f;
    3049          15 :                         memcpy(&f, pachTRE + *pnTreOffset, sizeof(f));
    3050          15 :                         CPL_MSBPTR32(&f);
    3051          15 :                         pszValue = static_cast<char *>(CPLMalloc(nBufferSize));
    3052          15 :                         CPLsnprintf(pszValue, nBufferSize, "%f", f);
    3053          15 :                         papszTmp = CSLSetNameValue(
    3054             :                             papszTmp, osMDItemName.c_str(), pszValue);
    3055             :                     }
    3056             :                     else
    3057             :                     {
    3058           0 :                         *pbError = TRUE;
    3059           0 :                         CPLError(bValidate ? CE_Failure : CE_Warning,
    3060             :                                  CPLE_AppDefined,
    3061             :                                  "IEEE754_Float32_BigEndian field must be 4 "
    3062             :                                  "bytes in %s %s",
    3063             :                                  pszDESOrTREName, pszDESOrTREKind);
    3064           0 :                         break;
    3065             :                     }
    3066             :                 }
    3067        5738 :                 else if (strcmp(pszType, "IEEE754_Float64_BigEndian") == 0)
    3068             :                 {
    3069          84 :                     if (nLength == 8)
    3070             :                     {
    3071             :                         double df;
    3072          84 :                         memcpy(&df, pachTRE + *pnTreOffset, sizeof(df));
    3073          84 :                         CPL_MSBPTR64(&df);
    3074          84 :                         const int nBufferSize = 24;
    3075          84 :                         pszValue = static_cast<char *>(CPLMalloc(nBufferSize));
    3076          84 :                         CPLsnprintf(pszValue, nBufferSize, "%.17g", df);
    3077          84 :                         papszTmp = CSLSetNameValue(
    3078             :                             papszTmp, osMDItemName.c_str(), pszValue);
    3079             :                     }
    3080             :                     else
    3081             :                     {
    3082           0 :                         *pbError = TRUE;
    3083           0 :                         CPLError(bValidate ? CE_Failure : CE_Warning,
    3084             :                                  CPLE_AppDefined,
    3085             :                                  "IEEE754_Float64_BigEndian field must be 8 "
    3086             :                                  "bytes in %s %s",
    3087             :                                  pszDESOrTREName, pszDESOrTREKind);
    3088           0 :                         break;
    3089             :                     }
    3090             :                 }
    3091        5654 :                 else if (strcmp(pszType, "UnsignedInt_BigEndian") == 0 ||
    3092        3747 :                          strcmp(pszType, "bitmask") == 0)
    3093             :                 {
    3094        1916 :                     if (nLength <= 8)
    3095             :                     {
    3096        1916 :                         const size_t nBufferSize = 21;
    3097        1916 :                         uint64_t nVal = 0;
    3098             :                         GByte byData;
    3099             : 
    3100             :                         int i;
    3101        8900 :                         for (i = 0; i < nLength; ++i)
    3102             :                         {
    3103        6984 :                             memcpy(&byData, pachTRE + *pnTreOffset + i, 1);
    3104        6984 :                             nVal += static_cast<uint64_t>(byData)
    3105        6984 :                                     << 8 * (nLength - i - 1);
    3106             :                         }
    3107             : 
    3108        1916 :                         pszValue = static_cast<char *>(CPLMalloc(nBufferSize));
    3109        1916 :                         CPLsnprintf(pszValue, nBufferSize, CPL_FRMT_GUIB,
    3110             :                                     static_cast<GUIntBig>(nVal));
    3111        1916 :                         papszTmp = CSLSetNameValue(
    3112             :                             papszTmp, osMDItemName.c_str(), pszValue);
    3113             :                     }
    3114             :                     else
    3115             :                     {
    3116           0 :                         *pbError = TRUE;
    3117           0 :                         CPLError(bValidate ? CE_Failure : CE_Warning,
    3118             :                                  CPLE_AppDefined,
    3119             :                                  "UnsignedInt/bitmask field must be <= 8 bytes "
    3120             :                                  "in %s %s",
    3121             :                                  pszDESOrTREName, pszDESOrTREKind);
    3122           0 :                         break;
    3123        1916 :                     }
    3124             :                 }
    3125        3738 :                 else if (strcmp(pszType, "ISO8859-1") == 0)
    3126             :                 {
    3127          41 :                     NITFExtractMetadata(&papszTmp, pachTRE, *pnTreOffset,
    3128             :                                         nLength, osMDItemName.c_str());
    3129             : 
    3130          41 :                     pszValue = CPLStrdup(
    3131             :                         CSLFetchNameValue(papszTmp, osMDItemName.c_str()));
    3132             :                 }
    3133             :                 else
    3134             :                 {
    3135        3697 :                     NITFExtractAndRecodeMetadata(
    3136             :                         &papszTmp, pachTRE, *pnTreOffset, nLength,
    3137             :                         osMDItemName.c_str(), CPL_ENC_UTF8);
    3138             : 
    3139        3697 :                     pszValue = CPLStrdup(strchr(papszTmp[0], '=') + 1);
    3140             :                 }
    3141             : 
    3142        5753 :                 if (papszTmp)
    3143             :                 {
    3144        5753 :                     if (bRPFIMGOrDES)
    3145             :                     {
    3146        1991 :                         const char *pszEqual = strchr(papszTmp[0], '=');
    3147        1991 :                         if (pszEqual)
    3148             :                         {
    3149        1991 :                             const int nIdx = CSLPartialFindString(
    3150             :                                 papszMD,
    3151        3982 :                                 std::string(papszTmp[0],
    3152        1991 :                                             (pszEqual - papszTmp[0]) + 1)
    3153             :                                     .c_str());
    3154        1991 :                             if (nIdx >= 0)
    3155             :                             {
    3156        1618 :                                 CPLFree(papszMD[nIdx]);
    3157        1618 :                                 papszMD[nIdx] = papszTmp[0];
    3158        1618 :                                 papszTmp[0] = nullptr;
    3159             :                             }
    3160             :                         }
    3161             :                     }
    3162        5753 :                     if (papszTmp[0])
    3163             :                     {
    3164        4135 :                         if (*pnMDSize + 1 >= *pnMDAlloc)
    3165             :                         {
    3166         177 :                             *pnMDAlloc = (*pnMDAlloc * 4 / 3) + 32;
    3167         177 :                             papszMD = static_cast<char **>(CPLRealloc(
    3168         177 :                                 papszMD, *pnMDAlloc * sizeof(char *)));
    3169             :                         }
    3170        4135 :                         papszMD[*pnMDSize] = papszTmp[0];
    3171        4135 :                         papszMD[(*pnMDSize) + 1] = nullptr;
    3172        4135 :                         (*pnMDSize)++;
    3173        4135 :                         papszTmp[0] = nullptr;
    3174             :                     }
    3175        5753 :                     CSLDestroy(papszTmp);
    3176             :                 }
    3177             : 
    3178        5753 :                 CPLXMLNode *psFieldNode = nullptr;
    3179        5753 :                 if (pszValue != nullptr && psOutXMLNode != nullptr)
    3180             :                 {
    3181             :                     CPLXMLNode *psNameNode;
    3182             :                     CPLXMLNode *psValueNode;
    3183             : 
    3184             :                     psFieldNode =
    3185        4951 :                         CPLCreateXMLNode(psOutXMLNode, CXT_Element, "field");
    3186             :                     psNameNode =
    3187        4951 :                         CPLCreateXMLNode(psFieldNode, CXT_Attribute, "name");
    3188             :                     psValueNode =
    3189        4951 :                         CPLCreateXMLNode(psFieldNode, CXT_Attribute, "value");
    3190        4951 :                     CPLCreateXMLNode(psNameNode, CXT_Text,
    3191        4951 :                                      (pszName[0] || pszLongName == nullptr)
    3192             :                                          ? pszName
    3193             :                                          : pszLongName);
    3194        4951 :                     CPLCreateXMLNode(psValueNode, CXT_Text, pszValue);
    3195             :                 }
    3196             : 
    3197        5753 :                 if (pszValue != nullptr)
    3198             :                 {
    3199        5753 :                     if (pszMinVal != nullptr)
    3200             :                     {
    3201         693 :                         bool bMinValConstraintOK = true;
    3202         693 :                         if (strcmp(pszType, "real") == 0)
    3203             :                         {
    3204         183 :                             bMinValConstraintOK =
    3205         183 :                                 CPLAtof(pszValue) >= CPLAtof(pszMinVal);
    3206             :                         }
    3207         510 :                         else if (strcmp(pszType, "integer") == 0)
    3208             :                         {
    3209         504 :                             bMinValConstraintOK = CPLAtoGIntBig(pszValue) >=
    3210         504 :                                                   CPLAtoGIntBig(pszMinVal);
    3211             :                         }
    3212         693 :                         if (!bMinValConstraintOK)
    3213             :                         {
    3214          68 :                             if (bValidate)
    3215             :                             {
    3216           2 :                                 CPLError(CE_Failure, CPLE_AppDefined,
    3217             :                                          "%s %s: minimum value constraint of "
    3218             :                                          "%s for %s=%s not met",
    3219             :                                          pszDESOrTREKind, pszDESOrTREName,
    3220             :                                          pszMinVal, pszName, pszValue);
    3221             :                             }
    3222          68 :                             if (psFieldNode)
    3223             :                             {
    3224          68 :                                 CPLCreateXMLElementAndValue(
    3225             :                                     psFieldNode,
    3226             :                                     bValidate ? "error" : "warning",
    3227             :                                     CPLSPrintf("Minimum value constraint of %s "
    3228             :                                                "not met",
    3229             :                                                pszMinVal));
    3230             :                             }
    3231             :                         }
    3232             :                     }
    3233        5753 :                     if (pszMaxVal != nullptr)
    3234             :                     {
    3235         660 :                         bool bMinValConstraintOK = true;
    3236         660 :                         if (strcmp(pszType, "real") == 0)
    3237             :                         {
    3238         165 :                             bMinValConstraintOK =
    3239         165 :                                 CPLAtof(pszValue) <= CPLAtof(pszMaxVal);
    3240             :                         }
    3241         495 :                         else if (strcmp(pszType, "integer") == 0)
    3242             :                         {
    3243         489 :                             bMinValConstraintOK = CPLAtoGIntBig(pszValue) <=
    3244         489 :                                                   CPLAtoGIntBig(pszMaxVal);
    3245             :                         }
    3246         660 :                         if (!bMinValConstraintOK)
    3247             :                         {
    3248           2 :                             if (bValidate)
    3249             :                             {
    3250           2 :                                 CPLError(CE_Failure, CPLE_AppDefined,
    3251             :                                          "%s %s: maximum value constraint of "
    3252             :                                          "%s for %s=%s not met",
    3253             :                                          pszDESOrTREKind, pszDESOrTREName,
    3254             :                                          pszMaxVal, pszName, pszValue);
    3255             :                             }
    3256           2 :                             if (psFieldNode)
    3257             :                             {
    3258           2 :                                 CPLCreateXMLElementAndValue(
    3259             :                                     psFieldNode,
    3260             :                                     bValidate ? "error" : "warning",
    3261             :                                     CPLSPrintf("Maximum value constraint of %s "
    3262             :                                                "not met",
    3263             :                                                pszMaxVal));
    3264             :                             }
    3265             :                         }
    3266             :                     }
    3267             :                 }
    3268             : 
    3269        5753 :                 if (bRPFIMGOrDES && pszValue != nullptr)
    3270             :                 {
    3271        1991 :                     if (EQUAL(pszName, "COMPONENT_ID"))
    3272             :                     {
    3273          54 :                         nRPFLocationId = atoi(pszValue);
    3274             :                     }
    3275        1937 :                     else if (EQUAL(pszName, "COMPONENT_LENGTH"))
    3276             :                     {
    3277          54 :                         nRPFLocationSize = static_cast<uint32_t>(
    3278          54 :                             strtoul(pszValue, nullptr, 10));
    3279             :                     }
    3280        1883 :                     else if (EQUAL(pszName, "COMPONENT_LOCATION"))
    3281             :                     {
    3282             :                         nRPFLocationOffset =
    3283          54 :                             std::strtoull(pszValue, nullptr, 10);
    3284             :                     }
    3285             :                 }
    3286             : 
    3287        5753 :                 CPLFree(pszValue);
    3288             : 
    3289       11506 :                 *pnTreOffset += nLength;
    3290             :             }
    3291          48 :             else if (nLength > 0)
    3292             :             {
    3293          48 :                 *pnTreOffset += nLength;
    3294             :             }
    3295             :             else
    3296             :             {
    3297           0 :                 *pbError = TRUE;
    3298           0 :                 CPLError(bValidate ? CE_Failure : CE_Warning, CPLE_AppDefined,
    3299             :                          "Invalid item construct in %s %s in XML resource",
    3300             :                          pszDESOrTREName, pszDESOrTREKind);
    3301           0 :                 break;
    3302        5801 :             }
    3303             :         }
    3304        8687 :         else if (psIter->eType == CXT_Element && psIter->pszValue != nullptr &&
    3305        1142 :                  strcmp(psIter->pszValue, "loop") == 0)
    3306             :         {
    3307         315 :             const char *pszCounter = CPLGetXMLValue(psIter, "counter", nullptr);
    3308             :             const char *pszIterations =
    3309         315 :                 CPLGetXMLValue(psIter, "iterations", nullptr);
    3310         315 :             const char *pszFormula = CPLGetXMLValue(psIter, "formula", nullptr);
    3311             :             const char *pszMDSubPrefix =
    3312         315 :                 CPLGetXMLValue(psIter, "md_prefix", nullptr);
    3313         315 :             int nIterations = -1;
    3314             : 
    3315         315 :             if (pszCounter != nullptr)
    3316             :             {
    3317         236 :                 const char *pszIterationsVal = NITFFindValRecursive(
    3318             :                     papszMD, *pnMDSize, pszMDPrefix, pszCounter);
    3319         472 :                 if (pszIterationsVal == nullptr ||
    3320         236 :                     (nIterations = atoi(pszIterationsVal)) < 0)
    3321             :                 {
    3322           0 :                     CPLError(
    3323             :                         bValidate ? CE_Failure : CE_Warning, CPLE_AppDefined,
    3324             :                         "Invalid loop construct in %s %s in XML resource : "
    3325             :                         "invalid 'counter' %s",
    3326             :                         pszDESOrTREName, pszDESOrTREKind, pszCounter);
    3327           0 :                     *pbError = TRUE;
    3328           0 :                     break;
    3329             :                 }
    3330             :             }
    3331          79 :             else if (pszIterations != nullptr)
    3332             :             {
    3333          68 :                 nIterations = atoi(pszIterations);
    3334             :             }
    3335          11 :             else if (pszFormula != nullptr &&
    3336          11 :                      strcmp(pszFormula, "NPAR*NPARO") == 0)
    3337             :             {
    3338             :                 char *pszMDNPARName =
    3339           1 :                     CPLStrdup(CPLSPrintf("%s%s", pszMDPrefix, "NPAR"));
    3340           1 :                 int NPAR = atoi(NITFFindValFromEnd(papszMD, *pnMDSize,
    3341             :                                                    pszMDNPARName, "-1"));
    3342             :                 char *pszMDNPAROName =
    3343           1 :                     CPLStrdup(CPLSPrintf("%s%s", pszMDPrefix, "NPARO"));
    3344           1 :                 int NPARO = atoi(NITFFindValFromEnd(papszMD, *pnMDSize,
    3345             :                                                     pszMDNPAROName, "-1"));
    3346           1 :                 CPLFree(pszMDNPARName);
    3347           1 :                 CPLFree(pszMDNPAROName);
    3348           1 :                 if (NPAR < 0)
    3349             :                 {
    3350           0 :                     CPLError(
    3351             :                         bValidate ? CE_Failure : CE_Warning, CPLE_AppDefined,
    3352             :                         "Invalid loop construct in %s %s in XML resource : "
    3353             :                         "invalid 'counter' %s",
    3354             :                         pszDESOrTREName, pszDESOrTREKind, "NPAR");
    3355           0 :                     *pbError = TRUE;
    3356           0 :                     break;
    3357             :                 }
    3358           1 :                 if (NPARO < 0)
    3359             :                 {
    3360           0 :                     CPLError(
    3361             :                         bValidate ? CE_Failure : CE_Warning, CPLE_AppDefined,
    3362             :                         "Invalid loop construct in %s %s in XML resource : "
    3363             :                         "invalid 'counter' %s",
    3364             :                         pszDESOrTREName, pszDESOrTREKind, "NPAR0");
    3365           0 :                     *pbError = TRUE;
    3366           0 :                     break;
    3367             :                 }
    3368           1 :                 nIterations = NPAR * NPARO;
    3369             :             }
    3370          10 :             else if (pszFormula != nullptr && strcmp(pszFormula, "NPLN-1") == 0)
    3371             :             {
    3372             :                 char *pszMDItemName =
    3373           0 :                     CPLStrdup(CPLSPrintf("%s%s", pszMDPrefix, "NPLN"));
    3374           0 :                 int NPLN = atoi(NITFFindValFromEnd(papszMD, *pnMDSize,
    3375             :                                                    pszMDItemName, "-1"));
    3376           0 :                 CPLFree(pszMDItemName);
    3377           0 :                 if (NPLN < 0)
    3378             :                 {
    3379           0 :                     CPLError(
    3380             :                         bValidate ? CE_Failure : CE_Warning, CPLE_AppDefined,
    3381             :                         "Invalid loop construct in %s %s in XML resource : "
    3382             :                         "invalid 'counter' %s",
    3383             :                         pszDESOrTREName, pszDESOrTREKind, "NPLN");
    3384           0 :                     *pbError = TRUE;
    3385           0 :                     break;
    3386             :                 }
    3387           0 :                 nIterations = NPLN - 1;
    3388             :             }
    3389          10 :             else if (pszFormula != nullptr &&
    3390          10 :                      strcmp(pszFormula, "NXPTS*NYPTS") == 0)
    3391             :             {
    3392             :                 char *pszMDNPARName =
    3393           0 :                     CPLStrdup(CPLSPrintf("%s%s", pszMDPrefix, "NXPTS"));
    3394           0 :                 int NXPTS = atoi(NITFFindValFromEnd(papszMD, *pnMDSize,
    3395             :                                                     pszMDNPARName, "-1"));
    3396             :                 char *pszMDNPAROName =
    3397           0 :                     CPLStrdup(CPLSPrintf("%s%s", pszMDPrefix, "NYPTS"));
    3398           0 :                 int NYPTS = atoi(NITFFindValFromEnd(papszMD, *pnMDSize,
    3399             :                                                     pszMDNPAROName, "-1"));
    3400           0 :                 CPLFree(pszMDNPARName);
    3401           0 :                 CPLFree(pszMDNPAROName);
    3402           0 :                 if (NXPTS < 0)
    3403             :                 {
    3404           0 :                     CPLError(
    3405             :                         bValidate ? CE_Failure : CE_Warning, CPLE_AppDefined,
    3406             :                         "Invalid loop construct in %s %s in XML resource : "
    3407             :                         "invalid 'counter' %s",
    3408             :                         pszDESOrTREName, pszDESOrTREKind, "NXPTS");
    3409           0 :                     *pbError = TRUE;
    3410           0 :                     break;
    3411             :                 }
    3412           0 :                 if (NYPTS < 0)
    3413             :                 {
    3414           0 :                     CPLError(
    3415             :                         bValidate ? CE_Failure : CE_Warning, CPLE_AppDefined,
    3416             :                         "Invalid loop construct in %s %s in XML resource : "
    3417             :                         "invalid 'counter' %s",
    3418             :                         pszDESOrTREName, pszDESOrTREKind, "NYPTS");
    3419           0 :                     *pbError = TRUE;
    3420           0 :                     break;
    3421             :                 }
    3422           0 :                 nIterations = NXPTS * NYPTS;
    3423             :             }
    3424          10 :             else if (pszFormula)
    3425             :             {
    3426          10 :                 const char *const apszVarAndFormulaNp1NDiv2[] = {
    3427             :                     "NPAR",         "(NPART+1)*(NPART)/2",
    3428             :                     "NUMOPG",       "(NUMOPG+1)*(NUMOPG)/2",
    3429             :                     "NUM_ADJ_PARM", "(NUM_ADJ_PARM+1)*(NUM_ADJ_PARM)/2",
    3430             :                     "N1_CAL",       "(N1_CAL+1)*(N1_CAL)/2",
    3431             :                     "NUM_PARA",     "(NUM_PARA+1)*(NUM_PARA)/2",
    3432             :                     nullptr,        nullptr};
    3433             : 
    3434          24 :                 for (int i = 0; apszVarAndFormulaNp1NDiv2[i]; i += 2)
    3435             :                 {
    3436          24 :                     if (strcmp(pszFormula, apszVarAndFormulaNp1NDiv2[i + 1]) ==
    3437             :                         0)
    3438             :                     {
    3439          10 :                         const char *pszVar = apszVarAndFormulaNp1NDiv2[i];
    3440             :                         char *pszMDItemName =
    3441          10 :                             CPLStrdup(CPLSPrintf("%s%s", pszMDPrefix, pszVar));
    3442          10 :                         int var = atoi(NITFFindValFromEnd(papszMD, *pnMDSize,
    3443             :                                                           pszMDItemName, "-1"));
    3444          10 :                         CPLFree(pszMDItemName);
    3445          10 :                         if (var < 0)
    3446             :                         {
    3447           0 :                             CPLError(bValidate ? CE_Failure : CE_Warning,
    3448             :                                      CPLE_AppDefined,
    3449             :                                      "Invalid loop construct in %s %s in XML "
    3450             :                                      "resource : "
    3451             :                                      "invalid 'counter' %s",
    3452             :                                      pszDESOrTREName, pszDESOrTREKind, pszVar);
    3453           0 :                             *pbError = TRUE;
    3454           0 :                             return papszMD;
    3455             :                         }
    3456          10 :                         nIterations = var * (var + 1) / 2;
    3457          10 :                         break;
    3458             :                     }
    3459             :                 }
    3460             : 
    3461          10 :                 if (nIterations < 0)
    3462             :                 {
    3463           0 :                     CPLError(
    3464             :                         bValidate ? CE_Failure : CE_Warning, CPLE_AppDefined,
    3465             :                         "Invalid loop construct in %s %s in XML resource : "
    3466             :                         "missing or invalid 'counter' or 'iterations' or "
    3467             :                         "'formula'",
    3468             :                         pszDESOrTREName, pszDESOrTREKind);
    3469           0 :                     *pbError = TRUE;
    3470           0 :                     break;
    3471             :                 }
    3472             :             }
    3473             : 
    3474         315 :             if (nIterations > 0)
    3475             :             {
    3476             :                 int iIter;
    3477             :                 const char *pszPercent;
    3478         296 :                 int bHasValidPercentD = FALSE;
    3479         296 :                 CPLXMLNode *psRepeatedNode = nullptr;
    3480         296 :                 CPLXMLNode *psLastChild = nullptr;
    3481             : 
    3482             :                 /* Check that md_prefix has one and only %XXXXd pattern */
    3483         533 :                 if (pszMDSubPrefix != nullptr &&
    3484         533 :                     (pszPercent = strchr(pszMDSubPrefix, '%')) != nullptr &&
    3485         237 :                     strchr(pszPercent + 1, '%') == nullptr)
    3486             :                 {
    3487         237 :                     const char *pszIter = pszPercent + 1;
    3488         693 :                     while (*pszIter != '\0')
    3489             :                     {
    3490         693 :                         if (*pszIter >= '0' && *pszIter <= '9')
    3491         456 :                             pszIter++;
    3492         237 :                         else if (*pszIter == 'd')
    3493             :                         {
    3494         237 :                             bHasValidPercentD = atoi(pszPercent + 1) <= 10;
    3495         237 :                             break;
    3496             :                         }
    3497             :                         else
    3498           0 :                             break;
    3499             :                     }
    3500             :                 }
    3501             : 
    3502         296 :                 if (psOutXMLNode != nullptr)
    3503             :                 {
    3504             :                     CPLXMLNode *psNumberNode;
    3505             :                     CPLXMLNode *psNameNode;
    3506             :                     const char *pszName =
    3507         201 :                         CPLGetXMLValue(psIter, "name", nullptr);
    3508             :                     psRepeatedNode =
    3509         201 :                         CPLCreateXMLNode(psOutXMLNode, CXT_Element, "repeated");
    3510         201 :                     if (pszName)
    3511             :                     {
    3512         148 :                         psNameNode = CPLCreateXMLNode(psRepeatedNode,
    3513             :                                                       CXT_Attribute, "name");
    3514         148 :                         CPLCreateXMLNode(psNameNode, CXT_Text, pszName);
    3515             :                     }
    3516         201 :                     psNumberNode = CPLCreateXMLNode(psRepeatedNode,
    3517             :                                                     CXT_Attribute, "number");
    3518         201 :                     CPLCreateXMLNode(psNumberNode, CXT_Text,
    3519             :                                      CPLSPrintf("%d", nIterations));
    3520             : 
    3521         201 :                     psLastChild = psRepeatedNode->psChild;
    3522         349 :                     while (psLastChild->psNext != nullptr)
    3523         148 :                         psLastChild = psLastChild->psNext;
    3524             :                 }
    3525             : 
    3526        3549 :                 for (iIter = 0; iIter < nIterations && *pbError == FALSE;
    3527             :                      iIter++)
    3528             :                 {
    3529        3253 :                     char *pszMDNewPrefix = nullptr;
    3530        3253 :                     CPLXMLNode *psGroupNode = nullptr;
    3531        3253 :                     if (bRPFIMGOrDES)
    3532             :                     {
    3533             :                         // As we need to fetch metadata items that are in
    3534             :                         // different RPF location, a prefix would hurt.
    3535        1413 :                         pszMDNewPrefix = CPLStrdup("");
    3536             :                     }
    3537        1840 :                     else if (pszMDSubPrefix != nullptr)
    3538             :                     {
    3539        1786 :                         if (bHasValidPercentD)
    3540             :                         {
    3541        1786 :                             const size_t nTmpLen =
    3542        1786 :                                 strlen(pszMDSubPrefix) + 10 + 1;
    3543             :                             char *szTmp =
    3544        1786 :                                 static_cast<char *>(CPLMalloc(nTmpLen));
    3545        1786 :                             snprintf(szTmp, nTmpLen, pszMDSubPrefix, iIter + 1);
    3546        1786 :                             pszMDNewPrefix = CPLStrdup(
    3547             :                                 CPLSPrintf("%s%s", pszMDPrefix, szTmp));
    3548        1786 :                             CPLFree(szTmp);
    3549             :                         }
    3550             :                         else
    3551           0 :                             pszMDNewPrefix = CPLStrdup(
    3552             :                                 CPLSPrintf("%s%s%04d_", pszMDPrefix,
    3553             :                                            pszMDSubPrefix, iIter + 1));
    3554             :                     }
    3555             :                     else
    3556          54 :                         pszMDNewPrefix = CPLStrdup(
    3557             :                             CPLSPrintf("%s%04d_", pszMDPrefix, iIter + 1));
    3558             : 
    3559        3253 :                     if (psRepeatedNode != nullptr)
    3560             :                     {
    3561             :                         CPLXMLNode *psIndexNode;
    3562             :                         psGroupNode =
    3563        3124 :                             CPLCreateXMLNode(nullptr, CXT_Element, "group");
    3564        3124 :                         CPLAssert(psLastChild->psNext == nullptr);
    3565        3124 :                         psLastChild->psNext = psGroupNode;
    3566        3124 :                         psLastChild = psGroupNode;
    3567        3124 :                         psIndexNode = CPLCreateXMLNode(psGroupNode,
    3568             :                                                        CXT_Attribute, "index");
    3569        3124 :                         CPLCreateXMLNode(psIndexNode, CXT_Text,
    3570             :                                          CPLSPrintf("%d", iIter));
    3571             :                     }
    3572             : 
    3573        3253 :                     papszMD = NITFGenericMetadataReadTREInternal(
    3574             :                         papszMD, pnMDSize, pnMDAlloc, psGroupNode,
    3575             :                         pszDESOrTREKind, pszDESOrTREName, pachTRE, nTRESize,
    3576             :                         psIter, pnTreOffset, pszMDNewPrefix, bValidate, fp,
    3577             :                         oMapLocIdToXML, pbError);
    3578             : 
    3579        3253 :                     CPLFree(pszMDNewPrefix);
    3580             :                 }
    3581         315 :             }
    3582             :         }
    3583        8372 :         else if (psIter->eType == CXT_Element && psIter->pszValue != nullptr &&
    3584         827 :                  strcmp(psIter->pszValue, "if") == 0)
    3585             :         {
    3586         727 :             const char *pszCond = CPLGetXMLValue(psIter, "cond", nullptr);
    3587         727 :             if (pszCond == nullptr)
    3588             :             {
    3589           0 :                 CPLError(bValidate ? CE_Failure : CE_Warning, CPLE_AppDefined,
    3590             :                          "Invalid if construct in %s %s in XML resource : "
    3591             :                          "missing 'cond' attribute",
    3592             :                          pszDESOrTREName, pszDESOrTREKind);
    3593           0 :                 *pbError = TRUE;
    3594           0 :                 break;
    3595             :             }
    3596             : 
    3597         727 :             int nRet = NITFEvaluateCond(pszCond, papszMD, pnMDSize, pszMDPrefix,
    3598             :                                         pszDESOrTREKind, pszDESOrTREName);
    3599         727 :             if (nRet < 0)
    3600             :             {
    3601           0 :                 *pbError = TRUE;
    3602           0 :                 break;
    3603             :             }
    3604         727 :             if (nRet > 0)
    3605             :             {
    3606         162 :                 papszMD = NITFGenericMetadataReadTREInternal(
    3607             :                     papszMD, pnMDSize, pnMDAlloc, psOutXMLNode, pszDESOrTREKind,
    3608             :                     pszDESOrTREName, pachTRE, nTRESize, psIter, pnTreOffset,
    3609             :                     pszMDPrefix, bValidate, fp, oMapLocIdToXML, pbError);
    3610         727 :             }
    3611             :         }
    3612        7645 :         else if (psIter->eType == CXT_Element && psIter->pszValue != nullptr &&
    3613         100 :                  strcmp(psIter->pszValue, "if_remaining_bytes") == 0)
    3614             :         {
    3615           4 :             if (*pnTreOffset < nTRESize)
    3616             :             {
    3617           0 :                 papszMD = NITFGenericMetadataReadTREInternal(
    3618             :                     papszMD, pnMDSize, pnMDAlloc, psOutXMLNode, pszDESOrTREKind,
    3619             :                     pszDESOrTREName, pachTRE, nTRESize, psIter, pnTreOffset,
    3620             :                     pszMDPrefix, bValidate, fp, oMapLocIdToXML, pbError);
    3621             :             }
    3622             :         }
    3623             :         else
    3624             :         {
    3625             :             // CPLDebug("NITF", "Unknown element : %s", psIter->pszValue ?
    3626             :             // psIter->pszValue : "null");
    3627             :         }
    3628             :     }
    3629             : 
    3630        3579 :     if (bRPFIMGOrDES && nRPFLocationId >= LID_HeaderComponent &&
    3631          54 :         nRPFLocationId <= LID_ColorTableIndexRecord &&
    3632          54 :         nRPFLocationSize < 1000 * 1000 && psOutXMLNode &&
    3633          54 :         strcmp(psOutXMLNode->pszValue, "group") == 0)
    3634             :     {
    3635             :         const auto oIter =
    3636          54 :             oMapLocIdToXML.find(static_cast<NITFLocId>(nRPFLocationId));
    3637          54 :         if (oIter != oMapLocIdToXML.end())
    3638             :         {
    3639          50 :             const CPLXMLNode *psRPFLocationXML = oIter->second;
    3640          50 :             VSIFSeekL(fp, nRPFLocationOffset, SEEK_SET);
    3641         100 :             std::vector<GByte> abyRPFLocationData;
    3642          50 :             abyRPFLocationData.resize(nRPFLocationSize);
    3643          50 :             if (VSIFReadL(abyRPFLocationData.data(), 1, nRPFLocationSize, fp) ==
    3644          50 :                 nRPFLocationSize)
    3645             :             {
    3646          49 :                 CPLXMLNode *psLastChild = psOutXMLNode->psChild;
    3647         196 :                 while (psLastChild->psNext)
    3648         147 :                     psLastChild = psLastChild->psNext;
    3649             :                 CPLXMLNode *psContent =
    3650          49 :                     CPLCreateXMLNode(nullptr, CXT_Element, "content");
    3651          49 :                 psLastChild->psNext = psContent;
    3652          49 :                 CPLAddXMLAttributeAndValue(
    3653             :                     psContent, "ComponentName",
    3654             :                     CPLGetXMLValue(psRPFLocationXML, "id", ""));
    3655          49 :                 int nLocationOffset = 0;
    3656          49 :                 papszMD = NITFGenericMetadataReadTREInternal(
    3657             :                     papszMD, pnMDSize, pnMDAlloc, psContent, pszDESOrTREKind,
    3658             :                     pszDESOrTREName,
    3659          49 :                     reinterpret_cast<const char *>(abyRPFLocationData.data()),
    3660             :                     nRPFLocationSize, psRPFLocationXML, &nLocationOffset,
    3661             :                     pszMDPrefix, bValidate, fp, oMapLocIdToXML, pbError);
    3662             :             }
    3663             :         }
    3664             :         else
    3665             :         {
    3666           4 :             CPLDebug("NITF",
    3667             :                      "No definition in nitf_spec.xml for location id %d",
    3668             :                      nRPFLocationId);
    3669             :         }
    3670             :     }
    3671             : 
    3672        3579 :     return papszMD;
    3673             : }
    3674             : 
    3675             : /************************************************************************/
    3676             : /*                     NITFGenericMetadataReadTRE()                     */
    3677             : /************************************************************************/
    3678             : 
    3679          41 : static char **NITFGenericMetadataReadTRE(char **papszMD, const char *pszTREName,
    3680             :                                          const char *pachTRE, int nTRESize,
    3681             :                                          CPLXMLNode *psTreNode, VSILFILE *fp)
    3682             : {
    3683          41 :     int bError = FALSE;
    3684          41 :     int nTreOffset = 0;
    3685             :     const char *pszMDPrefix;
    3686             :     int nMDSize, nMDAlloc;
    3687             : 
    3688          41 :     int nTreLength = atoi(CPLGetXMLValue(psTreNode, "length", "-1"));
    3689          41 :     int nTreMinLength = atoi(CPLGetXMLValue(psTreNode, "minlength", "-1"));
    3690             :     /* int nTreMaxLength = atoi(CPLGetXMLValue(psTreNode, "maxlength", "-1"));
    3691             :      */
    3692             : 
    3693          41 :     if (nTreLength > 0 && nTRESize != nTreLength)
    3694             :     {
    3695           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    3696             :                  "%s TRE wrong size (%d). Expected %d.", pszTREName, nTRESize,
    3697             :                  nTreLength);
    3698             :     }
    3699             : 
    3700          41 :     if (nTreMinLength > 0 && nTRESize < nTreMinLength)
    3701             :     {
    3702           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    3703             :                  "%s TRE wrong size (%d). Expected >= %d.", pszTREName,
    3704             :                  nTRESize, nTreMinLength);
    3705             :     }
    3706             : 
    3707          41 :     pszMDPrefix = CPLGetXMLValue(psTreNode, "md_prefix", "");
    3708             : 
    3709          41 :     nMDSize = nMDAlloc = CSLCount(papszMD);
    3710             : 
    3711          41 :     std::map<NITFLocId, const CPLXMLNode *> oMapLocIdToXML;
    3712          41 :     papszMD = NITFGenericMetadataReadTREInternal(
    3713             :         papszMD, &nMDSize, &nMDAlloc, nullptr, "TRE", pszTREName, pachTRE,
    3714             :         nTRESize, psTreNode, &nTreOffset, pszMDPrefix,
    3715             :         false,  // bValidate
    3716             :         fp, oMapLocIdToXML, &bError);
    3717             : 
    3718          41 :     if (bError == FALSE && nTreLength > 0 && nTreOffset != nTreLength)
    3719             :     {
    3720           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    3721             :                  "Inconsistent declaration of %s TRE", pszTREName);
    3722             :     }
    3723          41 :     if (nTreOffset < nTRESize)
    3724           0 :         CPLDebug("NITF", "%d remaining bytes at end of %s TRE",
    3725             :                  nTRESize - nTreOffset, pszTREName);
    3726             : 
    3727          82 :     return papszMD;
    3728             : }
    3729             : 
    3730             : /************************************************************************/
    3731             : /*                          NITFLoadXMLSpec()                           */
    3732             : /************************************************************************/
    3733             : 
    3734             : #define NITF_SPEC_FILE "nitf_spec.xml"
    3735             : 
    3736         847 : static CPLXMLNode *NITFLoadXMLSpec(NITFFile *psFile)
    3737             : {
    3738             : 
    3739         847 :     if (psFile->psNITFSpecNode == nullptr)
    3740             :     {
    3741             : #ifndef USE_ONLY_EMBEDDED_RESOURCE_FILES
    3742             : #ifdef EMBED_RESOURCE_FILES
    3743             :         CPLPushErrorHandler(CPLQuietErrorHandler);
    3744             : #endif
    3745         754 :         const char *pszXMLDescFilename = CPLFindFile("gdal", NITF_SPEC_FILE);
    3746             : #ifdef EMBED_RESOURCE_FILES
    3747             :         CPLPopErrorHandler();
    3748             :         CPLErrorReset();
    3749             : #endif
    3750         754 :         if (pszXMLDescFilename == nullptr)
    3751             : #endif
    3752             :         {
    3753             : #ifdef EMBED_RESOURCE_FILES
    3754             :             CPLDebug("NITF", "Using embedded %s", NITF_SPEC_FILE);
    3755             :             psFile->psNITFSpecNode = CPLParseXMLString(NITFGetSpecFile());
    3756             :             CPLAssert(psFile->psNITFSpecNode);
    3757             :             return psFile->psNITFSpecNode;
    3758             : #else
    3759           0 :             CPLDebug("NITF", "Cannot find XML file : %s", NITF_SPEC_FILE);
    3760           0 :             return nullptr;
    3761             : #endif
    3762             :         }
    3763             : #ifndef USE_ONLY_EMBEDDED_RESOURCE_FILES
    3764         754 :         psFile->psNITFSpecNode = CPLParseXMLFile(pszXMLDescFilename);
    3765         754 :         if (psFile->psNITFSpecNode == nullptr)
    3766             :         {
    3767           0 :             CPLDebug("NITF", "Invalid XML file : %s", pszXMLDescFilename);
    3768           0 :             return nullptr;
    3769             :         }
    3770             : #endif
    3771             :     }
    3772             : 
    3773         847 :     return psFile->psNITFSpecNode;
    3774             : }
    3775             : 
    3776             : /************************************************************************/
    3777             : /*                     NITFFindTREXMLDescFromName()                     */
    3778             : /************************************************************************/
    3779             : 
    3780          74 : static CPLXMLNode *NITFFindTREXMLDescFromName(NITFFile *psFile,
    3781             :                                               const char *pszTREName)
    3782             : {
    3783             :     CPLXMLNode *psTreeNode;
    3784             :     CPLXMLNode *psTresNode;
    3785             :     CPLXMLNode *psIter;
    3786             : 
    3787          74 :     psTreeNode = NITFLoadXMLSpec(psFile);
    3788          74 :     if (psTreeNode == nullptr)
    3789           0 :         return nullptr;
    3790             : 
    3791          74 :     psTresNode = CPLGetXMLNode(psTreeNode, "=root.tres");
    3792          74 :     if (psTresNode == nullptr)
    3793             :     {
    3794           0 :         CPLDebug("NITF", "Cannot find <root><tres> root element");
    3795           0 :         return nullptr;
    3796             :     }
    3797             : 
    3798        4203 :     for (psIter = psTresNode->psChild; psIter != nullptr;
    3799        4129 :          psIter = psIter->psNext)
    3800             :     {
    3801        4198 :         if (psIter->eType == CXT_Element && psIter->pszValue != nullptr &&
    3802        2966 :             strcmp(psIter->pszValue, "tre") == 0)
    3803             :         {
    3804        2966 :             const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
    3805        2966 :             if (pszName != nullptr && strcmp(pszName, pszTREName) == 0)
    3806             :             {
    3807          69 :                 return psIter;
    3808             :             }
    3809             :         }
    3810             :     }
    3811             : 
    3812           5 :     return nullptr;
    3813             : }
    3814             : 
    3815             : /************************************************************************/
    3816             : /*                          NITFCreateXMLTre()                          */
    3817             : /************************************************************************/
    3818             : 
    3819          74 : CPLXMLNode *NITFCreateXMLTre(NITFFile *psFile, const char *pszTREName,
    3820             :                              const char *pachTRE, int nTRESize, bool bValidate,
    3821             :                              bool *pbGotError)
    3822             : {
    3823          74 :     int nTreLength, nTreMinLength = -1 /* , nTreMaxLength = -1 */;
    3824          74 :     int bError = FALSE;
    3825          74 :     int nTreOffset = 0;
    3826             :     CPLXMLNode *psTreNode;
    3827          74 :     CPLXMLNode *psOutXMLNode = nullptr;
    3828          74 :     int nMDSize = 0, nMDAlloc = 0;
    3829             :     const char *pszMDPrefix;
    3830             : 
    3831          74 :     psTreNode = NITFFindTREXMLDescFromName(
    3832          74 :         psFile, EQUAL(pszTREName, "RPFIMG") || EQUAL(pszTREName, "RPFDES")
    3833             :                     ? "RPF"
    3834             :                     : pszTREName);
    3835          74 :     if (psTreNode == nullptr)
    3836             :     {
    3837           5 :         if (!(STARTS_WITH_CI(pszTREName, "RPF") ||
    3838           5 :               strcmp(pszTREName, "XXXXXX") == 0))
    3839             :         {
    3840           5 :             CPLDebug("NITF", "Cannot find definition of TRE %s in %s",
    3841             :                      pszTREName, NITF_SPEC_FILE);
    3842             :         }
    3843           5 :         return nullptr;
    3844             :     }
    3845             : 
    3846          69 :     nTreLength = atoi(CPLGetXMLValue(psTreNode, "length", "-1"));
    3847          69 :     nTreMinLength = atoi(CPLGetXMLValue(psTreNode, "minlength", "-1"));
    3848             :     /* nTreMaxLength = atoi(CPLGetXMLValue(psTreNode, "maxlength", "-1")); */
    3849             : 
    3850          69 :     psOutXMLNode = CPLCreateXMLNode(nullptr, CXT_Element, "tre");
    3851          69 :     CPLCreateXMLNode(CPLCreateXMLNode(psOutXMLNode, CXT_Attribute, "name"),
    3852             :                      CXT_Text, pszTREName);
    3853             : 
    3854          69 :     if (nTreLength > 0 && nTRESize != nTreLength)
    3855             :     {
    3856           2 :         CPLError(bValidate ? CE_Failure : CE_Warning, CPLE_AppDefined,
    3857             :                  "%s TRE wrong size (%d). Expected %d.", pszTREName, nTRESize,
    3858             :                  nTreLength);
    3859           2 :         CPLCreateXMLElementAndValue(
    3860             :             psOutXMLNode, bValidate ? "error" : "warning",
    3861             :             CPLSPrintf("%s TRE wrong size (%d). Expected %d.", pszTREName,
    3862             :                        nTRESize, nTreLength));
    3863           2 :         if (pbGotError)
    3864           2 :             *pbGotError = true;
    3865             :     }
    3866             : 
    3867          69 :     if (nTreMinLength > 0 && nTRESize < nTreMinLength)
    3868             :     {
    3869           0 :         CPLError(bValidate ? CE_Failure : CE_Warning, CPLE_AppDefined,
    3870             :                  "%s TRE wrong size (%d). Expected >= %d.", pszTREName,
    3871             :                  nTRESize, nTreMinLength);
    3872           0 :         CPLCreateXMLElementAndValue(
    3873             :             psOutXMLNode, bValidate ? "error" : "warning",
    3874             :             CPLSPrintf("%s TRE wrong size (%d). Expected >= %d.", pszTREName,
    3875             :                        nTRESize, nTreMinLength));
    3876           0 :         if (pbGotError)
    3877           0 :             *pbGotError = true;
    3878             :     }
    3879             : 
    3880          69 :     pszMDPrefix = CPLGetXMLValue(psTreNode, "md_prefix", "");
    3881          69 :     std::map<NITFLocId, const CPLXMLNode *> oMapLocIdToXML;
    3882          69 :     CSLDestroy(NITFGenericMetadataReadTREInternal(
    3883             :         nullptr, &nMDSize, &nMDAlloc, psOutXMLNode, "TRE", pszTREName, pachTRE,
    3884             :         nTRESize, psTreNode, &nTreOffset, pszMDPrefix, bValidate, psFile->fp,
    3885             :         oMapLocIdToXML, &bError));
    3886             : 
    3887          69 :     if (bError == FALSE && nTreLength > 0 && nTreOffset != nTreLength)
    3888             :     {
    3889           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    3890             :                  "Inconsistent declaration of %s TRE", pszTREName);
    3891             :     }
    3892          69 :     if (nTreOffset < nTRESize && !EQUAL(pszTREName, "RPFIMG"))
    3893             :     {
    3894           2 :         CPLCreateXMLElementAndValue(
    3895             :             psOutXMLNode, bValidate ? "error" : "warning",
    3896             :             CPLSPrintf("%d remaining bytes at end of %s TRE",
    3897             :                        nTRESize - nTreOffset, pszTREName));
    3898             :     }
    3899          69 :     if (pbGotError && bError)
    3900           0 :         *pbGotError = true;
    3901             : 
    3902          69 :     return psOutXMLNode;
    3903             : }
    3904             : 
    3905             : /************************************************************************/
    3906             : /*                     NITFFindTREXMLDescFromName()                     */
    3907             : /************************************************************************/
    3908             : 
    3909          21 : static CPLXMLNode *NITFFindDESXMLDescFromName(NITFFile *psFile,
    3910             :                                               const char *pszDESName)
    3911             : {
    3912             :     CPLXMLNode *psTreeNode;
    3913             :     CPLXMLNode *psTresNode;
    3914             :     CPLXMLNode *psIter;
    3915             : 
    3916          21 :     psTreeNode = NITFLoadXMLSpec(psFile);
    3917          21 :     if (psTreeNode == nullptr)
    3918           0 :         return nullptr;
    3919             : 
    3920          21 :     psTresNode = CPLGetXMLNode(psTreeNode, "=root.des_list");
    3921          21 :     if (psTresNode == nullptr)
    3922             :     {
    3923           0 :         CPLDebug("NITF", "Cannot find <root><des_list> root element");
    3924           0 :         return nullptr;
    3925             :     }
    3926             : 
    3927         301 :     for (psIter = psTresNode->psChild; psIter != nullptr;
    3928         280 :          psIter = psIter->psNext)
    3929             :     {
    3930         290 :         if (psIter->eType == CXT_Element && psIter->pszValue != nullptr &&
    3931         128 :             strcmp(psIter->pszValue, "des") == 0)
    3932             :         {
    3933         128 :             const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
    3934         128 :             if (pszName != nullptr && strcmp(pszName, pszDESName) == 0)
    3935             :             {
    3936          10 :                 return psIter;
    3937             :             }
    3938             :         }
    3939             :     }
    3940             : 
    3941          11 :     return nullptr;
    3942             : }
    3943             : 
    3944             : /************************************************************************/
    3945             : /*                NITFCreateXMLDesUserDefinedSubHeader()                */
    3946             : /************************************************************************/
    3947             : 
    3948           9 : CPLXMLNode *NITFCreateXMLDesUserDefinedSubHeader(NITFFile *psFile,
    3949             :                                                  const NITFDES *psDES,
    3950             :                                                  bool bValidate,
    3951             :                                                  bool *pbGotError)
    3952             : {
    3953           9 :     const char *pszDESID = CSLFetchNameValue(psDES->papszMetadata, "DESID");
    3954           9 :     CPLXMLNode *psDESDef = NITFFindDESXMLDescFromName(psFile, pszDESID);
    3955           9 :     if (psDESDef == nullptr)
    3956             :     {
    3957           5 :         CPLDebug("NITF", "Cannot find definition of DES %s in %s", pszDESID,
    3958             :                  NITF_SPEC_FILE);
    3959           5 :         return nullptr;
    3960             :     }
    3961             :     CPLXMLNode *psUserDefinedFields =
    3962           4 :         CPLGetXMLNode(psDESDef, "subheader_fields");
    3963           4 :     if (psUserDefinedFields == nullptr)
    3964             :     {
    3965           0 :         return nullptr;
    3966             :     }
    3967             : 
    3968             :     CPLXMLNode *psOutXMLNode =
    3969           4 :         CPLCreateXMLNode(nullptr, CXT_Element, "user_defined_fields");
    3970             : 
    3971           4 :     int bError = FALSE;
    3972           4 :     int nOffset = 200;
    3973           4 :     char **papszMD = CSLDuplicate(psDES->papszMetadata);
    3974           4 :     int nMDSize = CSLCount(papszMD);
    3975           4 :     int nMDAlloc = nMDSize;
    3976           4 :     const int nDESSize =
    3977           4 :         psFile->pasSegmentInfo[psDES->iSegment].nSegmentHeaderSize;
    3978           4 :     std::map<NITFLocId, const CPLXMLNode *> oMapLocIdToXML;
    3979           4 :     CSLDestroy(NITFGenericMetadataReadTREInternal(
    3980             :         papszMD, &nMDSize, &nMDAlloc, psOutXMLNode, "DES", pszDESID,
    3981           4 :         psDES->pachHeader, nDESSize, psUserDefinedFields, &nOffset,
    3982             :         "", /* pszMDPrefix, */
    3983             :         bValidate, psFile->fp, oMapLocIdToXML, &bError));
    3984             :     int nDESSHL =
    3985           4 :         atoi(CSLFetchNameValueDef(psDES->papszMetadata, "DESSHL", "0"));
    3986             : 
    3987             :     const int nLength =
    3988           4 :         atoi(CPLGetXMLValue(psUserDefinedFields, "length", "-1"));
    3989             :     const int nMinLength =
    3990           4 :         atoi(CPLGetXMLValue(psUserDefinedFields, "minlength", "-1"));
    3991             : 
    3992           4 :     if (nLength > 0 && nDESSHL != nLength)
    3993             :     {
    3994           2 :         CPLError(bValidate ? CE_Failure : CE_Warning, CPLE_AppDefined,
    3995             :                  "%s DES wrong header size (%d). Expected %d.", pszDESID,
    3996             :                  nDESSHL, nLength);
    3997           2 :         CPLCreateXMLElementAndValue(
    3998             :             psOutXMLNode, bValidate ? "error" : "warning",
    3999             :             CPLSPrintf("%s DES wrong size (%d). Expected %d.", pszDESID,
    4000             :                        nDESSHL, nLength));
    4001           2 :         if (pbGotError)
    4002           2 :             *pbGotError = true;
    4003             :     }
    4004             : 
    4005           4 :     if (nMinLength > 0 && nDESSHL < nMinLength)
    4006             :     {
    4007           0 :         CPLError(bValidate ? CE_Failure : CE_Warning, CPLE_AppDefined,
    4008             :                  "%s DES wrong size (%d). Expected >= %d.", pszDESID, nDESSHL,
    4009             :                  nMinLength);
    4010           0 :         CPLCreateXMLElementAndValue(
    4011             :             psOutXMLNode, bValidate ? "error" : "warning",
    4012             :             CPLSPrintf("%s DES wrong size (%d). Expected >= %d.", pszDESID,
    4013             :                        nDESSHL, nMinLength));
    4014           0 :         if (pbGotError)
    4015           0 :             *pbGotError = true;
    4016             :     }
    4017             : 
    4018           4 :     if (nOffset < nDESSHL)
    4019             :     {
    4020           0 :         bError = TRUE;
    4021           0 :         CPLCreateXMLElementAndValue(
    4022             :             psOutXMLNode, bValidate ? "error" : "warning",
    4023             :             CPLSPrintf(
    4024             :                 "%d remaining bytes at end of user defined subheader section",
    4025             :                 nDESSHL - nOffset));
    4026             :     }
    4027           4 :     if (pbGotError && bError)
    4028           2 :         *pbGotError = true;
    4029             : 
    4030           4 :     return psOutXMLNode;
    4031             : }
    4032             : 
    4033             : /************************************************************************/
    4034             : /*                     NITFCreateXMLDesDataFields()                     */
    4035             : /************************************************************************/
    4036             : 
    4037          12 : CPLXMLNode *NITFCreateXMLDesDataFields(NITFFile *psFile, const NITFDES *psDES,
    4038             :                                        const GByte *pabyData, int nDataLen,
    4039             :                                        bool bValidate, bool *pbGotError)
    4040             : {
    4041          12 :     const char *pszDESID = CSLFetchNameValue(psDES->papszMetadata, "DESID");
    4042          12 :     CPLXMLNode *psDESDef = NITFFindDESXMLDescFromName(psFile, pszDESID);
    4043          12 :     if (psDESDef == nullptr)
    4044             :     {
    4045           6 :         CPLDebug("NITF", "Cannot find definition of DES %s in %s", pszDESID,
    4046             :                  NITF_SPEC_FILE);
    4047           6 :         return nullptr;
    4048             :     }
    4049           6 :     CPLXMLNode *psFields = CPLGetXMLNode(psDESDef, "data_fields");
    4050           6 :     if (psFields == nullptr)
    4051             :     {
    4052           5 :         return nullptr;
    4053             :     }
    4054             : 
    4055             :     CPLXMLNode *psOutXMLNode =
    4056           1 :         CPLCreateXMLNode(nullptr, CXT_Element, "data_fields");
    4057             : 
    4058           1 :     int bError = FALSE;
    4059           1 :     int nOffset = 0;
    4060           1 :     char **papszMD = CSLDuplicate(psDES->papszMetadata);
    4061           1 :     int nMDSize = CSLCount(papszMD);
    4062           1 :     int nMDAlloc = nMDSize;
    4063           1 :     std::map<NITFLocId, const CPLXMLNode *> oMapLocIdToXML;
    4064           1 :     CSLDestroy(NITFGenericMetadataReadTREInternal(
    4065             :         papszMD, &nMDSize, &nMDAlloc, psOutXMLNode, "DES", pszDESID,
    4066             :         reinterpret_cast<const char *>(pabyData), nDataLen, psFields, &nOffset,
    4067             :         "", /* pszMDPrefix, */
    4068             :         bValidate, psFile->fp, oMapLocIdToXML, &bError));
    4069           1 :     if (nOffset < nDataLen)
    4070             :     {
    4071           0 :         bError = TRUE;
    4072           0 :         CPLCreateXMLElementAndValue(
    4073             :             psOutXMLNode, bValidate ? "error" : "warning",
    4074             :             CPLSPrintf("%d remaining bytes at end of data section",
    4075             :                        nDataLen - nOffset));
    4076             :     }
    4077           1 :     if (pbGotError && bError)
    4078           0 :         *pbGotError = true;
    4079             : 
    4080           1 :     return psOutXMLNode;
    4081             : }
    4082             : 
    4083             : /************************************************************************/
    4084             : /*                        NITFGenericMetadataRead()                     */
    4085             : /*                                                                      */
    4086             : /* Add metadata from TREs of file and image objects in the papszMD list */
    4087             : /* pszSpecificTRE can be nullptr, in which case all TREs listed in         */
    4088             : /* data/nitf_resources.xml that have md_prefix defined will be looked   */
    4089             : /* for. If not nullptr, only the specified one will be looked for.         */
    4090             : /************************************************************************/
    4091             : 
    4092         752 : char **NITFGenericMetadataRead(char **papszMD, NITFFile *psFile,
    4093             :                                NITFImage *psImage,
    4094             :                                const char *pszSpecificTREName)
    4095             : {
    4096         752 :     CPLXMLNode *psTreeNode = nullptr;
    4097         752 :     CPLXMLNode *psTresNode = nullptr;
    4098         752 :     CPLXMLNode *psIter = nullptr;
    4099             : 
    4100         752 :     if (psFile == nullptr)
    4101             :     {
    4102           0 :         if (psImage == nullptr)
    4103           0 :             return papszMD;
    4104           0 :         psTreeNode = NITFLoadXMLSpec(psImage->psFile);
    4105             :     }
    4106             :     else
    4107         752 :         psTreeNode = NITFLoadXMLSpec(psFile);
    4108             : 
    4109         752 :     if (psTreeNode == nullptr)
    4110           0 :         return papszMD;
    4111             : 
    4112         752 :     psTresNode = CPLGetXMLNode(psTreeNode, "=root.tres");
    4113         752 :     if (psTresNode == nullptr)
    4114             :     {
    4115           0 :         CPLDebug("NITF", "Cannot find <root><tres> root element");
    4116           0 :         return papszMD;
    4117             :     }
    4118             : 
    4119       71440 :     for (psIter = psTresNode->psChild; psIter != nullptr;
    4120       70688 :          psIter = psIter->psNext)
    4121             :     {
    4122       70688 :         if (psIter->eType == CXT_Element && psIter->pszValue != nullptr &&
    4123       50384 :             strcmp(psIter->pszValue, "tre") == 0)
    4124             :         {
    4125       50384 :             const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
    4126             :             const char *pszMDPrefix =
    4127       50384 :                 CPLGetXMLValue(psIter, "md_prefix", nullptr);
    4128       50384 :             int bHasRightPrefix = FALSE;
    4129       50384 :             if (pszName == nullptr)
    4130           0 :                 continue;
    4131       50384 :             if (pszSpecificTREName == nullptr)
    4132       50384 :                 bHasRightPrefix = (pszMDPrefix != nullptr);
    4133             :             else
    4134           0 :                 bHasRightPrefix = (strcmp(pszName, pszSpecificTREName) == 0);
    4135       50384 :             if (bHasRightPrefix)
    4136             :             {
    4137       16544 :                 if (psFile != nullptr)
    4138             :                 {
    4139       16544 :                     const char *pachTRE = nullptr;
    4140       16544 :                     int nTRESize = 0;
    4141             : 
    4142       16544 :                     pachTRE = NITFFindTRE(psFile->pachTRE, psFile->nTREBytes,
    4143             :                                           pszName, &nTRESize);
    4144       16544 :                     if (pachTRE != nullptr)
    4145           2 :                         papszMD = NITFGenericMetadataReadTRE(
    4146             :                             papszMD, pszName, pachTRE, nTRESize, psIter,
    4147             :                             psFile->fp);
    4148             :                 }
    4149       16544 :                 if (psImage != nullptr)
    4150             :                 {
    4151       16544 :                     const char *pachTRE = nullptr;
    4152       16544 :                     int nTRESize = 0;
    4153             : 
    4154       16544 :                     pachTRE = NITFFindTRE(psImage->pachTRE, psImage->nTREBytes,
    4155             :                                           pszName, &nTRESize);
    4156       16544 :                     if (pachTRE != nullptr)
    4157          39 :                         papszMD = NITFGenericMetadataReadTRE(
    4158             :                             papszMD, pszName, pachTRE, nTRESize, psIter,
    4159          39 :                             psImage->psFile->fp);
    4160             :                 }
    4161       16544 :                 if (pszSpecificTREName)
    4162           0 :                     break;
    4163             :             }
    4164             :         }
    4165             :     }
    4166             : 
    4167         752 :     return papszMD;
    4168             : }
    4169             : 
    4170             : #undef PLACE

Generated by: LCOV version 1.14