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