Coverage Report

Created: 2026-09-26 08:22

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