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