EVOLUTION-MANAGER
Edit File: nitffile.c
/****************************************************************************** * $Id: nitffile.c 38086 2017-04-21 14:34:14Z rouault $ * * Project: NITF Read/Write Library * Purpose: Module responsible for opening NITF file, populating NITFFile * structure, and instantiating segment specific access objects. * Author: Frank Warmerdam, warmerdam@pobox.com * ********************************************************************** * Copyright (c) 2002, Frank Warmerdam * Copyright (c) 2007-2013, Even Rouault <even dot rouault at mines-paris dot org> * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. ****************************************************************************/ #include "nitflib.h" #include "cpl_vsi.h" #include "cpl_conv.h" #include "cpl_string.h" CPL_CVSID("$Id: nitffile.c 38086 2017-04-21 14:34:14Z rouault $"); CPL_INLINE static void CPL_IGNORE_RET_VAL_INT(CPL_UNUSED int unused) {} static int NITFWriteBLOCKA( VSILFILE* fp, vsi_l_offset nOffsetUDIDL, int *pnOffset, char **papszOptions ); static int NITFWriteTREsFromOptions( VSILFILE* fp, vsi_l_offset nOffsetUDIDL, int *pnOffset, char **papszOptions, const char* pszTREPrefix); static int NITFCollectSegmentInfo( NITFFile *psFile, int nFileHeaderLenSize, int nOffset, const char szType[3], int nHeaderLenSize, int nDataLenSize, GUIntBig *pnNextData ); /************************************************************************/ /* NITFOpen() */ /************************************************************************/ NITFFile *NITFOpen( const char *pszFilename, int bUpdatable ) { VSILFILE *fp; /* -------------------------------------------------------------------- */ /* Open the file. */ /* -------------------------------------------------------------------- */ if( bUpdatable ) fp = VSIFOpenL( pszFilename, "r+b" ); else fp = VSIFOpenL( pszFilename, "rb" ); if( fp == NULL ) { CPLError( CE_Failure, CPLE_OpenFailed, "Failed to open file %s.", pszFilename ); return NULL; } return NITFOpenEx(fp, pszFilename); } /************************************************************************/ /* NITFOpenEx() */ /************************************************************************/ NITFFile *NITFOpenEx(VSILFILE *fp, const char *pszFilename) { char *pachHeader; NITFFile *psFile; int nHeaderLen, nOffset, nHeaderLenOffset; GUIntBig nNextData; char szTemp[128], achFSDWNG[6]; GIntBig currentPos; int bTriedStreamingFileHeader = FALSE; /* -------------------------------------------------------------------- */ /* Check file type. */ /* -------------------------------------------------------------------- */ if( VSIFSeekL( fp, 0, SEEK_SET ) != 0 || VSIFReadL( szTemp, 1, 9, fp ) != 9 || (!STARTS_WITH_CI(szTemp, "NITF") && !STARTS_WITH_CI(szTemp, "NSIF")) ) { CPLError( CE_Failure, CPLE_AppDefined, "The file %s is not an NITF file.", pszFilename ); CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp)); return NULL; } /* -------------------------------------------------------------------- */ /* Read the FSDWNG field. */ /* -------------------------------------------------------------------- */ if( VSIFSeekL( fp, 280, SEEK_SET ) != 0 || VSIFReadL( achFSDWNG, 1, 6, fp ) != 6 ) { CPLError( CE_Failure, CPLE_NotSupported, "Unable to read FSDWNG field from NITF file. File is either corrupt\n" "or empty." ); CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp)); return NULL; } /* -------------------------------------------------------------------- */ /* Get header length. */ /* -------------------------------------------------------------------- */ if( STARTS_WITH_CI(szTemp, "NITF01.") || STARTS_WITH_CI(achFSDWNG, "999998") ) nHeaderLenOffset = 394; else nHeaderLenOffset = 354; if( VSIFSeekL( fp, nHeaderLenOffset, SEEK_SET ) != 0 || VSIFReadL( szTemp, 1, 6, fp ) != 6 ) { CPLError( CE_Failure, CPLE_NotSupported, "Unable to read header length from NITF file. File is either corrupt\n" "or empty." ); CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp)); return NULL; } szTemp[6] = '\0'; nHeaderLen = atoi(szTemp); if( VSIFSeekL( fp, nHeaderLen, SEEK_SET ) != 0 ) currentPos = 0; else currentPos = VSIFTellL( fp ) ; if( nHeaderLen < nHeaderLenOffset || nHeaderLen > currentPos ) { CPLError( CE_Failure, CPLE_NotSupported, "NITF Header Length (%d) seems to be corrupt.", nHeaderLen ); CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp)); return NULL; } /* -------------------------------------------------------------------- */ /* Read the whole file header. */ /* -------------------------------------------------------------------- */ pachHeader = (char *) VSI_MALLOC_VERBOSE(nHeaderLen); if (pachHeader == NULL) { CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp)); return NULL; } if( VSIFSeekL( fp, 0, SEEK_SET ) != 0 || (int)VSIFReadL( pachHeader, 1, nHeaderLen, fp ) != nHeaderLen ) { CPLError( CE_Failure, CPLE_FileIO, "Cannot read %d bytes for NITF header", (nHeaderLen)); CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp)); CPLFree(pachHeader); return NULL; } /* -------------------------------------------------------------------- */ /* Create and initialize info structure about file. */ /* -------------------------------------------------------------------- */ psFile = (NITFFile *) CPLCalloc(sizeof(NITFFile),1); psFile->fp = fp; psFile->pachHeader = pachHeader; retry_read_header: /* -------------------------------------------------------------------- */ /* Get version. */ /* -------------------------------------------------------------------- */ NITFGetField( psFile->szVersion, pachHeader, 0, 9 ); /* -------------------------------------------------------------------- */ /* Collect a variety of information as metadata. */ /* -------------------------------------------------------------------- */ #define GetMD( target, hdr, start, length, name ) \ NITFExtractMetadata( &(target->papszMetadata), hdr, \ start, length, \ "NITF_" #name ); if( EQUAL(psFile->szVersion,"NITF02.10") || EQUAL(psFile->szVersion,"NSIF01.00") ) { char szWork[100]; GetMD( psFile, pachHeader, 0, 9, FHDR ); GetMD( psFile, pachHeader, 9, 2, CLEVEL ); GetMD( psFile, pachHeader, 11, 4, STYPE ); GetMD( psFile, pachHeader, 15, 10, OSTAID ); GetMD( psFile, pachHeader, 25, 14, FDT ); GetMD( psFile, pachHeader, 39, 80, FTITLE ); GetMD( psFile, pachHeader, 119, 1, FSCLAS ); GetMD( psFile, pachHeader, 120, 2, FSCLSY ); GetMD( psFile, pachHeader, 122, 11, FSCODE ); GetMD( psFile, pachHeader, 133, 2, FSCTLH ); GetMD( psFile, pachHeader, 135, 20, FSREL ); GetMD( psFile, pachHeader, 155, 2, FSDCTP ); GetMD( psFile, pachHeader, 157, 8, FSDCDT ); GetMD( psFile, pachHeader, 165, 4, FSDCXM ); GetMD( psFile, pachHeader, 169, 1, FSDG ); GetMD( psFile, pachHeader, 170, 8, FSDGDT ); GetMD( psFile, pachHeader, 178, 43, FSCLTX ); GetMD( psFile, pachHeader, 221, 1, FSCATP ); GetMD( psFile, pachHeader, 222, 40, FSCAUT ); GetMD( psFile, pachHeader, 262, 1, FSCRSN ); GetMD( psFile, pachHeader, 263, 8, FSSRDT ); GetMD( psFile, pachHeader, 271, 15, FSCTLN ); GetMD( psFile, pachHeader, 286, 5, FSCOP ); GetMD( psFile, pachHeader, 291, 5, FSCPYS ); GetMD( psFile, pachHeader, 296, 1, ENCRYP ); snprintf( szWork, sizeof(szWork), "%3d,%3d,%3d", ((GByte *)pachHeader)[297], ((GByte *)pachHeader)[298], ((GByte *)pachHeader)[299] ); GetMD( psFile, szWork, 0, 11, FBKGC ); GetMD( psFile, pachHeader, 300, 24, ONAME ); GetMD( psFile, pachHeader, 324, 18, OPHONE ); NITFGetField(szTemp, pachHeader, 342, 12); } else if( EQUAL(psFile->szVersion,"NITF02.00") ) { int nCOff = 0; GetMD( psFile, pachHeader, 0, 9, FHDR ); GetMD( psFile, pachHeader, 9, 2, CLEVEL ); GetMD( psFile, pachHeader, 11, 4, STYPE ); GetMD( psFile, pachHeader, 15, 10, OSTAID ); GetMD( psFile, pachHeader, 25, 14, FDT ); GetMD( psFile, pachHeader, 39, 80, FTITLE ); GetMD( psFile, pachHeader, 119, 1, FSCLAS ); GetMD( psFile, pachHeader, 120, 40, FSCODE ); GetMD( psFile, pachHeader, 160, 40, FSCTLH ); GetMD( psFile, pachHeader, 200, 40, FSREL ); GetMD( psFile, pachHeader, 240, 20, FSCAUT ); GetMD( psFile, pachHeader, 260, 20, FSCTLN ); GetMD( psFile, pachHeader, 280, 6, FSDWNG ); if( STARTS_WITH_CI(pachHeader+280, "999998") ) { GetMD( psFile, pachHeader, 286, 40, FSDEVT ); nCOff += 40; } GetMD( psFile, pachHeader, 286+nCOff, 5, FSCOP ); GetMD( psFile, pachHeader, 291+nCOff, 5, FSCPYS ); GetMD( psFile, pachHeader, 296+nCOff, 1, ENCRYP ); GetMD( psFile, pachHeader, 297+nCOff, 27, ONAME ); GetMD( psFile, pachHeader, 324+nCOff, 18, OPHONE ); NITFGetField(szTemp, pachHeader, 342+nCOff, 12); } if (!bTriedStreamingFileHeader && EQUAL(szTemp, "999999999999")) { GUIntBig nFileSize; GByte abyDELIM2_L2[12] = { 0 }; GByte abyL1_DELIM1[11] = { 0 }; int bOK; bTriedStreamingFileHeader = TRUE; CPLDebug("NITF", "Total file unknown. Trying to get a STREAMING_FILE_HEADER"); bOK = VSIFSeekL( fp, 0, SEEK_END ) == 0; nFileSize = VSIFTellL(fp); bOK &= VSIFSeekL( fp, nFileSize - 11, SEEK_SET ) == 0; abyDELIM2_L2[11] = '\0'; if (bOK && VSIFReadL( abyDELIM2_L2, 1, 11, fp ) == 11 && abyDELIM2_L2[0] == 0x0E && abyDELIM2_L2[1] == 0xCA && abyDELIM2_L2[2] == 0x14 && abyDELIM2_L2[3] == 0xBF) { int SFHL2 = atoi((const char*)(abyDELIM2_L2 + 4)); if (SFHL2 > 0 && (nFileSize > (size_t)(11 + SFHL2 + 11)) ) { bOK &= VSIFSeekL( fp, nFileSize - 11 - SFHL2 - 11 , SEEK_SET ) == 0; if ( bOK && VSIFReadL( abyL1_DELIM1, 1, 11, fp ) == 11 && abyL1_DELIM1[7] == 0x0A && abyL1_DELIM1[8] == 0x6E && abyL1_DELIM1[9] == 0x1D && abyL1_DELIM1[10] == 0x97 && memcmp(abyL1_DELIM1, abyDELIM2_L2 + 4, 7) == 0 ) { if (SFHL2 == nHeaderLen) { CSLDestroy(psFile->papszMetadata); psFile->papszMetadata = NULL; if ( (int)VSIFReadL( pachHeader, 1, SFHL2, fp ) != SFHL2 ) { CPL_IGNORE_RET_VAL_INT(VSIFCloseL(fp)); CPLFree(pachHeader); CPLFree(psFile); return NULL; } goto retry_read_header; } } } } if( !bOK ) { NITFClose(psFile); return NULL; } } /* -------------------------------------------------------------------- */ /* Collect segment info for the types we care about. */ /* -------------------------------------------------------------------- */ nNextData = nHeaderLen; nOffset = nHeaderLenOffset + 6; nOffset = NITFCollectSegmentInfo( psFile, nHeaderLen, nOffset,"IM",6, 10, &nNextData ); if (nOffset != -1) nOffset = NITFCollectSegmentInfo( psFile, nHeaderLen, nOffset, "GR", 4, 6, &nNextData); /* LA Called NUMX in NITF 2.1 */ if (nOffset != -1) nOffset = NITFCollectSegmentInfo( psFile, nHeaderLen, nOffset, "LA", 4, 3, &nNextData); if (nOffset != -1) nOffset = NITFCollectSegmentInfo( psFile, nHeaderLen, nOffset, "TX", 4, 5, &nNextData); if (nOffset != -1) nOffset = NITFCollectSegmentInfo( psFile, nHeaderLen, nOffset, "DE", 4, 9, &nNextData); if (nOffset != -1) nOffset = NITFCollectSegmentInfo( psFile, nHeaderLen, nOffset, "RE", 4, 7, &nNextData); else { NITFClose(psFile); return NULL; } /* -------------------------------------------------------------------- */ /* Is there User Define Header Data? (TREs) */ /* -------------------------------------------------------------------- */ if (nHeaderLen < nOffset + 5) { CPLError(CE_Failure, CPLE_AppDefined, "NITF header too small"); NITFClose(psFile); return NULL; } psFile->nTREBytes = atoi(NITFGetField( szTemp, pachHeader, nOffset, 5 )); if (psFile->nTREBytes < 0) { CPLError(CE_Failure, CPLE_AppDefined, "Invalid TRE size : %d", psFile->nTREBytes); NITFClose(psFile); return NULL; } nOffset += 5; if( psFile->nTREBytes == 3 ) { nOffset += 3; /* UDHOFL */ psFile->nTREBytes = 0; } else if( psFile->nTREBytes > 3 ) { nOffset += 3; /* UDHOFL */ psFile->nTREBytes -= 3; if (nHeaderLen < nOffset + psFile->nTREBytes) { CPLError(CE_Failure, CPLE_AppDefined, "NITF header too small"); NITFClose(psFile); return NULL; } psFile->pachTRE = (char *) VSI_MALLOC_VERBOSE(psFile->nTREBytes); if (psFile->pachTRE == NULL) { NITFClose(psFile); return NULL; } memcpy( psFile->pachTRE, pachHeader + nOffset, psFile->nTREBytes ); } /* -------------------------------------------------------------------- */ /* Is there Extended Header Data? (More TREs) */ /* -------------------------------------------------------------------- */ if( nHeaderLen > nOffset + 8 ) { int nXHDL = atoi(NITFGetField( szTemp, pachHeader, nOffset, 5 )); if (nXHDL < 0) { CPLError(CE_Failure, CPLE_AppDefined, "Invalid XHDL value : %d", nXHDL); NITFClose(psFile); return NULL; } nOffset += 5; /* XHDL */ if( nXHDL > 3 ) { char* pachNewTRE; nOffset += 3; /* XHDLOFL */ nXHDL -= 3; if (nHeaderLen < nOffset + nXHDL) { CPLError(CE_Failure, CPLE_AppDefined, "NITF header too small"); NITFClose(psFile); return NULL; } pachNewTRE = (char *) VSI_REALLOC_VERBOSE( psFile->pachTRE, psFile->nTREBytes + nXHDL ); if (pachNewTRE == NULL) { NITFClose(psFile); return NULL; } psFile->pachTRE = pachNewTRE; memcpy( psFile->pachTRE, pachHeader + nOffset, nXHDL ); psFile->nTREBytes += nXHDL; } } return psFile; } /************************************************************************/ /* NITFClose() */ /************************************************************************/ void NITFClose( NITFFile *psFile ) { int iSegment; for( iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++ ) { NITFSegmentInfo *psSegInfo = psFile->pasSegmentInfo + iSegment; if( psSegInfo->hAccess == NULL ) continue; if( EQUAL(psSegInfo->szSegmentType,"IM")) NITFImageDeaccess( (NITFImage *) psSegInfo->hAccess ); else if( EQUAL(psSegInfo->szSegmentType,"DE")) NITFDESDeaccess( (NITFDES *) psSegInfo->hAccess ); else { CPLAssert( FALSE ); } } CPLFree( psFile->pasSegmentInfo ); if( psFile->fp != NULL ) CPL_IGNORE_RET_VAL_INT(VSIFCloseL( psFile->fp )); CPLFree( psFile->pachHeader ); CSLDestroy( psFile->papszMetadata ); CPLFree( psFile->pachTRE ); if (psFile->psNITFSpecNode) CPLDestroyXMLNode(psFile->psNITFSpecNode); CPLFree( psFile ); } static int NITFGotoOffset(VSILFILE* fp, GUIntBig nLocation) { int bOK = TRUE; GUIntBig nCurrentLocation = VSIFTellL(fp); if (nLocation > nCurrentLocation) { GUIntBig nFileSize; size_t iFill; char cSpace = ' '; bOK &= VSIFSeekL(fp, 0, SEEK_END) == 0; nFileSize = VSIFTellL(fp); if (bOK && nLocation > nFileSize) { for(iFill = 0; bOK && iFill < nLocation - nFileSize; iFill++) bOK &= VSIFWriteL(&cSpace, 1, 1, fp) == 1; } else bOK &= VSIFSeekL(fp, nLocation, SEEK_SET) == 0; } else if (nLocation < nCurrentLocation) { bOK &= VSIFSeekL(fp, nLocation, SEEK_SET) == 0; } if( !bOK ) { CPLError(CE_Failure, CPLE_FileIO, "I/O error"); } return bOK; } /************************************************************************/ /* NITFCreate() */ /* */ /* Create a new uncompressed NITF file. */ /************************************************************************/ int NITFCreate( const char *pszFilename, int nPixels, int nLines, int nBands, int nBitsPerSample, const char *pszPVType, char **papszOptions ) { VSILFILE *fp; GUIntBig nCur = 0; int nOffset = 0, iBand, nIHSize, nNPPBH, nNPPBV; GIntBig nImageSize; int nNBPR, nNBPC; const char *pszIREP; const char *pszIC = CSLFetchNameValue(papszOptions,"IC"); int nCLevel; const char *pszNUMT; int nHL, nNUMT = 0; vsi_l_offset nOffsetUDIDL; const char *pszVersion; int iIM, nIM = 1; const char *pszNUMI; int iGS, nGS = 0; // number of graphic segment const char *pszNUMS; // graphic segment option string int bOK; if (nBands <= 0 || nBands > 99999) { CPLError(CE_Failure, CPLE_NotSupported, "Invalid band number : %d", nBands); return FALSE; } if( pszIC == NULL ) pszIC = "NC"; /* -------------------------------------------------------------------- */ /* Fetch some parameter overrides. */ /* -------------------------------------------------------------------- */ pszIREP = CSLFetchNameValue( papszOptions, "IREP" ); if( pszIREP == NULL ) pszIREP = "MONO"; pszNUMT = CSLFetchNameValue( papszOptions, "NUMT" ); if( pszNUMT != NULL ) { nNUMT = atoi(pszNUMT); if (nNUMT < 0 || nNUMT > 999) { CPLError( CE_Failure, CPLE_AppDefined, "Invalid NUMT value : %s", pszNUMT); return FALSE; } } pszNUMI = CSLFetchNameValue( papszOptions, "NUMI" ); if (pszNUMI != NULL) { nIM = atoi(pszNUMI); if (nIM < 1 || nIM > 999) { CPLError( CE_Failure, CPLE_AppDefined, "Invalid NUMI value : %s", pszNUMI); return FALSE; } if (nIM != 1 && !EQUAL(pszIC, "NC")) { CPLError( CE_Failure, CPLE_AppDefined, "Unable to create file with multiple images and compression at the same time"); return FALSE; } } // Reads and validates graphics segment number option pszNUMS = CSLFetchNameValue(papszOptions, "NUMS"); if (pszNUMS != NULL) { nGS = atoi(pszNUMS); if (nGS < 0 || nGS > 999) { CPLError(CE_Failure, CPLE_AppDefined, "Invalid NUMS value : %s", pszNUMS); return FALSE; } } /* -------------------------------------------------------------------- */ /* Compute raw image size, blocking factors and so forth. */ /* -------------------------------------------------------------------- */ nNPPBH = nPixels; nNPPBV = nLines; if( CSLFetchNameValue( papszOptions, "BLOCKXSIZE" ) != NULL ) nNPPBH = atoi(CSLFetchNameValue( papszOptions, "BLOCKXSIZE" )); if( CSLFetchNameValue( papszOptions, "BLOCKYSIZE" ) != NULL ) nNPPBV = atoi(CSLFetchNameValue( papszOptions, "BLOCKYSIZE" )); if( CSLFetchNameValue( papszOptions, "NPPBH" ) != NULL ) nNPPBH = atoi(CSLFetchNameValue( papszOptions, "NPPBH" )); if( CSLFetchNameValue( papszOptions, "NPPBV" ) != NULL ) nNPPBV = atoi(CSLFetchNameValue( papszOptions, "NPPBV" )); if ( (EQUAL(pszIC, "NC") || EQUAL(pszIC, "C8")) && (nPixels > 8192 || nLines > 8192) && nNPPBH == nPixels && nNPPBV == nLines) { /* See MIL-STD-2500-C, paragraph 5.4.2.2-d (#3263) */ nNBPR = 1; nNBPC = 1; nNPPBH = 0; nNPPBV = 0; nImageSize = ((nBitsPerSample)/8) * ((GIntBig) nPixels *nLines) * nBands; } else if ( (EQUAL(pszIC, "NC") || EQUAL(pszIC, "C8")) && nPixels > 8192 && nNPPBH == nPixels) { /* See MIL-STD-2500-C, paragraph 5.4.2.2-d */ nNBPR = 1; nNPPBH = 0; nNBPC = (nLines + nNPPBV - 1) / nNPPBV; if ( nNBPC > 9999 ) { CPLError( CE_Failure, CPLE_AppDefined, "Unable to create file %s,\n" "Too many blocks : %d x %d", pszFilename, nNBPR, nNBPC); return FALSE; } nImageSize = ((nBitsPerSample)/8) * ((GIntBig) nPixels * (nNBPC * nNPPBV)) * nBands; } else if ( (EQUAL(pszIC, "NC") || EQUAL(pszIC, "C8")) && nLines > 8192 && nNPPBV == nLines) { /* See MIL-STD-2500-C, paragraph 5.4.2.2-d */ nNBPC = 1; nNPPBV = 0; nNBPR = (nPixels + nNPPBH - 1) / nNPPBH; if ( nNBPR > 9999 ) { CPLError( CE_Failure, CPLE_AppDefined, "Unable to create file %s,\n" "Too many blocks : %d x %d", pszFilename, nNBPR, nNBPC); return FALSE; } nImageSize = ((nBitsPerSample)/8) * ((GIntBig) nLines * (nNBPR * nNPPBH)) * nBands; } else { if( nNPPBH <= 0 || nNPPBV <= 0 || nNPPBH > 9999 || nNPPBV > 9999 ) nNPPBH = nNPPBV = 256; nNBPR = (nPixels + nNPPBH - 1) / nNPPBH; nNBPC = (nLines + nNPPBV - 1) / nNPPBV; if ( nNBPR > 9999 || nNBPC > 9999 ) { CPLError( CE_Failure, CPLE_AppDefined, "Unable to create file %s,\n" "Too many blocks : %d x %d", pszFilename, nNBPR, nNBPC); return FALSE; } nImageSize = ((nBitsPerSample)/8) * ((GIntBig) nNBPR * nNBPC) * nNPPBH * nNPPBV * nBands; } if (EQUAL(pszIC, "NC")) { if ((double)nImageSize >= 1e10 - 1) { CPLError( CE_Failure, CPLE_AppDefined, "Unable to create file %s,\n" "Too big image size : " CPL_FRMT_GUIB, pszFilename, nImageSize ); return FALSE; } if ((double)(nImageSize * nIM) >= 1e12 - 1) { CPLError( CE_Failure, CPLE_AppDefined, "Unable to create file %s,\n" "Too big file size : " CPL_FRMT_GUIB, pszFilename, nImageSize * nIM ); return FALSE; } } /* -------------------------------------------------------------------- */ /* Open new file. */ /* -------------------------------------------------------------------- */ fp = VSIFOpenL( pszFilename, "wb+" ); if( fp == NULL ) { CPLError( CE_Failure, CPLE_OpenFailed, "Unable to create file %s,\n" "check path and permissions.", pszFilename ); return FALSE; } /* -------------------------------------------------------------------- */ /* Work out the version we are producing. For now we really */ /* only support creating NITF02.10 or the nato analog */ /* NSIF01.00. */ /* -------------------------------------------------------------------- */ pszVersion = CSLFetchNameValue( papszOptions, "FHDR" ); if( pszVersion == NULL ) pszVersion = "NITF02.10"; else if( !EQUAL(pszVersion,"NITF02.10") && !EQUAL(pszVersion,"NSIF01.00") ) { CPLError( CE_Warning, CPLE_AppDefined, "FHDR=%s not supported, switching to NITF02.10.", pszVersion ); pszVersion = "NITF02.10"; } /* -------------------------------------------------------------------- */ /* Prepare the file header. */ /* -------------------------------------------------------------------- */ #define PLACE(location,name,text) { \ const char* _text = text; \ bOK &= NITFGotoOffset(fp, location); \ bOK &= VSIFWriteL(_text, 1, strlen(_text), fp) == strlen(_text); } #define OVR(width,location,name,text) { \ const char* _text = text; \ const char *pszParmValueMacro; \ size_t to_write; \ pszParmValueMacro = CSLFetchNameValue( papszOptions, #name ); \ if( pszParmValueMacro == NULL ) \ pszParmValueMacro = _text; \ bOK &= NITFGotoOffset(fp, location); \ to_write = MIN(width,strlen(pszParmValueMacro)); \ bOK &= VSIFWriteL(pszParmValueMacro, 1, to_write, fp) == to_write; } #define WRITE_BYTE(location, val) { \ char cVal = val; \ bOK &= NITFGotoOffset(fp, location); \ bOK &= VSIFWriteL(&cVal, 1, 1, fp) == 1; } bOK = VSIFSeekL(fp, 0, SEEK_SET) == 0; PLACE ( 0, FDHR_FVER, pszVersion ); OVR( 2, 9, CLEVEL, "03" ); /* Patched at the end */ PLACE ( 11, STYPE ,"BF01" ); OVR(10, 15, OSTAID ,"GDAL" ); OVR(14, 25, FDT ,"20021216151629" ); OVR(80, 39, FTITLE ,"" ); OVR( 1,119, FSCLAS ,"U" ); OVR( 2,120, FSCLSY ,"" ); OVR(11,122, FSCODE ,"" ); OVR( 2,133, FSCTLH ,"" ); OVR(20,135, FSREL ,"" ); OVR( 2,155, FSDCTP ,"" ); OVR( 8,157, FSDCDT ,"" ); OVR( 4,165, FSDCXM ,"" ); OVR( 1,169, FSDG ,"" ); OVR( 8,170, FSDGDT ,"" ); OVR(43,178, FSCLTX ,"" ); OVR( 1,221, FSCATP ,"" ); OVR(40,222, FSCAUT ,"" ); OVR( 1,262, FSCRSN ,"" ); OVR( 8,263, FSSRDT ,"" ); OVR(15,271, FSCTLN ,"" ); OVR( 5,286, FSCOP ,"00000" ); OVR( 5,291, FSCPYS ,"00000" ); PLACE (296, ENCRYP ,"0" ); WRITE_BYTE(297, 0x00); /* FBKGC */ WRITE_BYTE(298, 0x00); WRITE_BYTE(299, 0x00); OVR(24,300, ONAME ,"" ); OVR(18,324, OPHONE ,"" ); PLACE (342, FL ,"????????????" ); PLACE (354, HL ,"??????" ); PLACE (360, NUMI ,CPLSPrintf("%03d", nIM) ); nHL = 363; for(iIM=0;iIM<nIM;iIM++) { PLACE (nHL, LISHi ,"??????" ); PLACE (nHL + 6, LIi ,CPLSPrintf("%010" CPL_FRMT_GB_WITHOUT_PREFIX "d", nImageSize) ); nHL += 6 + 10; } // Creates Header entries for graphic segment // NUMS: number of segment // For each segment: // LSSH[i]: subheader length (4 byte), set to be 258, the size for // minimal amount of information. // LS[i] data length (6 byte) PLACE (nHL, NUMS ,CPLSPrintf("%03d",nGS) ); nHL += 3; // Move three characters for (iGS = 0; iGS < nGS; iGS++) { PLACE (nHL, LSSHi ,CPLSPrintf("0000") ); PLACE (nHL + 4, LSi ,CPLSPrintf("000000") ); nHL += 4 + 6; } PLACE (nHL, NUMX ,"000" ); PLACE (nHL + 3, NUMT ,CPLSPrintf("%03d",nNUMT) ); PLACE (nHL + 6, LTSHnLTn ,"" ); nHL += 6 + (4+5) * nNUMT; PLACE (nHL, NUMDES ,"000" ); nHL += 3; PLACE (nHL, NUMRES ,"000" ); nHL += 3; PLACE (nHL, UDHDL ,"00000" ); nHL += 5; PLACE (nHL, XHDL ,"00000" ); nHL += 5; if( CSLFetchNameValue(papszOptions,"FILE_TRE") != NULL ) { bOK &= NITFWriteTREsFromOptions( fp, nHL - 10, &nHL, papszOptions, "FILE_TRE=" ); } if (nHL > 999999) { CPLError(CE_Failure, CPLE_AppDefined, "Too big file header length : %d", nHL); CPL_IGNORE_RET_VAL_INT(VSIFCloseL( fp )); return FALSE; } // update header length PLACE (354, HL ,CPLSPrintf("%06d",nHL) ); nCur = nHL; /* -------------------------------------------------------------------- */ /* Prepare the image header. */ /* -------------------------------------------------------------------- */ for(iIM=0;iIM<nIM;iIM++) { char** papszIREPBANDTokens = NULL; char** papszISUBCATTokens = NULL; if( CSLFetchNameValue(papszOptions,"IREPBAND") != NULL ) { papszIREPBANDTokens = CSLTokenizeStringComplex( CSLFetchNameValue(papszOptions,"IREPBAND"), ",", 0, 0 ); if( papszIREPBANDTokens != NULL && CSLCount( papszIREPBANDTokens ) != nBands) { CSLDestroy( papszIREPBANDTokens ); papszIREPBANDTokens = NULL; } } if( CSLFetchNameValue(papszOptions,"ISUBCAT") != NULL ) { papszISUBCATTokens = CSLTokenizeStringComplex( CSLFetchNameValue(papszOptions,"ISUBCAT"), ",", 0, 0 ); if( papszISUBCATTokens != NULL && CSLCount( papszISUBCATTokens ) != nBands) { CSLDestroy( papszISUBCATTokens ); papszISUBCATTokens = NULL; } } bOK &= VSIFSeekL(fp, nCur, SEEK_SET) == 0; PLACE (nCur+ 0, IM , "IM" ); OVR(10,nCur+ 2, IID1 , "Missing" ); OVR(14,nCur+ 12, IDATIM , "20021216151629" ); OVR(17,nCur+ 26, TGTID , "" ); OVR(80,nCur+ 43, IID2 , "" ); OVR( 1,nCur+123, ISCLAS , "U" ); OVR( 2,nCur+124, ISCLSY , "" ); OVR(11,nCur+126, ISCODE , "" ); OVR( 2,nCur+137, ISCTLH , "" ); OVR(20,nCur+139, ISREL , "" ); OVR( 2,nCur+159, ISDCTP , "" ); OVR( 8,nCur+161, ISDCDT , "" ); OVR( 4,nCur+169, ISDCXM , "" ); OVR( 1,nCur+173, ISDG , "" ); OVR( 8,nCur+174, ISDGDT , "" ); OVR(43,nCur+182, ISCLTX , "" ); OVR( 1,nCur+225, ISCATP , "" ); OVR(40,nCur+226, ISCAUT , "" ); OVR( 1,nCur+266, ISCRSN , "" ); OVR( 8,nCur+267, ISSRDT , "" ); OVR(15,nCur+275, ISCTLN , "" ); PLACE (nCur+290, ENCRYP , "0" ); OVR(42,nCur+291, ISORCE , "Unknown" ); PLACE (nCur+333, NROWS , CPLSPrintf("%08d", nLines) ); PLACE (nCur+341, NCOLS , CPLSPrintf("%08d", nPixels) ); PLACE (nCur+349, PVTYPE , pszPVType ); PLACE (nCur+352, IREP , pszIREP ); OVR( 8,nCur+360, ICAT , "VIS" ); OVR( 2,nCur+368, ABPP , CPLSPrintf("%02d",nBitsPerSample) ); OVR( 1,nCur+370, PJUST , "R" ); OVR( 1,nCur+371, ICORDS , " " ); nOffset = 372; { const char *pszParmValue; pszParmValue = CSLFetchNameValue( papszOptions, "ICORDS" ); if( pszParmValue == NULL ) pszParmValue = " "; if( *pszParmValue != ' ' ) { OVR(60,nCur+nOffset, IGEOLO, "" ); nOffset += 60; } } { const char* pszICOM = CSLFetchNameValue( papszOptions, "ICOM"); if (pszICOM != NULL) { int nLenICOM = (int)strlen(pszICOM); int nICOM = (79 + nLenICOM) / 80; size_t nToWrite; if (nICOM > 9) { CPLError(CE_Warning, CPLE_NotSupported, "ICOM will be truncated"); nICOM = 9; } PLACE (nCur+nOffset, NICOM , CPLSPrintf("%01d",nICOM) ); nToWrite = MIN(nICOM * 80, nLenICOM); bOK &= VSIFWriteL(pszICOM, 1, nToWrite, fp) == nToWrite; nOffset += nICOM * 80; } else { PLACE (nCur+nOffset, NICOM , "0" ); } } OVR( 2,nCur+nOffset+1, IC , "NC" ); if( pszIC[0] != 'N' ) { OVR( 4,nCur+nOffset+3, COMRAT , " " ); nOffset += 4; } if (nBands <= 9) { PLACE (nCur+nOffset+3, NBANDS , CPLSPrintf("%d",nBands) ); } else { PLACE (nCur+nOffset+3, NBANDS , "0" ); PLACE (nCur+nOffset+4, XBANDS , CPLSPrintf("%05d",nBands) ); nOffset += 5; } nOffset += 4; /* -------------------------------------------------------------------- */ /* Per band info */ /* -------------------------------------------------------------------- */ for( iBand = 0; iBand < nBands; iBand++ ) { const char *pszIREPBAND = "M"; if( papszIREPBANDTokens != NULL ) { if (strlen(papszIREPBANDTokens[iBand]) > 2) { papszIREPBANDTokens[iBand][2] = '\0'; CPLError(CE_Warning, CPLE_NotSupported, "Truncating IREPBAND[%d] to '%s'", iBand + 1, papszIREPBANDTokens[iBand]); } pszIREPBAND = papszIREPBANDTokens[iBand]; } else if( EQUAL(pszIREP,"RGB/LUT") ) pszIREPBAND = "LU"; else if( EQUAL(pszIREP,"RGB") ) { if( iBand == 0 ) pszIREPBAND = "R"; else if( iBand == 1 ) pszIREPBAND = "G"; else if( iBand == 2 ) pszIREPBAND = "B"; } else if( STARTS_WITH_CI(pszIREP, "YCbCr") ) { if( iBand == 0 ) pszIREPBAND = "Y"; else if( iBand == 1 ) pszIREPBAND = "Cb"; else if( iBand == 2 ) pszIREPBAND = "Cr"; } PLACE(nCur+nOffset+ 0, IREPBANDn, pszIREPBAND ); if( papszISUBCATTokens != NULL ) { if (strlen(papszISUBCATTokens[iBand]) > 6) { papszISUBCATTokens[iBand][6] = '\0'; CPLError(CE_Warning, CPLE_NotSupported, "Truncating ISUBCAT[%d] to '%s'", iBand + 1, papszISUBCATTokens[iBand]); } PLACE(nCur+nOffset+ 2, ISUBCATn, papszISUBCATTokens[iBand] ); } // else // PLACE(nCur+nOffset+ 2, ISUBCATn, "" ); PLACE(nCur+nOffset+ 8, IFCn , "N" ); // PLACE(nCur+nOffset+ 9, IMFLTn, "" ); if( !EQUAL(pszIREP,"RGB/LUT") ) { PLACE(nCur+nOffset+12, NLUTSn, "0" ); nOffset += 13; } else { int iC, nCount=256; if( CSLFetchNameValue(papszOptions,"LUT_SIZE") != NULL ) nCount = atoi(CSLFetchNameValue(papszOptions,"LUT_SIZE")); if (!(nCount >= 0 && nCount <= 99999)) { CPLError(CE_Warning, CPLE_AppDefined, "Invalid LUT value : %d. Defaulting to 256", nCount); nCount = 256; } PLACE(nCur+nOffset+12, NLUTSn, "3" ); PLACE(nCur+nOffset+13, NELUTn, CPLSPrintf("%05d",nCount) ); for( iC = 0; iC < nCount; iC++ ) { WRITE_BYTE(nCur+nOffset+18+iC+ 0, (char) iC); WRITE_BYTE(nCur+nOffset+18+iC+nCount*1, (char) iC); WRITE_BYTE(nCur+nOffset+18+iC+nCount*2, (char) iC); } nOffset += 18 + nCount*3; } } CSLDestroy(papszIREPBANDTokens); CSLDestroy(papszISUBCATTokens); /* -------------------------------------------------------------------- */ /* Remainder of image header info. */ /* -------------------------------------------------------------------- */ PLACE(nCur+nOffset+ 0, ISYNC , "0" ); /* RGB JPEG compressed NITF requires IMODE=P (see #3345) */ if (nBands >= 3 && (EQUAL(pszIC, "C3") || EQUAL(pszIC, "M3"))) { PLACE(nCur+nOffset+ 1, IMODE , "P" ); } else { PLACE(nCur+nOffset+ 1, IMODE , "B" ); } PLACE(nCur+nOffset+ 2, NBPR , CPLSPrintf("%04d",nNBPR) ); PLACE(nCur+nOffset+ 6, NBPC , CPLSPrintf("%04d",nNBPC) ); PLACE(nCur+nOffset+ 10, NPPBH , CPLSPrintf("%04d",nNPPBH) ); PLACE(nCur+nOffset+ 14, NPPBV , CPLSPrintf("%04d",nNPPBV) ); PLACE(nCur+nOffset+ 18, NBPP , CPLSPrintf("%02d",nBitsPerSample) ); PLACE(nCur+nOffset+ 20, IDLVL , "001" ); PLACE(nCur+nOffset+ 23, IALVL , "000" ); PLACE(nCur+nOffset+ 26, ILOC , "0000000000" ); PLACE(nCur+nOffset+ 36, IMAG , "1.0 " ); PLACE(nCur+nOffset+ 40, UDIDL , "00000" ); PLACE(nCur+nOffset+ 45, IXSHDL, "00000" ); nOffsetUDIDL = nCur + nOffset + 40; nOffset += 50; /* -------------------------------------------------------------------- */ /* Add BLOCKA TRE if requested. */ /* -------------------------------------------------------------------- */ if( CSLFetchNameValue(papszOptions,"BLOCKA_BLOCK_COUNT") != NULL ) { NITFWriteBLOCKA( fp, nOffsetUDIDL, &nOffset, papszOptions ); } if( CSLFetchNameValue(papszOptions,"TRE") != NULL ) { bOK &= NITFWriteTREsFromOptions( fp, nOffsetUDIDL, &nOffset, papszOptions, "TRE=" ); } /* -------------------------------------------------------------------- */ /* Update the image header length in the file header. */ /* -------------------------------------------------------------------- */ nIHSize = nOffset; if (nIHSize > 999999) { CPLError(CE_Failure, CPLE_AppDefined, "Too big image header length : %d", nIHSize); CPL_IGNORE_RET_VAL_INT(VSIFCloseL( fp )); return FALSE; } PLACE( 363 + iIM * 16, LISH1, CPLSPrintf("%06d",nIHSize) ); nCur += nIHSize + nImageSize; } /* -------------------------------------------------------------------- */ /* Compute and update CLEVEL ("complexity" level). */ /* See: http://164.214.2.51/ntb/baseline/docs/2500b/2500b_not2.pdf */ /* page 96u */ /* -------------------------------------------------------------------- */ nCLevel = 3; if (nBands > 9 || nIM > 20 || nPixels > 2048 || nLines > 2048 || nNPPBH > 2048 || nNPPBV > 2048 || nCur > 52428799 ) { nCLevel = 5; } if (nPixels > 8192 || nLines > 8192 || nNPPBH > 8192 || nNPPBV > 8192 || nCur > 1073741833) { nCLevel = 6; } if (nBands > 256 || nPixels > 65536 || nLines > 65536 || nCur > 2147483647) { nCLevel = 7; } OVR( 2, 9, CLEVEL, CPLSPrintf("%02d", nCLevel) ); /* -------------------------------------------------------------------- */ /* Update total file length */ /* -------------------------------------------------------------------- */ /* According to the spec, CLEVEL 7 supports up to 10,737,418,330 bytes */ /* but we can support technically much more */ if (EQUAL(pszIC, "NC") && GUINTBIG_TO_DOUBLE(nCur) >= 1e12 - 1) { CPLError(CE_Failure, CPLE_AppDefined, "Too big file : " CPL_FRMT_GUIB, nCur); CPL_IGNORE_RET_VAL_INT(VSIFCloseL( fp )); return FALSE; } PLACE( 342, FL, CPLSPrintf( "%012" CPL_FRMT_GB_WITHOUT_PREFIX "d", nCur) ); /* -------------------------------------------------------------------- */ /* Grow file to full required size by writing one byte at the end. */ /* -------------------------------------------------------------------- */ if( EQUAL(pszIC,"NC") ) { char cNul = 0; bOK &= VSIFSeekL( fp, nCur-1, SEEK_SET ) == 0; bOK &= VSIFWriteL( &cNul, 1, 1, fp ) == 1; } if( VSIFCloseL( fp ) != 0 ) bOK = FALSE; return bOK; } /************************************************************************/ /* NITFWriteTRE() */ /************************************************************************/ static int NITFWriteTRE( VSILFILE* fp, vsi_l_offset nOffsetUDIDL, int *pnOffset, const char *pszTREName, char *pabyTREData, int nTREDataSize ) { char szTemp[12]; int nOldOffset; int bOK = TRUE; /* -------------------------------------------------------------------- */ /* Update IXSHDL. */ /* -------------------------------------------------------------------- */ bOK &= VSIFSeekL(fp, nOffsetUDIDL + 5, SEEK_SET) == 0; bOK &= VSIFReadL(szTemp, 1, 5, fp) == 5; szTemp[5] = 0; nOldOffset = atoi(szTemp); if( nOldOffset == 0 ) { nOldOffset = 3; PLACE(nOffsetUDIDL+10, IXSOFL, "000" ); *pnOffset += 3; } if (nOldOffset + 11 + nTREDataSize > 99999 || nTREDataSize > 99999) { CPLError(CE_Failure, CPLE_AppDefined, "Too big TRE to be written"); return FALSE; } snprintf( szTemp, sizeof(szTemp), "%05d", nOldOffset + 11 + nTREDataSize ); PLACE( nOffsetUDIDL + 5, IXSHDL, szTemp ); /* -------------------------------------------------------------------- */ /* Create TRE prefix. */ /* -------------------------------------------------------------------- */ snprintf( szTemp, sizeof(szTemp), "%-6s%05d", pszTREName, nTREDataSize ); bOK &= VSIFSeekL(fp, nOffsetUDIDL + 10 + nOldOffset, SEEK_SET) == 0; bOK &= VSIFWriteL(szTemp, 11, 1, fp) == 1; bOK &= (int)VSIFWriteL(pabyTREData, 1, nTREDataSize, fp) == nTREDataSize; /* -------------------------------------------------------------------- */ /* Increment values. */ /* -------------------------------------------------------------------- */ *pnOffset += nTREDataSize + 11; return bOK; } /************************************************************************/ /* NITFWriteTREsFromOptions() */ /************************************************************************/ static int NITFWriteTREsFromOptions( VSILFILE* fp, vsi_l_offset nOffsetUDIDL, int *pnOffset, char **papszOptions, const char* pszTREPrefix ) { int bIgnoreBLOCKA = CSLFetchNameValue(papszOptions,"BLOCKA_BLOCK_COUNT") != NULL; int iOption; int nTREPrefixLen = (int)strlen(pszTREPrefix); if( papszOptions == NULL ) return TRUE; for( iOption = 0; papszOptions[iOption] != NULL; iOption++ ) { const char *pszEscapedContents; char *pszUnescapedContents; char *pszTREName; int nContentLength; const char* pszSpace; if( !EQUALN(papszOptions[iOption], pszTREPrefix, nTREPrefixLen) ) continue; if( STARTS_WITH_CI(papszOptions[iOption]+nTREPrefixLen, "BLOCKA=") && bIgnoreBLOCKA ) continue; /* We do no longer use CPLParseNameValue() as it removes leading spaces */ /* from the value (see #3088) */ pszSpace = strchr(papszOptions[iOption]+nTREPrefixLen, '='); if (pszSpace == NULL) { CPLError(CE_Failure, CPLE_AppDefined, "Could not parse creation options %s", papszOptions[iOption]+nTREPrefixLen); return FALSE; } pszTREName = CPLStrdup(papszOptions[iOption]+nTREPrefixLen); pszTREName[MIN(6, pszSpace - (papszOptions[iOption]+nTREPrefixLen))] = '\0'; pszEscapedContents = pszSpace + 1; pszUnescapedContents = CPLUnescapeString( pszEscapedContents, &nContentLength, CPLES_BackslashQuotable ); if( !NITFWriteTRE( fp, nOffsetUDIDL, pnOffset, pszTREName, pszUnescapedContents, nContentLength ) ) { CPLFree( pszTREName ); CPLFree( pszUnescapedContents ); return FALSE; } CPLFree( pszTREName ); CPLFree( pszUnescapedContents ); } return TRUE; } /************************************************************************/ /* NITFWriteBLOCKA() */ /************************************************************************/ static int NITFWriteBLOCKA( VSILFILE* fp, vsi_l_offset nOffsetUDIDL, int *pnOffset, char **papszOptions ) { static const char * const apszFields[] = { "BLOCK_INSTANCE", "0", "2", "N_GRAY", "2", "5", "L_LINES", "7", "5", "LAYOVER_ANGLE", "12", "3", "SHADOW_ANGLE", "15", "3", "BLANKS", "18", "16", "FRLC_LOC", "34", "21", "LRLC_LOC", "55", "21", "LRFC_LOC", "76", "21", "FRFC_LOC", "97", "21", NULL, NULL, NULL }; int nBlockCount = atoi(CSLFetchNameValue( papszOptions, "BLOCKA_BLOCK_COUNT" )); int iBlock; /* ==================================================================== */ /* Loop over all the blocks we have metadata for. */ /* ==================================================================== */ for( iBlock = 1; iBlock <= nBlockCount; iBlock++ ) { char szBLOCKA[123]; int iField; /* -------------------------------------------------------------------- */ /* Write all fields. */ /* -------------------------------------------------------------------- */ for( iField = 0; apszFields[iField*3] != NULL; iField++ ) { char szFullFieldName[64]; int iStart = atoi(apszFields[iField*3+1]); int iSize = atoi(apszFields[iField*3+2]); const char *pszValue; snprintf( szFullFieldName, sizeof(szFullFieldName), "BLOCKA_%s_%02d", apszFields[iField*3 + 0], iBlock ); pszValue = CSLFetchNameValue( papszOptions, szFullFieldName ); if( pszValue == NULL ) pszValue = ""; if (strlen(pszValue) > (size_t)iSize) { CPLError(CE_Failure, CPLE_AppDefined, "Too much data for %s. Got %d bytes, max allowed is %d", szFullFieldName, (int)strlen(pszValue), iSize); return FALSE; } /* Right align value and left pad with spaces */ memset( szBLOCKA + iStart, ' ', iSize ); /* unsigned is always >= 0 */ /* memcpy( szBLOCKA + iStart + MAX((size_t)0,iSize-strlen(pszValue)), */ memcpy( szBLOCKA + iStart + iSize-strlen(pszValue), pszValue, strlen(pszValue) ); } // required field - semantics unknown. memcpy( szBLOCKA + 118, "010.0", 5); if( !NITFWriteTRE( fp, nOffsetUDIDL, pnOffset, "BLOCKA", szBLOCKA, 123 ) ) return FALSE; } return TRUE; } /************************************************************************/ /* NITFCollectSegmentInfo() */ /* */ /* Collect the information about a set of segments of a */ /* particular type from the NITF file header, and add them to */ /* the segment list in the NITFFile object. */ /************************************************************************/ static int NITFCollectSegmentInfo( NITFFile *psFile, int nFileHeaderLen, int nOffset, const char szType[3], int nHeaderLenSize, int nDataLenSize, GUIntBig *pnNextData ) { char szTemp[12]; int nCount, nSegDefSize, iSegment; /* -------------------------------------------------------------------- */ /* Get the segment count, and grow the segmentinfo array */ /* accordingly. */ /* -------------------------------------------------------------------- */ if ( nFileHeaderLen < nOffset + 3 ) { CPLError(CE_Failure, CPLE_AppDefined, "Not enough bytes to read segment count"); return -1; } NITFGetField( szTemp, psFile->pachHeader, nOffset, 3 ); nCount = atoi(szTemp); if( nCount <= 0 ) return nOffset + 3; nSegDefSize = nCount * (nHeaderLenSize + nDataLenSize); if ( nFileHeaderLen < nOffset + 3 + nSegDefSize) { CPLError(CE_Failure, CPLE_AppDefined, "Not enough bytes to read segment info"); return -1; } if( psFile->pasSegmentInfo == NULL ) psFile->pasSegmentInfo = (NITFSegmentInfo *) CPLMalloc( sizeof(NITFSegmentInfo) * nCount ); else psFile->pasSegmentInfo = (NITFSegmentInfo *) CPLRealloc( psFile->pasSegmentInfo, sizeof(NITFSegmentInfo) * (psFile->nSegmentCount+nCount) ); /* -------------------------------------------------------------------- */ /* Collect detailed about segment. */ /* -------------------------------------------------------------------- */ for( iSegment = 0; iSegment < nCount; iSegment++ ) { NITFSegmentInfo *psInfo = psFile->pasSegmentInfo+psFile->nSegmentCount; psInfo->nDLVL = -1; psInfo->nALVL = -1; psInfo->nLOC_R = -1; psInfo->nLOC_C = -1; psInfo->nCCS_R = -1; psInfo->nCCS_C = -1; psInfo->hAccess = NULL; strncpy( psInfo->szSegmentType, szType, sizeof(psInfo->szSegmentType) ); psInfo->szSegmentType[sizeof(psInfo->szSegmentType)-1] = '\0'; psInfo->nSegmentHeaderSize = atoi(NITFGetField(szTemp, psFile->pachHeader, nOffset + 3 + iSegment * (nHeaderLenSize+nDataLenSize), nHeaderLenSize)); if (strchr(szTemp, '-') != NULL) /* Avoid negative values being mapped to huge unsigned values */ { CPLError(CE_Failure, CPLE_AppDefined, "Invalid segment header size : %s", szTemp); return -1; } if (strcmp(szType, "DE") == 0 && psInfo->nSegmentHeaderSize == 207) { /* DMAAC A.TOC files have a wrong header size. It says 207 but it is 209 really */ psInfo->nSegmentHeaderSize = 209; } psInfo->nSegmentSize = CPLScanUIntBig(NITFGetField(szTemp,psFile->pachHeader, nOffset + 3 + iSegment * (nHeaderLenSize+nDataLenSize) + nHeaderLenSize, nDataLenSize), nDataLenSize); if (strchr(szTemp, '-') != NULL) /* Avoid negative values being mapped to huge unsigned values */ { CPLError(CE_Failure, CPLE_AppDefined, "Invalid segment size : %s", szTemp); return -1; } psInfo->nSegmentHeaderStart = *pnNextData; psInfo->nSegmentStart = *pnNextData + psInfo->nSegmentHeaderSize; *pnNextData += (psInfo->nSegmentHeaderSize+psInfo->nSegmentSize); psFile->nSegmentCount++; } return nOffset + nSegDefSize + 3; } /************************************************************************/ /* NITFGetField() */ /* */ /* Copy a field from a passed in header buffer into a temporary */ /* buffer and zero terminate it. */ /************************************************************************/ char *NITFGetField( char *pszTarget, const char *pszSource, int nStart, int nLength ) { memcpy( pszTarget, pszSource + nStart, nLength ); pszTarget[nLength] = '\0'; return pszTarget; } /************************************************************************/ /* NITFFindTRE() */ /************************************************************************/ const char *NITFFindTRE( const char *pszTREData, int nTREBytes, const char *pszTag, int *pnFoundTRESize ) { char szTemp[100]; while( nTREBytes >= 11 ) { int nThisTRESize = atoi(NITFGetField(szTemp, pszTREData, 6, 5 )); if (nThisTRESize < 0) { NITFGetField(szTemp, pszTREData, 0, 6 ); CPLError(CE_Failure, CPLE_AppDefined, "Invalid size (%d) for TRE %s", nThisTRESize, szTemp); return NULL; } if (nTREBytes - 11 < nThisTRESize) { NITFGetField(szTemp, pszTREData, 0, 6 ); if (STARTS_WITH_CI(szTemp, "RPFIMG")) { /* See #3848 */ CPLDebug("NITF", "Adjusting RPFIMG TRE size from %d to %d, which is the remaining size", nThisTRESize, nTREBytes - 11); nThisTRESize = nTREBytes - 11; } else { CPLError(CE_Failure, CPLE_AppDefined, "Cannot read %s TRE. Not enough bytes : remaining %d, expected %d", szTemp, nTREBytes - 11, nThisTRESize); return NULL; } } if( EQUALN(pszTREData,pszTag,6) ) { if( pnFoundTRESize != NULL ) *pnFoundTRESize = nThisTRESize; return pszTREData + 11; } nTREBytes -= (nThisTRESize + 11); pszTREData += (nThisTRESize + 11); } return NULL; } /************************************************************************/ /* NITFFindTREByIndex() */ /************************************************************************/ const char *NITFFindTREByIndex( const char *pszTREData, int nTREBytes, const char *pszTag, int nTreIndex, int *pnFoundTRESize ) { char szTemp[100]; while( nTREBytes >= 11 ) { int nThisTRESize = atoi(NITFGetField(szTemp, pszTREData, 6, 5 )); if (nThisTRESize < 0) { NITFGetField(szTemp, pszTREData, 0, 6 ); CPLError(CE_Failure, CPLE_AppDefined, "Invalid size (%d) for TRE %s", nThisTRESize, szTemp); return NULL; } if (nTREBytes - 11 < nThisTRESize) { NITFGetField(szTemp, pszTREData, 0, 6 ); if (STARTS_WITH_CI(szTemp, "RPFIMG")) { /* See #3848 */ CPLDebug("NITF", "Adjusting RPFIMG TRE size from %d to %d, which is the remaining size", nThisTRESize, nTREBytes - 11); nThisTRESize = nTREBytes - 11; } else { CPLError(CE_Failure, CPLE_AppDefined, "Cannot read %s TRE. Not enough bytes : remaining %d, expected %d", szTemp, nTREBytes - 11, nThisTRESize); return NULL; } } if( EQUALN(pszTREData,pszTag,6) ) { if ( nTreIndex <= 0) { if( pnFoundTRESize != NULL ) *pnFoundTRESize = nThisTRESize; return pszTREData + 11; } /* Found a previous one - skip it ... */ nTreIndex--; } nTREBytes -= (nThisTRESize + 11); pszTREData += (nThisTRESize + 11); } return NULL; } /************************************************************************/ /* NITFExtractMetadata() */ /************************************************************************/ void NITFExtractMetadata( char ***ppapszMetadata, const char *pachHeader, int nStart, int nLength, const char *pszName ) { char szWork[400]; char* pszWork; if( nLength <= 0 ) return; if (nLength >= (int)(sizeof(szWork) - 1)) pszWork = (char*)CPLMalloc(nLength + 1); else pszWork = szWork; /* trim white space */ while( nLength > 0 && pachHeader[nStart + nLength - 1] == ' ' ) nLength--; memcpy( pszWork, pachHeader + nStart, nLength ); pszWork[nLength] = '\0'; *ppapszMetadata = CSLSetNameValue( *ppapszMetadata, pszName, pszWork ); if (szWork != pszWork) CPLFree(pszWork); } /************************************************************************/ /* NITF_WGS84_Geocentric_Latitude_To_Geodetic_Latitude() */ /* */ /* The input is a geocentric latitude in degrees. The output */ /* is a geodetic latitude in degrees. */ /************************************************************************/ /* * "The angle L' is called "geocentric latitude" and is defined as the * angle between the equatorial plane and the radius from the geocenter. * * The angle L is called "geodetic latitude" and is defined as the angle * between the equatorial plane and the normal to the surface of the * ellipsoid. The word "latitude" usually means geodetic latitude. This * is the basis for most of the maps and charts we use. The normal to the * surface is the direction that a plumb bob would hang were it not for * local anomalies in the earth's gravitational field." */ double NITF_WGS84_Geocentric_Latitude_To_Geodetic_Latitude( double dfLat ) { /* WGS84 Ellipsoid */ const double a = 6378137.0; const double b = 6356752.3142; /* convert to radians */ dfLat = dfLat * M_PI / 180.0; /* convert to geodetic */ dfLat = atan( ((a*a)/(b*b)) * tan(dfLat) ); /* convert back to degrees */ dfLat = dfLat * 180.0 / M_PI; return dfLat; } /************************************************************************/ /* NITFGetSeriesInfo() */ /************************************************************************/ /* From http://trac.osgeo.org/gdal/attachment/ticket/5353/MIL-STD-2411_1_CHG-3.pdf */ static const NITFSeries nitfSeries[] = { { "A1", "CM", "1:10K", "Combat Charts (1:10K)", "CADRG"}, { "A2", "CM", "1:25K", "Combat Charts (1:25K)", "CADRG"}, { "A3", "CM", "1:50K", "Combat Charts (1:50K)", "CADRG"}, { "A4", "CM", "1:100K", "Combat Charts (1:100K)", "CADRG"}, { "AT", "ATC", "1:200K", "Series 200 Air Target Chart", "CADRG"}, { "C1", "CG", "1:10000", "City Graphics", "CADRG"}, { "C2", "CG", "1:10560", "City Graphics", "CADRG"}, { "C3", "CG", "1:11000", "City Graphics", "CADRG"}, { "C4", "CG", "1:11800", "City Graphics", "CADRG"}, { "C5", "CG", "1:12000", "City Graphics", "CADRG"}, { "C6", "CG", "1:12500", "City Graphics", "CADRG"}, { "C7", "CG", "1:12800", "City Graphics", "CADRG"}, { "C8", "CG", "1:14000", "City Graphics", "CADRG"}, { "C9", "CG", "1:14700", "City Graphics", "CADRG"}, { "CA", "CG", "1:15000", "City Graphics", "CADRG"}, { "CB", "CG", "1:15500", "City Graphics", "CADRG"}, { "CC", "CG", "1:16000", "City Graphics", "CADRG"}, { "CD", "CG", "1:16666", "City Graphics", "CADRG"}, { "CE", "CG", "1:17000", "City Graphics", "CADRG"}, { "CF", "CG", "1:17500", "City Graphics", "CADRG"}, { "CG", "CG", "Various", "City Graphics", "CADRG"}, { "CH", "CG", "1:18000", "City Graphics", "CADRG"}, { "CJ", "CG", "1:20000", "City Graphics", "CADRG"}, { "CK", "CG", "1:21000", "City Graphics", "CADRG"}, { "CL", "CG", "1:21120", "City Graphics", "CADRG"}, { "CM", "CM", "Various", "Combat Charts", "CADRG"}, { "CN", "CG", "1:22000", "City Graphics", "CADRG"}, { "CO", "CO", "Various", "Coastal Charts", "CADRG"}, { "CP", "CG", "1:23000", "City Graphics", "CADRG"}, { "CQ", "CG", "1:25000", "City Graphics", "CADRG"}, { "CR", "CG", "1:26000", "City Graphics", "CADRG"}, { "CS", "CG", "1:35000", "City Graphics", "CADRG"}, { "CT", "CG", "1:36000", "City Graphics", "CADRG"}, { "D1", "", "100m", "Elevation Data from DTED level 1", "CDTED"}, { "D2", "", "30m", "Elevation Data from DTED level 2", "CDTED"}, { "EG", "NARC", "1:11,000,000", "North Atlantic Route Chart", "CADRG"}, { "ES", "SEC", "1:500K", "VFR Sectional", "CADRG"}, { "ET", "SEC", "1:250K", "VFR Sectional Inserts", "CADRG"}, { "F1", "TFC-1", "1:250K", "Transit Flying Chart (TBD #1)", "CADRG"}, { "F2", "TFC-2", "1:250K", "Transit Flying Chart (TBD #2)", "CADRG"}, { "F3", "TFC-3", "1:250K", "Transit Flying Chart (TBD #3)", "CADRG"}, { "F4", "TFC-4", "1:250K", "Transit Flying Chart (TBD #4)", "CADRG"}, { "F5", "TFC-5", "1:250K", "Transit Flying Chart (TBD #5)", "CADRG"}, { "GN", "GNC", "1:5M", "Global Navigation Chart", "CADRG"}, { "HA", "HA", "Various", "Harbor and Approach Charts", "CADRG"}, { "I1", "", "10m", "Imagery, 10 meter resolution", "CIB"}, { "I2", "", "5m", "Imagery, 5 meter resolution", "CIB"}, { "I3", "", "2m", "Imagery, 2 meter resolution", "CIB"}, { "I4", "", "1m", "Imagery, 1 meter resolution", "CIB"}, { "I5", "", ".5m", "Imagery, .5 (half) meter resolution", "CIB"}, { "IV", "", "Various > 10m", "Imagery, greater than 10 meter resolution", "CIB"}, { "JA", "JOG-A", "1:250K", "Joint Operation Graphic - Air", "CADRG"}, { "JG", "JOG", "1:250K", "Joint Operation Graphic", "CADRG"}, { "JN", "JNC", "1:2M", "Jet Navigation Chart", "CADRG"}, { "JO", "OPG", "1:250K", "Operational Planning Graphic", "CADRG"}, { "JR", "JOG-R", "1:250K", "Joint Operation Graphic - Radar", "CADRG"}, { "K1", "ICM", "1:8K", "Image City Maps", "CADRG"}, { "K2", "ICM", "1:10K", "Image City Maps", "CADRG"}, { "K3", "ICM", "1:10560", "Image City Maps", "CADRG"}, { "K7", "ICM", "1:12500", "Image City Maps", "CADRG"}, { "K8", "ICM", "1:12800", "Image City Maps", "CADRG"}, { "KB", "ICM", "1:15K", "Image City Maps", "CADRG"}, { "KE", "ICM", "1:16666", "Image City Maps", "CADRG"}, { "KM", "ICM", "1:21120", "Image City Maps", "CADRG"}, { "KR", "ICM", "1:25K", "Image City Maps", "CADRG"}, { "KS", "ICM", "1:26K", "Image City Maps", "CADRG"}, { "KU", "ICM", "1:36K", "Image City Maps", "CADRG"}, { "L1", "LFC-1", "1:500K", "Low Flying Chart (TBD #1)", "CADRG"}, { "L2", "LFC-2", "1:500K", "Low Flying Chart (TBD #2)", "CADRG"}, { "L3", "LFC-3", "1:500K", "Low Flying Chart (TBD #3)", "CADRG"}, { "L4", "LFC-4", "1:500K", "Low Flying Chart (TBD #4)", "CADRG"}, { "L5", "LFC-5", "1:500K", "Low Flying Chart (TBD #5)", "CADRG"}, { "LF", "LFC-FR (Day)", "1:500K", "Low Flying Chart (Day) - Host Nation", "CADRG"}, { "LN", "LN (Night)", "1:500K", "Low Flying Chart (Night) - Host Nation", "CADRG"}, { "M1", "MIM", "Various", "Military Installation Maps (TBD #1)", "CADRG"}, { "M2", "MIM", "Various", "Military Installation Maps (TBD #2)", "CADRG"}, { "MH", "MIM", "1:25K", "Military Installation Maps", "CADRG"}, { "MI", "MIM", "1:50K", "Military Installation Maps", "CADRG"}, { "MJ", "MIM", "1:100K", "Military Installation Maps", "CADRG"}, { "MM", "", "Various", "(Miscellaneous Maps & Charts)", "CADRG"}, { "OA", "OPAREA", "Various", "Naval Range Operation Area Chart", "CADRG"}, { "OH", "VHRC", "1:1M", "VFR Helicopter Route Chart", "CADRG"}, { "ON", "ONC", "1:1M", "Operational Navigation Chart", "CADRG"}, { "OW", "WAC", "1:1M", "High Flying Chart - Host Nation", "CADRG"}, { "P1", "", "1:25K", "Special Military Map - Overlay", "CADRG"}, { "P2", "", "1:25K", "Special Military Purpose", "CADRG"}, { "P3", "", "1:25K", "Special Military Purpose", "CADRG"}, { "P4", "", "1:25K", "Special Military Purpose", "CADRG"}, { "P5", "", "1:50K", "Special Military Map - Overlay", "CADRG"}, { "P6", "", "1:50K", "Special Military Purpose", "CADRG"}, { "P7", "", "1:50K", "Special Military Purpose", "CADRG"}, { "P8", "", "1:50K", "Special Military Purpose", "CADRG"}, { "P9", "", "1:100K", "Special Military Map - Overlay", "CADRG"}, { "PA", "", "1:100K", "Special Military Purpose", "CADRG"}, { "PB", "", "1:100K", "Special Military Purpose", "CADRG"}, { "PC", "", "1:100K", "Special Military Purpose", "CADRG"}, { "PD", "", "1:250K", "Special Military Map - Overlay", "CADRG"}, { "PE", "", "1:250K", "Special Military Purpose", "CADRG"}, { "PF", "", "1:250K", "Special Military Purpose", "CADRG"}, { "PG", "", "1:250K", "Special Military Purpose", "CADRG"}, { "PH", "", "1:500K", "Special Military Map - Overlay", "CADRG"}, { "PI", "", "1:500K", "Special Military Purpose", "CADRG"}, { "PJ", "", "1:500K", "Special Military Purpose", "CADRG"}, { "PK", "", "1:500K", "Special Military Purpose", "CADRG"}, { "PL", "", "1:1M", "Special Military Map - Overlay", "CADRG"}, { "PM", "", "1:1M", "Special Military Purpose", "CADRG"}, { "PN", "", "1:1M", "Special Military Purpose", "CADRG"}, { "PO", "", "1:1M", "Special Military Purpose", "CADRG"}, { "PP", "", "1:2M", "Special Military Map - Overlay", "CADRG"}, { "PQ", "", "1:2M", "Special Military Purpose", "CADRG"}, { "PR", "", "1:2M", "Special Military Purpose", "CADRG"}, { "PS", "", "1:5M", "Special Military Map - Overlay", "CADRG"}, { "PT", "", "1:5M", "Special Military Purpose", "CADRG"}, { "PU", "", "1:5M", "Special Military Purpose", "CADRG"}, { "PV", "", "1:5M", "Special Military Purpose", "CADRG"}, { "R1", "", "1:50K", "Range Charts", "CADRG"}, { "R2", "", "1:100K", "Range Charts", "CADRG"}, { "R3", "", "1:250K", "Range Charts", "CADRG"}, { "R4", "", "1:500K", "Range Charts", "CADRG"}, { "R5", "", "1:1M", "Range Charts", "CADRG"}, { "RC", "RGS-100", "1:100K", "Russian General Staff Maps", "CADRG"}, { "RL", "RGS-50", "1:50K", "Russian General Staff Maps", "CADRG"}, { "RR", "RGS-200", "1:200K", "Russian General Staff Maps", "CADRG"}, { "RV", "Riverine", "1:50K", "Riverine Map 1:50,000 scale", "CADRG"}, { "TC", "TLM 100", "1:100K", "Topographic Line Map 1:100,000 scale", "CADRG"}, { "TF", "TFC (Day)", "1:250K", "Transit Flying Chart (Day)", "CADRG"}, { "TL", "TLM50", "1:50K", "Topographic Line Map", "CADRG"}, { "TN", "TFC (Night)", "1:250K", "Transit Flying Chart (Night) - Host Nation", "CADRG"}, { "TP", "TPC", "1:500K", "Tactical Pilotage Chart", "CADRG"}, { "TQ", "TLM24", "1:24K", "Topographic Line Map 1:24,000 scale", "CADRG"}, { "TR", "TLM200", "1:200K", "Topographic Line Map 1:200,000 scale", "CADRG"}, { "TT", "TLM25", "1:25K", "Topographic Line Map 1:25,000 scale", "CADRG"}, { "UL", "TLM50 - Other", "1:50K", "Topographic Line Map (other 1:50,000 scale)", "CADRG"}, { "V1", "Inset HRC", "1:50", "Helicopter Route Chart Inset", "CADRG"}, { "V2", "Inset HRC", "1:62500", "Helicopter Route Chart Inset", "CADRG"}, { "V3", "Inset HRC", "1:90K", "Helicopter Route Chart Inset", "CADRG"}, { "V4", "Inset HRC", "1:250K", "Helicopter Route Chart Inset", "CADRG"}, { "VH", "HRC", "1:125K", "Helicopter Route Chart", "CADRG"}, { "VN", "VNC", "1:500K", "Visual Navigation Charts", "CADRG"}, { "VT", "VTAC", "1:250K", "VFR Terminal Area Chart", "CADRG"}, { "WA", "", "1:250K", "IFR Enroute Low", "CADRG"}, { "WB", "", "1:500K", "IFR Enroute Low", "CADRG"}, { "WC", "", "1:750K", "IFR Enroute Low", "CADRG"}, { "WD", "", "1:1M", "IFR Enroute Low", "CADRG"}, { "WE", "", "1:1.5M", "IFR Enroute Low", "CADRG"}, { "WF", "", "1:2M", "IFR Enroute Low", "CADRG"}, { "WG", "", "1:2.5M", "IFR Enroute Low", "CADRG"}, { "WH", "", "1:3M", "IFR Enroute Low", "CADRG"}, { "WI", "", "1:3.5M", "IFR Enroute Low", "CADRG"}, { "WK", "", "1:4M", "IFR Enroute Low", "CADRG"}, { "XD", "", "1:1M", "IFR Enroute High", "CADRG"}, { "XE", "", "1:1.5M", "IFR Enroute High", "CADRG"}, { "XF", "", "1:2M", "IFR Enroute High", "CADRG"}, { "XG", "", "1:2.5M", "IFR Enroute High", "CADRG"}, { "XH", "", "1:3M", "IFR Enroute High", "CADRG"}, { "XI", "", "1:3.5M", "IFR Enroute High", "CADRG"}, { "XJ", "", "1:4M", "IFR Enroute High", "CADRG"}, { "XK", "", "1:4.5M", "IFR Enroute High", "CADRG"}, { "Y9", "", "1:16.5M", "IFR Enroute Area", "CADRG"}, { "YA", "", "1:250K", "IFR Enroute Area", "CADRG"}, { "YB", "", "1:500K", "IFR Enroute Area", "CADRG"}, { "YC", "", "1:750K", "IFR Enroute Area", "CADRG"}, { "YD", "", "1:1M", "IFR Enroute Area", "CADRG"}, { "YE", "", "1:1.5M", "IFR Enroute Area", "CADRG"}, { "YF", "", "1:2M", "IFR Enroute Area", "CADRG"}, { "YI", "", "1:3.5M", "IFR Enroute Area", "CADRG"}, { "YJ", "", "1:4M", "IFR Enroute Area", "CADRG"}, { "YZ", "", "1:12M", "IFR Enroute Area", "CADRG"}, { "ZA", "", "1:250K", "IFR Enroute High/Low", "CADRG"}, { "ZB", "", "1:500K", "IFR Enroute High/Low", "CADRG"}, { "ZC", "", "1:750K", "IFR Enroute High/Low", "CADRG"}, { "ZD", "", "1:1M", "IFR Enroute High/Low", "CADRG"}, { "ZE", "", "1:1.5M", "IFR Enroute High/Low", "CADRG"}, { "ZF", "", "1:2M", "IFR Enroute High/Low", "CADRG"}, { "ZG", "", "1:2.5M", "IFR Enroute High/Low", "CADRG"}, { "ZH", "", "1:3M", "IFR Enroute High/Low", "CADRG"}, { "ZI", "", "1:3.5M", "IFR Enroute High/Low", "CADRG"}, { "ZJ", "", "1:4M", "IFR Enroute High/Low", "CADRG"}, { "ZK", "", "1:4.5M", "IFR Enroute High/Low", "CADRG"}, { "ZT", "", "1:9M", "IFR Enroute High/Low", "CADRG"}, { "ZV", "", "1:10M", "IFR Enroute High/Low", "CADRG"}, { "ZZ", "", "1:12M", "IFR Enroute High/Low", "CADRG"} }; /* See 24111CN1.pdf paragraph 5.1.4 */ const NITFSeries* NITFGetSeriesInfo(const char* pszFilename) { int i; char seriesCode[3] = {0,0,0}; if (pszFilename == NULL) return NULL; for (i=(int)strlen(pszFilename)-1;i>=0;i--) { if (pszFilename[i] == '.') { if (i < (int)strlen(pszFilename) - 3) { seriesCode[0] = pszFilename[i+1]; seriesCode[1] = pszFilename[i+2]; for(i=0;i < (int)(sizeof(nitfSeries) / sizeof(nitfSeries[0])); i++) { if (EQUAL(seriesCode, nitfSeries[i].code)) { return &nitfSeries[i]; } } return NULL; } } } return NULL; } /************************************************************************/ /* NITFCollectAttachments() */ /* */ /* Collect attachment, display level and location info into the */ /* segmentinfo structures. */ /************************************************************************/ int NITFCollectAttachments( NITFFile *psFile ) { int iSegment; /* ==================================================================== */ /* Loop over all segments. */ /* ==================================================================== */ for( iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++ ) { NITFSegmentInfo *psSegInfo = psFile->pasSegmentInfo + iSegment; /* -------------------------------------------------------------------- */ /* For image segments, we use the normal image access stuff. */ /* -------------------------------------------------------------------- */ if( EQUAL(psSegInfo->szSegmentType,"IM") ) { NITFImage *psImage = NITFImageAccess( psFile, iSegment ); if (psImage == NULL) return FALSE; psSegInfo->nDLVL = psImage->nIDLVL; psSegInfo->nALVL = psImage->nIALVL; psSegInfo->nLOC_R = psImage->nILOCRow; psSegInfo->nLOC_C = psImage->nILOCColumn; } /* -------------------------------------------------------------------- */ /* For graphic file we need to process the header. */ /* -------------------------------------------------------------------- */ else if( EQUAL(psSegInfo->szSegmentType,"SY") || EQUAL(psSegInfo->szSegmentType,"GR") ) { char achSubheader[298]; int nSTYPEOffset; char szTemp[100]; /* -------------------------------------------------------------------- */ /* Load the graphic subheader. */ /* -------------------------------------------------------------------- */ if( VSIFSeekL( psFile->fp, psSegInfo->nSegmentHeaderStart, SEEK_SET ) != 0 || VSIFReadL( achSubheader, 1, sizeof(achSubheader), psFile->fp ) < 258 ) { CPLError( CE_Warning, CPLE_FileIO, "Failed to read graphic subheader at " CPL_FRMT_GUIB ".", psSegInfo->nSegmentHeaderStart ); continue; } // NITF 2.0. (also works for NITF 2.1) nSTYPEOffset = 200; if( STARTS_WITH_CI(achSubheader+193, "999998") ) nSTYPEOffset += 40; /* -------------------------------------------------------------------- */ /* Report some standard info. */ /* -------------------------------------------------------------------- */ psSegInfo->nDLVL = atoi(NITFGetField(szTemp,achSubheader, nSTYPEOffset + 14, 3)); psSegInfo->nALVL = atoi(NITFGetField(szTemp,achSubheader, nSTYPEOffset + 17, 3)); psSegInfo->nLOC_R = atoi(NITFGetField(szTemp,achSubheader, nSTYPEOffset + 20, 5)); psSegInfo->nLOC_C = atoi(NITFGetField(szTemp,achSubheader, nSTYPEOffset + 25, 5)); } } return TRUE; } /************************************************************************/ /* NITFReconcileAttachments() */ /* */ /* Generate the CCS location information for all the segments */ /* if possible. */ /************************************************************************/ int NITFReconcileAttachments( NITFFile *psFile ) { int iSegment; int bSuccess = TRUE; int bMadeProgress = FALSE; for( iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++ ) { NITFSegmentInfo *psSegInfo = psFile->pasSegmentInfo + iSegment; int iOther; // already processed? if( psSegInfo->nCCS_R != -1 ) continue; // unattached segments are straight forward. if( psSegInfo->nALVL < 1 ) { psSegInfo->nCCS_R = psSegInfo->nLOC_R; psSegInfo->nCCS_C = psSegInfo->nLOC_C; if( psSegInfo->nCCS_R != -1 ) bMadeProgress = TRUE; continue; } // Loc for segment to which we are attached. for( iOther = 0; iOther < psFile->nSegmentCount; iOther++ ) { NITFSegmentInfo *psOtherSegInfo = psFile->pasSegmentInfo + iOther; if( psSegInfo->nALVL == psOtherSegInfo->nDLVL ) { if( psOtherSegInfo->nCCS_R != -1 ) { psSegInfo->nCCS_R = psOtherSegInfo->nLOC_R + psSegInfo->nLOC_R; psSegInfo->nCCS_C = psOtherSegInfo->nLOC_C + psSegInfo->nLOC_C; if ( psSegInfo->nCCS_R != -1 ) bMadeProgress = TRUE; } else { bSuccess = FALSE; } break; } } if( iOther == psFile->nSegmentCount ) bSuccess = FALSE; } /* -------------------------------------------------------------------- */ /* If succeeded or made no progress then return our success */ /* flag. Otherwise make another pass, hopefully filling in */ /* more values. */ /* -------------------------------------------------------------------- */ if( bSuccess || !bMadeProgress ) return bSuccess; else return NITFReconcileAttachments( psFile ); } /************************************************************************/ /* NITFFindValFromEnd() */ /************************************************************************/ static const char* NITFFindValFromEnd(char** papszMD, int nMDSize, const char* pszVar, CPL_UNUSED const char* pszDefault) { int nVarLen = (int)strlen(pszVar); int nIter = nMDSize-1; for(;nIter >= 0;nIter--) { if (strncmp(papszMD[nIter], pszVar, nVarLen) == 0 && papszMD[nIter][nVarLen] == '=') return papszMD[nIter] + nVarLen + 1; } return NULL; } /************************************************************************/ /* NITFGenericMetadataReadTREInternal() */ /************************************************************************/ static char** NITFGenericMetadataReadTREInternal(char **papszMD, int* pnMDSize, int* pnMDAlloc, CPLXMLNode* psOutXMLNode, const char* pszTREName, const char *pachTRE, int nTRESize, CPLXMLNode* psTreNode, int *pnTreOffset, const char* pszMDPrefix, int *pbError) { CPLXMLNode* psIter; for(psIter = psTreNode->psChild; psIter != NULL && *pbError == FALSE; psIter = psIter->psNext) { if (psIter->eType == CXT_Element && psIter->pszValue != NULL && strcmp(psIter->pszValue, "field") == 0) { const char* pszName = CPLGetXMLValue(psIter, "name", NULL); const char* pszLongName = CPLGetXMLValue(psIter, "longname", NULL); const char* pszLength = CPLGetXMLValue(psIter, "length", NULL); int nLength = -1; if (pszLength != NULL) nLength = atoi(pszLength); else { const char* pszLengthVar = CPLGetXMLValue(psIter, "length_var", NULL); if (pszLengthVar != NULL) { // Preferably look for item at the same level as ours. const char* pszLengthValue = CSLFetchNameValue( papszMD, CPLSPrintf("%s%s", pszMDPrefix, pszLengthVar) ); if( pszLengthValue != NULL ) { nLength = atoi(pszLengthValue); } else { char** papszMDIter = papszMD; while(papszMDIter != NULL && *papszMDIter != NULL) { if (strstr(*papszMDIter, pszLengthVar) != NULL) { const char* pszEqual = strchr(*papszMDIter, '='); if (pszEqual != NULL) { nLength = atoi(pszEqual + 1); // Voluntary missing break so as to find the // "closest" item to ours in case it is not // defined in the same level } } papszMDIter ++; } } } } if (pszName != NULL && nLength > 0) { char* pszMDItemName; char** papszTmp = NULL; if (*pnTreOffset + nLength > nTRESize) { *pbError = TRUE; CPLError( CE_Warning, CPLE_AppDefined, "Not enough bytes when reading %s TRE " "(at least %d needed, only %d available)", pszTREName, *pnTreOffset + nLength, nTRESize ); break; } pszMDItemName = CPLStrdup( CPLSPrintf("%s%s", pszMDPrefix, pszName)); NITFExtractMetadata( &papszTmp, pachTRE, *pnTreOffset, nLength, pszMDItemName ); if (*pnMDSize + 1 >= *pnMDAlloc) { *pnMDAlloc = (*pnMDAlloc * 4 / 3) + 32; papszMD = (char**)CPLRealloc(papszMD, *pnMDAlloc * sizeof(char*)); } papszMD[*pnMDSize] = papszTmp[0]; papszMD[(*pnMDSize) + 1] = NULL; (*pnMDSize) ++; papszTmp[0] = NULL; CPLFree(papszTmp); if (psOutXMLNode != NULL) { const char* pszVal = strchr(papszMD[(*pnMDSize) - 1], '=') + 1; CPLXMLNode* psFieldNode; CPLXMLNode* psNameNode; CPLXMLNode* psValueNode; CPLAssert(pszVal != NULL); psFieldNode = CPLCreateXMLNode(psOutXMLNode, CXT_Element, "field"); psNameNode = CPLCreateXMLNode(psFieldNode, CXT_Attribute, "name"); psValueNode = CPLCreateXMLNode(psFieldNode, CXT_Attribute, "value"); CPLCreateXMLNode(psNameNode, CXT_Text, (pszName[0] || pszLongName == NULL) ? pszName : pszLongName); CPLCreateXMLNode(psValueNode, CXT_Text, pszVal); } CPLFree(pszMDItemName); *pnTreOffset += nLength; } else if (nLength > 0) { *pnTreOffset += nLength; } else { *pbError = TRUE; CPLError( CE_Warning, CPLE_AppDefined, "Invalid item construct in %s TRE in XML resource", pszTREName ); break; } } else if (psIter->eType == CXT_Element && psIter->pszValue != NULL && strcmp(psIter->pszValue, "loop") == 0) { const char* pszCounter = CPLGetXMLValue(psIter, "counter", NULL); const char* pszIterations = CPLGetXMLValue(psIter, "iterations", NULL); const char* pszFormula = CPLGetXMLValue(psIter, "formula", NULL); const char* pszMDSubPrefix = CPLGetXMLValue(psIter, "md_prefix", NULL); int nIterations = -1; if (pszCounter != NULL) { char* pszMDItemName = CPLStrdup( CPLSPrintf("%s%s", pszMDPrefix, pszCounter)); nIterations = atoi(NITFFindValFromEnd(papszMD, *pnMDSize, pszMDItemName, "-1")); CPLFree(pszMDItemName); if (nIterations < 0) { CPLError( CE_Warning, CPLE_AppDefined, "Invalid loop construct in %s TRE in XML resource : " "invalid 'counter' %s", pszTREName, pszCounter ); *pbError = TRUE; break; } } else if (pszIterations != NULL) { nIterations = atoi(pszIterations); } else if (pszFormula != NULL && strcmp(pszFormula, "(NPART+1)*(NPART)/2") == 0) { char* pszMDItemName = CPLStrdup( CPLSPrintf("%s%s", pszMDPrefix, "NPART")); int NPART = atoi(NITFFindValFromEnd(papszMD, *pnMDSize, pszMDItemName, "-1")); CPLFree(pszMDItemName); if (NPART < 0) { CPLError( CE_Warning, CPLE_AppDefined, "Invalid loop construct in %s TRE in XML resource : " "invalid 'counter' %s", pszTREName, "NPART" ); *pbError = TRUE; break; } nIterations = NPART * (NPART + 1) / 2; } else if (pszFormula != NULL && strcmp(pszFormula, "(NUMOPG+1)*(NUMOPG)/2") == 0) { char* pszMDItemName = CPLStrdup( CPLSPrintf("%s%s", pszMDPrefix, "NUMOPG")); int NUMOPG = atoi(NITFFindValFromEnd(papszMD, *pnMDSize, pszMDItemName, "-1")); CPLFree(pszMDItemName); if (NUMOPG < 0) { CPLError( CE_Warning, CPLE_AppDefined, "Invalid loop construct in %s TRE in XML resource : " "invalid 'counter' %s", pszTREName, "NUMOPG" ); *pbError = TRUE; break; } nIterations = NUMOPG * (NUMOPG + 1) / 2; } else if (pszFormula != NULL && strcmp(pszFormula, "NPAR*NPARO") == 0) { char* pszMDNPARName = CPLStrdup( CPLSPrintf("%s%s", pszMDPrefix, "NPAR")); int NPAR = atoi(NITFFindValFromEnd(papszMD, *pnMDSize, pszMDNPARName, "-1")); char* pszMDNPAROName = CPLStrdup( CPLSPrintf("%s%s", pszMDPrefix, "NPARO")); int NPARO= atoi(NITFFindValFromEnd(papszMD, *pnMDSize, pszMDNPAROName, "-1")); CPLFree(pszMDNPARName); CPLFree(pszMDNPAROName); if (NPAR < 0) { CPLError( CE_Warning, CPLE_AppDefined, "Invalid loop construct in %s TRE in XML resource : " "invalid 'counter' %s", pszTREName, "NPAR" ); *pbError = TRUE; break; } if (NPARO < 0) { CPLError( CE_Warning, CPLE_AppDefined, "Invalid loop construct in %s TRE in XML resource : " "invalid 'counter' %s", pszTREName, "NPAR0" ); *pbError = TRUE; break; } nIterations = NPAR*NPARO; } else if (pszFormula != NULL && strcmp(pszFormula, "NPLN-1") == 0) { char* pszMDItemName = CPLStrdup( CPLSPrintf("%s%s", pszMDPrefix, "NPLN")); int NPLN = atoi(NITFFindValFromEnd(papszMD, *pnMDSize, pszMDItemName, "-1")); CPLFree(pszMDItemName); if (NPLN < 0) { CPLError( CE_Warning, CPLE_AppDefined, "Invalid loop construct in %s TRE in XML resource : " "invalid 'counter' %s", pszTREName, "NPLN" ); *pbError = TRUE; break; } nIterations = NPLN-1; } else if (pszFormula != NULL && strcmp(pszFormula, "NXPTS*NYPTS") == 0) { char* pszMDNPARName = CPLStrdup( CPLSPrintf("%s%s", pszMDPrefix, "NXPTS")); int NXPTS = atoi(NITFFindValFromEnd(papszMD, *pnMDSize, pszMDNPARName, "-1")); char* pszMDNPAROName = CPLStrdup( CPLSPrintf("%s%s", pszMDPrefix, "NYPTS")); int NYPTS= atoi(NITFFindValFromEnd(papszMD, *pnMDSize, pszMDNPAROName, "-1")); CPLFree(pszMDNPARName); CPLFree(pszMDNPAROName); if (NXPTS < 0) { CPLError( CE_Warning, CPLE_AppDefined, "Invalid loop construct in %s TRE in XML resource : " "invalid 'counter' %s", pszTREName, "NXPTS" ); *pbError = TRUE; break; } if (NYPTS < 0) { CPLError( CE_Warning, CPLE_AppDefined, "Invalid loop construct in %s TRE in XML resource : " "invalid 'counter' %s", pszTREName, "NYPTS" ); *pbError = TRUE; break; } nIterations = NXPTS*NYPTS; } else { CPLError( CE_Warning, CPLE_AppDefined, "Invalid loop construct in %s TRE in XML resource : " "missing or invalid 'counter' or 'iterations' or 'formula'", pszTREName ); *pbError = TRUE; break; } if (nIterations > 0) { int iIter; const char* pszPercent; int bHasValidPercentD = FALSE; CPLXMLNode* psRepeatedNode = NULL; CPLXMLNode* psLastChild = NULL; /* Check that md_prefix has one and only %XXXXd pattern */ if (pszMDSubPrefix != NULL && (pszPercent = strchr(pszMDSubPrefix, '%')) != NULL && strchr(pszPercent+1,'%') == NULL) { const char* pszIter = pszPercent + 1; while(*pszIter != '\0') { if (*pszIter >= '0' && *pszIter <= '9') pszIter ++; else if (*pszIter == 'd') { bHasValidPercentD = atoi(pszPercent + 1) <= 10; break; } else break; } } if (psOutXMLNode != NULL) { CPLXMLNode* psNumberNode; CPLXMLNode* psNameNode; const char* pszName = CPLGetXMLValue(psIter, "name", NULL); psRepeatedNode = CPLCreateXMLNode(psOutXMLNode, CXT_Element, "repeated"); if (pszName) { psNameNode = CPLCreateXMLNode(psRepeatedNode, CXT_Attribute, "name"); CPLCreateXMLNode(psNameNode, CXT_Text, pszName); } psNumberNode = CPLCreateXMLNode(psRepeatedNode, CXT_Attribute, "number"); CPLCreateXMLNode(psNumberNode, CXT_Text, CPLSPrintf("%d", nIterations)); psLastChild = psRepeatedNode->psChild; while(psLastChild->psNext != NULL) psLastChild = psLastChild->psNext; } for(iIter = 0; iIter < nIterations && *pbError == FALSE; iIter++) { char* pszMDNewPrefix = NULL; CPLXMLNode* psGroupNode = NULL; if (pszMDSubPrefix != NULL) { if (bHasValidPercentD) { const size_t nTmpLen = strlen(pszMDSubPrefix) + 10 + 1; char* szTmp = (char*)CPLMalloc(nTmpLen); snprintf(szTmp, nTmpLen, pszMDSubPrefix, iIter + 1); pszMDNewPrefix = CPLStrdup(CPLSPrintf("%s%s", pszMDPrefix, szTmp)); CPLFree(szTmp); } else pszMDNewPrefix = CPLStrdup(CPLSPrintf("%s%s%04d_", pszMDPrefix, pszMDSubPrefix, iIter + 1)); } else pszMDNewPrefix = CPLStrdup(CPLSPrintf("%s%04d_", pszMDPrefix, iIter + 1)); if (psRepeatedNode != NULL) { CPLXMLNode* psIndexNode; psGroupNode = CPLCreateXMLNode(NULL, CXT_Element, "group"); CPLAssert(psLastChild->psNext == NULL); psLastChild->psNext = psGroupNode; psLastChild = psGroupNode; psIndexNode = CPLCreateXMLNode(psGroupNode, CXT_Attribute, "index"); CPLCreateXMLNode(psIndexNode, CXT_Text, CPLSPrintf("%d", iIter)); } papszMD = NITFGenericMetadataReadTREInternal(papszMD, pnMDSize, pnMDAlloc, psGroupNode, pszTREName, pachTRE, nTRESize, psIter, pnTreOffset, pszMDNewPrefix, pbError); CPLFree(pszMDNewPrefix); } } } else if (psIter->eType == CXT_Element && psIter->pszValue != NULL && strcmp(psIter->pszValue, "if") == 0) { const char* pszCond = CPLGetXMLValue(psIter, "cond", NULL); const char* pszEqual = NULL; if (pszCond != NULL && strcmp(pszCond, "QSS!=U AND QOD!=Y") == 0) { char* pszQSSName = CPLStrdup( CPLSPrintf("%s%s", pszMDPrefix, "QSS")); char* pszQODName = CPLStrdup( CPLSPrintf("%s%s", pszMDPrefix, "QOD")); const char* pszQSSVal = NITFFindValFromEnd(papszMD, *pnMDSize, pszQSSName, NULL); const char* pszQODVal = NITFFindValFromEnd(papszMD, *pnMDSize, pszQODName, NULL); if (pszQSSVal == NULL) { CPLDebug("NITF", "Cannot find if cond variable %s", "QSS"); } else if (pszQODVal == NULL) { CPLDebug("NITF", "Cannot find if cond variable %s", "QOD"); } else if (strcmp(pszQSSVal, "U") != 0 && strcmp(pszQODVal, "Y") != 0) { papszMD = NITFGenericMetadataReadTREInternal(papszMD, pnMDSize, pnMDAlloc, psOutXMLNode, pszTREName, pachTRE, nTRESize, psIter, pnTreOffset, pszMDPrefix, pbError); } CPLFree(pszQSSName); CPLFree(pszQODName); } else if (pszCond != NULL && (pszEqual = strchr(pszCond, '=')) != NULL) { char* pszCondVar = (char*)CPLMalloc(pszEqual - pszCond + 1); const char* pszCondExpectedVal = pszEqual + 1; char* pszMDItemName; const char* pszCondVal; int bTestEqual = TRUE; memcpy(pszCondVar, pszCond, pszEqual - pszCond); if (pszEqual - pszCond > 1 && pszCondVar[pszEqual - pszCond - 1] == '!') { bTestEqual = FALSE; pszCondVar[pszEqual - pszCond - 1] = '\0'; } pszCondVar[pszEqual - pszCond] = '\0'; pszMDItemName = CPLStrdup( CPLSPrintf("%s%s", pszMDPrefix, pszCondVar)); pszCondVal = NITFFindValFromEnd(papszMD, *pnMDSize, pszMDItemName, NULL); if (pszCondVal == NULL) { CPLDebug("NITF", "Cannot find if cond variable %s", pszMDItemName); } else if ((bTestEqual && strcmp(pszCondVal, pszCondExpectedVal) == 0) || (!bTestEqual && strcmp(pszCondVal, pszCondExpectedVal) != 0)) { papszMD = NITFGenericMetadataReadTREInternal(papszMD, pnMDSize, pnMDAlloc, psOutXMLNode, pszTREName, pachTRE, nTRESize, psIter, pnTreOffset, pszMDPrefix, pbError); } CPLFree(pszMDItemName); CPLFree(pszCondVar); } else { CPLError( CE_Warning, CPLE_AppDefined, "Invalid if construct in %s TRE in XML resource : " "missing or invalid 'cond' attribute", pszTREName ); *pbError = TRUE; break; } } else if (psIter->eType == CXT_Element && psIter->pszValue != NULL && strcmp(psIter->pszValue, "if_remaining_bytes") == 0) { if (*pnTreOffset < nTRESize) { papszMD = NITFGenericMetadataReadTREInternal(papszMD, pnMDSize, pnMDAlloc, psOutXMLNode, pszTREName, pachTRE, nTRESize, psIter, pnTreOffset, pszMDPrefix, pbError); } } else { //CPLDebug("NITF", "Unknown element : %s", psIter->pszValue ? psIter->pszValue : "null"); } } return papszMD; } /************************************************************************/ /* NITFGenericMetadataReadTRE() */ /************************************************************************/ static char **NITFGenericMetadataReadTRE(char **papszMD, const char* pszTREName, const char *pachTRE, int nTRESize, CPLXMLNode* psTreNode) { int bError = FALSE; int nTreOffset = 0; const char* pszMDPrefix; int nMDSize, nMDAlloc; int nTreLength = atoi(CPLGetXMLValue(psTreNode, "length", "-1")); int nTreMinLength = atoi(CPLGetXMLValue(psTreNode, "minlength", "-1")); /* int nTreMaxLength = atoi(CPLGetXMLValue(psTreNode, "maxlength", "-1")); */ if( (nTreLength > 0 && nTRESize != nTreLength) || (nTreMinLength > 0 && nTRESize < nTreMinLength) ) { CPLError( CE_Warning, CPLE_AppDefined, "%s TRE wrong size, ignoring.", pszTREName ); return papszMD; } pszMDPrefix = CPLGetXMLValue(psTreNode, "md_prefix", ""); nMDSize = nMDAlloc = CSLCount(papszMD); papszMD = NITFGenericMetadataReadTREInternal(papszMD, &nMDSize, &nMDAlloc, NULL, pszTREName, pachTRE, nTRESize, psTreNode, &nTreOffset, pszMDPrefix, &bError); if (bError == FALSE && nTreLength > 0 && nTreOffset != nTreLength) { CPLError( CE_Warning, CPLE_AppDefined, "Inconsistent declaration of %s TRE", pszTREName ); } if (nTreOffset < nTRESize) CPLDebug("NITF", "%d remaining bytes at end of %s TRE", nTRESize -nTreOffset, pszTREName); return papszMD; } /************************************************************************/ /* NITFLoadXMLSpec() */ /************************************************************************/ #define NITF_SPEC_FILE "nitf_spec.xml" static CPLXMLNode* NITFLoadXMLSpec(NITFFile* psFile) { if (psFile->psNITFSpecNode == NULL) { const char* pszXMLDescFilename = CPLFindFile("gdal", NITF_SPEC_FILE); if (pszXMLDescFilename == NULL) { CPLDebug("NITF", "Cannot find XML file : %s", NITF_SPEC_FILE); return NULL; } psFile->psNITFSpecNode = CPLParseXMLFile(pszXMLDescFilename); if (psFile->psNITFSpecNode == NULL) { CPLDebug("NITF", "Invalid XML file : %s", pszXMLDescFilename); return NULL; } } return psFile->psNITFSpecNode; } /************************************************************************/ /* NITFFindTREXMLDescFromName() */ /************************************************************************/ static CPLXMLNode* NITFFindTREXMLDescFromName(NITFFile* psFile, const char* pszTREName) { CPLXMLNode* psTreeNode; CPLXMLNode* psTresNode; CPLXMLNode* psIter; psTreeNode = NITFLoadXMLSpec(psFile); if (psTreeNode == NULL) return NULL; psTresNode = CPLGetXMLNode(psTreeNode, "=tres"); if (psTresNode == NULL) { CPLDebug("NITF", "Cannot find <tres> root element"); return NULL; } for(psIter = psTresNode->psChild;psIter != NULL;psIter = psIter->psNext) { if (psIter->eType == CXT_Element && psIter->pszValue != NULL && strcmp(psIter->pszValue, "tre") == 0) { const char* pszName = CPLGetXMLValue(psIter, "name", NULL); if (pszName != NULL && strcmp(pszName, pszTREName) == 0) { return psIter; } } } return NULL; } /************************************************************************/ /* NITFCreateXMLTre() */ /************************************************************************/ CPLXMLNode* NITFCreateXMLTre(NITFFile* psFile, const char* pszTREName, const char *pachTRE, int nTRESize) { int nTreLength, nTreMinLength = -1 /* , nTreMaxLength = -1 */; int bError = FALSE; int nTreOffset = 0; CPLXMLNode* psTreNode; CPLXMLNode* psOutXMLNode = NULL; int nMDSize = 0, nMDAlloc = 0; psTreNode = NITFFindTREXMLDescFromName(psFile, pszTREName); if (psTreNode == NULL) { if (!(STARTS_WITH_CI(pszTREName, "RPF") || strcmp(pszTREName, "XXXXXX") == 0)) { CPLDebug("NITF", "Cannot find definition of TRE %s in %s", pszTREName, NITF_SPEC_FILE); } return NULL; } nTreLength = atoi(CPLGetXMLValue(psTreNode, "length", "-1")); nTreMinLength = atoi(CPLGetXMLValue(psTreNode, "minlength", "-1")); /* nTreMaxLength = atoi(CPLGetXMLValue(psTreNode, "maxlength", "-1")); */ if( (nTreLength > 0 && nTRESize != nTreLength) || (nTreMinLength > 0 && nTRESize < nTreMinLength) ) { CPLError( CE_Warning, CPLE_AppDefined, "%s TRE wrong size, ignoring.", pszTREName ); return NULL; } psOutXMLNode = CPLCreateXMLNode(NULL, CXT_Element, "tre"); CPLCreateXMLNode(CPLCreateXMLNode(psOutXMLNode, CXT_Attribute, "name"), CXT_Text, pszTREName); CSLDestroy(NITFGenericMetadataReadTREInternal(NULL, &nMDSize, &nMDAlloc, psOutXMLNode, pszTREName, pachTRE, nTRESize, psTreNode, &nTreOffset, "", &bError)); if (bError == FALSE && nTreLength > 0 && nTreOffset != nTreLength) { CPLError( CE_Warning, CPLE_AppDefined, "Inconsistent declaration of %s TRE", pszTREName ); } if (nTreOffset < nTRESize) CPLDebug("NITF", "%d remaining bytes at end of %s TRE", nTRESize -nTreOffset, pszTREName); return psOutXMLNode; } /************************************************************************/ /* NITFGenericMetadataRead() */ /* */ /* Add metadata from TREs of file and image objects in the papszMD list */ /* pszSpecificTRE can be NULL, in which case all TREs listed in */ /* data/nitf_resources.xml that have md_prefix defined will be looked */ /* for. If not NULL, only the specified one will be looked for. */ /************************************************************************/ char **NITFGenericMetadataRead( char **papszMD, NITFFile* psFile, NITFImage *psImage, const char* pszSpecificTREName) { CPLXMLNode* psTreeNode = NULL; CPLXMLNode* psTresNode = NULL; CPLXMLNode* psIter = NULL; if (psFile == NULL) { if( psImage == NULL) return papszMD; psTreeNode = NITFLoadXMLSpec(psImage->psFile); } else psTreeNode = NITFLoadXMLSpec(psFile); if (psTreeNode == NULL) return papszMD; psTresNode = CPLGetXMLNode(psTreeNode, "=tres"); if (psTresNode == NULL) { CPLDebug("NITF", "Cannot find <tres> root element"); return papszMD; } for(psIter = psTresNode->psChild;psIter!=NULL;psIter = psIter->psNext) { if (psIter->eType == CXT_Element && psIter->pszValue != NULL && strcmp(psIter->pszValue, "tre") == 0) { const char* pszName = CPLGetXMLValue(psIter, "name", NULL); const char* pszMDPrefix = CPLGetXMLValue(psIter, "md_prefix", NULL); int bHasRightPrefix = FALSE; if( pszName == NULL ) continue; if( pszSpecificTREName == NULL ) bHasRightPrefix = ( pszMDPrefix != NULL ); else bHasRightPrefix = ( strcmp(pszName, pszSpecificTREName) == 0 ); if ( bHasRightPrefix ) { if (psFile != NULL) { const char *pachTRE = NULL; int nTRESize = 0; pachTRE = NITFFindTRE( psFile->pachTRE, psFile->nTREBytes, pszName, &nTRESize); if( pachTRE != NULL ) papszMD = NITFGenericMetadataReadTRE( papszMD, pszName, pachTRE, nTRESize, psIter); } if (psImage != NULL) { const char *pachTRE = NULL; int nTRESize = 0; pachTRE = NITFFindTRE( psImage->pachTRE, psImage->nTREBytes, pszName, &nTRESize); if( pachTRE != NULL ) papszMD = NITFGenericMetadataReadTRE( papszMD, pszName, pachTRE, nTRESize, psIter); } if (pszSpecificTREName) break; } } } return papszMD; }