LCOV - code coverage report
Current view: top level - frmts/nitf - nitffile.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1319 1653 79.8 %
Date: 2025-10-01 17:07:58 Functions: 30 33 90.9 %

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

Generated by: LCOV version 1.14