/******************************************************************************
 * $Id: hfaband.cpp 33720 2016-03-15 00:39:53Z goatbar $
 *
 * Project:  Erdas Imagine (.img) Translator
 * Purpose:  Implementation of the HFABand, for accessing one Eimg_Layer.
 * Author:   Frank Warmerdam, warmerdam@pobox.com
 *
 ******************************************************************************
 * Copyright (c) 1999, Intergraph Corporation
 * Copyright (c) 2007-2012, 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 "hfa_p.h"
#include "cpl_conv.h"
#include "gdal_priv.h"

/* include the compression code */

CPL_CVSID("$Id: hfaband.cpp 33720 2016-03-15 00:39:53Z goatbar $");

/************************************************************************/
/*                              HFABand()                               */
/************************************************************************/

HFABand::HFABand( HFAInfo_t * psInfoIn, HFAEntry * poNodeIn ) :
    panBlockStart(NULL),
    panBlockSize(NULL),
    panBlockFlag(NULL),
    nPCTColors(-1),
    padfPCTBins(NULL),
    psInfo(psInfoIn),
    fpExternal(NULL),
    poNode(poNodeIn),
    bNoDataSet(FALSE),
    dfNoData(0.0),
    bOverviewsPending(TRUE),
    nOverviews(0),
    papoOverviews(NULL)
{
    nBlockXSize = poNodeIn->GetIntField( "blockWidth" );
    nBlockYSize = poNodeIn->GetIntField( "blockHeight" );
    int nDataType = poNodeIn->GetIntField( "pixelType" );

    nWidth = poNodeIn->GetIntField( "width" );
    nHeight = poNodeIn->GetIntField( "height" );

    apadfPCT[0] = apadfPCT[1] = apadfPCT[2] = apadfPCT[3] = NULL;

    if (nWidth <= 0 || nHeight <= 0 || nBlockXSize <= 0 || nBlockYSize <= 0)
    {
        nWidth = nHeight = 0;
        CPLError(CE_Failure, CPLE_AppDefined,
                 "HFABand::HFABand : (nWidth <= 0 || nHeight <= 0 || nBlockXSize <= 0 || nBlockYSize <= 0)");
        return;
    }
    if (nDataType < EPT_MIN || nDataType > EPT_MAX)
    {
        nWidth = nHeight = 0;
        CPLError(CE_Failure, CPLE_AppDefined,
                 "HFABand::HFABand : nDataType=%d unhandled", nDataType);
        return;
    }
    eDataType = static_cast<EPTType>(nDataType);

    nBlocksPerRow = DIV_ROUND_UP(nWidth, nBlockXSize);
    nBlocksPerColumn = DIV_ROUND_UP(nHeight, nBlockYSize);

    if( nBlocksPerRow > INT_MAX / nBlocksPerColumn )
    {
        nWidth = nHeight = 0;
        CPLError(CE_Failure, CPLE_AppDefined,
                 "HFABand::HFABand : too big dimensions / block size");
        return;
    }
    nBlocks = nBlocksPerRow * nBlocksPerColumn;

/* -------------------------------------------------------------------- */
/*      Check for nodata.  This is really an RDO (ESRI Raster Data      */
/*      Objects?), not used by Imagine itself.                          */
/* -------------------------------------------------------------------- */
    HFAEntry	*poNDNode = poNode->GetNamedChild("Eimg_NonInitializedValue");

    if( poNDNode != NULL )
    {
        bNoDataSet = TRUE;
        dfNoData = poNDNode->GetDoubleField( "valueBD" );
    }
}

/************************************************************************/
/*                              ~HFABand()                              */
/************************************************************************/

HFABand::~HFABand()

{
    for( int iOverview = 0; iOverview < nOverviews; iOverview++ )
        delete papoOverviews[iOverview];

    if( nOverviews > 0 )
        CPLFree( papoOverviews );

    CPLFree( panBlockStart );
    CPLFree( panBlockSize );
    CPLFree( panBlockFlag );

    CPLFree( apadfPCT[0] );
    CPLFree( apadfPCT[1] );
    CPLFree( apadfPCT[2] );
    CPLFree( apadfPCT[3] );
    CPLFree( padfPCTBins );

    if( fpExternal != NULL )
        CPL_IGNORE_RET_VAL(VSIFCloseL( fpExternal ));
}

/************************************************************************/
/*                           LoadOverviews()                            */
/************************************************************************/

CPLErr HFABand::LoadOverviews()

{
    if( !bOverviewsPending )
        return CE_None;

    bOverviewsPending = FALSE;

/* -------------------------------------------------------------------- */
/*      Does this band have overviews?  Try to find them.               */
/* -------------------------------------------------------------------- */
    HFAEntry	*poRRDNames = poNode->GetNamedChild( "RRDNamesList" );

    if( poRRDNames != NULL )
    {
        for( int iName = 0; true; iName++ )
        {
            char  szField[128];
            snprintf( szField, sizeof(szField), "nameList[%d].string", iName );

            CPLErr eErr;
            const char *pszName = poRRDNames->GetStringField( szField, &eErr );
            if( pszName == NULL || eErr != CE_None )
                break;

            char *pszFilename = CPLStrdup(pszName);
            char *pszEnd = strstr(pszFilename,"(:");
            if( pszEnd == NULL )
            {
                CPLFree( pszFilename );
                continue;
            }

            pszEnd[0] = '\0';

            char *pszJustFilename = CPLStrdup(CPLGetFilename(pszFilename));
            HFAInfo_t *psHFA = HFAGetDependent( psInfo, pszJustFilename );
            CPLFree( pszJustFilename );

            // Try finding the dependent file as this file with the
            // extension .rrd.  This is intended to address problems
            // with users changing the names of their files.
            if( psHFA == NULL )
            {
                char *pszBasename =
                    CPLStrdup(CPLGetBasename(psInfo->pszFilename));

                pszJustFilename =
                    CPLStrdup(CPLFormFilename(NULL, pszBasename, "rrd"));
                CPLDebug( "HFA", "Failed to find overview file with expected name,\ntry %s instead.",
                          pszJustFilename );
                psHFA = HFAGetDependent( psInfo, pszJustFilename );
                CPLFree( pszJustFilename );
                CPLFree( pszBasename );
            }

            if( psHFA == NULL )
            {
                CPLFree( pszFilename );
                continue;
            }

            char *pszPath = pszEnd + 2;
            if( pszPath[strlen(pszPath)-1] == ')' )
                pszPath[strlen(pszPath)-1] = '\0';

            for( int i=0; pszPath[i] != '\0'; i++ )
            {
                if( pszPath[i] == ':' )
                    pszPath[i] = '.';
            }

            HFAEntry *poOvEntry = psHFA->poRoot->GetNamedChild( pszPath );
            CPLFree( pszFilename );

            if( poOvEntry == NULL )
                continue;

            /*
             * We have an overview node.  Instanatiate a HFABand from it,
             * and add to the list.
             */
            papoOverviews = (HFABand **)
                CPLRealloc(papoOverviews, sizeof(void*) * ++nOverviews );
            papoOverviews[nOverviews-1] = new HFABand( psHFA, poOvEntry );
            if (papoOverviews[nOverviews-1]->nWidth == 0)
            {
                nWidth = nHeight = 0;
                delete papoOverviews[nOverviews-1];
                papoOverviews[nOverviews-1] = NULL;
                return CE_None;
            }
        }
    }

/* -------------------------------------------------------------------- */
/*      If there are no overviews mentioned in this file, probe for     */
/*      an .rrd file anyways.                                           */
/* -------------------------------------------------------------------- */
    HFAEntry *poBandProxyNode = poNode;
    HFAInfo_t *psOvHFA = psInfo;

    if( nOverviews == 0
        && EQUAL(CPLGetExtension(psInfo->pszFilename),"aux") )
    {
        CPLString osRRDFilename = CPLResetExtension( psInfo->pszFilename,"rrd");
        CPLString osFullRRD = CPLFormFilename( psInfo->pszPath, osRRDFilename,
                                               NULL );
        VSIStatBufL sStatBuf;

        if( VSIStatL( osFullRRD, &sStatBuf ) == 0 )
        {
            psOvHFA = HFAGetDependent( psInfo, osRRDFilename );
            if( psOvHFA )
                poBandProxyNode =
                    psOvHFA->poRoot->GetNamedChild( poNode->GetName() );
            else
                psOvHFA = psInfo;
        }
    }

/* -------------------------------------------------------------------- */
/*      If there are no named overviews, try looking for unnamed        */
/*      overviews within the same layer, as occurs in floodplain.img    */
/*      for instance, or in the not-referenced rrd mentioned in #3463.  */
/* -------------------------------------------------------------------- */
    if( nOverviews == 0 && poBandProxyNode != NULL )
    {
        for( HFAEntry *poChild = poBandProxyNode->GetChild();
             poChild != NULL;
             poChild = poChild->GetNext() )
        {
            if( EQUAL(poChild->GetType(),"Eimg_Layer_SubSample") )
            {
                papoOverviews = (HFABand **)
                    CPLRealloc(papoOverviews, sizeof(void*) * ++nOverviews );
                papoOverviews[nOverviews-1] = new HFABand( psOvHFA, poChild );
                if (papoOverviews[nOverviews-1]->nWidth == 0)
                {
                    nWidth = nHeight = 0;
                    delete papoOverviews[nOverviews-1];
                    papoOverviews[nOverviews-1] = NULL;
                    return CE_None;
                }
            }
        }

        // bubble sort into biggest to smallest order.
        for( int i1 = 0; i1 < nOverviews; i1++ )
        {
            for( int i2 = 0; i2 < nOverviews-1; i2++ )
            {
                if( papoOverviews[i2]->nWidth <
                    papoOverviews[i2+1]->nWidth )
                {
                    HFABand *poTemp = papoOverviews[i2+1];
                    papoOverviews[i2+1] = papoOverviews[i2];
                    papoOverviews[i2] = poTemp;
                }
            }
        }
    }
    return CE_None;
}

/************************************************************************/
/*                           LoadBlockInfo()                            */
/************************************************************************/

CPLErr	HFABand::LoadBlockInfo()

{
    if( panBlockFlag != NULL )
        return( CE_None );

    HFAEntry *poDMS = poNode->GetNamedChild( "RasterDMS" );
    if( poDMS == NULL )
    {
        if( poNode->GetNamedChild( "ExternalRasterDMS" ) != NULL )
            return LoadExternalBlockInfo();

        CPLError( CE_Failure, CPLE_AppDefined,
               "Can't find RasterDMS field in Eimg_Layer with block list.\n");

        return CE_Failure;
    }

    panBlockStart = (vsi_l_offset *)VSI_MALLOC2_VERBOSE(sizeof(vsi_l_offset), nBlocks);
    panBlockSize = (int *) VSI_MALLOC2_VERBOSE(sizeof(int), nBlocks);
    panBlockFlag = (int *) VSI_MALLOC2_VERBOSE(sizeof(int), nBlocks);

    if (panBlockStart == NULL || panBlockSize == NULL || panBlockFlag == NULL)
    {
        CPLFree(panBlockStart);
        CPLFree(panBlockSize);
        CPLFree(panBlockFlag);
        panBlockStart = NULL;
        panBlockSize = NULL;
        panBlockFlag = NULL;
        return CE_Failure;
    }

    for( int iBlock = 0; iBlock < nBlocks; iBlock++ )
    {
        CPLErr  eErr = CE_None;
        char	szVarName[64];
        int	nLogvalid, nCompressType;

        snprintf( szVarName, sizeof(szVarName), "blockinfo[%d].offset", iBlock );
        panBlockStart[iBlock] = (GUInt32)poDMS->GetIntField( szVarName, &eErr);
        if( eErr == CE_Failure )
        {
            CPLError(CE_Failure, CPLE_AppDefined, "Cannot read %s", szVarName);
            return eErr;
        }

        snprintf( szVarName, sizeof(szVarName), "blockinfo[%d].size", iBlock );
        panBlockSize[iBlock] = poDMS->GetIntField( szVarName, &eErr );
        if( eErr == CE_Failure )
        {
            CPLError(CE_Failure, CPLE_AppDefined, "Cannot read %s", szVarName);
            return eErr;
        }
        if( panBlockSize[iBlock] < 0 )
        {
            CPLError(CE_Failure, CPLE_AppDefined, "Invalid block size");
            return CE_Failure;
        }

        snprintf( szVarName, sizeof(szVarName), "blockinfo[%d].logvalid", iBlock );
        nLogvalid = poDMS->GetIntField( szVarName, &eErr );
        if( eErr == CE_Failure )
        {
            CPLError(CE_Failure, CPLE_AppDefined, "Cannot read %s", szVarName);
            return eErr;
        }

        snprintf( szVarName, sizeof(szVarName), "blockinfo[%d].compressionType", iBlock );
        nCompressType = poDMS->GetIntField( szVarName, &eErr );
        if( eErr == CE_Failure )
        {
            CPLError(CE_Failure, CPLE_AppDefined, "Cannot read %s", szVarName);
            return eErr;
        }

        panBlockFlag[iBlock] = 0;
        if( nLogvalid )
            panBlockFlag[iBlock] |= BFLG_VALID;
        if( nCompressType != 0 )
            panBlockFlag[iBlock] |= BFLG_COMPRESSED;
    }

    return( CE_None );
}

/************************************************************************/
/*                       LoadExternalBlockInfo()                        */
/************************************************************************/

CPLErr	HFABand::LoadExternalBlockInfo()

{
    if( panBlockFlag != NULL )
        return( CE_None );

/* -------------------------------------------------------------------- */
/*      Get the info structure.                                         */
/* -------------------------------------------------------------------- */
    HFAEntry *poDMS = poNode->GetNamedChild( "ExternalRasterDMS" );
    CPLAssert( poDMS != NULL );

    nLayerStackCount = poDMS->GetIntField( "layerStackCount" );
    nLayerStackIndex = poDMS->GetIntField( "layerStackIndex" );

/* -------------------------------------------------------------------- */
/*      Open raw data file.                                             */
/* -------------------------------------------------------------------- */
    const char *pszFullFilename = HFAGetIGEFilename( psInfo );
    if (pszFullFilename == NULL)
    {
        CPLError( CE_Failure, CPLE_OpenFailed,
                  "Cannot find external data file name" );
        return CE_Failure;
    }

    if( psInfo->eAccess == HFA_ReadOnly )
	fpExternal = VSIFOpenL( pszFullFilename, "rb" );
    else
	fpExternal = VSIFOpenL( pszFullFilename, "r+b" );
    if( fpExternal == NULL )
    {
        CPLError( CE_Failure, CPLE_OpenFailed,
                  "Unable to open external data file:\n%s\n",
                  pszFullFilename );
        return CE_Failure;
    }

/* -------------------------------------------------------------------- */
/*      Verify header.                                                  */
/* -------------------------------------------------------------------- */
    char	szHeader[49];

    if( VSIFReadL( szHeader, 49, 1, fpExternal ) != 1 ||
        !STARTS_WITH( szHeader, "ERDAS_IMG_EXTERNAL_RASTER") )
    {
        CPLError( CE_Failure, CPLE_AppDefined,
                  "Raw data file %s appears to be corrupt.\n",
                  pszFullFilename );
        return CE_Failure;
    }

/* -------------------------------------------------------------------- */
/*      Allocate blockmap.                                              */
/* -------------------------------------------------------------------- */
    panBlockFlag = (int *) VSI_MALLOC2_VERBOSE(sizeof(int), nBlocks);
    if (panBlockFlag == NULL)
    {
        return CE_Failure;
    }

/* -------------------------------------------------------------------- */
/*      Load the validity bitmap.                                       */
/* -------------------------------------------------------------------- */
    const int nBytesPerRow = (nBlocksPerRow + 7) / 8;
    unsigned char *pabyBlockMap = (unsigned char *)
        VSI_MALLOC_VERBOSE(nBytesPerRow*nBlocksPerColumn+20);
    if (pabyBlockMap == NULL)
    {
        return CE_Failure;
    }

    if( VSIFSeekL( fpExternal,
               poDMS->GetBigIntField( "layerStackValidFlagsOffset" ),
               SEEK_SET ) < 0 ||
        VSIFReadL( pabyBlockMap, nBytesPerRow * nBlocksPerColumn + 20, 1,
                   fpExternal ) != 1 )
    {
        CPLError( CE_Failure, CPLE_FileIO,
                  "Failed to read block validity map." );
        return CE_Failure;
    }

/* -------------------------------------------------------------------- */
/*      Establish block information.  Block position is computed        */
/*      from data base address.  Blocks are never compressed.           */
/*      Validity is determined from the validity bitmap.                */
/* -------------------------------------------------------------------- */
    nBlockStart = poDMS->GetBigIntField( "layerStackDataOffset" );
    nBlockSize = (nBlockXSize*static_cast<vsi_l_offset>(nBlockYSize)
                  *HFAGetDataTypeBits(eDataType)+7) / 8;

    for( int iBlock = 0; iBlock < nBlocks; iBlock++ )
    {
        const int nColumn = iBlock % nBlocksPerRow;
        const int nRow = iBlock / nBlocksPerRow;
        const int nBit = nRow * nBytesPerRow * 8 + nColumn + 20 * 8;

        if( (pabyBlockMap[nBit>>3] >> (nBit&7)) & 0x1 )
            panBlockFlag[iBlock] = BFLG_VALID;
        else
            panBlockFlag[iBlock] = 0;
    }

    CPLFree( pabyBlockMap );

    return( CE_None );
}

/************************************************************************/
/*                          UncompressBlock()                           */
/*                                                                      */
/*      Uncompress ESRI Grid compression format block.                  */
/************************************************************************/

#define CHECK_ENOUGH_BYTES(n) \
    if (nSrcBytes < (n)) goto not_enough_bytes;

static CPLErr UncompressBlock( GByte *pabyCData, int nSrcBytes,
                               GByte *pabyDest, int nMaxPixels,
                               EPTType eDataType )

{
    GUInt32  nDataMin;
    int      nNumBits, nPixelsOutput=0;
    GInt32   nNumRuns, nDataOffset;
    GByte *pabyCounter, *pabyValues;
    int   nValueBitOffset;
    int nCounterOffset;

    CHECK_ENOUGH_BYTES(13);

    memcpy( &nDataMin, pabyCData, 4 );
    nDataMin = CPL_LSBWORD32( nDataMin );

    memcpy( &nNumRuns, pabyCData+4, 4 );
    nNumRuns = CPL_LSBWORD32( nNumRuns );

    memcpy( &nDataOffset, pabyCData+8, 4 );
    nDataOffset = CPL_LSBWORD32( nDataOffset );

    nNumBits = pabyCData[12];

/* ==================================================================== */
/*      If this is not run length encoded, but just reduced             */
/*      precision, handle it now.                                       */
/* ==================================================================== */
    if( nNumRuns == -1 )
    {
        pabyValues = pabyCData + 13;
        nValueBitOffset = 0;

        if (nNumBits > INT_MAX / nMaxPixels ||
            nNumBits * nMaxPixels > INT_MAX - 7 ||
            (nNumBits * nMaxPixels + 7)/8 > INT_MAX - 13)
        {
            CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow : nNumBits * nMaxPixels + 7");
            return CE_Failure;
        }
        CHECK_ENOUGH_BYTES(13 + (nNumBits * nMaxPixels + 7)/8);

/* -------------------------------------------------------------------- */
/*      Loop over block pixels.                                         */
/* -------------------------------------------------------------------- */
        for( nPixelsOutput = 0; nPixelsOutput < nMaxPixels; nPixelsOutput++ )
        {
            int	nDataValue, nRawValue;

/* -------------------------------------------------------------------- */
/*      Extract the data value in a way that depends on the number      */
/*      of bits in it.                                                  */
/* -------------------------------------------------------------------- */
            if( nNumBits == 0 )
            {
                nRawValue = 0;
            }
            else if( nNumBits == 1 )
            {
                nRawValue =
                    (pabyValues[nValueBitOffset>>3] >> (nValueBitOffset&7)) & 0x1;
                nValueBitOffset++;
            }
            else if( nNumBits == 2 )
            {
                nRawValue =
                    (pabyValues[nValueBitOffset>>3] >> (nValueBitOffset&7)) & 0x3;
                nValueBitOffset += 2;
            }
            else if( nNumBits == 4 )
            {
                nRawValue =
                    (pabyValues[nValueBitOffset>>3] >> (nValueBitOffset&7)) & 0xf;
                nValueBitOffset += 4;
            }
            else if( nNumBits == 8 )
            {
                nRawValue = *pabyValues;
                pabyValues++;
            }
            else if( nNumBits == 16 )
            {
                nRawValue = 256 * *(pabyValues++);
                nRawValue += *(pabyValues++);
            }
            else if( nNumBits == 32 )
            {
                nRawValue = 256 * 256 * 256 * *(pabyValues++);
                nRawValue += 256 * 256 * *(pabyValues++);
                nRawValue += 256 * *(pabyValues++);
                nRawValue += *(pabyValues++);
            }
            else
            {
                CPLError(CE_Failure, CPLE_NotSupported,
                         "Unsupported nNumBits value : %d", nNumBits);
                return CE_Failure;
            }

/* -------------------------------------------------------------------- */
/*      Offset by the minimum value.                                    */
/* -------------------------------------------------------------------- */
            nDataValue = nRawValue + nDataMin;

/* -------------------------------------------------------------------- */
/*      Now apply to the output buffer in a type specific way.          */
/* -------------------------------------------------------------------- */
            if( eDataType == EPT_u8 )
            {
                ((GByte *) pabyDest)[nPixelsOutput] = (GByte) nDataValue;
            }
            else if( eDataType == EPT_u1 )
            {
                if( nDataValue == 1 )
                    pabyDest[nPixelsOutput>>3] |= (1 << (nPixelsOutput & 0x7));
                else
                    pabyDest[nPixelsOutput>>3] &= ~(1<<(nPixelsOutput & 0x7));
            }
            else if( eDataType == EPT_u2 )
            {
                if( (nPixelsOutput & 0x3) == 0 )
                    pabyDest[nPixelsOutput>>2] = (GByte) nDataValue;
                else if( (nPixelsOutput & 0x3) == 1 )
                    pabyDest[nPixelsOutput>>2] |= (GByte) (nDataValue<<2);
                else if( (nPixelsOutput & 0x3) == 2 )
                    pabyDest[nPixelsOutput>>2] |= (GByte) (nDataValue<<4);
                else
                    pabyDest[nPixelsOutput>>2] |= (GByte) (nDataValue<<6);
            }
            else if( eDataType == EPT_u4 )
            {
                if( (nPixelsOutput & 0x1) == 0 )
                    pabyDest[nPixelsOutput>>1] = (GByte) nDataValue;
                else
                    pabyDest[nPixelsOutput>>1] |= (GByte) (nDataValue<<4);
            }
            else if( eDataType == EPT_s8 )
            {
                ((GByte *) pabyDest)[nPixelsOutput] = (GByte) nDataValue;
            }
            else if( eDataType == EPT_u16 )
            {
                ((GUInt16 *) pabyDest)[nPixelsOutput] = (GUInt16) nDataValue;
            }
            else if( eDataType == EPT_s16 )
            {
                ((GInt16 *) pabyDest)[nPixelsOutput] = (GInt16) nDataValue;
            }
            else if( eDataType == EPT_s32 )
            {
                ((GInt32 *) pabyDest)[nPixelsOutput] = nDataValue;
            }
            else if( eDataType == EPT_u32 )
            {
                ((GUInt32 *) pabyDest)[nPixelsOutput] = nDataValue;
            }
            else if( eDataType == EPT_f32 )
            {
/* -------------------------------------------------------------------- */
/*      Note, floating point values are handled as if they were signed  */
/*      32-bit integers (bug #1000).                                    */
/* -------------------------------------------------------------------- */
                memcpy(&(((float *) pabyDest)[nPixelsOutput]),
                       &nDataValue,
                       sizeof(float));
            }
            else
            {
                CPLError( CE_Failure, CPLE_AppDefined,
                      "Attempt to uncompress an unsupported pixel data type.");
                return CE_Failure;
            }
        }

        return CE_None;
    }

/* ==================================================================== */
/*      Establish data pointers for runs.                               */
/* ==================================================================== */
    if (nNumRuns < 0 || nDataOffset < 0)
    {
        CPLError(CE_Failure, CPLE_AppDefined, "nNumRuns=%d, nDataOffset=%d",
                 nNumRuns, nDataOffset);
        return CE_Failure;
    }

    if (nNumRuns != 0 &&
        (nNumBits > INT_MAX / nNumRuns ||
         nNumBits * nNumRuns > INT_MAX - 7 ||
         (nNumBits * nNumRuns + 7)/8 > INT_MAX - nDataOffset) )
    {
        CPLError(CE_Failure, CPLE_AppDefined,
                 "Integer overflow : nDataOffset + (nNumBits * nNumRuns + 7)/8");
        return CE_Failure;
    }
    CHECK_ENOUGH_BYTES(nDataOffset + (nNumBits * nNumRuns + 7)/8);

    pabyCounter = pabyCData + 13;
    nCounterOffset = 13;
    pabyValues = pabyCData + nDataOffset;
    nValueBitOffset = 0;

/* -------------------------------------------------------------------- */
/*      Loop over runs.                                                 */
/* -------------------------------------------------------------------- */
    for( int iRun = 0; iRun < nNumRuns; iRun++ )
    {
        int	nRepeatCount = 0;
        int	nDataValue;

/* -------------------------------------------------------------------- */
/*      Get the repeat count.  This can be stored as one, two, three    */
/*      or four bytes depending on the low order two bits of the        */
/*      first byte.                                                     */
/* -------------------------------------------------------------------- */
        CHECK_ENOUGH_BYTES(nCounterOffset+1);
        if( ((*pabyCounter) & 0xc0) == 0x00 )
        {
            nRepeatCount = (*(pabyCounter++)) & 0x3f;
            nCounterOffset ++;
        }
        else if( ((*pabyCounter) & 0xc0) == 0x40 )
        {
            CHECK_ENOUGH_BYTES(nCounterOffset + 2);
            nRepeatCount = (*(pabyCounter++)) & 0x3f;
            nRepeatCount = nRepeatCount * 256 + (*(pabyCounter++));
            nCounterOffset += 2;
        }
        else if( ((*pabyCounter) & 0xc0) == 0x80 )
        {
            CHECK_ENOUGH_BYTES(nCounterOffset + 3);
            nRepeatCount = (*(pabyCounter++)) & 0x3f;
            nRepeatCount = nRepeatCount * 256 + (*(pabyCounter++));
            nRepeatCount = nRepeatCount * 256 + (*(pabyCounter++));
            nCounterOffset += 3;
        }
        else if( ((*pabyCounter) & 0xc0) == 0xc0 )
        {
            CHECK_ENOUGH_BYTES(nCounterOffset + 4);
            nRepeatCount = (*(pabyCounter++)) & 0x3f;
            nRepeatCount = nRepeatCount * 256 + (*(pabyCounter++));
            nRepeatCount = nRepeatCount * 256 + (*(pabyCounter++));
            nRepeatCount = nRepeatCount * 256 + (*(pabyCounter++));
            nCounterOffset += 4;
        }

/* -------------------------------------------------------------------- */
/*      Extract the data value in a way that depends on the number      */
/*      of bits in it.                                                  */
/* -------------------------------------------------------------------- */
        if( nNumBits == 0 )
        {
            nDataValue = 0;
        }
        else if( nNumBits == 1 )
        {
            nDataValue =
                (pabyValues[nValueBitOffset>>3] >> (nValueBitOffset&7)) & 0x1;
            nValueBitOffset++;
        }
        else if( nNumBits == 2 )
        {
            nDataValue =
                (pabyValues[nValueBitOffset>>3] >> (nValueBitOffset&7)) & 0x3;
            nValueBitOffset += 2;
        }
        else if( nNumBits == 4 )
        {
            nDataValue =
                (pabyValues[nValueBitOffset>>3] >> (nValueBitOffset&7)) & 0xf;
            nValueBitOffset += 4;
        }
        else if( nNumBits == 8 )
        {
            nDataValue = *pabyValues;
            pabyValues++;
        }
        else if( nNumBits == 16 )
        {
            nDataValue = 256 * *(pabyValues++);
            nDataValue += *(pabyValues++);
        }
        else if( nNumBits == 32 )
        {
            nDataValue = 256 * 256 * 256 * *(pabyValues++);
            nDataValue += 256 * 256 * *(pabyValues++);
            nDataValue += 256 * *(pabyValues++);
            nDataValue += *(pabyValues++);
        }
        else
        {
            CPLError( CE_Failure, CPLE_NotSupported,
                      "nNumBits = %d", nNumBits );
            return CE_Failure;
        }

/* -------------------------------------------------------------------- */
/*      Offset by the minimum value.                                    */
/* -------------------------------------------------------------------- */
        nDataValue += nDataMin;

/* -------------------------------------------------------------------- */
/*      Now apply to the output buffer in a type specific way.          */
/* -------------------------------------------------------------------- */
        if( nRepeatCount > INT_MAX - nPixelsOutput ||
            nPixelsOutput + nRepeatCount > nMaxPixels )
        {
            CPLDebug("HFA", "Repeat count too big : %d", nRepeatCount);
            nRepeatCount = nMaxPixels - nPixelsOutput;
        }

        if( eDataType == EPT_u8 )
        {
            for( int i = 0; i < nRepeatCount; i++ )
            {
                //CPLAssert( nDataValue < 256 );
                ((GByte *) pabyDest)[nPixelsOutput++] = (GByte)nDataValue;
            }
        }
        else if( eDataType == EPT_u16 )
        {
            for( int i = 0; i < nRepeatCount; i++ )
            {
                ((GUInt16 *) pabyDest)[nPixelsOutput++] = (GUInt16)nDataValue;
            }
        }
        else if( eDataType == EPT_s8 )
        {
            for( int i = 0; i < nRepeatCount; i++ )
            {
                //CPLAssert( nDataValue < 256 );
                ((GByte *) pabyDest)[nPixelsOutput++] = (GByte)nDataValue;
            }
        }
        else if( eDataType == EPT_s16 )
        {
            for( int i = 0; i < nRepeatCount; i++ )
            {
                ((GInt16 *) pabyDest)[nPixelsOutput++] = (GInt16)nDataValue;
            }
        }
        else if( eDataType == EPT_u32 )
        {
            for( int i = 0; i < nRepeatCount; i++ )
            {
                ((GUInt32 *) pabyDest)[nPixelsOutput++] = (GUInt32)nDataValue;
            }
        }
        else if( eDataType == EPT_s32 )
        {
            for( int i = 0; i < nRepeatCount; i++ )
            {
                ((GInt32 *) pabyDest)[nPixelsOutput++] = (GInt32)nDataValue;
            }
        }
        else if( eDataType == EPT_f32 )
        {
            float fDataValue;

            memcpy( &fDataValue, &nDataValue, 4);
            for( int i = 0; i < nRepeatCount; i++ )
            {
                ((float *) pabyDest)[nPixelsOutput++] = fDataValue;
            }
        }
        else if( eDataType == EPT_u1 )
        {
            //CPLAssert( nDataValue == 0 || nDataValue == 1 );

            if( nDataValue == 1 )
            {
                for( int i = 0; i < nRepeatCount; i++ )
                {
                    pabyDest[nPixelsOutput>>3] |= (1 << (nPixelsOutput & 0x7));
                    nPixelsOutput++;
                }
            }
            else
            {
                for( int i = 0; i < nRepeatCount; i++ )
                {
                    pabyDest[nPixelsOutput>>3] &= ~(1<<(nPixelsOutput & 0x7));
                    nPixelsOutput++;
                }
            }
        }
        else if( eDataType == EPT_u2 )
        {
            //CPLAssert( nDataValue >= 0 && nDataValue < 4 );

            for( int i = 0; i < nRepeatCount; i++ )
            {
                if( (nPixelsOutput & 0x3) == 0 )
                    pabyDest[nPixelsOutput>>2] = (GByte) nDataValue;
                else if( (nPixelsOutput & 0x3) == 1 )
                    pabyDest[nPixelsOutput>>2] |= (GByte) (nDataValue<<2);
                else if( (nPixelsOutput & 0x3) == 2 )
                    pabyDest[nPixelsOutput>>2] |= (GByte) (nDataValue<<4);
                else
                    pabyDest[nPixelsOutput>>2] |= (GByte) (nDataValue<<6);
                nPixelsOutput++;
            }
        }
        else if( eDataType == EPT_u4 )
        {
            //CPLAssert( nDataValue >= 0 && nDataValue < 16 );

            for( int i = 0; i < nRepeatCount; i++ )
            {
                if( (nPixelsOutput & 0x1) == 0 )
                    pabyDest[nPixelsOutput>>1] = (GByte) nDataValue;
                else
                    pabyDest[nPixelsOutput>>1] |= (GByte) (nDataValue<<4);

                nPixelsOutput++;
            }
        }
        else
        {
            CPLError( CE_Failure, CPLE_AppDefined,
                      "Attempt to uncompress an unsupported pixel data type.");
            return CE_Failure;
        }
    }

    return CE_None;

  not_enough_bytes:

    CPLError(CE_Failure, CPLE_AppDefined, "Not enough bytes in compressed block");
    return CE_Failure;
}

/************************************************************************/
/*                             NullBlock()                              */
/*                                                                      */
/*      Set the block buffer to zero or the nodata value as             */
/*      appropriate.                                                    */
/************************************************************************/

void HFABand::NullBlock( void *pData )

{
    int nChunkSize = MAX(1,HFAGetDataTypeBits(eDataType)/8);
    int nWords = nBlockXSize * nBlockYSize;

    if( !bNoDataSet )
    {
#ifdef ESRI_BUILD
        // We want special defaulting for 1 bit data in ArcGIS.
        if ( eDataType >= EPT_u2 )
            memset( pData,   0, nChunkSize*nWords );
        else
            memset( pData, 255, nChunkSize*nWords );
#else
        memset( pData,   0, nChunkSize*nWords );
#endif
    }
    else
    {
        GByte abyTmp[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

        switch( eDataType )
        {
          case EPT_u1:
          {
              nWords = (nWords + 7)/8;
              if( dfNoData != 0.0 )
                  ((unsigned char *) abyTmp)[0] = 0xff;
              else
                  ((unsigned char *) abyTmp)[0] = 0x00;
          }
          break;

          case EPT_u2:
          {
              nWords = (nWords + 3)/4;
              if( dfNoData == 0.0 )
                  ((unsigned char *) abyTmp)[0] = 0x00;
              else if( dfNoData == 1.0 )
                  ((unsigned char *) abyTmp)[0] = 0x55;
              else if( dfNoData == 2.0 )
                  ((unsigned char *) abyTmp)[0] = 0xaa;
              else
                  ((unsigned char *) abyTmp)[0] = 0xff;
          }
          break;

          case EPT_u4:
          {
              unsigned char byVal =
                  (unsigned char) MAX(0,MIN(15,(int)dfNoData));

              nWords = (nWords + 1)/2;

              ((unsigned char *) abyTmp)[0] = byVal + (byVal << 4);
          }
          break;

          case EPT_u8:
            ((unsigned char *) abyTmp)[0] =
                (unsigned char) MAX(0,MIN(255,(int)dfNoData));
            break;

          case EPT_s8:
            ((signed char *) abyTmp)[0] =
                (signed char) MAX(-128,MIN(127,(int)dfNoData));
            break;

          case EPT_u16:
          {
            GUInt16 nTmp = (GUInt16) dfNoData;
            memcpy(abyTmp, &nTmp, sizeof(nTmp));
            break;
          }

          case EPT_s16:
          {
            GInt16 nTmp = (GInt16) dfNoData;
            memcpy(abyTmp, &nTmp, sizeof(nTmp));
            break;
          }

          case EPT_u32:
          {
            GUInt32 nTmp = (GUInt32) dfNoData;
            memcpy(abyTmp, &nTmp, sizeof(nTmp));
            break;
          }

          case EPT_s32:
          {
            GInt32 nTmp = (GInt32) dfNoData;
            memcpy(abyTmp, &nTmp, sizeof(nTmp));
            break;
          }

          case EPT_f32:
          {
            float fTmp = (float) dfNoData;
            memcpy(abyTmp, &fTmp, sizeof(fTmp));
            break;
          }

          case EPT_f64:
          {
            memcpy(abyTmp, &dfNoData, sizeof(dfNoData));
            break;
          }

          case EPT_c64:
          {
            float fTmp = (float) dfNoData;
            memcpy(abyTmp, &fTmp, sizeof(fTmp));
            memset(abyTmp+4, 0, sizeof(float));
            break;
          }

          case EPT_c128:
          {
            memcpy(abyTmp, &dfNoData, sizeof(dfNoData));
            memset(abyTmp+8, 0, sizeof(double));
            break;
          }
        }

        for( int i = 0; i < nWords; i++ )
            memcpy( ((GByte *) pData) + nChunkSize * i,
                    abyTmp, nChunkSize );
    }

}

/************************************************************************/
/*                           GetRasterBlock()                           */
/************************************************************************/

CPLErr HFABand::GetRasterBlock( int nXBlock, int nYBlock, void * pData, int nDataSize )

{
    if( LoadBlockInfo() != CE_None )
        return CE_Failure;

    int iBlock = nXBlock + nYBlock * nBlocksPerRow;

/* -------------------------------------------------------------------- */
/*      If the block isn't valid, we just return all zeros, and an	*/
/*	indication of success.                        			*/
/* -------------------------------------------------------------------- */
    if( (panBlockFlag[iBlock] & BFLG_VALID) == 0 )
    {
        NullBlock( pData );
        return( CE_None );
    }

/* -------------------------------------------------------------------- */
/*      Otherwise we really read the data.                              */
/* -------------------------------------------------------------------- */
    vsi_l_offset    nBlockOffset;
    VSILFILE	*fpData;

    // Calculate block offset in case we have spill file. Use predefined
    // block map otherwise.
    if ( fpExternal )
    {
        fpData = fpExternal;
        nBlockOffset = nBlockStart + nBlockSize * iBlock * nLayerStackCount
            + nLayerStackIndex * nBlockSize;
    }
    else
    {
        fpData = psInfo->fp;
        nBlockOffset = panBlockStart[iBlock];
        nBlockSize = panBlockSize[iBlock];
    }

    if( VSIFSeekL( fpData, nBlockOffset, SEEK_SET ) != 0 )
    {
        // XXX: We will not report error here, because file just may be
	// in update state and data for this block will be available later
        if ( psInfo->eAccess == HFA_Update )
        {
            memset( pData, 0,
                    HFAGetDataTypeBits(eDataType)*nBlockXSize*nBlockYSize/8 );
            return CE_None;
        }
        else
        {
            CPLError( CE_Failure, CPLE_FileIO,
                      "Seek to %x:%08x on %p failed\n%s",
                      (int) (nBlockOffset >> 32),
                      (int) (nBlockOffset & 0xffffffff),
                      fpData, VSIStrerror(errno) );
            return CE_Failure;
        }
    }

/* -------------------------------------------------------------------- */
/*	If the block is compressed, read into an intermediate buffer	*/
/*	and convert.							*/
/* -------------------------------------------------------------------- */
    if( panBlockFlag[iBlock] & BFLG_COMPRESSED )
    {
        GByte *pabyCData = (GByte *) VSI_MALLOC_VERBOSE( (size_t) nBlockSize );
        if (pabyCData == NULL)
        {
            return CE_Failure;
        }

        if( VSIFReadL( pabyCData, (size_t) nBlockSize, 1, fpData ) != 1 )
        {
            CPLFree( pabyCData );

	    // XXX: Suppose that file in update state
            if ( psInfo->eAccess == HFA_Update )
            {
                memset( pData, 0,
                    HFAGetDataTypeBits(eDataType)*nBlockXSize*nBlockYSize/8 );
                return CE_None;
            }
            else
            {
                CPLError( CE_Failure, CPLE_FileIO,
                          "Read of %d bytes at %x:%08x on %p failed.\n%s",
                          (int) nBlockSize,
                          (int) (nBlockOffset >> 32),
                          (int) (nBlockOffset & 0xffffffff),
                          fpData, VSIStrerror(errno) );
                return CE_Failure;
            }
        }

        CPLErr eErr = UncompressBlock( pabyCData, (int) nBlockSize,
                                       (GByte *) pData, nBlockXSize*nBlockYSize,
                                       eDataType );

        CPLFree( pabyCData );

        return eErr;
    }

/* -------------------------------------------------------------------- */
/*      Read uncompressed data directly into the return buffer.         */
/* -------------------------------------------------------------------- */
    if ( nDataSize != -1 && (nBlockSize > INT_MAX ||
                             (int)nBlockSize > nDataSize) )
    {
        CPLError( CE_Failure, CPLE_AppDefined,
                    "Invalid block size : %d", (int)nBlockSize);
        return CE_Failure;
    }

    if( VSIFReadL( pData, (size_t) nBlockSize, 1, fpData ) != 1 )
    {
	memset( pData, 0,
	    HFAGetDataTypeBits(eDataType)*nBlockXSize*nBlockYSize/8 );

        if( fpData != fpExternal )
            CPLDebug( "HFABand",
                      "Read of %x:%08x bytes at %d on %p failed.\n%s",
                      (int) nBlockSize,
                      (int) (nBlockOffset >> 32),
                      (int) (nBlockOffset & 0xffffffff),
                      fpData, VSIStrerror(errno) );

	return CE_None;
    }

/* -------------------------------------------------------------------- */
/*      Byte swap to local byte order if required.  It appears that     */
/*      raster data is always stored in Intel byte order in Imagine     */
/*      files.                                                          */
/* -------------------------------------------------------------------- */

#ifdef CPL_MSB
    if( HFAGetDataTypeBits(eDataType) == 16 )
    {
        for( int ii = 0; ii < nBlockXSize*nBlockYSize; ii++ )
            CPL_SWAP16PTR( ((unsigned char *) pData) + ii*2 );
    }
    else if( HFAGetDataTypeBits(eDataType) == 32 )
    {
        for( int ii = 0; ii < nBlockXSize*nBlockYSize; ii++ )
            CPL_SWAP32PTR( ((unsigned char *) pData) + ii*4 );
    }
    else if( eDataType == EPT_f64 )
    {
        for( int ii = 0; ii < nBlockXSize*nBlockYSize; ii++ )
            CPL_SWAP64PTR( ((unsigned char *) pData) + ii*8 );
    }
    else if( eDataType == EPT_c64 )
    {
        for( int ii = 0; ii < nBlockXSize*nBlockYSize*2; ii++ )
            CPL_SWAP32PTR( ((unsigned char *) pData) + ii*4 );
    }
    else if( eDataType == EPT_c128 )
    {
        for( int ii = 0; ii < nBlockXSize*nBlockYSize*2; ii++ )
            CPL_SWAP64PTR( ((unsigned char *) pData) + ii*8 );
    }
#endif /* def CPL_MSB */

    return( CE_None );
}

/************************************************************************/
/*                           ReAllocBlock()                           */
/************************************************************************/

void HFABand::ReAllocBlock( int iBlock, int nSize )
{
    /* For compressed files - need to realloc the space for the block */

    // TODO: Should check to see if panBlockStart[iBlock] is not zero then do a HFAFreeSpace()
    // but that doesn't exist yet.
    // Instead as in interim measure it will reuse the existing block if
    // the new data will fit in.
    if( ( panBlockStart[iBlock] != 0 ) && ( nSize <= panBlockSize[iBlock] ) )
    {
        panBlockSize[iBlock] = nSize;
        //fprintf( stderr, "Reusing block %d\n", iBlock );
        return;
    }

    panBlockStart[iBlock] = HFAAllocateSpace( psInfo, nSize );

    panBlockSize[iBlock] = nSize;

    // need to re - write this info to the RasterDMS node
    HFAEntry	*poDMS = poNode->GetNamedChild( "RasterDMS" );

    if (!poDMS)
    {
        CPLError(CE_Failure, CPLE_FileIO, "Unable to load RasterDMS");
        return;
    }

    char	szVarName[64];
    snprintf( szVarName, sizeof(szVarName), "blockinfo[%d].offset", iBlock );
    poDMS->SetIntField( szVarName, (int) panBlockStart[iBlock] );

    snprintf( szVarName, sizeof(szVarName), "blockinfo[%d].size", iBlock );
    poDMS->SetIntField( szVarName, panBlockSize[iBlock] );
}


/************************************************************************/
/*                           SetRasterBlock()                           */
/************************************************************************/

CPLErr HFABand::SetRasterBlock( int nXBlock, int nYBlock, void * pData )

{
    if( psInfo->eAccess == HFA_ReadOnly )
    {
        CPLError( CE_Failure, CPLE_NoWriteAccess,
                  "Attempt to write block to read-only HFA file failed." );
        return CE_Failure;
    }

    if( LoadBlockInfo() != CE_None )
        return CE_Failure;

    const int iBlock = nXBlock + nYBlock * nBlocksPerRow;

/* -------------------------------------------------------------------- */
/*      For now we don't support write invalid uncompressed blocks.     */
/*      To do so we will need logic to make space at the end of the     */
/*      file in the right size.                                         */
/* -------------------------------------------------------------------- */
    if( (panBlockFlag[iBlock] & BFLG_VALID) == 0
        && !(panBlockFlag[iBlock] & BFLG_COMPRESSED)
        && panBlockStart[iBlock] == 0 )
    {
        CPLError( CE_Failure, CPLE_AppDefined,
                  "Attempt to write to invalid tile with number %d "
                  "(X position %d, Y position %d).  This\n operation currently "
                  "unsupported by HFABand::SetRasterBlock().\n",
                  iBlock, nXBlock, nYBlock );

        return CE_Failure;
    }

/* -------------------------------------------------------------------- */
/*      Move to the location that the data sits.                        */
/* -------------------------------------------------------------------- */
    VSILFILE *fpData = NULL;
    vsi_l_offset nBlockOffset = 0;

    // Calculate block offset in case we have spill file. Use predefined
    // block map otherwise.
    if ( fpExternal )
    {
        fpData = fpExternal;
        nBlockOffset = nBlockStart + nBlockSize * iBlock * nLayerStackCount
            + nLayerStackIndex * nBlockSize;
    }
    else
    {
        fpData = psInfo->fp;
        nBlockOffset = panBlockStart[iBlock];
        nBlockSize = panBlockSize[iBlock];
    }

/* ==================================================================== */
/*      Compressed Tile Handling.                                       */
/* ==================================================================== */
    if( panBlockFlag[iBlock] & BFLG_COMPRESSED )
    {
        /* ------------------------------------------------------------ */
        /*      Write compressed data.				        */
        /* ------------------------------------------------------------ */
        int nInBlockSize = (nBlockXSize * nBlockYSize * HFAGetDataTypeBits(eDataType) + 7 ) / 8;

        /* create the compressor object */
        HFACompress compress( pData, nInBlockSize, eDataType );
        if( compress.getCounts() == NULL ||
            compress.getValues() == NULL)
        {
            return CE_Failure;
        }

        /* compress the data */
        if( compress.compressBlock() )
        {
            /* get the data out of the object */
            GByte *pCounts      = compress.getCounts();
            GUInt32 nSizeCount  = compress.getCountSize();
            GByte *pValues      = compress.getValues();
            GUInt32 nSizeValues = compress.getValueSize();
            GUInt32 nMin        = compress.getMin();
            GUInt32 nNumRuns    = compress.getNumRuns();
            GByte nNumBits      = compress.getNumBits();

            /* Compensate for the header info */
            GUInt32 nDataOffset = nSizeCount + 13;
            int nTotalSize  = nSizeCount + nSizeValues + 13;

            // Allocate space for the compressed block and seek to it.
            ReAllocBlock( iBlock, nTotalSize );

            nBlockOffset = panBlockStart[iBlock];
            nBlockSize = panBlockSize[iBlock];

            // Seek to offset
            if( VSIFSeekL( fpData, nBlockOffset, SEEK_SET ) != 0 )
            {
                CPLError( CE_Failure, CPLE_FileIO, "Seek to %x:%08x on %p failed\n%s",
                          (int) (nBlockOffset >> 32),
                          (int) (nBlockOffset & 0xffffffff),
                          fpData, VSIStrerror(errno) );
                return CE_Failure;
            }

   /* -------------------------------------------------------------------- */
   /*      Byte swap to local byte order if required.  It appears that     */
   /*      raster data is always stored in Intel byte order in Imagine     */
   /*      files.                                                          */
   /* -------------------------------------------------------------------- */

#ifdef CPL_MSB

            CPL_SWAP32PTR( &nMin );
            CPL_SWAP32PTR( &nNumRuns );
            CPL_SWAP32PTR( &nDataOffset );

#endif /* def CPL_MSB */

       /* Write out the Minimum value */
            bool bRet = VSIFWriteL( &nMin, (size_t) sizeof( nMin ), 1, fpData ) > 0;

            /* the number of runs */
            bRet &= VSIFWriteL( &nNumRuns, (size_t) sizeof( nNumRuns ), 1, fpData ) > 0;

            /* The offset to the data */
            bRet &= VSIFWriteL( &nDataOffset, (size_t) sizeof( nDataOffset ), 1, fpData ) > 0;

            /* The number of bits */
            bRet &= VSIFWriteL( &nNumBits, (size_t) sizeof( nNumBits ), 1, fpData ) > 0;

            /* The counters - MSB stuff handled in HFACompress */
            bRet &= VSIFWriteL( pCounts, nSizeCount, 1, fpData ) > 0;

            /* The values - MSB stuff handled in HFACompress */
            bRet &= VSIFWriteL( pValues, nSizeValues, 1, fpData ) > 0;

            if( !bRet )
                return CE_Failure;

            /* Compressed data is freed in the HFACompress destructor */
        }
        else
        {
            /* If we have actually made the block bigger - i.e. does not compress well */
            panBlockFlag[iBlock] ^= BFLG_COMPRESSED;
            // alloc more space for the uncompressed block
            ReAllocBlock( iBlock, nInBlockSize );

            nBlockOffset = panBlockStart[iBlock];
            nBlockSize = panBlockSize[iBlock];

            /* Need to change the RasterDMS entry */
            HFAEntry	*poDMS = poNode->GetNamedChild( "RasterDMS" );

            if (!poDMS)
            {
                CPLError(CE_Failure, CPLE_FileIO, "Unable to load RasterDMS");
                return CE_Failure;
            }

            char	szVarName[64];
            snprintf( szVarName, sizeof(szVarName), "blockinfo[%d].compressionType", iBlock );
            poDMS->SetIntField( szVarName, 0 );
        }

/* -------------------------------------------------------------------- */
/*      If the block was previously invalid, mark it as valid now.      */
/* -------------------------------------------------------------------- */
        if( (panBlockFlag[iBlock] & BFLG_VALID) == 0 )
        {
            char	szVarName[64];
            HFAEntry	*poDMS = poNode->GetNamedChild( "RasterDMS" );

            if (!poDMS)
            {
                CPLError(CE_Failure, CPLE_FileIO, "Unable to load RasterDMS");
                return CE_Failure;
            }

            snprintf( szVarName, sizeof(szVarName), "blockinfo[%d].logvalid", iBlock );
            poDMS->SetStringField( szVarName, "true" );

            panBlockFlag[iBlock] |= BFLG_VALID;
        }
    }

/* ==================================================================== */
/*      Uncompressed TILE handling.                                     */
/* ==================================================================== */
    if( ( panBlockFlag[iBlock] & BFLG_COMPRESSED ) == 0 )
    {

        if( VSIFSeekL( fpData, nBlockOffset, SEEK_SET ) != 0 )
        {
            CPLError( CE_Failure, CPLE_FileIO, "Seek to %x:%08x on %p failed\n%s",
                      (int) (nBlockOffset >> 32),
                      (int) (nBlockOffset & 0xffffffff),
                      fpData, VSIStrerror(errno) );
            return CE_Failure;
        }

/* -------------------------------------------------------------------- */
/*      Byte swap to local byte order if required.  It appears that     */
/*      raster data is always stored in Intel byte order in Imagine     */
/*      files.                                                          */
/* -------------------------------------------------------------------- */

#ifdef CPL_MSB
        if( HFAGetDataTypeBits(eDataType) == 16 )
        {
            for( int ii = 0; ii < nBlockXSize*nBlockYSize; ii++ )
                CPL_SWAP16PTR( ((unsigned char *) pData) + ii*2 );
        }
        else if( HFAGetDataTypeBits(eDataType) == 32 )
        {
            for( int ii = 0; ii < nBlockXSize*nBlockYSize; ii++ )
                CPL_SWAP32PTR( ((unsigned char *) pData) + ii*4 );
        }
        else if( eDataType == EPT_f64 )
        {
            for( int ii = 0; ii < nBlockXSize*nBlockYSize; ii++ )
                CPL_SWAP64PTR( ((unsigned char *) pData) + ii*8 );
        }
        else if( eDataType == EPT_c64 )
        {
            for( int ii = 0; ii < nBlockXSize*nBlockYSize*2; ii++ )
                CPL_SWAP32PTR( ((unsigned char *) pData) + ii*4 );
        }
        else if( eDataType == EPT_c128 )
        {
            for( int ii = 0; ii < nBlockXSize*nBlockYSize*2; ii++ )
                CPL_SWAP64PTR( ((unsigned char *) pData) + ii*8 );
        }
#endif /* def CPL_MSB */

/* -------------------------------------------------------------------- */
/*      Write uncompressed data.				        */
/* -------------------------------------------------------------------- */
        if( VSIFWriteL( pData, (size_t) nBlockSize, 1, fpData ) != 1 )
        {
            CPLError( CE_Failure, CPLE_FileIO,
                      "Write of %d bytes at %x:%08x on %p failed.\n%s",
                      (int) nBlockSize,
                      (int) (nBlockOffset >> 32),
                      (int) (nBlockOffset & 0xffffffff),
                      fpData, VSIStrerror(errno) );
            return CE_Failure;
        }

/* -------------------------------------------------------------------- */
/*      If the block was previously invalid, mark it as valid now.      */
/* -------------------------------------------------------------------- */
        if( (panBlockFlag[iBlock] & BFLG_VALID) == 0 )
        {
            char	szVarName[64];
            HFAEntry	*poDMS = poNode->GetNamedChild( "RasterDMS" );
            if (poDMS == NULL) {
                CPLError( CE_Failure, CPLE_AppDefined,
                          "Unable to get RasterDMS when trying to mark "
                          "block valid." );
                return CE_Failure;
            }
            snprintf( szVarName, sizeof(szVarName), "blockinfo[%d].logvalid", iBlock );
            poDMS->SetStringField( szVarName, "true" );

            panBlockFlag[iBlock] |= BFLG_VALID;
        }
    }
/* -------------------------------------------------------------------- */
/*      Swap back, since we don't really have permission to change      */
/*      the callers buffer.                                             */
/* -------------------------------------------------------------------- */

#ifdef CPL_MSB
    if( HFAGetDataTypeBits(eDataType) == 16 )
    {
        for( int ii = 0; ii < nBlockXSize*nBlockYSize; ii++ )
            CPL_SWAP16PTR( ((unsigned char *) pData) + ii*2 );
    }
    else if( HFAGetDataTypeBits(eDataType) == 32 )
    {
        for( int ii = 0; ii < nBlockXSize*nBlockYSize; ii++ )
            CPL_SWAP32PTR( ((unsigned char *) pData) + ii*4 );
    }
    else if( eDataType == EPT_f64 )
    {
        for( int ii = 0; ii < nBlockXSize*nBlockYSize; ii++ )
            CPL_SWAP64PTR( ((unsigned char *) pData) + ii*8 );
    }
    else if( eDataType == EPT_c64 )
    {
        for( int ii = 0; ii < nBlockXSize*nBlockYSize*2; ii++ )
            CPL_SWAP32PTR( ((unsigned char *) pData) + ii*4 );
    }
    else if( eDataType == EPT_c128 )
    {
        for( int ii = 0; ii < nBlockXSize*nBlockYSize*2; ii++ )
            CPL_SWAP64PTR( ((unsigned char *) pData) + ii*8 );
    }
#endif /* def CPL_MSB */

    return( CE_None );
}

/************************************************************************/
/*                         GetBandName()                                */
/*                                                                      */
/*      Return the Layer Name                                           */
/************************************************************************/

const char * HFABand::GetBandName()
{
    if( strlen(poNode->GetName()) > 0 )
        return poNode->GetName();

    for( int iBand = 0; iBand < psInfo->nBands; iBand++ )
    {
        if( psInfo->papoBand[iBand] == this )
        {
            osOverName.Printf( "Layer_%d", iBand+1 );
            return osOverName;
        }
    }

    osOverName.Printf( "Layer_%x", poNode->GetFilePos() );
    return osOverName;
}

/************************************************************************/
/*                         SetBandName()                                */
/*                                                                      */
/*      Set the Layer Name                                              */
/************************************************************************/

void HFABand::SetBandName(const char *pszName)
{
    if( psInfo->eAccess == HFA_Update )
    {
        poNode->SetName(pszName);
    }
}

/************************************************************************/
/*                         SetNoDataValue()                             */
/*                                                                      */
/*      Set the band no-data value                                      */
/************************************************************************/

CPLErr HFABand::SetNoDataValue( double dfValue )
{
    if ( psInfo->eAccess != HFA_Update )
        return CE_Failure;

    HFAEntry *poNDNode = poNode->GetNamedChild( "Eimg_NonInitializedValue" );

    if ( poNDNode == NULL )
    {
        poNDNode = HFAEntry::New( psInfo,
                                 "Eimg_NonInitializedValue",
                                 "Eimg_NonInitializedValue",
                                 poNode );
    }

    poNDNode->MakeData( 8 + 12 + 8 );
    poNDNode->SetPosition();

    poNDNode->SetIntField( "valueBD[-3]", EPT_f64 );
    poNDNode->SetIntField( "valueBD[-2]", 1 );
    poNDNode->SetIntField( "valueBD[-1]", 1 );

    if ( poNDNode->SetDoubleField( "valueBD[0]", dfValue) == CE_Failure )
        return CE_Failure;

    bNoDataSet = TRUE;
    dfNoData = dfValue;
    return CE_None;
}

/************************************************************************/
/*                        HFAReadBFUniqueBins()                         */
/*                                                                      */
/*      Attempt to read the bins used for a PCT or RAT from a           */
/*      BinFunction node.  On failure just return NULL.                 */
/************************************************************************/

double *HFAReadBFUniqueBins( HFAEntry *poBinFunc, int nPCTColors )

{
/* -------------------------------------------------------------------- */
/*      First confirm this is a "BFUnique" bin function.  We don't      */
/*      know what to do with any other types.                           */
/* -------------------------------------------------------------------- */
    const char *pszBinFunctionType =
        poBinFunc->GetStringField( "binFunction.type.string" );

    if( pszBinFunctionType == NULL
        || !EQUAL(pszBinFunctionType,"BFUnique") )
        return NULL;

/* -------------------------------------------------------------------- */
/*      Process dictionary.                                             */
/* -------------------------------------------------------------------- */
    const char *pszDict =
        poBinFunc->GetStringField( "binFunction.MIFDictionary.string" );
    if( pszDict == NULL )
        pszDict = poBinFunc->GetStringField( "binFunction.MIFDictionary" );
    if( pszDict == NULL )
        return NULL;

    HFADictionary oMiniDict( pszDict );

    HFAType *poBFUnique = oMiniDict.FindType( "BFUnique" );
    if( poBFUnique == NULL )
        return NULL;

/* -------------------------------------------------------------------- */
/*      Field the MIFObject raw data pointer.                           */
/* -------------------------------------------------------------------- */
    const GByte *pabyMIFObject = (const GByte *)
        poBinFunc->GetStringField("binFunction.MIFObject");

    if( pabyMIFObject == NULL )
        return NULL;

/* -------------------------------------------------------------------- */
/*      Confirm that this is a 64bit floating point basearray.          */
/* -------------------------------------------------------------------- */
    if( pabyMIFObject[20] != 0x0a || pabyMIFObject[21] != 0x00 )
    {
        CPLDebug( "HFA", "HFAReadPCTBins(): The basedata does not appear to be EGDA_TYPE_F64." );
        return NULL;
    }

/* -------------------------------------------------------------------- */
/*      Decode bins.                                                    */
/* -------------------------------------------------------------------- */
    double *padfBins = (double *) CPLCalloc(sizeof(double),nPCTColors);

    memcpy( padfBins, pabyMIFObject + 24, sizeof(double) * nPCTColors );

    for( int i = 0; i < nPCTColors; i++ )
    {
        HFAStandard( 8, padfBins + i );
//        CPLDebug( "HFA", "Bin[%d] = %g", i, padfBins[i] );
    }

    return padfBins;
}

/************************************************************************/
/*                               GetPCT()                               */
/*                                                                      */
/*      Return PCT information, if any exists.                          */
/************************************************************************/

CPLErr HFABand::GetPCT( int * pnColors,
                        double **ppadfRed,
                        double **ppadfGreen,
                        double **ppadfBlue,
                        double **ppadfAlpha,
                        double **ppadfBins )

{
    *pnColors = 0;
    *ppadfRed = NULL;
    *ppadfGreen = NULL;
    *ppadfBlue = NULL;
    *ppadfAlpha = NULL;
    *ppadfBins = NULL;

/* -------------------------------------------------------------------- */
/*      If we haven't already tried to load the colors, do so now.      */
/* -------------------------------------------------------------------- */
    if( nPCTColors == -1 )
    {

        nPCTColors = 0;

        HFAEntry *poColumnEntry = poNode->GetNamedChild("Descriptor_Table.Red");
        if( poColumnEntry == NULL )
            return( CE_Failure );

        /* FIXME? : we could also check that nPCTColors is not too big */
        nPCTColors = poColumnEntry->GetIntField( "numRows" );
        for( int iColumn = 0; iColumn < 4; iColumn++ )
        {
            apadfPCT[iColumn] = (double *)VSI_MALLOC2_VERBOSE(sizeof(double),nPCTColors);
            if (apadfPCT[iColumn] == NULL)
            {
                return CE_Failure;
            }

            if( iColumn == 0 )
                poColumnEntry = poNode->GetNamedChild("Descriptor_Table.Red");
            else if( iColumn == 1 )
                poColumnEntry= poNode->GetNamedChild("Descriptor_Table.Green");
            else if( iColumn == 2 )
                poColumnEntry = poNode->GetNamedChild("Descriptor_Table.Blue");
            else if( iColumn == 3 ) {
                poColumnEntry = poNode->GetNamedChild("Descriptor_Table.Opacity");
	    }

            if( poColumnEntry == NULL )
            {
                double  *pdCol = apadfPCT[iColumn];
                for( int i = 0; i < nPCTColors; i++ )
                    pdCol[i] = 1.0;
            }
            else
            {
                if (VSIFSeekL( psInfo->fp, poColumnEntry->GetIntField("columnDataPtr"),
                               SEEK_SET ) < 0)
                {
                    CPLError( CE_Failure, CPLE_FileIO,
                              "VSIFSeekL() failed in HFABand::GetPCT()." );
                    return CE_Failure;
                }
                if (VSIFReadL( apadfPCT[iColumn], sizeof(double), nPCTColors,
                               psInfo->fp) != (size_t)nPCTColors)
                {
                    CPLError( CE_Failure, CPLE_FileIO,
                              "VSIFReadL() failed in HFABand::GetPCT()." );
                    return CE_Failure;
                }

                for( int i = 0; i < nPCTColors; i++ )
                    HFAStandard( 8, apadfPCT[iColumn] + i );
            }
        }

/* -------------------------------------------------------------------- */
/*      Do we have a custom binning function? If so, try reading it.    */
/* -------------------------------------------------------------------- */
        HFAEntry *poBinFunc =
            poNode->GetNamedChild("Descriptor_Table.#Bin_Function840#");

        if( poBinFunc != NULL )
        {
            padfPCTBins = HFAReadBFUniqueBins( poBinFunc, nPCTColors );
        }
    }

/* -------------------------------------------------------------------- */
/*      Return the values.                                              */
/* -------------------------------------------------------------------- */
    if( nPCTColors == 0 )
        return( CE_Failure );

    *pnColors = nPCTColors;
    *ppadfRed = apadfPCT[0];
    *ppadfGreen = apadfPCT[1];
    *ppadfBlue = apadfPCT[2];
    *ppadfAlpha = apadfPCT[3];
    *ppadfBins = padfPCTBins;

    return( CE_None );
}

/************************************************************************/
/*                               SetPCT()                               */
/*                                                                      */
/*      Set the PCT information for this band.                          */
/************************************************************************/

CPLErr HFABand::SetPCT( int nColors,
                        double *padfRed,
                        double *padfGreen,
                        double *padfBlue ,
			double *padfAlpha)

{
    static const char * const apszColNames[4] = {"Red", "Green", "Blue", "Opacity"};
    HFAEntry	*poEdsc_Table;

/* -------------------------------------------------------------------- */
/*      Do we need to try and clear any existing color table?           */
/* -------------------------------------------------------------------- */
    if( nColors == 0 )
    {
        poEdsc_Table = poNode->GetNamedChild( "Descriptor_Table" );
        if( poEdsc_Table == NULL )
            return CE_None;

        for( int iColumn = 0; iColumn < 4; iColumn++ )
        {
          HFAEntry *poEdsc_Column
              = poEdsc_Table->GetNamedChild(apszColNames[iColumn]);
            if( poEdsc_Column )
                poEdsc_Column->RemoveAndDestroy();
        }

        return CE_None;
    }

/* -------------------------------------------------------------------- */
/*      Create the Descriptor table.                                    */
/* -------------------------------------------------------------------- */
    poEdsc_Table = poNode->GetNamedChild( "Descriptor_Table" );
    if( poEdsc_Table == NULL
        || !EQUAL(poEdsc_Table->GetType(),"Edsc_Table") )
        poEdsc_Table = HFAEntry::New( psInfo, "Descriptor_Table",
                                     "Edsc_Table", poNode );

    poEdsc_Table->SetIntField( "numrows", nColors );

/* -------------------------------------------------------------------- */
/*      Create the Binning function node.  I am not sure that we        */
/*      really need this though.                                        */
/* -------------------------------------------------------------------- */
    HFAEntry *poEdsc_BinFunction
        = poEdsc_Table->GetNamedChild( "#Bin_Function#" );
    if( poEdsc_BinFunction == NULL
        || !EQUAL(poEdsc_BinFunction->GetType(),"Edsc_BinFunction") )
        poEdsc_BinFunction = HFAEntry::New( psInfo, "#Bin_Function#",
                                           "Edsc_BinFunction",
                                           poEdsc_Table );

    // Because of the BaseData we have to hardcode the size.
    poEdsc_BinFunction->MakeData( 30 );

    poEdsc_BinFunction->SetIntField( "numBins", nColors );
    poEdsc_BinFunction->SetStringField( "binFunction", "direct" );
    poEdsc_BinFunction->SetDoubleField( "minLimit", 0.0 );
    poEdsc_BinFunction->SetDoubleField( "maxLimit", nColors - 1.0 );

/* -------------------------------------------------------------------- */
/*      Process each color component                                    */
/* -------------------------------------------------------------------- */
    for( int iColumn = 0; iColumn < 4; iColumn++ )
    {
        double	    *padfValues=NULL;
        const char      *pszName = apszColNames[iColumn];

        if( iColumn == 0 )
            padfValues = padfRed;
        else if( iColumn == 1 )
            padfValues = padfGreen;
        else if( iColumn == 2 )
            padfValues = padfBlue;
        else if( iColumn == 3 )
            padfValues = padfAlpha;

/* -------------------------------------------------------------------- */
/*      Create the Edsc_Column.                                         */
/* -------------------------------------------------------------------- */
        HFAEntry *poEdsc_Column = poEdsc_Table->GetNamedChild( pszName );
        if( poEdsc_Column == NULL
            || !EQUAL(poEdsc_Column->GetType(),"Edsc_Column") )
            poEdsc_Column = HFAEntry::New( psInfo, pszName, "Edsc_Column",
                                          poEdsc_Table );

        poEdsc_Column->SetIntField( "numRows", nColors );
        poEdsc_Column->SetStringField( "dataType", "real" );
        poEdsc_Column->SetIntField( "maxNumChars", 0 );

/* -------------------------------------------------------------------- */
/*      Write the data out.                                             */
/* -------------------------------------------------------------------- */
        int nOffset = HFAAllocateSpace( psInfo, 8*nColors);

        poEdsc_Column->SetIntField( "columnDataPtr", nOffset );

        double *padfFileData = (double *) CPLMalloc(nColors*sizeof(double));
        for( int iColor = 0; iColor < nColors; iColor++ )
        {
            padfFileData[iColor] = padfValues[iColor];
            HFAStandard( 8, padfFileData + iColor );
        }
        bool bRet = VSIFSeekL( psInfo->fp, nOffset, SEEK_SET ) >= 0;
        bRet &= VSIFWriteL( padfFileData, 8, nColors, psInfo->fp ) == (size_t)nColors;
        CPLFree( padfFileData );
        if( !bRet )
            return CE_Failure;
    }

/* -------------------------------------------------------------------- */
/*      Update the layer type to be thematic.                           */
/* -------------------------------------------------------------------- */
    poNode->SetStringField( "layerType", "thematic" );

    return( CE_None );
}

/************************************************************************/
/*                           CreateOverview()                           */
/************************************************************************/

int HFABand::CreateOverview( int nOverviewLevel, const char *pszResampling )

{
    const int nOXSize = (psInfo->nXSize + nOverviewLevel - 1) / nOverviewLevel;
    const int nOYSize = (psInfo->nYSize + nOverviewLevel - 1) / nOverviewLevel;

/* -------------------------------------------------------------------- */
/*      Do we want to use a dependent file (.rrd) for the overviews?    */
/*      Or just create them directly in this file?                      */
/* -------------------------------------------------------------------- */
    HFAInfo_t *psRRDInfo = psInfo;
    HFAEntry *poParent = poNode;

    if( CPLTestBool( CPLGetConfigOption( "HFA_USE_RRD", "NO" ) ) )
    {
        psRRDInfo = HFACreateDependent( psInfo );
        if( psRRDInfo == NULL )
            return -1;

        poParent = psRRDInfo->poRoot->GetNamedChild( GetBandName() );

        // Need to create layer object.
        if( poParent == NULL )
        {
            poParent =
                HFAEntry::New( psRRDInfo, GetBandName(),
                              "Eimg_Layer", psRRDInfo->poRoot );
        }
    }

/* -------------------------------------------------------------------- */
/*      What pixel type should we use for the overview.  Usually        */
/*      this is the same as the base layer, but when                    */
/*      AVERAGE_BIT2GRAYSCALE is in effect we force it to u8 from u1.   */
/* -------------------------------------------------------------------- */
    EPTType eOverviewDataType = eDataType;

    if( STARTS_WITH_CI(pszResampling, "AVERAGE_BIT2GR") )
        eOverviewDataType = EPT_u8;

/* -------------------------------------------------------------------- */
/*      Eventually we need to decide on the whether to use the spill    */
/*      file, primarily on the basis of whether the new overview        */
/*      will drive our .img file size near 4GB.  For now, just base     */
/*      it on the config options.                                       */
/* -------------------------------------------------------------------- */
    int bCreateLargeRaster = CPLTestBool(
        CPLGetConfigOption("USE_SPILL","NO") );
    GIntBig nValidFlagsOffset = 0, nDataOffset = 0;

    if( (psRRDInfo->nEndOfFile
         + (nOXSize * (double) nOYSize)
         * (HFAGetDataTypeBits(eOverviewDataType) / 8)) > 2000000000.0 )
        bCreateLargeRaster = TRUE;

    if( bCreateLargeRaster )
    {
        if( !HFACreateSpillStack( psRRDInfo, nOXSize, nOYSize, 1,
                                  64, eOverviewDataType,
                                  &nValidFlagsOffset, &nDataOffset ) )
	{
	    return -1;
	}
    }

/* -------------------------------------------------------------------- */
/*      Are we compressed? If so, overview should be too (unless        */
/*      HFA_COMPRESS_OVR is defined).                                   */
/*      Check RasterDMS like HFAGetBandInfo                             */
/* -------------------------------------------------------------------- */
    int bCompressionType = FALSE;
    const char* pszCompressOvr = CPLGetConfigOption("HFA_COMPRESS_OVR", NULL);
    if( pszCompressOvr != NULL )
        bCompressionType = CPLTestBool(pszCompressOvr);
    else
    {
        HFAEntry *poDMS = poNode->GetNamedChild( "RasterDMS" );

        if( poDMS != NULL )
            bCompressionType = poDMS->GetIntField( "compressionType" ) != 0;
    }

/* -------------------------------------------------------------------- */
/*      Create the layer.                                               */
/* -------------------------------------------------------------------- */
    CPLString osLayerName;
    osLayerName.Printf( "_ss_%d_", nOverviewLevel );

    if( !HFACreateLayer( psRRDInfo, poParent, osLayerName,
                         TRUE, 64, bCompressionType, bCreateLargeRaster, FALSE,
                         nOXSize, nOYSize, eOverviewDataType, NULL,
                         nValidFlagsOffset, nDataOffset, 1, 0 ) )
        return -1;

    HFAEntry *poOverLayer = poParent->GetNamedChild( osLayerName );
    if( poOverLayer == NULL )
        return -1;

/* -------------------------------------------------------------------- */
/*      Create RRDNamesList list if it does not yet exist.              */
/* -------------------------------------------------------------------- */
    HFAEntry *poRRDNamesList = poNode->GetNamedChild("RRDNamesList");
    if( poRRDNamesList == NULL )
    {
        poRRDNamesList = HFAEntry::New( psInfo, "RRDNamesList",
                                       "Eimg_RRDNamesList",
                                       poNode );
        poRRDNamesList->MakeData( 23+16+8+ 3000 /* hack for growth room*/ );

        /* we need to hardcode file offset into the data, so locate it now */
        poRRDNamesList->SetPosition();

        poRRDNamesList->SetStringField( "algorithm.string",
                                        "IMAGINE 2X2 Resampling" );
    }

/* -------------------------------------------------------------------- */
/*      Add new overview layer to RRDNamesList.                         */
/* -------------------------------------------------------------------- */
    int iNextName = poRRDNamesList->GetFieldCount( "nameList" );
    char szName[50];
    CPLString osNodeName;

    snprintf( szName, sizeof(szName), "nameList[%d].string", iNextName );

    osLayerName.Printf( "%s(:%s:_ss_%d_)",
                        psRRDInfo->pszFilename, GetBandName(),
                        nOverviewLevel );

    // TODO: Need to add to end of array (that is pretty hard).
    if( poRRDNamesList->SetStringField( szName, osLayerName ) != CE_None )
    {
        poRRDNamesList->MakeData( poRRDNamesList->GetDataSize() + 3000 );
        if( poRRDNamesList->SetStringField( szName, osLayerName ) != CE_None )
            return -1;
    }

/* -------------------------------------------------------------------- */
/*      Add to the list of overviews for this band.                     */
/* -------------------------------------------------------------------- */
    papoOverviews = (HFABand **)
        CPLRealloc(papoOverviews, sizeof(void*) * ++nOverviews );
    papoOverviews[nOverviews-1] = new HFABand( psRRDInfo, poOverLayer );

/* -------------------------------------------------------------------- */
/*      If there is a nodata value, copy it to the overview band.       */
/* -------------------------------------------------------------------- */
    if( bNoDataSet )
        papoOverviews[nOverviews-1]->SetNoDataValue( dfNoData );

    return nOverviews-1;
}
