/******************************************************************************
 * $Id: nitfdump.c 3cff1e81746abaf4f030b46c64242f0e6818e631 2021-08-19 10:20:33 +0200 Even Rouault $
 *
 * Project:  NITF Read/Write Library
 * Purpose:  Simple test mainline to dump info about NITF file.
 * Author:   Frank Warmerdam, warmerdam@pobox.com
 *
 **********************************************************************
 * Copyright (c) 2002, Frank Warmerdam
 * Copyright (c) 2009-2010, Even Rouault <even dot rouault at spatialys.com>
 *
 * 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_string.h"
#include "cpl_multiproc.h"
#include "cpl_vsi.h"

#include "ogr_api.h"

CPL_CVSID("$Id: nitfdump.c 3cff1e81746abaf4f030b46c64242f0e6818e631 2021-08-19 10:20:33 +0200 Even Rouault $")

static void DumpRPC( NITFImage *psImage, NITFRPC00BInfo *psRPC );
static void DumpMetadata( const char *, const char *, char ** );

/* Below a few internal functions from nitffile.c and nitfimage.c */
/* To be used by nitfdump.c, they should be CPL_DLL exported, but */
/* they are mostly internal beasts so we copy&paste them here... */

/************************************************************************/
/*                            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;
}


static GUInt16 NITFReadMSBGUInt16(VSILFILE* fp, int* pbSuccess)
{
    GUInt16 nVal;
    if (VSIFReadL(&nVal, 1, sizeof(nVal), fp) != sizeof(nVal))
    {
        *pbSuccess = FALSE;
        return 0;
    }
    CPL_MSBPTR16( &nVal );
    return nVal;
}

static GUInt32 NITFReadMSBGUInt32(VSILFILE* fp, int* pbSuccess)
{
    GUInt32 nVal;
    if (VSIFReadL(&nVal, 1, sizeof(nVal), fp) != sizeof(nVal))
    {
        *pbSuccess = FALSE;
        return 0;
    }
    CPL_MSBPTR32( &nVal );
    return nVal;
}

/************************************************************************/
/*                     NITFReadRPFLocationTable()                       */
/************************************************************************/

NITFLocation* NITFReadRPFLocationTable(VSILFILE* fp, int* pnLocCount)
{
    GUInt32 nLocSectionOffset;
    GUInt16 iLoc;
    GUInt16 nLocCount;
    GUInt16 nLocRecordLength;
    NITFLocation* pasLocations = NULL;
    int bSuccess;
    GUIntBig nCurOffset;

    if (fp == NULL || pnLocCount == NULL)
        return NULL;

    *pnLocCount = 0;

    nCurOffset = VSIFTellL(fp);

    bSuccess = TRUE;
    /*nLocSectionLength = */ NITFReadMSBGUInt16(fp, &bSuccess);
    nLocSectionOffset = NITFReadMSBGUInt32(fp, &bSuccess);
    if (nLocSectionOffset != 14)
    {
        CPLDebug("NITF", "Unusual location section offset : %d", nLocSectionOffset);
    }

    nLocCount = NITFReadMSBGUInt16(fp, &bSuccess);

    if (!bSuccess || nLocCount == 0)
    {
        return NULL;
    }

    nLocRecordLength = NITFReadMSBGUInt16(fp, &bSuccess);
    if (nLocRecordLength != 10)
    {
        CPLError(CE_Failure, CPLE_AppDefined,
                 "Did not get expected record length : %d", nLocRecordLength);
        return NULL;
    }

    /*nLocComponentAggregateLength = */ NITFReadMSBGUInt32(fp, &bSuccess);

    VSIFSeekL(fp, nCurOffset + nLocSectionOffset, SEEK_SET);

    pasLocations = (NITFLocation *)  VSI_CALLOC_VERBOSE(sizeof(NITFLocation), nLocCount);
    if (pasLocations == NULL)
    {
        return NULL;
    }

/* -------------------------------------------------------------------- */
/*      Process the locations.                                          */
/* -------------------------------------------------------------------- */
    for( iLoc = 0; iLoc < nLocCount; iLoc++ )
    {
        pasLocations[iLoc].nLocId = NITFReadMSBGUInt16(fp, &bSuccess);
        pasLocations[iLoc].nLocSize = NITFReadMSBGUInt32(fp, &bSuccess);
        pasLocations[iLoc].nLocOffset = NITFReadMSBGUInt32(fp, &bSuccess);
    }

    if (!bSuccess)
    {
        CPLFree(pasLocations);
        return NULL;
    }

    *pnLocCount = nLocCount;
    return pasLocations;
}

typedef struct
{
    const char* pszLocName;
    int         nLocID;
} LocationNameId;

static const LocationNameId asLocationTable[] =
{
    { "HeaderComponent", 128 },
    { "LocationComponent", 129 },
    { "CoverageSectionSubheader", 130 },
    { "CompressionSectionSubsection", 131 },
    { "CompressionLookupSubsection", 132 },
    { "CompressionParameterSubsection", 133 },
    { "ColorGrayscaleSectionSubheader", 134 },
    { "ColormapSubsection", 135 },
    { "ImageDescriptionSubheader", 136 },
    { "ImageDisplayParametersSubheader", 137 },
    { "MaskSubsection", 138 },
    { "ColorConverterSubsection", 139 },
    { "SpatialDataSubsection", 140 },
    { "AttributeSectionSubheader", 141 },
    { "AttributeSubsection", 142 },
    { "ExplicitArealCoverageTable", 143 },
    { "RelatedImagesSectionSubheader", 144 },
    { "RelatedImagesSubsection", 145 },
    { "ReplaceUpdateSectionSubheader", 146 },
    { "ReplaceUpdateTable", 147 },
    { "BoundaryRectangleSectionSubheader", 148 },
    { "BoundaryRectangleTable", 149 },
    { "FrameFileIndexSectionSubHeader", 150 },
    { "FrameFileIndexSubsection", 151 },
    { "ColorTableIndexSectionSubheader", 152 },
    { "ColorTableIndexRecord", 153 }
};

static const char* GetLocationNameFromId(int nID)
{
    unsigned int i;
    for(i=0;i<sizeof(asLocationTable) / sizeof(asLocationTable[0]);i++)
    {
        if (asLocationTable[i].nLocID == nID)
            return asLocationTable[i].pszLocName;
    }
    return "(unknown)";
}

/************************************************************************/
/*                                main()                                */
/************************************************************************/

int main( int nArgc, char ** papszArgv )

{
    NITFFile	*psFile;
    int          iSegment, iFile;
    char         szTemp[100];
    int          bDisplayTRE = FALSE;
    int          bExtractSHP = FALSE, bExtractSHPInMem = FALSE;

    if( nArgc < 2 )
    {
        printf( "Usage: nitfdump [-tre] [-extractshp | -extractshpinmem] <nitf_filename>*\n" );
        exit( 1 );
    }

    for( iFile = 1; iFile < nArgc; iFile++ )
    {
        if ( EQUAL(papszArgv[iFile], "-tre") )
            bDisplayTRE = TRUE;
        else if ( EQUAL(papszArgv[iFile], "-extractshp") )
            bExtractSHP = TRUE;
        else if ( EQUAL(papszArgv[iFile], "-extractshpinmem") )
        {
            bExtractSHP = TRUE;
            bExtractSHPInMem = TRUE;
        }
    }

/* ==================================================================== */
/*      Loop over all files.                                            */
/* ==================================================================== */
    for( iFile = 1; iFile < nArgc; iFile++ )
    {
        int bHasFoundLocationTable = FALSE;

        if ( EQUAL(papszArgv[iFile], "-tre") )
            continue;
        if ( EQUAL(papszArgv[iFile], "-extractshp") )
            continue;
        if ( EQUAL(papszArgv[iFile], "-extractshpinmem") )
            continue;

/* -------------------------------------------------------------------- */
/*      Open the file.                                                  */
/* -------------------------------------------------------------------- */
        psFile = NITFOpen( papszArgv[iFile], FALSE );
        if( psFile == NULL )
            exit( 2 );

        printf( "Dump for %s\n", papszArgv[iFile] );

/* -------------------------------------------------------------------- */
/*      Dump first TRE list.                                            */
/* -------------------------------------------------------------------- */
        if( psFile->pachTRE != NULL )
        {
            int nTREBytes = psFile->nTREBytes;
            const char *pszTREData = psFile->pachTRE;


            printf( "File TREs:" );

            while( nTREBytes > 10 )
            {
                int nThisTRESize = atoi(NITFGetField(szTemp, pszTREData, 6, 5 ));
                if (nThisTRESize < 0 || nThisTRESize > nTREBytes - 11)
                {
                    NITFGetField(szTemp, pszTREData, 0, 6 );
                    printf(" Invalid size (%d) for TRE %s", nThisTRESize, szTemp);
                    break;
                }

                printf( " %6.6s(%d)", pszTREData, nThisTRESize );
                pszTREData += nThisTRESize + 11;
                nTREBytes -= (nThisTRESize + 11);
            }
            printf( "\n" );

            if (bDisplayTRE)
            {
                nTREBytes = psFile->nTREBytes;
                pszTREData = psFile->pachTRE;

                while( nTREBytes > 10 )
                {
                    char *pszEscaped;
                    int nThisTRESize = atoi(NITFGetField(szTemp, pszTREData, 6, 5 ));
                    if (nThisTRESize < 0 || nThisTRESize > nTREBytes - 11)
                    {
                        break;
                    }

                    pszEscaped = CPLEscapeString( pszTREData + 11, nThisTRESize,
                                                       CPLES_BackslashQuotable );
                    printf( "TRE '%6.6s' : %s\n", pszTREData, pszEscaped);
                    CPLFree(pszEscaped);

                    pszTREData += nThisTRESize + 11;
                    nTREBytes -= (nThisTRESize + 11);
                }
            }
        }

/* -------------------------------------------------------------------- */
/*      Dump Metadata                                                   */
/* -------------------------------------------------------------------- */
        DumpMetadata( "File Metadata:", "  ", psFile->papszMetadata );

/* -------------------------------------------------------------------- */
/*      Dump general info about segments.                               */
/* -------------------------------------------------------------------- */
        NITFCollectAttachments( psFile );
        NITFReconcileAttachments( psFile );

        for( iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++ )
        {
            NITFSegmentInfo *psSegInfo = psFile->pasSegmentInfo + iSegment;

            printf( "Segment %d (Type=%s):\n",
                    iSegment + 1, psSegInfo->szSegmentType );

            printf( "  HeaderStart=" CPL_FRMT_GUIB ", HeaderSize=%u, DataStart=" CPL_FRMT_GUIB ", DataSize=" CPL_FRMT_GUIB "\n",
                    psSegInfo->nSegmentHeaderStart,
                    psSegInfo->nSegmentHeaderSize,
                    psSegInfo->nSegmentStart,
                    psSegInfo->nSegmentSize );
            printf( "  DLVL=%d, ALVL=%d, LOC=C%d,R%d, CCS=C%d,R%d\n",
                    psSegInfo->nDLVL,
                    psSegInfo->nALVL,
                    psSegInfo->nLOC_C,
                    psSegInfo->nLOC_R,
                    psSegInfo->nCCS_C,
                    psSegInfo->nCCS_R );
            printf( "\n" );
        }

/* -------------------------------------------------------------------- */
/*      Report details of images.                                       */
/* -------------------------------------------------------------------- */
        for( iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++ )
        {
            NITFSegmentInfo *psSegInfo = psFile->pasSegmentInfo + iSegment;
            NITFImage *psImage;
            NITFRPC00BInfo sRPCInfo;
            int iBand;
            char **papszMD;

            if( !EQUAL(psSegInfo->szSegmentType,"IM") )
                continue;

            psImage = NITFImageAccess( psFile, iSegment );
            if( psImage == NULL )
            {
                printf( "NITFAccessImage(%d) failed!\n", iSegment );
                continue;
            }

            printf( "Image Segment %d, %dPx%dLx%dB x %dbits:\n",
                    iSegment + 1, psImage->nCols, psImage->nRows,
                    psImage->nBands, psImage->nBitsPerSample );
            printf( "  PVTYPE=%s, IREP=%s, ICAT=%s, IMODE=%c, IC=%s, COMRAT=%s, ICORDS=%c\n",
                    psImage->szPVType, psImage->szIREP, psImage->szICAT,
                    psImage->chIMODE, psImage->szIC, psImage->szCOMRAT,
                    psImage->chICORDS );
            if( psImage->chICORDS != ' ' )
            {
                printf( "  UL=(%.15g,%.15g), UR=(%.15g,%.15g) Center=%d\n  LL=(%.15g,%.15g), LR=(%.15g,%.15g)\n",
                        psImage->dfULX, psImage->dfULY,
                        psImage->dfURX, psImage->dfURY,
                        psImage->bIsBoxCenterOfPixel,
                        psImage->dfLLX, psImage->dfLLY,
                        psImage->dfLRX, psImage->dfLRY );
            }

            printf( "  IDLVL=%d, IALVL=%d, ILOC R=%d,C=%d, IMAG=%s\n",
                    psImage->nIDLVL, psImage->nIALVL,
                    psImage->nILOCRow, psImage->nILOCColumn,
                    psImage->szIMAG );

            printf( "  %d x %d blocks of size %d x %d\n",
                    psImage->nBlocksPerRow, psImage->nBlocksPerColumn,
                    psImage->nBlockWidth, psImage->nBlockHeight );

            if( psImage->pachTRE != NULL )
            {
                int nTREBytes = psImage->nTREBytes;
                const char *pszTREData = psImage->pachTRE;

                printf( "  Image TREs:" );

                while( nTREBytes > 10 )
                {
                    int nThisTRESize = atoi(NITFGetField(szTemp, pszTREData, 6, 5 ));
                    if (nThisTRESize < 0 || nThisTRESize > nTREBytes - 11)
                    {
                        NITFGetField(szTemp, pszTREData, 0, 6 );
                        printf(" Invalid size (%d) for TRE %s", nThisTRESize, szTemp);
                        break;
                    }

                    printf( " %6.6s(%d)", pszTREData, nThisTRESize );
                    pszTREData += nThisTRESize + 11;
                    nTREBytes -= (nThisTRESize + 11);
                }
                printf( "\n" );

                if (bDisplayTRE)
                {
                    nTREBytes = psImage->nTREBytes;
                    pszTREData = psImage->pachTRE;

                    while( nTREBytes > 10 )
                    {
                        char *pszEscaped;
                        int nThisTRESize = atoi(NITFGetField(szTemp, pszTREData, 6, 5 ));
                        if (nThisTRESize < 0 || nThisTRESize > nTREBytes - 11)
                        {
                            break;
                        }

                        pszEscaped = CPLEscapeString( pszTREData + 11, nThisTRESize,
                                                        CPLES_BackslashQuotable );
                        printf( "  TRE '%6.6s' : %s\n", pszTREData, pszEscaped);
                        CPLFree(pszEscaped);

                        pszTREData += nThisTRESize + 11;
                        nTREBytes -= (nThisTRESize + 11);
                    }
                }
            }

            /* Report info from location table, if found.                  */
            if( psImage->nLocCount > 0 )
            {
                int i;
                bHasFoundLocationTable = TRUE;
                printf( "  Location Table\n" );
                for( i = 0; i < psImage->nLocCount; i++ )
                {
                    printf( "    LocName=%s, LocId=%d, Offset=%u, Size=%u\n",
                            GetLocationNameFromId(psImage->pasLocations[i].nLocId),
                            psImage->pasLocations[i].nLocId,
                            psImage->pasLocations[i].nLocOffset,
                            psImage->pasLocations[i].nLocSize );
                }
                printf( "\n" );
            }

            if( strlen(psImage->pszComments) > 0 )
                printf( "  Comments:\n%s\n", psImage->pszComments );

            for( iBand = 0; iBand < psImage->nBands; iBand++ )
            {
                NITFBandInfo *psBandInfo = psImage->pasBandInfo + iBand;

                printf( "  Band %d: IREPBAND=%s, ISUBCAT=%s, %d LUT entries.\n",
                        iBand + 1, psBandInfo->szIREPBAND, psBandInfo->szISUBCAT,
                        psBandInfo->nSignificantLUTEntries );
            }

            if( NITFReadRPC00B( psImage, &sRPCInfo ) )
            {
                DumpRPC( psImage, &sRPCInfo );
            }

            papszMD = NITFReadUSE00A( psImage );
            if( papszMD != NULL )
            {
                DumpMetadata( "  USE00A TRE:", "    ", papszMD );
                CSLDestroy( papszMD );
            }

            papszMD = NITFReadBLOCKA( psImage );
            if( papszMD != NULL )
            {
                DumpMetadata( "  BLOCKA TRE:", "    ", papszMD );
                CSLDestroy( papszMD );
            }

            papszMD = NITFReadSTDIDC( psImage );
            if( papszMD != NULL )
            {
                DumpMetadata( "  STDIDC TRE:", "    ", papszMD );
                CSLDestroy( papszMD );
            }

            DumpMetadata( "  Image Metadata:", "    ", psImage->papszMetadata );
            printf("\n");
        }

/* ==================================================================== */
/*      Report details of graphic segments.                             */
/* ==================================================================== */
        for( iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++ )
        {
            NITFSegmentInfo *psSegInfo = psFile->pasSegmentInfo + iSegment;
            char achSubheader[298];
            int  nSTYPEOffset;

            if( !EQUAL(psSegInfo->szSegmentType,"GR")
                && !EQUAL(psSegInfo->szSegmentType,"SY") )
                continue;

/* -------------------------------------------------------------------- */
/*      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.                                      */
/* -------------------------------------------------------------------- */
            printf( "Graphic Segment %d, type=%2.2s, sfmt=%c, sid=%10.10s\n",
                    iSegment + 1,
                    achSubheader + 0,
                    achSubheader[nSTYPEOffset],
                    achSubheader + 2 );

            printf( "  sname=%20.20s\n", achSubheader + 12 );
            printf("\n");
        }

/* ==================================================================== */
/*      Report details of text segments.                                */
/* ==================================================================== */
        for( iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++ )
        {
            char *pabyHeaderData;
            char *pabyTextData;

            NITFSegmentInfo *psSegment = psFile->pasSegmentInfo + iSegment;

            if( !EQUAL(psSegment->szSegmentType,"TX") )
                continue;

            printf( "Text Segment %d\n", iSegment + 1);

/* -------------------------------------------------------------------- */
/*      Load the text header                                            */
/* -------------------------------------------------------------------- */

            /* Allocate one extra byte for the NULL terminating character */
            pabyHeaderData = (char *) CPLCalloc(1,
                    (size_t) psSegment->nSegmentHeaderSize + 1);
            if (VSIFSeekL(psFile->fp, psSegment->nSegmentHeaderStart,
                        SEEK_SET) != 0 ||
                VSIFReadL(pabyHeaderData, 1, (size_t) psSegment->nSegmentHeaderSize,
                        psFile->fp) != psSegment->nSegmentHeaderSize)
            {
                CPLError( CE_Warning, CPLE_FileIO,
                        "Failed to read %d bytes of text header data at " CPL_FRMT_GUIB ".",
                        psSegment->nSegmentHeaderSize,
                        psSegment->nSegmentHeaderStart);
                CPLFree(pabyHeaderData);
                continue;
            }

            printf("  Header : %s\n", pabyHeaderData);
            CPLFree(pabyHeaderData);

/* -------------------------------------------------------------------- */
/*      Load the raw TEXT data itself.                                  */
/* -------------------------------------------------------------------- */


            /* Allocate one extra byte for the NULL terminating character */
            pabyTextData = (char *) CPLCalloc(1,(size_t)psSegment->nSegmentSize+1);
            if( VSIFSeekL( psFile->fp, psSegment->nSegmentStart,
                        SEEK_SET ) != 0
                || VSIFReadL( pabyTextData, 1, (size_t)psSegment->nSegmentSize,
                            psFile->fp ) != psSegment->nSegmentSize )
            {
                CPLError( CE_Warning, CPLE_FileIO,
                        "Failed to read " CPL_FRMT_GUIB " bytes of text data at " CPL_FRMT_GUIB ".",
                        psSegment->nSegmentSize,
                        psSegment->nSegmentStart );
                CPLFree( pabyTextData );
                continue;
            }

            printf("  Data  : %s\n", pabyTextData);
            printf("\n");
            CPLFree( pabyTextData );

        }

/* -------------------------------------------------------------------- */
/*      Report details of DES.                                          */
/* -------------------------------------------------------------------- */
        for( iSegment = 0; iSegment < psFile->nSegmentCount; iSegment++ )
        {
            NITFSegmentInfo *psSegInfo = psFile->pasSegmentInfo + iSegment;
            NITFDES *psDES;
            int nOffset = 0;
            char szTREName[7];
            int nThisTRESize;
            int nRPFDESOffset = -1;

            if( !EQUAL(psSegInfo->szSegmentType,"DE") )
                continue;

            psDES = NITFDESAccess( psFile, iSegment );
            if( psDES == NULL )
            {
                printf( "NITFDESAccess(%d) failed!\n", iSegment );
                continue;
            }

            printf( "DE Segment %d:\n", iSegment + 1 );

            printf( "  Segment TREs:" );
            nOffset = 0;
            while (NITFDESGetTRE( psDES, nOffset, szTREName, NULL, &nThisTRESize))
            {
                printf( " %6.6s(%d)", szTREName, nThisTRESize );
                if (strcmp(szTREName, "RPFDES") == 0)
                    nRPFDESOffset = nOffset + 11;
                nOffset += 11 + nThisTRESize;
            }
            printf( "\n" );

            if (bDisplayTRE)
            {
                char* pabyTREData = NULL;
                nOffset = 0;
                while (NITFDESGetTRE( psDES, nOffset, szTREName, &pabyTREData, &nThisTRESize))
                {
                    char* pszEscaped = CPLEscapeString( pabyTREData, nThisTRESize,
                                                        CPLES_BackslashQuotable );
                    printf( "  TRE '%6.6s' : %s\n", szTREName, pszEscaped);
                    CPLFree(pszEscaped);

                    nOffset += 11 + nThisTRESize;

                    NITFDESFreeTREData(pabyTREData);
                }
            }

            /* Report info from location table, if found. */
            if( !bHasFoundLocationTable && nRPFDESOffset >= 0 )
            {
                int i;
                int nLocCount = 0;
                NITFLocation* pasLocations;

                VSIFSeekL(psFile->fp, psSegInfo->nSegmentStart + nRPFDESOffset, SEEK_SET);
                pasLocations = NITFReadRPFLocationTable(psFile->fp, &nLocCount);
                if (pasLocations)
                {
                    printf( "  Location Table\n" );
                    for( i = 0; i < nLocCount; i++ )
                    {
                        printf( "    LocName=%s, LocId=%d, Offset=%u, Size=%u\n",
                                GetLocationNameFromId(pasLocations[i].nLocId),
                                pasLocations[i].nLocId,
                                pasLocations[i].nLocOffset,
                                pasLocations[i].nLocSize );
                    }

                    CPLFree(pasLocations);
                    printf( "\n" );
                }
            }

            DumpMetadata( "  DES Metadata:", "    ", psDES->papszMetadata );

            if ( bExtractSHP && EQUAL(CSLFetchNameValueDef(psDES->papszMetadata, "DESID", ""), "CSSHPA DES") )
            {
                char szFilename[32];
                char szRadix[32];
                if (bExtractSHPInMem)
                    snprintf(szRadix, sizeof(szRadix), "/vsimem/nitf_segment_%d", iSegment + 1);
                else
                    snprintf(szRadix, sizeof(szRadix), "nitf_segment_%d", iSegment + 1);

                if (NITFDESExtractShapefile(psDES, szRadix))
                {
                    OGRDataSourceH hDS;
                    OGRRegisterAll();
                    snprintf(szFilename, sizeof(szFilename), "%s.SHP", szRadix);
                    hDS = OGROpen(szFilename, FALSE, NULL);
                    if (hDS)
                    {
                        int nGeom = 0;
                        OGRLayerH hLayer = OGR_DS_GetLayer(hDS, 0);
                        if (hLayer)
                        {
                            OGRFeatureH hFeat;
                            printf("\n");
                            while ( (hFeat = OGR_L_GetNextFeature(hLayer)) != NULL )
                            {
                                OGRGeometryH hGeom = OGR_F_GetGeometryRef(hFeat);
                                if (hGeom)
                                {
                                    char* pszWKT = NULL;
                                    OGR_G_ExportToWkt(hGeom, &pszWKT);
                                    if (pszWKT)
                                        printf("    Geometry %d : %s\n", nGeom ++, pszWKT);
                                    CPLFree(pszWKT);
                                }
                                OGR_F_Destroy(hFeat);
                            }
                        }
                        OGR_DS_Destroy(hDS);
                    }
                }

                if (bExtractSHPInMem)
                {
                    snprintf(szFilename, sizeof(szFilename), "%s.SHP", szRadix);
                    VSIUnlink(szFilename);
                    snprintf(szFilename, sizeof(szFilename), "%s.SHX", szRadix);
                    VSIUnlink(szFilename);
                    snprintf(szFilename, sizeof(szFilename), "%s.DBF", szRadix);
                    VSIUnlink(szFilename);
                }
            }
        }

/* -------------------------------------------------------------------- */
/*      Close.                                                          */
/* -------------------------------------------------------------------- */
        NITFClose( psFile );
    }

    CPLFinderClean();
    CPLCleanupTLS();
    VSICleanupFileManager();
    OGRCleanupAll();

    exit( 0 );
}

/************************************************************************/
/*                            DumpMetadata()                            */
/************************************************************************/

static void DumpMetadata( const char *pszTitle, const char *pszPrefix,
                          char ** papszMD )
{
    int i;

    if( papszMD == NULL )
        return;

    printf( "%s\n", pszTitle );

    for( i = 0; papszMD[i] != NULL; i++ )
        printf( "%s%s\n", pszPrefix, papszMD[i] );
}

/************************************************************************/
/*                              DumpRPC()                               */
/************************************************************************/

static void DumpRPC( NITFImage *psImage, NITFRPC00BInfo *psRPC )

{
    int  i;

    printf( "  RPC00B:\n" );
    printf( "    SUCCESS=%d\n", psRPC->SUCCESS );
    printf( "    ERR_BIAS=%.16g\n", psRPC->ERR_BIAS );
    printf( "    ERR_RAND=%.16g\n", psRPC->ERR_RAND );

    printf( "    LINE_OFF=%.16g\n", psRPC->LINE_OFF );
    printf( "    SAMP_OFF=%.16g\n", psRPC->SAMP_OFF );
    printf( "    LAT_OFF =%.16g\n", psRPC->LAT_OFF );
    printf( "    LONG_OFF=%.16g\n", psRPC->LONG_OFF );
    printf( "    HEIGHT_OFF=%.16g\n", psRPC->HEIGHT_OFF );

    printf( "    LINE_SCALE=%.16g\n", psRPC->LINE_SCALE );
    printf( "    SAMP_SCALE=%.16g\n", psRPC->SAMP_SCALE );
    printf( "    LAT_SCALE =%.16g\n", psRPC->LAT_SCALE );
    printf( "    LONG_SCALE=%.16g\n", psRPC->LONG_SCALE );
    printf( "    HEIGHT_SCALE=%.16g\n", psRPC->HEIGHT_SCALE );

    printf( "    LINE_NUM_COEFF = " );
    for( i=0; i < 20; i++ )
    {
        printf( "%.12g ", psRPC->LINE_NUM_COEFF[i] );

        if( i == 19 )
            printf( "\n" );
        else if( (i%5) == 4  )
            printf( "\n                     " );
    }

    printf( "    LINE_DEN_COEFF = " );
    for( i=0; i < 20; i++ )
    {
        printf( "%.12g ", psRPC->LINE_DEN_COEFF[i] );

        if( i == 19 )
            printf( "\n" );
        else if( (i%5) == 4  )
            printf( "\n                     " );
    }

    printf( "    SAMP_NUM_COEFF = " );
    for( i=0; i < 20; i++ )
    {
        printf( "%.12g ", psRPC->SAMP_NUM_COEFF[i] );

        if( i == 19 )
            printf( "\n" );
        else if( (i%5) == 4  )
            printf( "\n                     " );
    }

    printf( "    SAMP_DEN_COEFF = " );
    for( i=0; i < 20; i++ )
    {
        printf( "%.12g ", psRPC->SAMP_DEN_COEFF[i] );

        if( i == 19 )
            printf( "\n" );
        else if( (i%5) == 4  )
            printf( "\n                     " );
    }

/* -------------------------------------------------------------------- */
/*      Dump some known locations.                                      */
/* -------------------------------------------------------------------- */
    {
        double adfLong[] = { psImage->dfULX, psImage->dfURX,
                             psImage->dfLLX, psImage->dfLRX,
                             (psImage->dfULX + psImage->dfLRX) / 2,
                             (psImage->dfULX + psImage->dfLRX) / 2 };
        double adfLat[] = { psImage->dfULY, psImage->dfURY,
                            psImage->dfLLY, psImage->dfLRY,
                            (psImage->dfULY + psImage->dfLRY) / 2,
                            (psImage->dfULY + psImage->dfLRY) / 2 };
        double adfHeight[] = { 0.0, 0.0, 0.0, 0.0, 0.0, 300.0 };
        double dfPixel, dfLine;

        for( i = 0; i < sizeof(adfLong) / sizeof(double); i++ )
        {
            NITFRPCGeoToImage( psRPC, adfLong[i], adfLat[i], adfHeight[i],
                               &dfPixel, &dfLine );

            printf( "    RPC Transform (%.12g,%.12g,%g) -> (%g,%g)\n",
                    adfLong[i], adfLat[i], adfHeight[i], dfPixel, dfLine );
        }
    }
}
