GDALDataset *IntergraphDataset::Open( GDALOpenInfo *poOpenInfo )
{
    if( poOpenInfo->nHeaderBytes < 1024 )
    {
        return NULL;
    }

    // --------------------------------------------------------------------
    // Assign Header Information
    // --------------------------------------------------------------------

    INGR_HeaderOne hHeaderOne;

    INGR_HeaderOneDiskToMem( &hHeaderOne, (GByte*) poOpenInfo->pabyHeader);

    // --------------------------------------------------------------------
    // Check Header Type (HTC) Version
    // --------------------------------------------------------------------

    if( hHeaderOne.HeaderType.Version != INGR_HEADER_VERSION )
    {
        return NULL;
    }

    // --------------------------------------------------------------------
    // Check Header Type (HTC) 2D / 3D Flag
    // --------------------------------------------------------------------

    if( ( hHeaderOne.HeaderType.Is2Dor3D != INGR_HEADER_2D ) &&
            ( hHeaderOne.HeaderType.Is2Dor3D != INGR_HEADER_3D ) )
    {
        return NULL;
    }

    // --------------------------------------------------------------------
    // Check Header Type (HTC) Type Flag
    // --------------------------------------------------------------------

    if( hHeaderOne.HeaderType.Type != INGR_HEADER_TYPE )
    {
        return NULL;
    }

    // --------------------------------------------------------------------
    // Check Grid File Version (VER)
    // --------------------------------------------------------------------

    if( hHeaderOne.GridFileVersion != 1 &&
            hHeaderOne.GridFileVersion != 2 &&
            hHeaderOne.GridFileVersion != 3 )
    {
        return NULL;
    }

    // --------------------------------------------------------------------
    // Check Words To Follow (WTC) Minimum Value
    // --------------------------------------------------------------------

    if( hHeaderOne.WordsToFollow < 254 )
    {
        return NULL;
    }

    // --------------------------------------------------------------------
    // Check Words To Follow (WTC) Integrity
    // --------------------------------------------------------------------

    float fHeaderBlocks = (float) ( hHeaderOne.WordsToFollow + 2 ) / 256;

    if( ( fHeaderBlocks - (int) fHeaderBlocks ) != 0.0 )
    {
        return NULL;
    }

    // --------------------------------------------------------------------
    // Get Data Type Code (DTC) => Format Type
    // --------------------------------------------------------------------

    INGR_Format eFormat = (INGR_Format) hHeaderOne.DataTypeCode;

    // --------------------------------------------------------------------
    // We need to scan around the file, so we open it now.
    // --------------------------------------------------------------------

    VSILFILE   *fp;

    if( poOpenInfo->eAccess == GA_ReadOnly  )
    {
        fp = VSIFOpenL( poOpenInfo->pszFilename, "rb" );
    }
    else
    {
        fp = VSIFOpenL( poOpenInfo->pszFilename, "r+b" );
    }

    if( fp == NULL )
    {
        CPLError( CE_Failure, CPLE_OpenFailed, "%s", VSIStrerror( errno ) );
        return NULL;
    }

    // --------------------------------------------------------------------
    // Get Format Type from the tile directory
    // --------------------------------------------------------------------

    if( hHeaderOne.DataTypeCode == TiledRasterData )
    {
        INGR_TileHeader hTileDir;

        int nOffset = 2 + ( 2 * ( hHeaderOne.WordsToFollow + 1 ) );

        GByte abyBuffer[SIZEOF_TDIR];

        if( (VSIFSeekL( fp, nOffset, SEEK_SET ) == -1 )  ||
                (VSIFReadL( abyBuffer, 1, SIZEOF_TDIR, fp ) == 0) )
        {
            VSIFCloseL( fp );
            CPLError( CE_Failure, CPLE_AppDefined,
                      "Error reading tiles header" );
            return NULL;
        }

        INGR_TileHeaderDiskToMem( &hTileDir, abyBuffer );

        if( !
                ( hTileDir.ApplicationType     == 1 &&
                  hTileDir.SubTypeCode         == 7 &&
                  ( hTileDir.WordsToFollow % 4 ) == 0 &&
                  hTileDir.PacketVersion       == 1 &&
                  hTileDir.Identifier          == 1 ) )
        {
            CPLError( CE_Failure, CPLE_AppDefined,
                      "Cannot recognize tiles header info");
            VSIFCloseL( fp );
            return NULL;
        }

        eFormat = (INGR_Format) hTileDir.DataTypeCode;
    }

    // --------------------------------------------------------------------
    // Check Scannable Flag
    // --------------------------------------------------------------------
    /*
        if (hHeaderOne.ScannableFlag == HasLineHeader)
        {
            CPLError( CE_Failure, CPLE_AppDefined,
                "Intergraph Raster Scannable Line Header not supported yet" );
            VSIFCloseL( fp );
            return NULL;
        }
    */
    // --------------------------------------------------------------------
    // Check supported Format Type
    // --------------------------------------------------------------------

    if( eFormat != ByteInteger &&
            eFormat != WordIntegers &&
            eFormat != Integers32Bit &&
            eFormat != FloatingPoint32Bit &&
            eFormat != FloatingPoint64Bit &&
            eFormat != RunLengthEncoded &&
            eFormat != RunLengthEncodedC &&
            eFormat != CCITTGroup4 &&
            eFormat != AdaptiveRGB &&
            eFormat != Uncompressed24bit &&
            eFormat != AdaptiveGrayScale &&
            eFormat != ContinuousTone &&
            eFormat != JPEGGRAY &&
            eFormat != JPEGRGB &&
            eFormat != JPEGCYMK )
    {
        CPLError( CE_Failure, CPLE_AppDefined,
                  "Intergraph Raster Format %d ( \"%s\" ) not supported",
                  hHeaderOne.DataTypeCode, INGR_GetFormatName( (uint16) eFormat ) );
        VSIFCloseL( fp );
        return NULL;
    }

    // -----------------------------------------------------------------
    // Create a corresponding GDALDataset
    // -----------------------------------------------------------------

    IntergraphDataset *poDS;

    poDS = new IntergraphDataset();
    poDS->eAccess = poOpenInfo->eAccess;
    poDS->pszFilename = CPLStrdup( poOpenInfo->pszFilename );
    poDS->fp = fp;

    // --------------------------------------------------------------------
    // Get X Size from Pixels Per Line (PPL)
    // --------------------------------------------------------------------

    poDS->nRasterXSize = hHeaderOne.PixelsPerLine;

    // --------------------------------------------------------------------
    // Get Y Size from Number of Lines (NOL)
    // --------------------------------------------------------------------

    poDS->nRasterYSize = hHeaderOne.NumberOfLines;

    if (poDS->nRasterXSize <= 0 || poDS->nRasterYSize <= 0)
    {
        CPLError( CE_Failure, CPLE_AppDefined,
                  "Invalid dimensions : %d x %d",
                  poDS->nRasterXSize, poDS->nRasterYSize);
        delete poDS;
        return NULL;
    }

    // --------------------------------------------------------------------
    // Get Geo Transformation from Homogeneous Transformation Matrix (TRN)
    // --------------------------------------------------------------------

    INGR_GetTransMatrix( &hHeaderOne, poDS->adfGeoTransform );

    // --------------------------------------------------------------------
    // Set Metadata Information
    // --------------------------------------------------------------------

    poDS->SetMetadataItem( "VERSION",
                           CPLSPrintf ( "%d", hHeaderOne.GridFileVersion ), "IMAGE_STRUCTURE" );
    poDS->SetMetadataItem( "RESOLUTION",
                           CPLSPrintf ( "%d", (hHeaderOne.DeviceResolution < 0)?-hHeaderOne.DeviceResolution:1) );

    // --------------------------------------------------------------------
    // Create Band Information
    // --------------------------------------------------------------------

    int nBands = 0;
    int nBandOffset = 0;

    GByte abyBuf[MAX(SIZEOF_HDR1,SIZEOF_HDR2_A)];

    do
    {
        VSIFSeekL( poDS->fp, nBandOffset, SEEK_SET );

        VSIFReadL( abyBuf, 1, SIZEOF_HDR1, poDS->fp );

        INGR_HeaderOneDiskToMem( &poDS->hHeaderOne, abyBuf );

        VSIFReadL( abyBuf, 1, SIZEOF_HDR2_A, poDS->fp );

        INGR_HeaderTwoADiskToMem( &poDS->hHeaderTwo, abyBuf );

        switch( eFormat )
        {
        case JPEGRGB:
        case JPEGCYMK:
        {
            IntergraphBitmapBand* poBand;
            nBands++;
            poDS->SetBand( nBands,
                           poBand = new IntergraphBitmapBand( poDS, nBands, nBandOffset, 1 ));
            if (poBand->pabyBMPBlock == NULL)
            {
                delete poDS;
                return NULL;
            }
            nBands++;
            poDS->SetBand( nBands,
                           poBand = new IntergraphBitmapBand( poDS, nBands, nBandOffset, 2 ));
            if (poBand->pabyBMPBlock == NULL)
            {
                delete poDS;
                return NULL;
            }
            nBands++;
            poDS->SetBand( nBands,
                           poBand = new IntergraphBitmapBand( poDS, nBands, nBandOffset, 3 ));
            if (poBand->pabyBMPBlock == NULL)
            {
                delete poDS;
                return NULL;
            }
            break;
        }
        case JPEGGRAY:
        case CCITTGroup4:
        {
            IntergraphBitmapBand* poBand;
            nBands++;
            poDS->SetBand( nBands,
                           poBand = new IntergraphBitmapBand( poDS, nBands, nBandOffset ));
            if (poBand->pabyBMPBlock == NULL)
            {
                delete poDS;
                return NULL;
            }
            break;
        }
        case RunLengthEncoded:
        case RunLengthEncodedC:
        case AdaptiveGrayScale:
        {
            IntergraphRLEBand* poBand;
            nBands++;
            poDS->SetBand( nBands,
                           poBand = new IntergraphRLEBand( poDS, nBands, nBandOffset ));
            if (poBand->pabyBlockBuf == NULL || poBand->pabyRLEBlock == NULL)
            {
                delete poDS;
                return NULL;
            }
            break;
        }
        case AdaptiveRGB:
        case ContinuousTone:
        {
            IntergraphRLEBand* poBand;
            nBands++;
            poDS->SetBand( nBands,
                           poBand = new IntergraphRLEBand( poDS, nBands, nBandOffset, 1 ));
            if (poBand->pabyBlockBuf == NULL || poBand->pabyRLEBlock == NULL)
            {
                delete poDS;
                return NULL;
            }
            nBands++;
            poDS->SetBand( nBands,
                           poBand = new IntergraphRLEBand( poDS, nBands, nBandOffset, 2 ));
            if (poBand->pabyBlockBuf == NULL || poBand->pabyRLEBlock == NULL)
            {
                delete poDS;
                return NULL;
            }
            nBands++;
            poDS->SetBand( nBands,
                           poBand = new IntergraphRLEBand( poDS, nBands, nBandOffset, 3 ));
            if (poBand->pabyBlockBuf == NULL || poBand->pabyRLEBlock == NULL)
            {
                delete poDS;
                return NULL;
            }
            break;
        }
        case Uncompressed24bit:
        {
            IntergraphRGBBand* poBand;
            nBands++;
            poDS->SetBand( nBands,
                           poBand = new IntergraphRGBBand( poDS, nBands, nBandOffset, 1 ));
            if (poBand->pabyBlockBuf == NULL)
            {
                delete poDS;
                return NULL;
            }
            nBands++;
            poDS->SetBand( nBands,
                           poBand = new IntergraphRGBBand( poDS, nBands, nBandOffset, 2 ));
            if (poBand->pabyBlockBuf == NULL)
            {
                delete poDS;
                return NULL;
            }
            nBands++;
            poDS->SetBand( nBands,
                           poBand = new IntergraphRGBBand( poDS, nBands, nBandOffset, 3 ));
            if (poBand->pabyBlockBuf == NULL)
            {
                delete poDS;
                return NULL;
            }
            break;
        }
        default:
        {
            IntergraphRasterBand* poBand;
            nBands++;
            poDS->SetBand( nBands,
                           poBand = new IntergraphRasterBand( poDS, nBands, nBandOffset ));
            if (poBand->pabyBlockBuf == NULL)
            {
                delete poDS;
                return NULL;
            }
        }
        }

        // ----------------------------------------------------------------
        // Get next band offset from Catenated File Pointer (CFP)
        // ----------------------------------------------------------------

        nBandOffset = poDS->hHeaderTwo.CatenatedFilePointer;
    }
    while( nBandOffset != 0 );

    poDS->nBands = nBands;

    // --------------------------------------------------------------------
    // Initialize any PAM information
    // --------------------------------------------------------------------

    poDS->SetDescription( poOpenInfo->pszFilename );
    poDS->TryLoadXML();

    /* -------------------------------------------------------------------- */
    /*      Check for external overviews.                                   */
    /* -------------------------------------------------------------------- */

    poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );

    return ( poDS );
}
IntergraphRasterBand::IntergraphRasterBand( IntergraphDataset *poDS, 
                                            int nBand,
                                            int nBandOffset,
                                            GDALDataType eType )
{
    this->poColorTable  = new GDALColorTable();

    this->poDS          = poDS;
    this->nBand         = nBand != 0 ? nBand : poDS->nBands;
    this->nTiles        = 0;
    this->eDataType     = eType;
    this->pabyBlockBuf  = NULL;
    this->pahTiles      = NULL;
    this->nRGBIndex     = 0;
    this->nBandStart    = nBandOffset;
    this->bTiled        = FALSE;

    // -------------------------------------------------------------------- 
    // Get Header Info
    // -------------------------------------------------------------------- 

    memcpy(&hHeaderOne, &poDS->hHeaderOne, sizeof(hHeaderOne));
    memcpy(&hHeaderTwo, &poDS->hHeaderTwo, sizeof(hHeaderTwo));

    // -------------------------------------------------------------------- 
    // Get the image start from Words to Follow (WTF)
    // -------------------------------------------------------------------- 

    nDataOffset = nBandOffset + 2 + ( 2 * ( hHeaderOne.WordsToFollow + 1 ) );

    // -------------------------------------------------------------------- 
    // Get Color Tabel from Color Table Type (CTV)
    // -------------------------------------------------------------------- 

    uint32 nEntries = hHeaderTwo.NumberOfCTEntries;

    if( nEntries > 0 )
    {
        switch ( hHeaderTwo.ColorTableType )
        {
        case EnvironVColorTable:
            INGR_GetEnvironVColors( poDS->fp, nBandOffset, nEntries, poColorTable );
            if (poColorTable->GetColorEntryCount() == 0)
                return;
            break;
        case IGDSColorTable:
            INGR_GetIGDSColors( poDS->fp, nBandOffset, nEntries, poColorTable );
            if (poColorTable->GetColorEntryCount() == 0)
                return;
            break;
        default:
            CPLDebug( "INGR", "Wrong Color table type (%d), number of colors (%d)", 
                hHeaderTwo.ColorTableType, nEntries );
        }
    }

    // -------------------------------------------------------------------- 
    // Set Dimension
    // -------------------------------------------------------------------- 

    nRasterXSize  = hHeaderOne.PixelsPerLine;
    nRasterYSize  = hHeaderOne.NumberOfLines;
    
    nBlockXSize   = nRasterXSize;
    nBlockYSize   = 1;

    // -------------------------------------------------------------------- 
    // Get tile directory
    // -------------------------------------------------------------------- 

    this->eFormat = (INGR_Format) hHeaderOne.DataTypeCode;

    this->bTiled = (hHeaderOne.DataTypeCode == TiledRasterData);

    if( bTiled )
    {
        nTiles = INGR_GetTileDirectory( poDS->fp, 
                                        nDataOffset, 
                                        nRasterXSize, 
                                        nRasterYSize,
                                        &hTileDir, 
                                        &pahTiles );
        if (nTiles == 0)
            return;

        eFormat = (INGR_Format) hTileDir.DataTypeCode;

        // ----------------------------------------------------------------
        // Set blocks dimensions based on tiles
        // ----------------------------------------------------------------

        nBlockXSize = MIN( hTileDir.TileSize, (uint32) nRasterXSize );
        nBlockYSize = MIN( hTileDir.TileSize, (uint32) nRasterYSize );
    }

    if (nBlockXSize <= 0 || nBlockYSize <= 0)
    {
        pabyBlockBuf = NULL;
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid block dimensions");
        return;
    }

    // -------------------------------------------------------------------- 
    // Incomplete tiles have Block Offset greater than: 
    // -------------------------------------------------------------------- 

    nFullBlocksX  = ( nRasterXSize / nBlockXSize );
    nFullBlocksY  = ( nRasterYSize / nBlockYSize );

    // -------------------------------------------------------------------- 
    // Get the Data Type from Format
    // -------------------------------------------------------------------- 

    this->eDataType = INGR_GetDataType( (uint16) eFormat );

    // -------------------------------------------------------------------- 
    // Allocate buffer for a Block of data
    // -------------------------------------------------------------------- 

    nBlockBufSize = nBlockXSize * nBlockYSize * 
                    GDALGetDataTypeSize( eDataType ) / 8;
        
    pabyBlockBuf = (GByte*) VSIMalloc3( nBlockXSize, nBlockYSize,
                                        GDALGetDataTypeSize( eDataType ) / 8);
    if (pabyBlockBuf == NULL)
    {
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot allocate %d bytes", nBlockBufSize);
        return;
    }

    // -------------------------------------------------------------------- 
    // More Metadata Information
    // -------------------------------------------------------------------- 

    SetMetadataItem( "FORMAT", INGR_GetFormatName( (uint16) eFormat ), 
        "IMAGE_STRUCTURE" );

    if( bTiled )
    {
        SetMetadataItem( "TILESSIZE", CPLSPrintf ("%d", hTileDir.TileSize), 
            "IMAGE_STRUCTURE" );
    }
    else
    {
        SetMetadataItem( "TILED", "NO", "IMAGE_STRUCTURE" ); 
    }

    SetMetadataItem( "ORIENTATION", 
        INGR_GetOrientation( hHeaderOne.ScanlineOrientation ),
        "IMAGE_STRUCTURE" );
}
GDALDataset *IntergraphDataset::Create( const char *pszFilename,
                                        int nXSize,
                                        int nYSize,
                                        int nBands,
                                        GDALDataType eType,
                                        char **papszOptions )
{
    int nDeviceResolution = 1;
    const char *pszValue;
    const char *pszCompression = NULL;

    pszValue = CSLFetchNameValue(papszOptions, "RESOLUTION");
    if( pszValue != NULL )
        nDeviceResolution = -atoi( pszValue );

    char *pszExtension = CPLStrlwr(CPLStrdup(CPLGetExtension(pszFilename)));
    if ( EQUAL( pszExtension, "rle" ) )
        pszCompression = INGR_GetFormatName(RunLengthEncoded);
    CPLFree(pszExtension);

    if( eType != GDT_Byte &&
            eType != GDT_Int16 &&
            eType != GDT_Int32 &&
            eType != GDT_UInt16 &&
            eType != GDT_UInt32 &&
            eType != GDT_Float32&&
            eType != GDT_Float64 )
    {
        CPLError( CE_Failure, CPLE_AppDefined, "Data type not supported (%s)",
                  GDALGetDataTypeName( eType ) );
        return NULL;
    }

    // --------------------------------------------------------------------
    //  Fill headers with minimun information
    // --------------------------------------------------------------------

    INGR_HeaderOne  hHdr1;
    INGR_HeaderTwoA hHdr2;
    INGR_ColorTable256 hCTab;
    int             i;

    memset(&hHdr1, 0, SIZEOF_HDR1);
    memset(&hHdr2, 0, SIZEOF_HDR2_A);
    memset(&hCTab, 0, SIZEOF_CTAB);

    hHdr1.HeaderType.Version    = INGR_HEADER_VERSION;
    hHdr1.HeaderType.Type       = INGR_HEADER_TYPE;
    hHdr1.HeaderType.Is2Dor3D   = INGR_HEADER_2D;
    hHdr1.DataTypeCode          = (uint16) INGR_GetFormat( eType, (pszCompression!=NULL)?pszCompression:"None" );
    hHdr1.WordsToFollow         = ( ( SIZEOF_HDR1 * 3 ) / 2 ) - 2;
    hHdr1.ApplicationType       = GenericRasterImageFile;
    hHdr1.XViewOrigin           = 0.0;
    hHdr1.YViewOrigin           = 0.0;
    hHdr1.ZViewOrigin           = 0.0;
    hHdr1.XViewExtent           = 0.0;
    hHdr1.YViewExtent           = 0.0;
    hHdr1.ZViewExtent           = 0.0;
    for( i = 0; i < 15; i++ )
        hHdr1.TransformationMatrix[i]   = 0.0;
    hHdr1.TransformationMatrix[15]      = 1.0;
    hHdr1.PixelsPerLine         = nXSize;
    hHdr1.NumberOfLines         = nYSize;
    hHdr1.DeviceResolution      = nDeviceResolution;
    hHdr1.ScanlineOrientation   = UpperLeftHorizontal;
    hHdr1.ScannableFlag         = NoLineHeader;
    hHdr1.RotationAngle         = 0.0;
    hHdr1.SkewAngle             = 0.0;
    hHdr1.DataTypeModifier      = 0;
    hHdr1.DesignFileName[0]     = '\0';
    hHdr1.DataBaseFileName[0]   = '\0';
    hHdr1.ParentGridFileName[0] = '\0';
    hHdr1.FileDescription[0]    = '\0';
    hHdr1.Minimum               = INGR_SetMinMax( eType, 0.0 );
    hHdr1.Maximum               = INGR_SetMinMax( eType, 0.0 );
    hHdr1.GridFileVersion       = 3;
    hHdr1.Reserved[0]           = 0;
    hHdr1.Reserved[1]           = 0;
    hHdr1.Reserved[2]           = 0;
    hHdr2.Gain                  = 0;
    hHdr2.OffsetThreshold       = 0;
    hHdr2.View1                 = 0;
    hHdr2.View2                 = 0;
    hHdr2.ViewNumber            = 0;
    hHdr2.Reserved2             = 0;
    hHdr2.Reserved3             = 0;
    hHdr2.AspectRatio           = nXSize / nYSize;
    hHdr2.CatenatedFilePointer  = 0;
    hHdr2.ColorTableType        = NoColorTable;
    hHdr2.NumberOfCTEntries     = 0;
    hHdr2.Reserved8             = 0;
    for( i = 0; i < 110; i++ )
        hHdr2.Reserved[i]       = 0;
    hHdr2.ApplicationPacketLength   = 0;
    hHdr2.ApplicationPacketPointer  = 0;

    // --------------------------------------------------------------------
    //  RGB Composite assumption
    // --------------------------------------------------------------------

    if( eType  == GDT_Byte  &&
            nBands == 3 )
    {
        hHdr1.DataTypeCode = Uncompressed24bit;
    }

    // --------------------------------------------------------------------
    //  Create output file with minimum header info
    // --------------------------------------------------------------------

    VSILFILE *fp = VSIFOpenL( pszFilename, "wb+" );

    if( fp == NULL )
    {
        CPLError( CE_Failure, CPLE_OpenFailed,
                  "Attempt to create file %s' failed.\n", pszFilename );
        return NULL;
    }

    GByte abyBuf[MAX(SIZEOF_HDR1,SIZEOF_CTAB)];

    INGR_HeaderOneMemToDisk( &hHdr1, abyBuf );

    VSIFWriteL( abyBuf, 1, SIZEOF_HDR1, fp );

    INGR_HeaderTwoAMemToDisk( &hHdr2, abyBuf );

    VSIFWriteL( abyBuf, 1, SIZEOF_HDR2_A, fp );

    unsigned int n = 0;

    for( i = 0; i < 256; i++ )
    {
        STRC2BUF( abyBuf, n, hCTab.Entry[i].v_red );
        STRC2BUF( abyBuf, n, hCTab.Entry[i].v_green );
        STRC2BUF( abyBuf, n, hCTab.Entry[i].v_blue );
    }

    VSIFWriteL( abyBuf, 1, SIZEOF_CTAB, fp );

    VSIFCloseL( fp );

    // --------------------------------------------------------------------
    //  Returns a new IntergraphDataset from the created file
    // --------------------------------------------------------------------

    return ( IntergraphDataset * ) GDALOpen( pszFilename, GA_Update );
}