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