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