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