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