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